@adia-ai/web-components 0.7.4 → 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/components/action-list/action-list.a2ui.json +2 -2
- package/components/action-list/action-list.yaml +2 -2
- package/components/badge/badge.yaml +1 -0
- package/components/block/block.class.js +20 -4
- package/components/block/block.css +18 -13
- package/components/button/button.a2ui.json +1 -2
- package/components/button/button.css +44 -10
- package/components/button/button.yaml +1 -0
- package/components/card/card.yaml +1 -0
- package/components/chart/chart.a2ui.json +1 -2
- package/components/chart/chart.d.ts +1 -1
- package/components/chart/chart.yaml +1 -0
- package/components/check/check.a2ui.json +0 -3
- package/components/check/check.yaml +0 -2
- package/components/combobox/combobox.a2ui.json +1 -2
- package/components/combobox/combobox.yaml +1 -0
- package/components/command/command.a2ui.json +0 -3
- package/components/command/command.class.js +19 -1
- package/components/command/command.yaml +0 -2
- package/components/context-menu/context-menu.class.js +1 -0
- package/components/description-list/description-list.a2ui.json +1 -2
- package/components/description-list/description-list.d.ts +1 -1
- package/components/description-list/description-list.yaml +1 -0
- package/components/field/field.class.js +1 -0
- package/components/fields/fields.class.js +1 -0
- package/components/grid/grid.yaml +2 -0
- package/components/input/input.a2ui.json +2 -14
- package/components/input/input.yaml +2 -0
- package/components/integration-card/integration-card.class.js +1 -0
- package/components/list/list.class.js +1 -0
- package/components/list/list.yaml +1 -0
- package/components/menu/menu.class.js +1 -0
- package/components/menu/menu.css +14 -2
- package/components/pipeline-status/pipeline-status.yaml +1 -0
- package/components/radio/radio.a2ui.json +0 -3
- package/components/radio/radio.yaml +0 -2
- package/components/rating/rating.yaml +1 -0
- package/components/search/search.a2ui.json +1 -8
- package/components/search/search.yaml +1 -5
- package/components/select/select.a2ui.json +1 -2
- package/components/select/select.class.js +46 -1
- package/components/select/select.test.js +33 -0
- package/components/select/select.yaml +2 -0
- package/components/slider/slider.a2ui.json +0 -3
- package/components/slider/slider.yaml +0 -2
- package/components/swatch/swatch.class.js +1 -0
- package/components/table/table.a2ui.json +2 -4
- package/components/table/table.d.ts +2 -2
- package/components/table/table.yaml +2 -0
- package/components/tabs/tabs.yaml +1 -0
- package/components/tag/tag.yaml +1 -0
- package/components/toggle-group/toggle-group.yaml +1 -0
- package/components/toolbar/toolbar.class.js +1 -0
- package/components/tree/tree.a2ui.json +2 -2
- package/components/tree/tree.yaml +2 -2
- package/dist/host.min.css +1 -1
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +28 -28
- package/index.css +11 -17
- package/package.json +1 -1
- package/styles/api/sizing.css +18 -6
- package/styles/api/text.css +9 -2
- package/styles/foundation/space.css +33 -0
- package/styles/host.css +20 -22
- package/styles/index.css +14 -17
- package/styles/verse.css +14 -0
|
@@ -15,6 +15,7 @@ props:
|
|
|
15
15
|
description: 'Component property: complete.'
|
|
16
16
|
type: boolean
|
|
17
17
|
default: false
|
|
18
|
+
reflect: false # declarative presentational attribute — CSS/author-time read, non-reactive by design
|
|
18
19
|
message:
|
|
19
20
|
description: 'Component property: message.'
|
|
20
21
|
type: string
|
|
@@ -108,9 +108,6 @@
|
|
|
108
108
|
"dot": {
|
|
109
109
|
"description": "Radio dot indicator, sized via --dot-size"
|
|
110
110
|
},
|
|
111
|
-
"hint": {
|
|
112
|
-
"description": "Hint text container, rendered via CSS attr(hint) on ::after"
|
|
113
|
-
},
|
|
114
111
|
"label": {
|
|
115
112
|
"description": "Label text container, rendered via CSS attr(label) on ::after"
|
|
116
113
|
}
|
|
@@ -59,8 +59,6 @@ events:
|
|
|
59
59
|
slots:
|
|
60
60
|
dot:
|
|
61
61
|
description: Radio dot indicator, sized via --dot-size
|
|
62
|
-
hint:
|
|
63
|
-
description: Hint text container, rendered via CSS attr(hint) on ::after
|
|
64
62
|
label:
|
|
65
63
|
description: Label text container, rendered via CSS attr(label) on ::after
|
|
66
64
|
states:
|
|
@@ -99,14 +99,7 @@
|
|
|
99
99
|
"Command",
|
|
100
100
|
"Select"
|
|
101
101
|
],
|
|
102
|
-
"slots": {
|
|
103
|
-
"clear": {
|
|
104
|
-
"description": "Clear button (shown when value is non-empty)"
|
|
105
|
-
},
|
|
106
|
-
"input": {
|
|
107
|
-
"description": "Native search input element"
|
|
108
|
-
}
|
|
109
|
-
},
|
|
102
|
+
"slots": {},
|
|
110
103
|
"states": [
|
|
111
104
|
{
|
|
112
105
|
"description": "Default, ready for interaction.",
|
|
@@ -48,11 +48,7 @@ events:
|
|
|
48
48
|
description: Current search-input value.
|
|
49
49
|
search:
|
|
50
50
|
description: Debounced CustomEvent with detail.query containing the search string
|
|
51
|
-
slots:
|
|
52
|
-
clear:
|
|
53
|
-
description: Clear button (shown when value is non-empty)
|
|
54
|
-
input:
|
|
55
|
-
description: Native search input element
|
|
51
|
+
slots: {}
|
|
56
52
|
states:
|
|
57
53
|
- name: idle
|
|
58
54
|
description: Default, ready for interaction.
|
|
@@ -108,8 +108,7 @@
|
|
|
108
108
|
},
|
|
109
109
|
"options": {
|
|
110
110
|
"description": "Option list. Array of {value, label, disabled?, icon?, avatar?} or grouped {label, options: [...]}. Alternative to declarative <option> / <optgroup> children. Per-option icon/avatar render in the list AND reflect in the trigger's selected state.",
|
|
111
|
-
"
|
|
112
|
-
"default": []
|
|
111
|
+
"$ref": "common_types.json#/$defs/DynamicStringList"
|
|
113
112
|
},
|
|
114
113
|
"pattern": {
|
|
115
114
|
"description": "Regex pattern for validation",
|
|
@@ -77,6 +77,9 @@ export class UISelect extends UIFormElement {
|
|
|
77
77
|
#query = '';
|
|
78
78
|
#searchInput = null;
|
|
79
79
|
#rafId = null;
|
|
80
|
+
// FB-98: re-parse interpolated <option> children (.map()/repeat()) that arrive
|
|
81
|
+
// wrapper-nested and/or after connected(); torn down in disconnected().
|
|
82
|
+
#optionObserver = null;
|
|
80
83
|
|
|
81
84
|
#onSearchFocus = () => { if (!this.disabled) this.open = true; };
|
|
82
85
|
#onSearchClick = (e) => { e.stopPropagation(); if (!this.disabled) this.open = true; };
|
|
@@ -371,6 +374,27 @@ export class UISelect extends UIFormElement {
|
|
|
371
374
|
if (this.#options.length === 0) {
|
|
372
375
|
this.#parseOptions();
|
|
373
376
|
}
|
|
377
|
+
// FB-98: interpolated <option>/<optgroup> (.map()/repeat()) are nested in the
|
|
378
|
+
// engine's display:contents wrapper spans and often applied in the template's
|
|
379
|
+
// update() pass AFTER connected() — so the parse above can see nothing ("No
|
|
380
|
+
// options"). Observe for them and re-parse once they appear. Guards: skip if
|
|
381
|
+
// options are already parsed/set, and skip unless there's a real
|
|
382
|
+
// <option>/<optgroup> to parse — so the component's own listbox/empty-state
|
|
383
|
+
// renders (and #parseOptions's own option removals) don't re-enter or loop.
|
|
384
|
+
if (!this.#optionObserver) {
|
|
385
|
+
this.#optionObserver = new MutationObserver(() => {
|
|
386
|
+
if (this.#options.length) return;
|
|
387
|
+
const kids = this.#logicalOptionChildren();
|
|
388
|
+
if (!kids.some((c) => c.tagName === 'OPTION' || c.tagName === 'OPTGROUP')) return;
|
|
389
|
+
this.#parseOptions();
|
|
390
|
+
if (this.#options.length) {
|
|
391
|
+
this.#renderOptions();
|
|
392
|
+
const display = this.querySelector('[slot="display"]');
|
|
393
|
+
if (display) display.textContent = this.#displayText();
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
this.#optionObserver.observe(this, { childList: true, subtree: true });
|
|
374
398
|
}
|
|
375
399
|
|
|
376
400
|
render() {
|
|
@@ -544,6 +568,25 @@ export class UISelect extends UIFormElement {
|
|
|
544
568
|
}
|
|
545
569
|
|
|
546
570
|
/** Parse <option> and <optgroup> children into internal model. */
|
|
571
|
+
// FB-98: collect declarative <option>/<optgroup> children — direct OR nested
|
|
572
|
+
// inside the template engine's display:contents wrapper spans (which carry
|
|
573
|
+
// role="presentation" since 0.7.2) — so .map()/repeat()-rendered options are
|
|
574
|
+
// seen, not just static literals. Skips component-stamped [slot] children
|
|
575
|
+
// (display/listbox/trigger). Mirrors tree-ui's #logicalSlotChildren (FB-96).
|
|
576
|
+
#logicalOptionChildren() {
|
|
577
|
+
const out = [];
|
|
578
|
+
const walk = (parent) => {
|
|
579
|
+
for (const ch of parent.children) {
|
|
580
|
+
if (ch.hasAttribute('slot')) continue; // component-stamped — skip, don't descend
|
|
581
|
+
if (ch.tagName === 'OPTION' || ch.tagName === 'OPTGROUP') { out.push(ch); continue; }
|
|
582
|
+
if (ch.matches('[role="presentation"]') || ch.style?.display === 'contents') { walk(ch); continue; }
|
|
583
|
+
out.push(ch); // unknown authored child — caller warns
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
walk(this);
|
|
587
|
+
return out;
|
|
588
|
+
}
|
|
589
|
+
|
|
547
590
|
#parseOptions() {
|
|
548
591
|
this.#options = [];
|
|
549
592
|
// §225 (v0.5.9): track unknown-child-tag names so we can warn once per element.
|
|
@@ -552,7 +595,8 @@ export class UISelect extends UIFormElement {
|
|
|
552
595
|
// declarative initial selection is honoured (native <select> parity).
|
|
553
596
|
// §FB-46: collect ALL selected options for [multiple] mode (not just first).
|
|
554
597
|
const preSelectedArr = [];
|
|
555
|
-
|
|
598
|
+
// FB-98: iterate wrapper-pierced logical children so interpolated options work.
|
|
599
|
+
for (const child of this.#logicalOptionChildren()) {
|
|
556
600
|
if (child.tagName === 'OPTGROUP') {
|
|
557
601
|
const group = { label: child.label || child.getAttribute('label') || '', options: [] };
|
|
558
602
|
for (const opt of child.querySelectorAll('option')) {
|
|
@@ -974,6 +1018,7 @@ export class UISelect extends UIFormElement {
|
|
|
974
1018
|
this.removeEventListener('click', this.#onClick);
|
|
975
1019
|
this.removeEventListener('keydown', this.#onKey);
|
|
976
1020
|
this.removeEventListener('remove', this.#onChipRemove);
|
|
1021
|
+
this.#optionObserver?.disconnect(); // FB-98
|
|
977
1022
|
if (this.#rafId != null) {
|
|
978
1023
|
cancelAnimationFrame(this.#rafId);
|
|
979
1024
|
this.#rafId = null;
|
|
@@ -27,6 +27,39 @@ describe('select-ui', () => {
|
|
|
27
27
|
expect(s.value).toBe('b');
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
// §FB-98 — <option> children nested in the template engine's display:contents
|
|
31
|
+
// wrapper spans (.map()/repeat()) must be parsed, not just static literals.
|
|
32
|
+
it('FB-98: parses options nested in display:contents/role=presentation wrappers (present at connect)', async () => {
|
|
33
|
+
const s = mount(`
|
|
34
|
+
<select-ui value="a">
|
|
35
|
+
<span style="display:contents" role="presentation">
|
|
36
|
+
<span style="display:contents" role="presentation"><option value="a">Alpha</option></span>
|
|
37
|
+
<span style="display:contents" role="presentation"><option value="b">Beta</option></span>
|
|
38
|
+
<span style="display:contents" role="presentation"><option value="c">Gamma</option></span>
|
|
39
|
+
</span>
|
|
40
|
+
</select-ui>
|
|
41
|
+
`);
|
|
42
|
+
await tick();
|
|
43
|
+
expect(s.querySelectorAll('[role="option"]').length).toBe(3);
|
|
44
|
+
expect(s.options.map((o) => o.value)).toEqual(['a', 'b', 'c']);
|
|
45
|
+
expect(s.value).toBe('a');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// §FB-98 — interpolated options that ARRIVE after connect (the template's
|
|
49
|
+
// update() pass) are picked up by the re-parse MutationObserver.
|
|
50
|
+
it('FB-98: re-parses wrapped options that arrive after connect (observer)', async () => {
|
|
51
|
+
const s = mount(`<select-ui></select-ui>`);
|
|
52
|
+
await tick();
|
|
53
|
+
expect(s.querySelectorAll('[role="option"]').length).toBe(0);
|
|
54
|
+
const wrap = document.createElement('span');
|
|
55
|
+
wrap.style.display = 'contents';
|
|
56
|
+
wrap.setAttribute('role', 'presentation');
|
|
57
|
+
wrap.innerHTML = `<option value="x">X</option><option value="y">Y</option>`;
|
|
58
|
+
s.appendChild(wrap);
|
|
59
|
+
await tick(); await tick(); // mutation → observer microtask → re-parse + render
|
|
60
|
+
expect(s.querySelectorAll('[role="option"]').length).toBe(2);
|
|
61
|
+
});
|
|
62
|
+
|
|
30
63
|
// §FB-46 — <option selected> must capture ALL values in [multiple] mode
|
|
31
64
|
it('captures all <option selected> values as comma-separated for [multiple]', async () => {
|
|
32
65
|
const s = mount(`
|
|
@@ -35,6 +35,7 @@ props:
|
|
|
35
35
|
type: string
|
|
36
36
|
default: default
|
|
37
37
|
enum: [default, ghost]
|
|
38
|
+
reflect: false # declarative presentational attribute — CSS/author-time read, non-reactive by design
|
|
38
39
|
size:
|
|
39
40
|
description: >-
|
|
40
41
|
Sizing scale via universal `[size]` attribute system
|
|
@@ -154,6 +155,7 @@ props:
|
|
|
154
155
|
description: "Option list. Array of {value, label, disabled?, icon?, avatar?} or grouped {label, options: [...]}. Alternative to declarative <option> / <optgroup> children. Per-option icon/avatar render in the list AND reflect in the trigger's selected state."
|
|
155
156
|
type: array
|
|
156
157
|
default: []
|
|
158
|
+
dynamic: true # JS-set collection prop with a custom setter — deliberately not in static properties
|
|
157
159
|
pattern:
|
|
158
160
|
description: Regex pattern for validation
|
|
159
161
|
type: string
|
|
@@ -139,9 +139,6 @@
|
|
|
139
139
|
"hint": {
|
|
140
140
|
"description": "Override slot for hint markup richer than the plain [hint] attribute string (inline links, code spans). Renders beneath the slider at body-subtle typography. Mutually exclusive with [hint]."
|
|
141
141
|
},
|
|
142
|
-
"input": {
|
|
143
|
-
"description": "Native range input element"
|
|
144
|
-
},
|
|
145
142
|
"label": {
|
|
146
143
|
"description": "Label element above the slider"
|
|
147
144
|
},
|
|
@@ -409,6 +409,7 @@ export class UISwatch extends UIElement {
|
|
|
409
409
|
#absorbChromeSlot() {
|
|
410
410
|
if (!this.#tileEl || !this.#badgeEl) return;
|
|
411
411
|
const chromeChildren = [];
|
|
412
|
+
// wrapper-trap-ok: swatch chrome-slot children are always a single literal element (e.g. a selected-state checkmark); interpolated chrome collections are not a documented pattern
|
|
412
413
|
for (const child of this.children) {
|
|
413
414
|
if (child === this.#tileEl) continue;
|
|
414
415
|
if (child === this.#badgeEl) break; // hit the badge — done scanning the head region
|
|
@@ -15,16 +15,14 @@
|
|
|
15
15
|
"properties": {
|
|
16
16
|
"columns": {
|
|
17
17
|
"description": "Column definitions. Array of {key, label, type?, width?, minWidth?, maxWidth?, flex?, sortable?, resizable?, filterable?, pinned?, hidden?, wrap?, accessor?, format?, render?, sortFn?, filterType?, meta?}. Alternative to declarative <col-def> children.",
|
|
18
|
-
"
|
|
19
|
-
"default": []
|
|
18
|
+
"$ref": "common_types.json#/$defs/DynamicStringList"
|
|
20
19
|
},
|
|
21
20
|
"component": {
|
|
22
21
|
"const": "Table"
|
|
23
22
|
},
|
|
24
23
|
"data": {
|
|
25
24
|
"description": "Row records. Array of plain objects keyed to columns[].key.",
|
|
26
|
-
"
|
|
27
|
-
"default": []
|
|
25
|
+
"$ref": "common_types.json#/$defs/DynamicStringList"
|
|
28
26
|
},
|
|
29
27
|
"density": {
|
|
30
28
|
"description": "Controls cell padding and row height. 'compact' for dense data, 'standard' for default spacing, 'comfortable' for spacious rows.",
|
|
@@ -87,9 +87,9 @@ export type TableSortEvent = CustomEvent<TableSortEventDetail>;
|
|
|
87
87
|
|
|
88
88
|
export class UITable extends UIElement {
|
|
89
89
|
/** Column definitions. Array of {key, label, type?, width?, minWidth?, maxWidth?, flex?, sortable?, resizable?, filterable?, pinned?, hidden?, wrap?, accessor?, format?, render?, sortFn?, filterType?, meta?}. Alternative to declarative <col-def> children. */
|
|
90
|
-
columns:
|
|
90
|
+
columns: string;
|
|
91
91
|
/** Row records. Array of plain objects keyed to columns[].key. */
|
|
92
|
-
data:
|
|
92
|
+
data: string;
|
|
93
93
|
/** Controls cell padding and row height. 'compact' for dense data, 'standard' for default spacing, 'comfortable' for spacious rows. */
|
|
94
94
|
density: 'compact' | 'standard' | 'comfortable';
|
|
95
95
|
/** Enable row expansion */
|
|
@@ -23,10 +23,12 @@ props:
|
|
|
23
23
|
description: Column definitions. Array of {key, label, type?, width?, minWidth?, maxWidth?, flex?, sortable?, resizable?, filterable?, pinned?, hidden?, wrap?, accessor?, format?, render?, sortFn?, filterType?, meta?}. Alternative to declarative <col-def> children.
|
|
24
24
|
type: array
|
|
25
25
|
default: []
|
|
26
|
+
dynamic: true # JS-set collection prop with a custom setter — deliberately not in static properties
|
|
26
27
|
data:
|
|
27
28
|
description: Row records. Array of plain objects keyed to columns[].key.
|
|
28
29
|
type: array
|
|
29
30
|
default: []
|
|
31
|
+
dynamic: true # JS-set collection prop with a custom setter — deliberately not in static properties
|
|
30
32
|
density:
|
|
31
33
|
description: Controls cell padding and row height. 'compact' for dense data, 'standard' for default
|
|
32
34
|
spacing, 'comfortable' for spacious rows.
|
|
@@ -26,6 +26,7 @@ props:
|
|
|
26
26
|
type: string
|
|
27
27
|
default: default
|
|
28
28
|
enum: [default, bordered]
|
|
29
|
+
reflect: false # declarative presentational attribute — CSS/author-time read, non-reactive by design
|
|
29
30
|
value:
|
|
30
31
|
description: Value of the currently active tab. Set programmatically or auto-assigned to the first
|
|
31
32
|
non-disabled tab on connect.
|
package/components/tag/tag.yaml
CHANGED
|
@@ -18,6 +18,7 @@ props:
|
|
|
18
18
|
description: When true, restrict to one active option (single-select).
|
|
19
19
|
type: boolean
|
|
20
20
|
default: false
|
|
21
|
+
reflect: false # declarative presentational attribute — CSS/author-time read, non-reactive by design
|
|
21
22
|
value:
|
|
22
23
|
description: Comma-separated selected values
|
|
23
24
|
type: string
|
|
@@ -297,6 +297,7 @@ export class UIToolbar extends UIElement {
|
|
|
297
297
|
// (Items moved to the popover and back must restore their original slot —
|
|
298
298
|
// otherwise dividers end up clumped next to dividers and groups split.)
|
|
299
299
|
let idx = 0;
|
|
300
|
+
// wrapper-trap-ok: stamps original DOM order on toolbar items for overflow tracking; toolbar children are always static literal buttons/groups (dynamic toolbars not documented)
|
|
300
301
|
for (const el of this.children) {
|
|
301
302
|
if (el === trigger || el === popover) continue;
|
|
302
303
|
if (el.dataset.toolbarOrder == null) el.dataset.toolbarOrder = String(idx);
|
|
@@ -77,8 +77,8 @@
|
|
|
77
77
|
"Accordion"
|
|
78
78
|
],
|
|
79
79
|
"slots": {
|
|
80
|
-
"default
|
|
81
|
-
"description": "
|
|
80
|
+
"default": {
|
|
81
|
+
"description": "Holds the tree's top-level `<tree-item-ui>` children."
|
|
82
82
|
},
|
|
83
83
|
"icon": {
|
|
84
84
|
"description": "Override slot for the per-node icon glyph. Tree-item's `[icon]` attr stamps a default Phosphor icon; the slot lets consumers fill custom icons (folder open/closed, file-type glyphs, branded markers)."
|
|
@@ -41,8 +41,8 @@ events:
|
|
|
41
41
|
type: boolean
|
|
42
42
|
description: Shift key held during activation (false for programmatic select()).
|
|
43
43
|
slots:
|
|
44
|
-
default
|
|
45
|
-
description: "
|
|
44
|
+
default:
|
|
45
|
+
description: "Holds the tree's top-level `<tree-item-ui>` children."
|
|
46
46
|
icon:
|
|
47
47
|
description: >-
|
|
48
48
|
Override slot for the per-node icon glyph. Tree-item's `[icon]` attr
|