@adia-ai/web-components 0.8.38 → 0.8.39
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 +18 -0
- package/MIGRATION.md +3 -2
- package/components/calendar-picker/calendar-picker.css +4 -1
- package/components/combobox/combobox.css +6 -0
- package/components/command/command.a2ui.json +3 -0
- package/components/command/command.class.js +28 -6
- package/components/command/command.css +14 -3
- package/components/command/command.yaml +5 -0
- package/components/date-range-picker/date-range-picker.css +6 -0
- package/components/datetime-picker/datetime-picker.css +4 -0
- package/components/drilldown/drilldown.a2ui.json +3 -1
- package/components/drilldown/drilldown.class.js +12 -4
- package/components/drilldown/drilldown.css +7 -8
- package/components/drilldown/drilldown.yaml +2 -0
- package/components/modal/modal.class.js +68 -4
- package/components/nav/nav.a2ui.json +5 -0
- package/components/nav/nav.class.js +24 -4
- package/components/nav/nav.d.ts +2 -0
- package/components/nav/nav.yaml +18 -0
- package/components/select/select.class.js +30 -12
- package/components/select/select.css +11 -2
- package/components/swatch/swatch.css +6 -4
- package/components/toggle-group/toggle-group.class.js +21 -11
- package/components/toggle-group/toggle-group.css +16 -8
- package/components/toggle-group/toggle-group.d.ts +6 -0
- package/components/toggle-group/toggle-group.yaml +10 -0
- package/components/toggle-group/toggle-option.a2ui.json +5 -0
- package/components/toggle-group/toggle-option.yaml +18 -2
- package/custom-elements.json +95 -75
- package/dist/theme-provider.min.js +3 -3
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +56 -53
- package/dist/web-components.sheet.js +1 -1
- package/package.json +1 -1
|
@@ -439,8 +439,14 @@ export class UISelect extends UIFormElement {
|
|
|
439
439
|
: this.icon
|
|
440
440
|
? `<icon-ui slot="leading" data-select-leading name="${escapeHTML(this.icon)}"></icon-ui>`
|
|
441
441
|
: '';
|
|
442
|
+
// No native <input> wrap (per ADR-0055 / ADR-0025): the searchable
|
|
443
|
+
// trigger is a `contenteditable="plaintext-only"` surface carrying
|
|
444
|
+
// `role="combobox"`, mirroring combobox-ui's editable-surface pattern.
|
|
445
|
+
// Placeholder text renders via the [data-empty]::before pseudo (see
|
|
446
|
+
// select.css), same mechanism as input-ui / combobox-ui.
|
|
447
|
+
const initialDisplayText = this.#displayText() === this.placeholder ? '' : this.#displayText();
|
|
442
448
|
const displayMarkup = this.searchable
|
|
443
|
-
? `<
|
|
449
|
+
? `<span slot="display" contenteditable="plaintext-only" role="combobox" aria-autocomplete="list" tabindex="0" data-placeholder="${escapeHTML(this.placeholder || '')}"${initialDisplayText ? '' : ' data-empty'}>${escapeHTML(initialDisplayText)}</span>`
|
|
444
450
|
: `<span slot="display">${escapeHTML(this.#displayText())}</span>`;
|
|
445
451
|
// §184 (v0.5.5, FEEDBACK-08 §7): optional hint slot beneath the
|
|
446
452
|
// trigger. Mirrors slider-ui's hint pattern + matches the
|
|
@@ -478,7 +484,7 @@ export class UISelect extends UIFormElement {
|
|
|
478
484
|
this.#searchInput.removeEventListener('focus', this.#onSearchFocus);
|
|
479
485
|
this.#searchInput.removeEventListener('click', this.#onSearchClick);
|
|
480
486
|
}
|
|
481
|
-
this.#searchInput = this.querySelector('
|
|
487
|
+
this.#searchInput = this.querySelector('[slot="display"][contenteditable]');
|
|
482
488
|
if (this.#searchInput) {
|
|
483
489
|
this.#searchInput.addEventListener('input', this.#onSearchInput);
|
|
484
490
|
this.#searchInput.addEventListener('focus', this.#onSearchFocus);
|
|
@@ -492,9 +498,13 @@ export class UISelect extends UIFormElement {
|
|
|
492
498
|
const display = this.querySelector('[slot="display"]');
|
|
493
499
|
if (display && !this.multiple) {
|
|
494
500
|
// Single-select: keep the canonical text-rendering path.
|
|
495
|
-
if (display.
|
|
496
|
-
// Only update
|
|
497
|
-
if (!this.#query)
|
|
501
|
+
if (display.hasAttribute('contenteditable')) {
|
|
502
|
+
// Only update text when not actively editing (no active query)
|
|
503
|
+
if (!this.#query) {
|
|
504
|
+
const text = this.#displayText() === this.placeholder ? '' : this.#displayText();
|
|
505
|
+
display.textContent = text;
|
|
506
|
+
display.toggleAttribute('data-empty', !text);
|
|
507
|
+
}
|
|
498
508
|
} else {
|
|
499
509
|
display.textContent = this.#displayText();
|
|
500
510
|
}
|
|
@@ -502,10 +512,13 @@ export class UISelect extends UIFormElement {
|
|
|
502
512
|
// Multi-select: display element holds the placeholder ONLY when
|
|
503
513
|
// no chips are present (otherwise the chip row IS the display).
|
|
504
514
|
const hasChips = this.#values().length > 0;
|
|
505
|
-
if (display.
|
|
515
|
+
if (display.hasAttribute('contenteditable')) {
|
|
506
516
|
// search input — keep placeholder; never inject the value text.
|
|
507
|
-
display.placeholder
|
|
508
|
-
if (!this.#query)
|
|
517
|
+
display.setAttribute('data-placeholder', hasChips ? '' : (this.placeholder || ''));
|
|
518
|
+
if (!this.#query) {
|
|
519
|
+
display.textContent = '';
|
|
520
|
+
display.toggleAttribute('data-empty', true);
|
|
521
|
+
}
|
|
509
522
|
} else {
|
|
510
523
|
display.textContent = hasChips ? '' : (this.placeholder || '');
|
|
511
524
|
}
|
|
@@ -991,7 +1004,7 @@ export class UISelect extends UIFormElement {
|
|
|
991
1004
|
// before it would start removing query characters.
|
|
992
1005
|
if (e.key === 'Backspace' && this.multiple && !this.disabled && !this.readonly) {
|
|
993
1006
|
const target = e.target;
|
|
994
|
-
const fromInput = target && target.
|
|
1007
|
+
const fromInput = target && target.hasAttribute?.('contenteditable') && target.getAttribute('slot') === 'display';
|
|
995
1008
|
const queryEmpty = !this.#query || this.#query.length === 0;
|
|
996
1009
|
if (!fromInput || queryEmpty) {
|
|
997
1010
|
if (this.#values().length > 0) {
|
|
@@ -1005,8 +1018,11 @@ export class UISelect extends UIFormElement {
|
|
|
1005
1018
|
if (this.searchable && this.#query) {
|
|
1006
1019
|
// First Escape: clear query
|
|
1007
1020
|
this.#query = '';
|
|
1008
|
-
const input = this.querySelector('
|
|
1009
|
-
if (input)
|
|
1021
|
+
const input = this.querySelector('[slot="display"][contenteditable]');
|
|
1022
|
+
if (input) {
|
|
1023
|
+
input.textContent = '';
|
|
1024
|
+
input.toggleAttribute('data-empty', true);
|
|
1025
|
+
}
|
|
1010
1026
|
this.#applyFilter();
|
|
1011
1027
|
return;
|
|
1012
1028
|
}
|
|
@@ -1061,7 +1077,9 @@ export class UISelect extends UIFormElement {
|
|
|
1061
1077
|
};
|
|
1062
1078
|
|
|
1063
1079
|
#onSearchInput = (e) => {
|
|
1064
|
-
|
|
1080
|
+
const text = e.target.textContent || '';
|
|
1081
|
+
this.#query = text;
|
|
1082
|
+
e.target.toggleAttribute('data-empty', !text);
|
|
1065
1083
|
if (!this.open) this.open = true;
|
|
1066
1084
|
this.#applyFilter();
|
|
1067
1085
|
};
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
white-space: nowrap;
|
|
150
150
|
color: var(--select-fg);
|
|
151
151
|
}
|
|
152
|
-
|
|
152
|
+
[slot="display"][contenteditable] {
|
|
153
153
|
border: none;
|
|
154
154
|
outline: none;
|
|
155
155
|
background: transparent;
|
|
@@ -159,9 +159,18 @@
|
|
|
159
159
|
color: var(--select-fg);
|
|
160
160
|
min-width: 0;
|
|
161
161
|
width: 100%;
|
|
162
|
+
/* Positioning context for the [data-empty]::before placeholder pseudo
|
|
163
|
+
below — out-of-flow, same rationale as input-ui/combobox-ui: an
|
|
164
|
+
in-flow pseudo would render the caret after the placeholder text
|
|
165
|
+
instead of at content-start. */
|
|
166
|
+
position: relative;
|
|
162
167
|
}
|
|
163
|
-
|
|
168
|
+
[slot="display"][contenteditable][data-empty]::before {
|
|
169
|
+
content: attr(data-placeholder);
|
|
164
170
|
color: var(--select-placeholder-fg);
|
|
171
|
+
pointer-events: none;
|
|
172
|
+
position: absolute;
|
|
173
|
+
inset: 0;
|
|
165
174
|
}
|
|
166
175
|
:scope:not([value]) [slot="display"],
|
|
167
176
|
:scope[value=""] [slot="display"] {
|
|
@@ -274,20 +274,22 @@
|
|
|
274
274
|
z-index: 1;
|
|
275
275
|
}
|
|
276
276
|
:scope[shape="block"][auto-contrast] > [data-label][data-on-dark] {
|
|
277
|
-
color: var(--a-chrome-light
|
|
277
|
+
color: var(--a-chrome-light);
|
|
278
278
|
}
|
|
279
279
|
:scope[shape="block"][auto-contrast] > [data-label][data-on-light] {
|
|
280
|
-
color: var(--a-chrome-dark
|
|
280
|
+
color: var(--a-chrome-dark);
|
|
281
281
|
}
|
|
282
282
|
/* Detail line follows the same auto-contrast choice. */
|
|
283
283
|
:scope[shape="block"][auto-contrast] > [data-detail] {
|
|
284
284
|
z-index: 1;
|
|
285
285
|
}
|
|
286
286
|
:scope[shape="block"][auto-contrast] > [data-label][data-on-dark] ~ [data-detail] {
|
|
287
|
-
color:
|
|
287
|
+
color: var(--a-chrome-light);
|
|
288
|
+
opacity: 0.8;
|
|
288
289
|
}
|
|
289
290
|
:scope[shape="block"][auto-contrast] > [data-label][data-on-light] ~ [data-detail] {
|
|
290
|
-
color:
|
|
291
|
+
color: var(--a-chrome-dark);
|
|
292
|
+
opacity: 0.7;
|
|
291
293
|
}
|
|
292
294
|
|
|
293
295
|
/* ═══════ Copy button — a stamped <button-ui variant="ghost" size="sm">
|
|
@@ -30,10 +30,15 @@ import { UIElement } from '../../core/element.js';
|
|
|
30
30
|
// ── Toggle Option ──
|
|
31
31
|
//
|
|
32
32
|
// Parent-managed. Selection state flows through `toggle-group-ui`'s `value`
|
|
33
|
-
// (and `single` mode flag)
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
33
|
+
// (and `single` mode flag) — the group's `value` stays the single source of
|
|
34
|
+
// truth. ADR-0056 (gh#1303): the child's current-state surface is the
|
|
35
|
+
// declared, reflected `selected` prop below, converging onto <segment-ui>'s
|
|
36
|
+
// mechanism (yaml → catalog → generative grammar) instead of a private
|
|
37
|
+
// stamp. `toggle-group-ui` also stamps the legacy `data-selected` attribute
|
|
38
|
+
// on the same element in parallel — DEPRECATED, kept only because external
|
|
39
|
+
// CSS may already target it as an undocumented hook; its removal rides the
|
|
40
|
+
// 0.9.0 breaking wave. Don't set `selected` directly on an option; drive
|
|
41
|
+
// the group's `value` instead.
|
|
37
42
|
|
|
38
43
|
export class UIToggleOption extends UIElement {
|
|
39
44
|
static properties = {
|
|
@@ -41,6 +46,7 @@ export class UIToggleOption extends UIElement {
|
|
|
41
46
|
text: { type: String, default: '', reflect: true },
|
|
42
47
|
icon: { type: String, default: '', reflect: true },
|
|
43
48
|
disabled: { type: Boolean, default: false, reflect: true },
|
|
49
|
+
selected: { type: Boolean, default: false, reflect: true },
|
|
44
50
|
};
|
|
45
51
|
|
|
46
52
|
static template = () => null;
|
|
@@ -59,6 +65,7 @@ export class UIToggleOption extends UIElement {
|
|
|
59
65
|
}
|
|
60
66
|
if (this.disabled) this.setAttribute('aria-disabled', 'true');
|
|
61
67
|
else this.removeAttribute('aria-disabled');
|
|
68
|
+
this.setAttribute('aria-pressed', this.selected ? 'true' : 'false');
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
#stamp() {
|
|
@@ -100,13 +107,16 @@ export class UIToggleGroup extends UIElement {
|
|
|
100
107
|
const selected = this.#valueSet;
|
|
101
108
|
for (const opt of this.querySelectorAll('toggle-option-ui')) {
|
|
102
109
|
const optVal = opt.value || opt.getAttribute('value') || '';
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
const isSelected = selected.has(optVal);
|
|
111
|
+
// ADR-0056 (gh#1303): `selected` is the declared, reflected mechanism —
|
|
112
|
+
// converges onto <segment-ui>'s convention (opt's own render() stamps
|
|
113
|
+
// aria-pressed from it). `data-selected` is stamped alongside it only
|
|
114
|
+
// as a DEPRECATED parallel hook for pre-existing external CSS; it no
|
|
115
|
+
// longer drives styling (toggle-group.css) and its removal rides the
|
|
116
|
+
// 0.9.0 breaking wave.
|
|
117
|
+
opt.selected = isSelected;
|
|
118
|
+
if (isSelected) opt.setAttribute('data-selected', '');
|
|
119
|
+
else opt.removeAttribute('data-selected');
|
|
110
120
|
}
|
|
111
121
|
}
|
|
112
122
|
|
|
@@ -1,12 +1,23 @@
|
|
|
1
|
-
/* Safari 17.x bug: `:scope:not(...):hover`
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/* Safari 17.x bug: `:scope:not(...):hover` (Flavor A) and `:scope[selected]`
|
|
2
|
+
(Flavor B — attribute-removal restyle) both fail inside `@scope`.
|
|
3
|
+
Selectors moved out. The @scope is `(toggle-option-ui)` — NOT
|
|
4
|
+
`(toggle-group-ui)` — so the moved-out selectors target
|
|
5
|
+
`toggle-option-ui:hover` / `toggle-option-ui[selected]`. See
|
|
6
|
+
docs/BROWSER-COMPAT.md §3a; mirrors segment.css. */
|
|
5
7
|
toggle-option-ui:not([disabled]):hover {
|
|
6
8
|
--toggle-option-bg: var(--toggle-option-bg-hover);
|
|
7
9
|
--toggle-option-fg: var(--toggle-option-fg-hover);
|
|
8
10
|
}
|
|
9
11
|
|
|
12
|
+
/* ADR-0056 (gh#1303): converged onto <segment-ui>'s declared, reflected
|
|
13
|
+
[selected] prop, set by <toggle-group-ui> (toggle-group.class.js). The
|
|
14
|
+
parent also stamps the legacy `data-selected` attribute in parallel —
|
|
15
|
+
DEPRECATED, no longer styled here; removal rides the 0.9.0 breaking wave. */
|
|
16
|
+
toggle-option-ui[selected] {
|
|
17
|
+
--toggle-option-bg: var(--toggle-option-selected-bg);
|
|
18
|
+
--toggle-option-fg: var(--toggle-option-selected-fg);
|
|
19
|
+
}
|
|
20
|
+
|
|
10
21
|
@scope (toggle-group-ui) {
|
|
11
22
|
:where(:scope) {
|
|
12
23
|
/* ── Tokens ── */
|
|
@@ -94,10 +105,7 @@ toggle-option-ui:not([disabled]):hover {
|
|
|
94
105
|
z-index: 1;
|
|
95
106
|
}
|
|
96
107
|
|
|
97
|
-
|
|
98
|
-
--toggle-option-bg: var(--toggle-option-selected-bg);
|
|
99
|
-
--toggle-option-fg: var(--toggle-option-selected-fg);
|
|
100
|
-
}
|
|
108
|
+
/* [selected] state rule moved outside @scope — see Safari 17.x bug note at top. */
|
|
101
109
|
|
|
102
110
|
:scope[disabled] {
|
|
103
111
|
--toggle-option-fg: var(--toggle-option-fg-disabled);
|
|
@@ -38,6 +38,12 @@ export class UIToggleOption extends UIElement {
|
|
|
38
38
|
disabled: boolean;
|
|
39
39
|
/** Optional Phosphor icon name; renders as a leading icon. */
|
|
40
40
|
icon: string;
|
|
41
|
+
/** Whether this option is currently selected. Managed by the parent
|
|
42
|
+
<toggle-group-ui> container (ADR-0056) — don't set directly; the group's
|
|
43
|
+
`value` is the single source of truth. Converges onto <segment-ui>'s
|
|
44
|
+
declared reflected-prop mechanism (was previously a private
|
|
45
|
+
`data-selected` stamp with no declared API surface). */
|
|
46
|
+
selected: boolean;
|
|
41
47
|
/** Visible label text. */
|
|
42
48
|
text: string;
|
|
43
49
|
/** Stable identifier matched against the parent group's `value`. */
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Generated by scripts/migrate-yamls-to-v1.mjs — migrated to v1 contract.
|
|
2
2
|
# Edit this file; run `npm run build:components` to regenerate a2ui.json.
|
|
3
|
+
#
|
|
4
|
+
# ADR-0056 (gh#1303): <toggle-option-ui>'s current-state surface converges
|
|
5
|
+
# onto <segment-ui>'s declared, reflected `[selected]` prop (see
|
|
6
|
+
# toggle-option.yaml). This container sets that prop, keeping `value` as the
|
|
7
|
+
# single source of truth. It also stamps the legacy `data-selected`
|
|
8
|
+
# attribute on each option in parallel — DEPRECATED (undocumented external
|
|
9
|
+
# CSS may target it); its removal rides the 0.9.0 breaking wave, not this
|
|
10
|
+
# change.
|
|
3
11
|
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
4
12
|
name: UIToggleGroup
|
|
5
13
|
tag: toggle-group-ui
|
|
@@ -80,6 +88,8 @@ a2ui:
|
|
|
80
88
|
reason: 'Selection model distinction.'
|
|
81
89
|
- rule: 'Use for filter chips, multi-flag toggles, day-of-week pickers; for binary on/off use <switch-ui>.'
|
|
82
90
|
reason: 'Capacity guidance.'
|
|
91
|
+
- rule: 'Sets the child <toggle-option-ui>''s declared [selected] prop to reflect the group value (ADR-0056) — do not author [selected] directly in generated markup; author the group''s value instead.'
|
|
92
|
+
reason: 'Parent-managed declared state — group value stays the single source of truth.'
|
|
83
93
|
anti_patterns: []
|
|
84
94
|
examples:
|
|
85
95
|
- name: basic-toggle-group
|
|
@@ -26,6 +26,11 @@
|
|
|
26
26
|
"type": "string",
|
|
27
27
|
"default": ""
|
|
28
28
|
},
|
|
29
|
+
"selected": {
|
|
30
|
+
"description": "Whether this option is currently selected. Managed by the parent\n<toggle-group-ui> container (ADR-0056) — don't set directly; the group's\n`value` is the single source of truth. Converges onto <segment-ui>'s\ndeclared reflected-prop mechanism (was previously a private\n`data-selected` stamp with no declared API surface).",
|
|
31
|
+
"type": "boolean",
|
|
32
|
+
"default": false
|
|
33
|
+
},
|
|
29
34
|
"text": {
|
|
30
35
|
"description": "Visible label text.",
|
|
31
36
|
"type": "string",
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
# §228 (v0.5.9): authored to close the §229 baseline class-export-undeclared
|
|
4
4
|
# drift for UIToggleOption. Ships as a sibling class in toggle-group/toggle-group.class.js
|
|
5
5
|
# + is registered alongside UIToggleGroup.
|
|
6
|
+
#
|
|
7
|
+
# ADR-0056 (gh#1303): `selected` converges onto <segment-ui>'s declared,
|
|
8
|
+
# parent-managed, reflected-prop convention. `<toggle-group-ui>` also stamps
|
|
9
|
+
# the legacy `data-selected` attribute in parallel — DEPRECATED, kept only
|
|
10
|
+
# because it was an undocumented hook external CSS may already target;
|
|
11
|
+
# removal rides the 0.9.0 breaking wave (see toggle-group.yaml + CHANGELOG).
|
|
6
12
|
|
|
7
13
|
# Child component of <toggle-group-ui>. Surface only inside that parent.
|
|
8
14
|
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
@@ -32,6 +38,16 @@ props:
|
|
|
32
38
|
description: Disabled state — blocks pointer + keyboard activation.
|
|
33
39
|
type: boolean
|
|
34
40
|
default: false
|
|
41
|
+
selected:
|
|
42
|
+
description: |-
|
|
43
|
+
Whether this option is currently selected. Managed by the parent
|
|
44
|
+
<toggle-group-ui> container (ADR-0056) — don't set directly; the group's
|
|
45
|
+
`value` is the single source of truth. Converges onto <segment-ui>'s
|
|
46
|
+
declared reflected-prop mechanism (was previously a private
|
|
47
|
+
`data-selected` stamp with no declared API surface).
|
|
48
|
+
type: boolean
|
|
49
|
+
default: false
|
|
50
|
+
reflect: true
|
|
35
51
|
|
|
36
52
|
keywords:
|
|
37
53
|
- toggle-option
|
|
@@ -53,5 +69,5 @@ a2ui:
|
|
|
53
69
|
reason: 'Multi-select cluster contract.'
|
|
54
70
|
- rule: 'Different from <segment-ui> (which is single-select inside <segmented-ui>).'
|
|
55
71
|
reason: 'Selection model boundary.'
|
|
56
|
-
- rule: '
|
|
57
|
-
reason: 'Parent
|
|
72
|
+
- rule: 'Selected state exposed via its own declared [selected] reflected prop, set by the parent <toggle-group-ui> (ADR-0056) — mirrors <segment-ui>''s convention. Do not set [selected] directly; drive the group''s value instead.'
|
|
73
|
+
reason: 'Parent-managed declared state — group value stays the single source of truth.'
|