@adia-ai/web-components 0.8.43 → 0.8.45

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.
Files changed (113) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/components/avatar/avatar.class.js +12 -1
  3. package/components/button/button.d.ts +4 -0
  4. package/components/chart/chart.a2ui.json +26 -0
  5. package/components/chart/chart.class.js +1304 -222
  6. package/components/chart/chart.css +151 -1
  7. package/components/chart/chart.d.ts +6 -0
  8. package/components/chart/chart.yaml +57 -0
  9. package/components/chart-legend/chart-legend.a2ui.json +17 -2
  10. package/components/chart-legend/chart-legend.class.js +69 -4
  11. package/components/chart-legend/chart-legend.css +65 -1
  12. package/components/chart-legend/chart-legend.d.ts +4 -2
  13. package/components/chart-legend/chart-legend.examples.md +4 -4
  14. package/components/chart-legend/chart-legend.yaml +43 -3
  15. package/components/combobox/combobox.a2ui.json +3 -0
  16. package/components/combobox/combobox.class.js +9 -8
  17. package/components/combobox/combobox.css +25 -4
  18. package/components/combobox/combobox.yaml +6 -0
  19. package/components/command/command.css +17 -1
  20. package/components/context-menu/context-menu.a2ui.json +8 -3
  21. package/components/context-menu/context-menu.class.js +46 -5
  22. package/components/context-menu/context-menu.d.ts +8 -3
  23. package/components/context-menu/context-menu.examples.md +2 -2
  24. package/components/context-menu/context-menu.yaml +22 -5
  25. package/components/heatmap/heatmap.a2ui.json +5 -0
  26. package/components/heatmap/heatmap.d.ts +2 -0
  27. package/components/heatmap/heatmap.yaml +8 -0
  28. package/components/input/input.a2ui.json +2 -2
  29. package/components/input/input.class.js +12 -7
  30. package/components/input/input.yaml +6 -5
  31. package/components/integration-card/integration-card.d.ts +2 -0
  32. package/components/nav/nav.a2ui.json +2 -2
  33. package/components/nav/nav.css +1 -1
  34. package/components/nav/nav.d.ts +1 -1
  35. package/components/nav/nav.yaml +14 -3
  36. package/components/nav-group/nav-group.css +37 -3
  37. package/components/noodles/noodles.a2ui.json +0 -15
  38. package/components/noodles/noodles.yaml +0 -12
  39. package/components/number-format/number-format.class.js +10 -0
  40. package/components/pagination/pagination.class.js +140 -26
  41. package/components/popover/popover.d.ts +2 -0
  42. package/components/progress/progress.class.js +11 -6
  43. package/components/range/range.class.js +9 -1
  44. package/components/search/search.class.js +39 -5
  45. package/components/select/select.a2ui.json +10 -1
  46. package/components/select/select.class.js +34 -14
  47. package/components/select/select.css +39 -1
  48. package/components/select/select.d.ts +2 -0
  49. package/components/select/select.yaml +19 -1
  50. package/components/stream/stream.d.ts +2 -0
  51. package/components/table/cell-types.js +9 -0
  52. package/components/table/table.a2ui.json +6 -1
  53. package/components/table/table.class.js +265 -34
  54. package/components/table/table.css +38 -10
  55. package/components/table/table.d.ts +3 -1
  56. package/components/table/table.examples.md +2 -2
  57. package/components/table/table.yaml +15 -2
  58. package/components/table-toolbar/table-toolbar.a2ui.json +43 -4
  59. package/components/table-toolbar/table-toolbar.class.js +407 -28
  60. package/components/table-toolbar/table-toolbar.css +299 -0
  61. package/components/table-toolbar/table-toolbar.d.ts +15 -1
  62. package/components/table-toolbar/table-toolbar.yaml +162 -15
  63. package/components/tag/tag.class.js +13 -1
  64. package/components/text/text.a2ui.json +3 -3
  65. package/components/text/text.d.ts +5 -3
  66. package/components/text/text.yaml +3 -3
  67. package/components/textarea/textarea.class.js +9 -1
  68. package/components/tour/tour.d.ts +10 -0
  69. package/core/a11y.d.ts +22 -0
  70. package/core/a11y.js +64 -0
  71. package/core/anchor.js +9 -5
  72. package/core/data-stream.js +37 -2
  73. package/core/element.js +1 -1
  74. package/core/index.d.ts +1 -0
  75. package/core/index.js +1 -0
  76. package/core/provider.d.ts +9 -13
  77. package/core/provider.js +9 -113
  78. package/core/store.d.ts +46 -0
  79. package/core/store.js +89 -0
  80. package/custom-elements.json +127 -33
  81. package/dist/host.min.css +1 -1
  82. package/dist/host.sheet.js +1 -1
  83. package/dist/theme-provider.min.js +1 -1
  84. package/dist/web-components.min.css +1 -1
  85. package/dist/web-components.min.js +119 -119
  86. package/dist/web-components.sheet.js +1 -1
  87. package/package.json +1 -1
  88. package/patterns/access-requests/access-requests.examples.html +1 -1
  89. package/patterns/admin-keys-and-export/admin-keys-and-export.examples.html +7 -7
  90. package/patterns/agent-cost/agent-cost.examples.html +6 -6
  91. package/patterns/agent-memory/agent-memory.examples.html +3 -3
  92. package/patterns/agent-prompt-library/agent-prompt-library.examples.html +8 -8
  93. package/patterns/agent-tool-call/agent-tool-call.examples.html +1 -1
  94. package/patterns/approvals/approvals.examples.html +6 -6
  95. package/patterns/audit-log/audit-log.examples.html +1 -1
  96. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.html +4 -4
  97. package/patterns/chart-in-card/chart-in-card.examples.html +114 -0
  98. package/patterns/chart-in-card/chart-in-card.examples.js +37 -0
  99. package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +5 -5
  100. package/patterns/data-tables-inline-edit-and-tree/data-tables-inline-edit-and-tree.examples.html +2 -2
  101. package/patterns/diff-review/diff-review.examples.html +6 -6
  102. package/patterns/export-flow/export-flow.examples.html +2 -2
  103. package/patterns/filter-bar/filter-bar.examples.html +2 -2
  104. package/patterns/forms-conditional-and-autocomplete/forms-conditional-and-autocomplete.examples.html +2 -2
  105. package/patterns/inline-dialog/inline-dialog.examples.html +2 -2
  106. package/patterns/marketing-engagement/marketing-engagement.examples.html +4 -4
  107. package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +1 -1
  108. package/patterns/permissions-matrix/permissions-matrix.examples.html +1 -1
  109. package/patterns/permissions-sharing/permissions-sharing.examples.html +2 -2
  110. package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +4 -4
  111. package/patterns/search-discovery/search-discovery.examples.html +2 -2
  112. package/styles/api/sizing.css +46 -0
  113. package/styles/type/roles.css +29 -9
@@ -23,8 +23,12 @@ props:
23
23
  reflect: true
24
24
  items:
25
25
  description: >-
26
- JSON array of {key, label, slot?, pct?} legend items. Takes precedence
27
- over [for] when both are provided.
26
+ JSON array of {key, label, slot?, pct?, deemphasized?} legend items.
27
+ Takes precedence over [for] when both are provided. `deemphasized`
28
+ (REQ-R-007) mutes the row's swatch/label to the same muted-neutral
29
+ tier chart-ui's own internal legend uses for a de-emphasized series
30
+ (e.g. `composed@1:1`'s secondary line) — mirrored automatically from
31
+ a `[for]`-bound chart's own `.legendData`, or settable explicitly here.
28
32
  type: string
29
33
  default: ''
30
34
  shape:
@@ -38,7 +42,11 @@ props:
38
42
  - dashed
39
43
  reflect: true
40
44
  position:
41
- description: Layout hint — drives flex-direction. Actual placement follows DOM order.
45
+ description: >-
46
+ Layout hint — drives flex-direction. Actual placement follows DOM
47
+ order. Superseded by a resolved `ratio` bucket (below) whenever one
48
+ is in effect; this is the pre-ADR-0074 fallback for a legend with
49
+ neither an explicit `ratio` nor a `[for]`-bound chart.
42
50
  type: string
43
51
  default: bottom
44
52
  enum:
@@ -47,6 +55,26 @@ props:
47
55
  - left
48
56
  - right
49
57
  reflect: true
58
+ ratio:
59
+ description: >-
60
+ Chart 2.0 legend layout bucket (ADR-0074, gh#1624 plan step 5,
61
+ REQ-R-005): 3:2 renders a horizontal row, 1:1 a wrapped 2-column
62
+ grid, 2:3 a vertical stacked list. An explicit value PINS the
63
+ bucket regardless of any bound chart. Unset (default) auto-mirrors
64
+ the `[for]`-bound chart's own resolved `data-ratio-resolved` bucket
65
+ — the legend never runs its own container-query classification,
66
+ since its own box is typically a thin strip with no studied aspect
67
+ of its own. With neither a pin nor a bound chart, no bucket
68
+ resolves and `position`'s flex-direction rules apply unchanged. The
69
+ resolved bucket reflects read-only onto the host as
70
+ `data-ratio-resolved`, mirroring chart-ui's own convention.
71
+ type: string
72
+ default: ""
73
+ enum:
74
+ - "3:2"
75
+ - "1:1"
76
+ - "2:3"
77
+ reflect: true
50
78
  interactive:
51
79
  description: >-
52
80
  Canonical opt-in spelling (ADR-0063, gh#1563) — the deprecated
@@ -107,6 +135,8 @@ a2ui:
107
135
  reason: 'Composition contract.'
108
136
  - rule: 'position attribute (top|bottom|left|right) places legend relative to its chart; interactive="false" disables interactivity.'
109
137
  reason: 'Layout + interaction knobs.'
138
+ - rule: 'ratio attribute (3:2|1:1|2:3, ADR-0074) selects the legend layout shape (row/grid/list); left unset it auto-mirrors a [for]-bound chart''s own resolved ratio.'
139
+ reason: 'Ratio-responsive layout contract, gh#1624 plan step 5.'
110
140
  anti_patterns: []
111
141
  examples:
112
142
  - name: standalone-with-items
@@ -119,6 +149,16 @@ examples:
119
149
  "items": "[{\"key\":\"revenue\",\"label\":\"Revenue\",\"slot\":0},{\"key\":\"users\",\"label\":\"Users\",\"slot\":1}]"
120
150
  }
121
151
  ]
152
+ - name: ratio-pinned-grid
153
+ description: Standalone legend pinned to the 1:1 (2-col grid) layout bucket.
154
+ a2ui: >-
155
+ [
156
+ {
157
+ "id": "legend", "component": "ChartLegend",
158
+ "ratio": "1:1",
159
+ "items": "[{\"key\":\"revenue\",\"label\":\"Revenue\",\"slot\":0},{\"key\":\"users\",\"label\":\"Users\",\"slot\":1},{\"key\":\"churn\",\"label\":\"Churn\",\"slot\":2},{\"key\":\"nps\",\"label\":\"NPS\",\"slot\":3}]"
160
+ }
161
+ ]
122
162
  - name: mirror-chart-by-id
123
163
  description: Legend mirrors a named chart's series data.
124
164
  a2ui: >-
@@ -339,6 +339,9 @@
339
339
  "--combobox-match-bg": {
340
340
  "description": "Background for `<mark>` spans that highlight matched substrings"
341
341
  },
342
+ "--combobox-min-width": {
343
+ "description": "Intrinsic min-width floor on the host (default `20ch`, matching input-ui's own `--input-min-width` UA-baseline derivation) so a combobox in a shrinking flex/grid cell doesn't crush to unusable width. Set to `0` to opt back into full collapse (gh#1633)."
344
+ },
342
345
  "--combobox-option-bg-active": {
343
346
  "description": "Background for the currently active (arrow-navigated) option"
344
347
  },
@@ -41,6 +41,7 @@ import { UIFormElement } from '../../core/form.js';
41
41
  import { anchorPopover } from '../../core/anchor.js';
42
42
  import { untracked } from '../../core/signals.js';
43
43
  import { logicalSlotted } from '../../core/logical-children.js';
44
+ import { syncAutoAriaLabel } from '../../core/a11y.js';
44
45
 
45
46
  function escapeHTML(s) {
46
47
  return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
@@ -103,6 +104,10 @@ export class UICombobox extends UIFormElement {
103
104
  #anchorCleanup = null;
104
105
  #rafId = null;
105
106
  #instanceId = `combobox-${++cbInstanceSeq}`;
107
+ // gh#1647: tracks the aria-label VALUE this element itself last derived
108
+ // from `placeholder` (undefined when none) — value-tracking guard
109
+ // ported from tag.class.js/select.class.js (gh#1644/#1646).
110
+ #lastAutoAriaLabel = undefined;
106
111
 
107
112
  // ── Stable handler refs (so removeEventListener finds them) ──
108
113
  #onInputEvent = () => this.#handleInput();
@@ -176,14 +181,10 @@ export class UICombobox extends UIFormElement {
176
181
  this.#labelEl.style.display = this.label ? '' : 'none';
177
182
  }
178
183
 
179
- // aria-label fallback when no [label] is set.
180
- if (this.label) {
181
- this.removeAttribute('aria-label');
182
- } else if (this.placeholder) {
183
- this.setAttribute('aria-label', this.placeholder);
184
- } else {
185
- this.removeAttribute('aria-label');
186
- }
184
+ // aria-label fallback when no [label] is set (gh#1647) — never clobber
185
+ // a consumer's own aria-label (value-tracking guard, gh#1644/#1646).
186
+ const ariaSource = this.label ? '' : (this.placeholder || '');
187
+ this.#lastAutoAriaLabel = syncAutoAriaLabel(this, this.#lastAutoAriaLabel, ariaSource);
187
188
 
188
189
  // Toggle clear-button visibility when [clearable].
189
190
  if (this.#clearBtn) {
@@ -6,6 +6,15 @@
6
6
  --combobox-height: var(--a-size);
7
7
  --combobox-px: var(--a-ui-px);
8
8
  --combobox-radius: var(--a-radius);
9
+ /* gh#1633 / ADIA2-9136 (adiav2#12791) — the host's own `min-width: 0`
10
+ below (the ellipsis-reset trick, kept for [data-input] inside this
11
+ floor) had no intrinsic lower bound, so a combobox in a shrinking
12
+ flex/grid cell crushed to unusable width — the same gap already
13
+ fixed on --input-min-width (this family's reference; see input.css,
14
+ gh#781) and on --select-min-width (same fix, select.css). Same
15
+ ch-based UA-baseline derivation. Override to 0 to opt back into
16
+ full collapse. */
17
+ --combobox-min-width: 20ch;
9
18
 
10
19
  /* ── Typography ── */
11
20
  --combobox-font-size: var(--a-ui-size);
@@ -62,7 +71,11 @@
62
71
  flex-direction: column;
63
72
  gap: var(--combobox-gap);
64
73
  position: relative;
65
- min-width: 0;
74
+ /* Floored, not zeroed — see --combobox-min-width above (gh#1633).
75
+ [data-input]'s own `min-width: 0` further down stays 0: that one
76
+ exists so its value text can ellipsis-truncate INSIDE this floor,
77
+ not to guard the host's own collapse. */
78
+ min-width: var(--combobox-min-width);
66
79
  /* font-family floor on the host — internal input/options inherit it. */
67
80
  font-family: var(--combobox-font-family, var(--a-font-family-ui));
68
81
  font-size: var(--combobox-font-size);
@@ -326,9 +339,17 @@ combobox-ui [data-listbox] [role="group"] [data-group-label] {
326
339
  white-space: nowrap;
327
340
  }
328
341
 
329
- combobox-ui [data-empty],
330
- combobox-ui [data-loading],
331
- combobox-ui [data-footer] {
342
+ /* Scoped to `[data-listbox]`'s own markers — gh#1700, same shape as
343
+ gh#1697/#1698. The input span (`[data-input]`) toggles the SAME
344
+ `data-empty` attribute for an unrelated reason (gating its own
345
+ `::before` placeholder pseudo) — a bare, unscoped `combobox-ui
346
+ [data-empty]` selector matched BOTH uses, and (being (0,1,1)) outranked
347
+ `[data-input]`'s own (0,1,0) padding/color/font-size, one property away
348
+ from repeating the command-ui leak. Scoping to the listbox container
349
+ removes the collision at its root. */
350
+ combobox-ui [data-listbox] [data-empty],
351
+ combobox-ui [data-listbox] [data-loading],
352
+ combobox-ui [data-listbox] [data-footer] {
332
353
  padding: var(--a-space-2) var(--a-ui-px);
333
354
  color: var(--a-ui-text-muted);
334
355
  font-size: var(--a-ui-size);
@@ -257,6 +257,12 @@ tokens:
257
257
  tinted selected row.
258
258
  --combobox-match-bg:
259
259
  description: Background for `<mark>` spans that highlight matched substrings
260
+ --combobox-min-width:
261
+ description: >-
262
+ Intrinsic min-width floor on the host (default `20ch`, matching
263
+ input-ui's own `--input-min-width` UA-baseline derivation) so a
264
+ combobox in a shrinking flex/grid cell doesn't crush to unusable
265
+ width. Set to `0` to opt back into full collapse (gh#1633).
260
266
  requiredIcons:
261
267
  - caret-down
262
268
  - x-circle
@@ -221,7 +221,23 @@
221
221
  }
222
222
 
223
223
  /* ── Empty state ── */
224
- [data-empty] {
224
+ /* Scoped to `[slot="list"]`'s own `[data-empty]` — gh#1697. The search
225
+ input (`& > header [slot="input"]`) toggles the SAME `data-empty`
226
+ attribute for an unrelated reason (gating its own `::before`
227
+ placeholder pseudo, per REQ above) — a bare, unscoped `[data-empty]`
228
+ selector matched BOTH uses, so this rule's `text-align: center` leaked
229
+ onto the input span whenever it was empty, centering the placeholder
230
+ (and, structurally, any text a consumer's own script inserted while
231
+ the attribute was still present). `padding`/`color` never leaked in
232
+ practice — `& > header [slot="input"]`'s own compound selector
233
+ (header-descendant + attribute, (0,1,1)) already outranks the bare
234
+ `[data-empty]` (0,1,0) for those two properties — but `text-align` had
235
+ no competing declaration anywhere else in this file, so the leaked
236
+ value won by default with nothing to lose against. Scoping the
237
+ selector to the list container removes the collision at its root
238
+ instead of only patching the one property a screenshot happened to
239
+ catch. */
240
+ [slot="list"] [data-empty] {
225
241
  padding: var(--command-py) var(--command-px);
226
242
  color: var(--command-empty-fg);
227
243
  text-align: center;
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://adiaui.dev/a2ui/v0_9/components/ContextMenu.json",
4
4
  "title": "ContextMenu",
5
- "description": "Right-click activated menu — the OS-native context-menu pattern as a\nweb component. Distinct from `menu-ui` (which is button-triggered):\nsame item shape (`menu-item-ui` children), different trigger surface\n(`contextmenu` event), and pointer-anchored positioning instead of\nelement-anchored. Pattern: WAI-APG Menu.\n\nTwo binding modes:\n **A. Wrap.** Default-slot child becomes the target:\n `<context-menu-ui><my-table>...</my-table>...items</context-menu-ui>`.\n **B. Selector.** Point at one or more existing elements via [for]:\n `<context-menu-ui for=\"#my-table\">...items</context-menu-ui>`.\n\nOn `contextmenu` event on a target: `preventDefault()`, position the\nmenu at the pointer coords, show via Popover API. Touch long-press\n(configurable via [long-press-ms]) does the same. Shift+F10 / Menu\nkey opens at the focused target's center for keyboard users.\n",
5
+ "description": "Right-click activated menu — the OS-native context-menu pattern as a\nweb component. Distinct from `menu-ui` (which is button-triggered):\nsame item shape (`menu-item-ui` children), different trigger surface\n(`contextmenu` event), and pointer-anchored positioning instead of\nelement-anchored. Pattern: WAI-APG Menu.\n\nTwo binding modes:\n **A. Wrap.** Default-slot child becomes the target:\n `<context-menu-ui><my-table>...</my-table>...items</context-menu-ui>`.\n **B. Selector.** Point at one or more existing elements via\n [target-selector]:\n `<context-menu-ui target-selector=\"#my-table\">...items</context-menu-ui>`.\n\nOn `contextmenu` event on a target: `preventDefault()`, position the\nmenu at the pointer coords, show via Popover API. Touch long-press\n(configurable via [long-press-ms]) does the same. Shift+F10 / Menu\nkey opens at the focused target's center for keyboard users.\n",
6
6
  "type": "object",
7
7
  "allOf": [
8
8
  {
@@ -17,7 +17,7 @@
17
17
  "const": "ContextMenu"
18
18
  },
19
19
  "for": {
20
- "description": "CSS selector(s) for target element(s). Empty = use default-slot child.",
20
+ "description": "DEPRECATED alias for [target-selector] — still a CSS selector, NOT an id-ref. Honored when [target-selector] is unset, with a one-shot console.warn pointing consumers at the replacement. New authoring should use [target-selector].",
21
21
  "type": "string",
22
22
  "default": ""
23
23
  },
@@ -30,6 +30,11 @@
30
30
  "description": "Programmatic open state. Set true to open at target center.",
31
31
  "type": "boolean",
32
32
  "default": false
33
+ },
34
+ "targetSelector": {
35
+ "description": "CSS selector(s) for target element(s). Empty = use default-slot child. Ratified name (gh#1764/#1780, ADR-0079) for what [for] used to carry — [for] means an id-ref everywhere else in AdiaUI (table-toolbar-ui, chart-legend-ui, tooltip-ui); context-menu-ui's own selector meaning predates that convention and was a same-name/ different-contract collision (ADR-0053's no-shadowing doctrine).",
36
+ "type": "string",
37
+ "default": ""
33
38
  }
34
39
  },
35
40
  "required": [
@@ -39,7 +44,7 @@
39
44
  "x-adiaui": {
40
45
  "anti_patterns": [
41
46
  {
42
- "fix": "Wrap a target: `<context-menu-ui><my-target></my-target>...items</context-menu-ui>` OR point at one: `<context-menu-ui for=\"#my-target\">...items</context-menu-ui>`.",
47
+ "fix": "Wrap a target: `<context-menu-ui><my-target></my-target>...items</context-menu-ui>` OR point at one: `<context-menu-ui target-selector=\"#my-target\">...items</context-menu-ui>`.",
43
48
  "why": "No target binding — the menu never opens.",
44
49
  "wrong": "<context-menu-ui>...just items...</context-menu-ui>"
45
50
  }
@@ -4,8 +4,16 @@
4
4
  * Two binding shapes:
5
5
  * A. Wrap a target — first non-menu-item-ui default-slot child becomes
6
6
  * the contextmenu host.
7
- * B. Point at targets via [for="<selector>"] — useful for whole-table
8
- * / whole-canvas menus where wrapping isn't practical.
7
+ * B. Point at targets via [target-selector="<css-selector>"] — useful for
8
+ * whole-table / whole-canvas menus where wrapping isn't practical.
9
+ * [for] is a DEPRECATED alias for this (gh#1764/#1780, ADR-0079): `for=`
10
+ * means an id-ref everywhere else in AdiaUI (table-toolbar-ui,
11
+ * chart-legend-ui, tooltip-ui) — context-menu-ui's own pre-ruling [for]
12
+ * carried a CSS-selector meaning instead, the exact same-attribute-
13
+ * different-contract shape ADR-0053's no-shadowing doctrine forbids.
14
+ * [target-selector] still resolves via `document.querySelectorAll()`
15
+ * (multiple targets); a still-set legacy [for] is honored with a
16
+ * one-shot console.warn pointing at the replacement.
9
17
  *
10
18
  * Architecture: reuses the existing anchor pattern (core/anchor.js) by
11
19
  * creating a virtual 1×1 anchor element at the pointer coords on each
@@ -23,8 +31,21 @@ import { anchorPopover } from '../../core/anchor.js';
23
31
 
24
32
  const LONG_PRESS_DEFAULT = 500;
25
33
 
34
+ // DEPRECATED [for]-alias one-shot warning guard (gh#1764/#1780, ADR-0079) —
35
+ // module-scope, matching this file's own idiom (a plain top-level const
36
+ // rather than a lazily-created public static field, which stayed mutable
37
+ // and externally visible for no reason).
38
+ const warnedForDeprecated = new WeakSet();
39
+
26
40
  export class UIContextMenu extends UIElement {
27
41
  static properties = {
42
+ // gh#1764/#1780, ADR-0079 — the ratified replacement for the
43
+ // selector-flavored [for] below; a plain CSS selector, not an id-ref.
44
+ targetSelector: { type: String, default: '', reflect: true, attribute: 'target-selector' },
45
+ // DEPRECATED — kept as a back-compat alias for `targetSelector` only.
46
+ // Honored (with a one-shot console.warn, see #legacyForSelector())
47
+ // whenever [target-selector] is unset. Never given id-ref semantics;
48
+ // the sweep did not attempt to dual-purpose this attribute.
28
49
  for: { type: String, default: '', reflect: true },
29
50
  open: { type: Boolean, default: false, reflect: true },
30
51
  longPressMs: { type: Number, default: LONG_PRESS_DEFAULT, reflect: false, attribute: 'long-press-ms' },
@@ -97,11 +118,12 @@ export class UIContextMenu extends UIElement {
97
118
  }
98
119
 
99
120
  #resolveTargets() {
100
- if (this.for) {
101
- try { return [...document.querySelectorAll(this.for)]; }
121
+ const selector = this.targetSelector || this.#legacyForSelector();
122
+ if (selector) {
123
+ try { return [...document.querySelectorAll(selector)]; }
102
124
  catch (e) {
103
125
  // eslint-disable-next-line no-console
104
- console.warn(`[context-menu-ui] invalid [for] selector: ${this.for}`, e);
126
+ console.warn(`[context-menu-ui] invalid [target-selector] selector: ${selector}`, e);
105
127
  return [];
106
128
  }
107
129
  }
@@ -113,6 +135,25 @@ export class UIContextMenu extends UIElement {
113
135
  return [];
114
136
  }
115
137
 
138
+ // DEPRECATED alias path (gh#1764/#1780, ADR-0079) — [for] used to carry
139
+ // context-menu-ui's own CSS-selector binding; [target-selector] is now
140
+ // the canonical name. Warn once per element, never per resolve (this
141
+ // runs on every #bindTargets() call, including reconnects).
142
+ #legacyForSelector() {
143
+ if (!this.for) return '';
144
+ if (!warnedForDeprecated.has(this)) {
145
+ warnedForDeprecated.add(this);
146
+ // eslint-disable-next-line no-console
147
+ console.warn(
148
+ `[context-menu-ui] [for="${this.for}"] is deprecated — use ` +
149
+ `[target-selector="${this.for}"] instead. [for] now means an ` +
150
+ 'id-ref everywhere else in AdiaUI; context-menu-ui\'s own prior ' +
151
+ '[for] carried a CSS-selector meaning (ADR-0079).',
152
+ );
153
+ }
154
+ return this.for;
155
+ }
156
+
116
157
  /* ── Trigger event handlers ──────────────────────────────────────── */
117
158
 
118
159
  #onContextMenu = (e) => {
@@ -8,8 +8,9 @@ element-anchored. Pattern: WAI-APG Menu.
8
8
  Two binding modes:
9
9
  **A. Wrap.** Default-slot child becomes the target:
10
10
  `<context-menu-ui><my-table>...</my-table>...items</context-menu-ui>`.
11
- **B. Selector.** Point at one or more existing elements via [for]:
12
- `<context-menu-ui for="#my-table">...items</context-menu-ui>`.
11
+ **B. Selector.** Point at one or more existing elements via
12
+ [target-selector]:
13
+ `<context-menu-ui target-selector="#my-table">...items</context-menu-ui>`.
13
14
 
14
15
  On `contextmenu` event on a target: `preventDefault()`, position the
15
16
  menu at the pointer coords, show via Popover API. Touch long-press
@@ -54,10 +55,14 @@ export interface ContextMenuSelectEventDetail {
54
55
  export type ContextMenuSelectEvent = CustomEvent<ContextMenuSelectEventDetail>;
55
56
 
56
57
  export class UIContextMenu extends UIElement {
57
- /** CSS selector(s) for target element(s). Empty = use default-slot child. */
58
+ /** DEPRECATED alias for [target-selector] — still a CSS selector, NOT an id-ref. Honored when [target-selector] is unset, with a one-shot console.warn pointing consumers at the replacement. New authoring should use [target-selector]. */
58
59
  for: string;
60
+ /** Long-press duration (ms) on touch devices to open the menu. */
61
+ longPressMs: number;
59
62
  /** Programmatic open state. Set true to open at target center. */
60
63
  open: boolean;
64
+ /** CSS selector(s) for target element(s). Empty = use default-slot child. Ratified name (gh#1764/#1780, ADR-0079) for what [for] used to carry — [for] means an id-ref everywhere else in AdiaUI (table-toolbar-ui, chart-legend-ui, tooltip-ui); context-menu-ui's own selector meaning predates that convention and was a same-name/ different-contract collision (ADR-0053's no-shadowing doctrine). */
65
+ targetSelector: string;
61
66
 
62
67
  addEventListener(type: 'context-menu-close', listener: (ev: ContextMenuCloseEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
63
68
  addEventListener(type: 'context-menu-open', listener: (ev: ContextMenuOpenEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
@@ -17,7 +17,7 @@
17
17
  </context-menu-ui>
18
18
  ```
19
19
 
20
- ## [for] selector mode — point at existing targets
20
+ ## [target-selector] mode — point at existing targets
21
21
 
22
22
  ```html
23
23
  <div style="display: grid; gap: var(--a-space-2);">
@@ -25,7 +25,7 @@
25
25
  <div data-row tabindex="0" style="padding: var(--a-space-3); border: 1px solid var(--md-sys-color-neutral-outline); border-radius: var(--a-radius-sm); background: var(--md-sys-color-neutral-background);">Row B — right-click</div>
26
26
  <div data-row tabindex="0" style="padding: var(--a-space-3); border: 1px solid var(--md-sys-color-neutral-outline); border-radius: var(--a-radius-sm); background: var(--md-sys-color-neutral-background);">Row C — right-click</div>
27
27
  </div>
28
- <context-menu-ui for='[data-chunk="context-menu-for"] [data-row]'>
28
+ <context-menu-ui target-selector='[data-chunk="context-menu-for"] [data-row]'>
29
29
  <menu-item-ui value="edit" text="Edit"></menu-item-ui>
30
30
  <menu-item-ui value="duplicate" text="Duplicate"></menu-item-ui>
31
31
  <menu-divider-ui></menu-divider-ui>
@@ -15,8 +15,9 @@ description: |
15
15
  Two binding modes:
16
16
  **A. Wrap.** Default-slot child becomes the target:
17
17
  `<context-menu-ui><my-table>...</my-table>...items</context-menu-ui>`.
18
- **B. Selector.** Point at one or more existing elements via [for]:
19
- `<context-menu-ui for="#my-table">...items</context-menu-ui>`.
18
+ **B. Selector.** Point at one or more existing elements via
19
+ [target-selector]:
20
+ `<context-menu-ui target-selector="#my-table">...items</context-menu-ui>`.
20
21
 
21
22
  On `contextmenu` event on a target: `preventDefault()`, position the
22
23
  menu at the pointer coords, show via Popover API. Touch long-press
@@ -25,8 +26,24 @@ description: |
25
26
  composes:
26
27
  - menu-item-ui
27
28
  props:
29
+ targetSelector:
30
+ description: >-
31
+ CSS selector(s) for target element(s). Empty = use default-slot
32
+ child. Ratified name (gh#1764/#1780, ADR-0079) for what [for] used
33
+ to carry — [for] means an id-ref everywhere else in AdiaUI
34
+ (table-toolbar-ui, chart-legend-ui, tooltip-ui); context-menu-ui's
35
+ own selector meaning predates that convention and was a same-name/
36
+ different-contract collision (ADR-0053's no-shadowing doctrine).
37
+ type: string
38
+ default: ""
39
+ reflect: true
40
+ attribute: target-selector
28
41
  for:
29
- description: CSS selector(s) for target element(s). Empty = use default-slot child.
42
+ description: >-
43
+ DEPRECATED alias for [target-selector] — still a CSS selector, NOT
44
+ an id-ref. Honored when [target-selector] is unset, with a one-shot
45
+ console.warn pointing consumers at the replacement. New authoring
46
+ should use [target-selector].
30
47
  type: string
31
48
  default: ""
32
49
  reflect: true
@@ -101,12 +118,12 @@ a2ui:
101
118
  reason: 'Trigger surface boundary.'
102
119
  - rule: 'Items are <menu-item-ui> children inside the default slot — same shape as <menu-ui> items.'
103
120
  reason: 'Single menu vocabulary.'
104
- - rule: 'Bind target via wrap (default-slot first non-menu-item-ui child) OR [for] selector. The selector form is useful for whole-table or whole-canvas menus where wrapping isn''t practical.'
121
+ - rule: 'Bind target via wrap (default-slot first non-menu-item-ui child) OR [target-selector]. The selector form is useful for whole-table or whole-canvas menus where wrapping isn''t practical. [for] still works as a deprecated alias but new authoring should use [target-selector] (gh#1764/#1780, ADR-0079).'
105
122
  reason: 'Two binding shapes.'
106
123
  anti_patterns:
107
124
  - wrong: '<context-menu-ui>...just items...</context-menu-ui>'
108
125
  why: 'No target binding — the menu never opens.'
109
- fix: 'Wrap a target: `<context-menu-ui><my-target></my-target>...items</context-menu-ui>` OR point at one: `<context-menu-ui for="#my-target">...items</context-menu-ui>`.'
126
+ fix: 'Wrap a target: `<context-menu-ui><my-target></my-target>...items</context-menu-ui>` OR point at one: `<context-menu-ui target-selector="#my-target">...items</context-menu-ui>`.'
110
127
  examples:
111
128
  - name: file-actions
112
129
  description: Right-click a file row for Open / Rename / Delete.
@@ -53,6 +53,11 @@
53
53
  "component": {
54
54
  "const": "Heatmap"
55
55
  },
56
+ "data": {
57
+ "description": "Cell data as a JSON array of `{ r, c, v, label? }` objects, or set the `.data` property directly with an already-parsed array. Parsed on connect if the attribute is present and `.data` hasn't already been set programmatically.",
58
+ "type": "string",
59
+ "default": ""
60
+ },
56
61
  "noLegend": {
57
62
  "description": "Hide the Less/More legend strip",
58
63
  "type": "boolean",
@@ -27,6 +27,8 @@ export class UIHeatmap extends UIElement {
27
27
  colorScheme: 'accent' | 'success' | 'warning' | 'danger' | 'info' | 'data-ramp';
28
28
  /** Column count */
29
29
  cols: number;
30
+ /** Cell data as a JSON array of `{ r, c, v, label? }` objects, or set the `.data` property directly with an already-parsed array. Parsed on connect if the attribute is present and `.data` hasn't already been set programmatically. */
31
+ data: string;
30
32
  /** Hide the Less/More legend strip */
31
33
  noLegend: boolean;
32
34
  /** Row count */
@@ -25,6 +25,14 @@ props:
25
25
  enum:
26
26
  - square
27
27
  - wide
28
+ data:
29
+ description: >-
30
+ Cell data as a JSON array of `{ r, c, v, label? }` objects, or set the
31
+ `.data` property directly with an already-parsed array. Parsed on
32
+ connect if the attribute is present and `.data` hasn't already been
33
+ set programmatically.
34
+ type: string
35
+ default: ""
28
36
  colorScheme:
29
37
  description: Color ramp
30
38
  type: string
@@ -166,7 +166,7 @@
166
166
  },
167
167
  {
168
168
  "fix": "{\"component\": \"CalendarPicker\", \"precision\": \"minute\"}\n",
169
- "why": "Same gap as \"date\" — no native datetime picker rendered. Use\nCalendarPicker with precision=\"minute\" for combined date + time\ninput (DatetimePicker was a deprecated thin alias, cut in 0.8.43,\ngh#1617 — CalendarPicker is the sole tag now).\n",
169
+ "why": "Same gap as \"date\" — no native datetime picker rendered. Use\nCalendarPicker with precision=\"minute\" for combined date + time\ninput (DatetimePicker was a deprecated thin alias, cut in 0.8.43,\ngh#1617 — CalendarPicker is the sole A2UI component now).\n",
170
170
  "wrong": "{\"component\": \"Input\", \"type\": \"datetime-local\"}\n"
171
171
  },
172
172
  {
@@ -186,7 +186,7 @@
186
186
  },
187
187
  {
188
188
  "fix": "{\"component\": \"ColorInput\"}\n",
189
- "why": "No native color swatch/picker rendered — just a plain text\nsurface. Use ColorInput for a compact form-bearing swatch +\npopover, or ColorArea directly for the full OKLCH picker surface\n(ColorPicker was a deprecated thin alias, cut in 0.8.43, gh#1617 —\nColorArea is the sole tag now).\n",
189
+ "why": "No native color swatch/picker rendered — just a plain text\nsurface. Use ColorInput for a compact form-bearing swatch +\npopover, or ColorArea directly for the full OKLCH picker surface\n(ColorPicker was a deprecated thin alias, cut in 0.8.43, gh#1617 —\nColorArea is the sole A2UI component now).\n",
190
190
  "wrong": "{\"component\": \"Input\", \"type\": \"color\"}\n"
191
191
  },
192
192
  {
@@ -53,6 +53,7 @@
53
53
  import { UIFormElement } from '../../core/form.js';
54
54
  import { isIconName, whenIconRegistryReady } from '../../core/icons.js';
55
55
  import { logicalSlotted } from '../../core/logical-children.js';
56
+ import { syncAutoAriaLabel } from '../../core/a11y.js';
56
57
 
57
58
  const renderAffix = (v) => isIconName(v)
58
59
  ? `<icon-ui name="${v}"></icon-ui>`
@@ -106,6 +107,12 @@ export class UIInput extends UIFormElement {
106
107
  #cachedGroup = '';
107
108
  #cachedSepFor = null;
108
109
  static #labelSeq = 0;
110
+ // gh#1647: tracks the aria-label VALUE this element itself last derived
111
+ // from `placeholder` (undefined when none), so re-renders refresh/clear
112
+ // OUR name without ever clobbering a consumer's own aria-label —
113
+ // value-tracking guard ported from tag.class.js/select.class.js
114
+ // (gh#1644/#1646).
115
+ #lastAutoAriaLabel = undefined;
109
116
 
110
117
  // Hold-to-repeat tuning. Initial delay before autorepeat begins, and the
111
118
  // interval between repeats. Values match the cadence of the native
@@ -471,13 +478,11 @@ export class UIInput extends UIFormElement {
471
478
  // Re-resolve prefix/suffix from the live property each render (FEEDBACK-93).
472
479
  this.#syncAffixSlots();
473
480
 
474
- if (this.label) {
475
- this.removeAttribute('aria-label');
476
- } else if (this.placeholder) {
477
- this.setAttribute('aria-label', this.placeholder);
478
- } else {
479
- this.removeAttribute('aria-label');
480
- }
481
+ // A11y (gh#1647): a visible `label` already names the field; otherwise
482
+ // fall back to `placeholder` — but never clobber a consumer's own
483
+ // aria-label (value-tracking guard, gh#1644/#1646).
484
+ const ariaSource = this.label ? '' : (this.placeholder || '');
485
+ this.#lastAutoAriaLabel = syncAutoAriaLabel(this, this.#lastAutoAriaLabel, ariaSource);
481
486
 
482
487
  if (this.#isNumberMode) {
483
488
  const n = this.valueAsNumber;
@@ -264,9 +264,10 @@ a2ui:
264
264
  <input type="password"> for masking (per ADR-0025).
265
265
  - >-
266
266
  Wrap <input-ui> in <field-ui label="…" hint="…" error="…"> for
267
- the canonical stacked label / hint / error chrome. The inline
268
- [label] / [hint] / [error] props are also supported on the
269
- primitive for compact use.
267
+ the canonical stacked label / hint / error chrome. input-ui itself
268
+ only supports inline [label] / [error] (gh#1669 [hint] is a
269
+ field-ui-only prop; input-ui has no equivalent) — use field-ui
270
+ when a hint line is needed.
270
271
  - >-
271
272
  Form participation is implicit via UIFormElement. Set [name] for
272
273
  FormData submission; [required] / [disabled] / [readonly]
@@ -309,7 +310,7 @@ anti_patterns:
309
310
  Same gap as "date" — no native datetime picker rendered. Use
310
311
  CalendarPicker with precision="minute" for combined date + time
311
312
  input (DatetimePicker was a deprecated thin alias, cut in 0.8.43,
312
- gh#1617 — CalendarPicker is the sole tag now).
313
+ gh#1617 — CalendarPicker is the sole A2UI component now).
313
314
  fix: |
314
315
  {"component": "CalendarPicker", "precision": "minute"}
315
316
  - wrong: |
@@ -342,7 +343,7 @@ anti_patterns:
342
343
  surface. Use ColorInput for a compact form-bearing swatch +
343
344
  popover, or ColorArea directly for the full OKLCH picker surface
344
345
  (ColorPicker was a deprecated thin alias, cut in 0.8.43, gh#1617 —
345
- ColorArea is the sole tag now).
346
+ ColorArea is the sole A2UI component now).
346
347
  fix: |
347
348
  {"component": "ColorInput"}
348
349
  - wrong: |
@@ -42,6 +42,8 @@ export class UIIntegrationCard extends UIElement {
42
42
  description: string;
43
43
  /** Disables the action button. */
44
44
  disabled: boolean;
45
+ /** When `status="error"`, the message to show below the description. Ignored for other statuses. */
46
+ errorMessage: string;
45
47
  /** Logo URL (preferred — renders as <img>) or icon name (renders as <icon-ui>). URLs are sniffed by presence of `/`. */
46
48
  logo: string;
47
49
  /** Display name shown as the card title. Required. */
@@ -27,7 +27,7 @@
27
27
  "default": false
28
28
  },
29
29
  "heading": {
30
- "description": "Optional kicker label. Section variant renders it via ::before; primary uses it as aria-label only.",
30
+ "description": "Optional kicker label. Section variant renders it via ::before; primary uses it as aria-label only. For a VISIBLE kicker inside a primary-variant rail — or more than one kicker per <nav-ui> — hand-place <span data-nav-label> in the default slot instead; see slots below.",
31
31
  "type": "string",
32
32
  "default": ""
33
33
  },
@@ -108,7 +108,7 @@
108
108
  ],
109
109
  "slots": {
110
110
  "default": {
111
- "description": "Primary slot — accepts <nav-group-ui> + <nav-item-ui> children, plus <hr data-nav-divider> for hand-placed dividers."
111
+ "description": "Primary slot — accepts <nav-group-ui> + <nav-item-ui> children, plus <hr data-nav-divider> for hand-placed dividers and <span data-nav-label> for hand-placed group-label kickers (titled runs of items/groups that aren't wrapped in a <nav-group-ui>). <span data-nav-label> renders with the same uppercase/tracking/muted kicker treatment as the [heading] ::before kicker, and is hidden alongside dividers whenever the primary-variant rail collapses ([collapsed] or ≤96px container width)."
112
112
  }
113
113
  },
114
114
  "states": [
@@ -32,7 +32,7 @@
32
32
  --nav-label-fg: var(--a-fg-muted);
33
33
  --nav-label-font-size: var(--a-kicker-sm);
34
34
  --nav-label-weight: var(--a-weight-medium);
35
- --nav-label-px: var(--a-space-2);
35
+ --nav-label-px: var(--a-space-4);
36
36
  --nav-label-py: var(--a-space-3) var(--a-space-1);
37
37
  }
38
38
 
@@ -36,7 +36,7 @@ export class UINav extends UIElement {
36
36
  collapsed: boolean;
37
37
  /** Auto-place dividers between adjacent groups + items. */
38
38
  divider: boolean;
39
- /** Optional kicker label. Section variant renders it via ::before; primary uses it as aria-label only. */
39
+ /** Optional kicker label. Section variant renders it via ::before; primary uses it as aria-label only. For a VISIBLE kicker inside a primary-variant rail — or more than one kicker per <nav-ui> — hand-place <span data-nav-label> in the default slot instead; see slots below. */
40
40
  heading: string;
41
41
  /** Opt out of the default single-expanded-group behavior. When unset, selecting a nav-item-ui collapses every nav-group-ui except the one containing the newly selected item (all, when the selection is ungrouped); manual multi-expansion still works up until the next selection. */
42
42
  multiExpand: boolean;