Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a5f7a7ecee | |||
| dede3216fb | |||
| 5ee89b31b1 | |||
| 2d354ace55 |
14
changelog.md
14
changelog.md
@@ -1,5 +1,19 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-04-05 - 3.61.1 - fix(dees-input-list)
|
||||
align list input with dees-tile styling and improve item affordances
|
||||
|
||||
- wrap the list in dees-tile with a dynamic item count heading and move the add-item controls into the tile footer
|
||||
- replace custom container styling with shared tile and theme tokens for hover, focus, row, and disabled states
|
||||
- show a bullet icon for non-sortable or disabled items when no candidate state icon is present
|
||||
|
||||
## 2026-04-05 - 3.61.0 - feat(dees-input-list)
|
||||
allow freeform entries alongside candidate suggestions in dees-input-list
|
||||
|
||||
- adds an allowFreeform option so Enter can add values that do not exactly match the candidate list
|
||||
- shows check and question-mark indicators to distinguish known candidates from custom freeform items
|
||||
- updates the component demo with a freeform-plus-candidates example
|
||||
|
||||
## 2026-04-05 - 3.60.0 - feat(dees-input-list)
|
||||
add candidate autocomplete with tab completion and payload retrieval
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@design.estate/dees-catalog",
|
||||
"version": "3.60.0",
|
||||
"version": "3.61.1",
|
||||
"private": false,
|
||||
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
|
||||
"main": "dist_ts_web/index.js",
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-catalog',
|
||||
version: '3.60.0',
|
||||
version: '3.61.1',
|
||||
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
||||
}
|
||||
|
||||
@@ -322,7 +322,25 @@ export const demoFunc = () => html`
|
||||
></dees-input-list>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'11. Empty State'} .subtitle=${'How the component looks with no items'}>
|
||||
<dees-panel .title=${'11. Freeform + Candidates'} .subtitle=${'Allow adding items not in the candidate list (shown with a question mark)'}>
|
||||
<dees-input-list
|
||||
.label=${'Tags'}
|
||||
.placeholder=${'Type a tag... (freeform allowed)'}
|
||||
.allowFreeform=${true}
|
||||
.candidates=${[
|
||||
{ viewKey: 'bug', payload: { color: 'red' } },
|
||||
{ viewKey: 'feature', payload: { color: 'blue' } },
|
||||
{ viewKey: 'docs', payload: { color: 'green' } },
|
||||
{ viewKey: 'refactor', payload: { color: 'purple' } },
|
||||
{ viewKey: 'performance', payload: { color: 'orange' } },
|
||||
{ viewKey: 'security', payload: { color: 'red' } },
|
||||
]}
|
||||
.value=${['bug', 'my-custom-tag', 'feature']}
|
||||
.description=${'Known tags get a checkmark, custom tags get a question mark. Tab to complete, Enter to add freeform.'}
|
||||
></dees-input-list>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'12. Empty State'} .subtitle=${'How the component looks with no items'}>
|
||||
<dees-input-list
|
||||
.label=${'Your Ideas'}
|
||||
.placeholder=${'Share your ideas...'}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from '@design.estate/dees-element';
|
||||
import { DeesInputBase } from '../dees-input-base/dees-input-base.js';
|
||||
import '../../00group-utility/dees-icon/dees-icon.js';
|
||||
import '../../00group-layout/dees-tile/dees-tile.js';
|
||||
import { demoFunc } from './dees-input-list.demo.js';
|
||||
import { themeDefaultStyles } from '../../00theme.js';
|
||||
|
||||
@@ -54,6 +55,9 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
|
||||
@property({ type: Array })
|
||||
accessor candidates: IListCandidate[] = [];
|
||||
|
||||
@property({ type: Boolean })
|
||||
accessor allowFreeform: boolean = false;
|
||||
|
||||
@property({ type: String })
|
||||
accessor validationText: string = '';
|
||||
|
||||
@@ -116,26 +120,19 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.list-container {
|
||||
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 3.9%)')};
|
||||
border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
transition: all 0.15s ease;
|
||||
dees-tile:hover::part(outer) {
|
||||
border-color: var(--dees-color-border-strong);
|
||||
}
|
||||
|
||||
.list-container:hover:not(.disabled) {
|
||||
border-color: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')};
|
||||
}
|
||||
|
||||
.list-container:focus-within {
|
||||
dees-tile:focus-within::part(outer) {
|
||||
border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 51.2%)', 'hsl(217.2 91.2% 59.8%)')};
|
||||
box-shadow: 0 0 0 3px ${cssManager.bdTheme('hsl(222.2 47.4% 51.2% / 0.1)', 'hsl(217.2 91.2% 59.8% / 0.1)')};
|
||||
}
|
||||
|
||||
.list-container.disabled {
|
||||
:host([disabled]) dees-tile {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.list-items {
|
||||
@@ -148,8 +145,8 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 10px;
|
||||
border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
|
||||
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 3.9%)')};
|
||||
border-bottom: 1px solid var(--dees-color-border-subtle);
|
||||
background: transparent;
|
||||
transition: transform 0.2s ease, background 0.15s ease, box-shadow 0.15s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@@ -160,7 +157,7 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
|
||||
}
|
||||
|
||||
.list-items:not(.is-dragging) .list-item:hover:not(.disabled) {
|
||||
background: ${cssManager.bdTheme('hsl(0 0% 97.5%)', 'hsl(0 0% 6.9%)')};
|
||||
background: var(--dees-color-row-hover);
|
||||
}
|
||||
|
||||
/* Dragging item - follows cursor */
|
||||
@@ -184,6 +181,28 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
|
||||
}
|
||||
|
||||
|
||||
.candidate-check {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: ${cssManager.bdTheme('hsl(142.1 76.2% 36.3%)', 'hsl(142.1 70.6% 45.3%)')};
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.candidate-unknown {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: ${cssManager.bdTheme('hsl(45 93% 47%)', 'hsl(45 93% 58%)')};
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.item-bullet {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: var(--dees-color-text-muted);
|
||||
flex-shrink: 0;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -286,8 +305,6 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
padding: 6px 10px;
|
||||
background: ${cssManager.bdTheme('hsl(0 0% 97.5%)', 'hsl(0 0% 6.9%)')};
|
||||
border-top: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
|
||||
}
|
||||
|
||||
.add-input {
|
||||
@@ -425,7 +442,7 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
|
||||
<div class="input-wrapper">
|
||||
${this.label ? html`<dees-label .label=${this.label} .required=${this.required}></dees-label>` : ''}
|
||||
|
||||
<div class="list-container ${this.disabled ? 'disabled' : ''}">
|
||||
<dees-tile .heading="${this.value.length} item${this.value.length !== 1 ? 's' : ''}">
|
||||
<div class="list-items">
|
||||
${this.value.length > 0 ? this.value.map((item, index) => html`
|
||||
<div
|
||||
@@ -441,7 +458,17 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
|
||||
<dees-icon .icon=${'lucide:gripVertical'}></dees-icon>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
|
||||
${this.candidates.length > 0 ? html`
|
||||
${this.candidates.some(c => c.viewKey === item) ? html`
|
||||
<dees-icon class="candidate-check" .icon=${'lucide:check'}></dees-icon>
|
||||
` : html`
|
||||
<dees-icon class="candidate-unknown" .icon=${'lucide:helpCircle'}></dees-icon>
|
||||
`}
|
||||
` : !this.sortable || this.disabled ? html`
|
||||
<dees-icon class="item-bullet" .icon=${'lucide:dot'}></dees-icon>
|
||||
` : ''}
|
||||
|
||||
<div class="item-content">
|
||||
${this.editingIndex === index ? html`
|
||||
<input
|
||||
@@ -487,7 +514,7 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
|
||||
</div>
|
||||
|
||||
${!this.disabled && (!this.maxItems || this.value.length < this.maxItems) ? html`
|
||||
<div class="add-item-container">
|
||||
<div slot="footer" class="add-item-container">
|
||||
<div class="autocomplete-wrapper">
|
||||
${this.ghostText ? html`
|
||||
<span class="ghost-text">
|
||||
@@ -513,7 +540,7 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
|
||||
</button>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
</dees-tile>
|
||||
|
||||
${this.validationText ? html`
|
||||
<div class="validation-message">${this.validationText}</div>
|
||||
@@ -589,12 +616,18 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
|
||||
if (e.key === 'Enter' && this.inputValue.trim()) {
|
||||
e.preventDefault();
|
||||
if (this.candidates.length > 0) {
|
||||
// In candidate mode, only allow exact matches
|
||||
// Try exact candidate match first
|
||||
const match = this.candidates.find(
|
||||
c => c.viewKey.toLowerCase() === this.inputValue.trim().toLowerCase()
|
||||
);
|
||||
if (match) {
|
||||
this.selectCandidate(match);
|
||||
} else if (this.allowFreeform) {
|
||||
// Allow freeform entry (won't have a candidate checkmark)
|
||||
this.ghostText = '';
|
||||
this.currentCandidateIndex = -1;
|
||||
this.matchingCandidates = [];
|
||||
this.addItem();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user