@adia-ai/web-components 0.8.18 → 0.8.20

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 (126) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/components/nav-group/nav-group.a2ui.json +5 -0
  3. package/components/nav-group/nav-group.class.js +14 -6
  4. package/components/nav-group/nav-group.css +5 -2
  5. package/components/nav-group/nav-group.d.ts +2 -0
  6. package/components/nav-group/nav-group.yaml +4 -0
  7. package/components/select/select.a2ui.json +6 -1
  8. package/components/select/select.class.js +34 -7
  9. package/components/select/select.d.ts +2 -0
  10. package/components/select/select.yaml +16 -1
  11. package/dist/host.min.css +1 -1
  12. package/dist/host.sheet.js +1 -1
  13. package/dist/theme-provider.min.js +1 -1
  14. package/dist/web-components.min.css +1 -1
  15. package/dist/web-components.min.js +18 -18
  16. package/dist/web-components.sheet.js +1 -1
  17. package/package.json +2 -1
  18. package/styles/colors/semantics/core.css +10 -4
  19. package/components/adia-mark/adia-mark.test.js +0 -69
  20. package/components/adia-wordmark/adia-wordmark.test.js +0 -69
  21. package/components/agent-artifact/agent-artifact.test.js +0 -61
  22. package/components/alert/alert.test.js +0 -364
  23. package/components/color-picker/color-picker.test.js +0 -96
  24. package/components/combobox/combobox.test.js +0 -256
  25. package/components/command/command.test.js +0 -99
  26. package/components/date-range-picker/date-range-picker.test.js +0 -387
  27. package/components/datetime-picker/datetime-picker.test.js +0 -454
  28. package/components/demo-toggle/demo-toggle.test.js +0 -102
  29. package/components/description-list/description-list.test.js +0 -72
  30. package/components/display-field/display-field.test.js +0 -115
  31. package/components/empty-state/empty-state.test.js +0 -77
  32. package/components/field/field.test.js +0 -260
  33. package/components/fields/fields.test.js +0 -88
  34. package/components/inline-message/inline-message.test.js +0 -203
  35. package/components/input/input.test.js +0 -335
  36. package/components/integration-card/integration-card.test.js +0 -318
  37. package/components/link/link.test.js +0 -143
  38. package/components/list/list.test.js +0 -106
  39. package/components/list-window/list-window.test.js +0 -303
  40. package/components/loading-overlay/loading-overlay.test.js +0 -257
  41. package/components/menu/menu.test.js +0 -176
  42. package/components/nav/nav.test.js +0 -428
  43. package/components/nav-group/nav-group.test.js +0 -67
  44. package/components/option-card/option-card.test.js +0 -32
  45. package/components/pagination/pagination.test.js +0 -195
  46. package/components/select/select.test.js +0 -453
  47. package/components/slider/slider.test.js +0 -202
  48. package/components/spinner/spinner.test.js +0 -272
  49. package/components/stat/stat.test.js +0 -108
  50. package/components/step-progress/step-progress.test.js +0 -129
  51. package/components/swatch/swatch.test.js +0 -563
  52. package/components/table/table.test.js +0 -479
  53. package/components/table-toolbar/table-toolbar.test.js +0 -152
  54. package/components/tag/tag.test.js +0 -137
  55. package/components/tags-input/tags-input.test.js +0 -477
  56. package/components/text/text.test.js +0 -202
  57. package/components/theme-provider/theme-provider.test.js +0 -94
  58. package/components/time-picker/time-picker.test.js +0 -287
  59. package/components/timeline/timeline.test.js +0 -31
  60. package/components/toggle-scheme/toggle-scheme.test.js +0 -110
  61. package/components/tree/tree.test.js +0 -312
  62. package/core/element.test.js +0 -389
  63. package/core/icons.test.js +0 -187
  64. package/core/logical-children.test.js +0 -54
  65. package/core/markdown.test.js +0 -47
  66. package/core/responsive.test.js +0 -121
  67. package/core/template.test.js +0 -611
  68. package/traits/active-state/active-state.test.js +0 -28
  69. package/traits/anchor-positioning/anchor-positioning.test.js +0 -122
  70. package/traits/announcer/announcer.test.js +0 -268
  71. package/traits/arrow-grid-nav/arrow-grid-nav.test.js +0 -375
  72. package/traits/attention-pulse/attention-pulse.test.js +0 -26
  73. package/traits/confetti/confetti.test.js +0 -46
  74. package/traits/confetti-burst/confetti-burst.test.js +0 -46
  75. package/traits/count-up/count-up.test.js +0 -54
  76. package/traits/declarative.test.js +0 -138
  77. package/traits/dirty-state/dirty-state.test.js +0 -45
  78. package/traits/drag-ghost/drag-ghost.test.js +0 -19
  79. package/traits/draggable/draggable.test.js +0 -60
  80. package/traits/draggable-list-item/draggable-list-item.test.js +0 -51
  81. package/traits/drop-target/drop-target.test.js +0 -241
  82. package/traits/droppable/droppable.test.js +0 -54
  83. package/traits/droppable-collection/droppable-collection.test.js +0 -99
  84. package/traits/error-shake/error-shake.test.js +0 -114
  85. package/traits/fade-presence/fade-presence.test.js +0 -20
  86. package/traits/focus-restore/focus-restore.test.js +0 -202
  87. package/traits/focus-trap/focus-trap.test.js +0 -42
  88. package/traits/focusable/focusable.test.js +0 -53
  89. package/traits/glow-focus/glow-focus.test.js +0 -31
  90. package/traits/gradient-shift/gradient-shift.test.js +0 -22
  91. package/traits/haptic-feedback/haptic-feedback.test.js +0 -52
  92. package/traits/hotkey/hotkey.test.js +0 -61
  93. package/traits/hoverable/hoverable.test.js +0 -24
  94. package/traits/inertia-drag/inertia-drag.test.js +0 -33
  95. package/traits/input-mask/input-mask.test.js +0 -151
  96. package/traits/intersection-observer/intersection-observer.test.js +0 -38
  97. package/traits/keyboard-nav/keyboard-nav.test.js +0 -41
  98. package/traits/keyboard-reorderable/keyboard-reorderable.test.js +0 -45
  99. package/traits/layout-animation/layout-animation.test.js +0 -114
  100. package/traits/long-press/long-press.test.js +0 -244
  101. package/traits/magnetic-hover/magnetic-hover.test.js +0 -30
  102. package/traits/noise-texture/noise-texture.test.js +0 -20
  103. package/traits/parallax/parallax.test.js +0 -26
  104. package/traits/portal/portal.test.js +0 -30
  105. package/traits/pressable/pressable.test.js +0 -73
  106. package/traits/resettable/resettable.test.js +0 -100
  107. package/traits/resizable/resizable.test.js +0 -39
  108. package/traits/resize-observer/resize-observer.test.js +0 -38
  109. package/traits/ripple/ripple.test.js +0 -32
  110. package/traits/roving-tabindex/roving-tabindex.test.js +0 -28
  111. package/traits/scale-press/scale-press.test.js +0 -39
  112. package/traits/scroll-lock/scroll-lock.test.js +0 -45
  113. package/traits/scroll-progress/scroll-progress.test.js +0 -182
  114. package/traits/shimmer-loading/shimmer-loading.test.js +0 -43
  115. package/traits/smoke.test.js +0 -111
  116. package/traits/snap-to-grid/snap-to-grid.test.js +0 -40
  117. package/traits/sound-feedback/sound-feedback.test.js +0 -26
  118. package/traits/spring-animate/spring-animate.test.js +0 -28
  119. package/traits/success-checkmark/success-checkmark.test.js +0 -120
  120. package/traits/tilt-hover/tilt-hover.test.js +0 -32
  121. package/traits/tossable/tossable.test.js +0 -31
  122. package/traits/traits-host.test.js +0 -73
  123. package/traits/typeahead/typeahead.test.js +0 -38
  124. package/traits/typewriter/typewriter.test.js +0 -47
  125. package/traits/validation/validation.test.js +0 -93
  126. package/traits/view-transition/view-transition.test.js +0 -268
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog — @adia-ai/web-components
2
2
 
3
+ ## [0.8.20] — 2026-07-28
4
+
5
+ ### Fixed
6
+ - **The npm tarball no longer ships `*.test.js`** (gh#462) — the `files` roster excluded examples/html but never unit tests, so 108 test file(s) rode the 0.8.19 tarball. `!**/*.test.js` appended as the roster's LAST entry (npm's files globbing is order-sensitive — a leading exclude is overridden by later directory includes). Proof: `npm pack --dry-run` → 0 test files, runtime files intact.
7
+
8
+ ### Changed
9
+ - **`nav-group-ui`: the selected-look header color also keys on `[selected]`** — the CSS half of the gh#459 ruling (PR #460 corrected the icon weight, but the `--nav-group-fg-selected` header treatment still keyed on `[open]`, so an expanded-but-inactive group read as the active section while a collapsed-but-active one read plain — the exact screenshot that reopened this). The `[open]` indent rail stays structural.
10
+ - **`nav-group-ui`: the filled header glyph tracks `[selected]`, not `[open]`** (gh#459, operator ruling) — the fill marks the group containing the selected route; a merely-expanded group stays regular, and a collapsed-but-active one fills. `selected` becomes a declared reflected prop (yaml + types); `[open]` keeps driving `aria-expanded` only. Consumers that already set `[selected]` from active-route logic (the adiav2 admin shell does) get the corrected behavior with no changes.
11
+
12
+ ### Maintenance
13
+ - **`components/` touched in this release window** (6 file(s), e.g. `nav-group/nav-group.a2ui.json`) — carried by the entries above.
14
+ - **`dist/` bundles rebuilt** in this cut's window (4 file(s)) — regenerated from the source changes described above, not independent edits.
15
+
16
+ ## [0.8.19] — 2026-07-27
17
+
18
+ ### Added
19
+ - **`select-ui[summary-label]` — multi-select trigger as one text line, not chips** (gh#442, ruled from the adiav2 admin dashboard filter bar). With the attribute set, a single selection shows its own label and N > 1 shows "Label (N)" ("Organizations (2)"); none shows the placeholder. The popover's checkbox rows, Backspace-removes-last, the comma-separated form value, `[select-all]`, and `[clearable]` semantics are unchanged, and chip mode stays the default when the attribute is absent (`[data-multi-chips]` is simply not set in summary mode, so the trigger keeps the plain single-line layout). Motivation: in a narrow filter-bar trigger, three selected organizations wrapped into a vertically ballooning chip stack; `max-chips` still renders chips and could not express the ruling. Entry re-homed from the root CHANGELOG (package-local change; PR #445 filed it there mid-flight while #452's re-home was landing).
20
+
21
+ ### Fixed
22
+ - **Row hover no longer goes near-black — `--a-bg-hover`/`--a-bg-active` (and the `--a-canvas-*` twins) move off the fill ladder onto the container alpha ladder** (`styles/colors/semantics/core.css`; operator directive 2026-07-27). The roles pointed at `neutral-hover`/`neutral-active` — the FILLED-BUTTON stops, opaque 650/700 — so all ten row/option/cell consumers (list, tree, select, command, calendar-grid, calendar-picker, date-range-picker, tag, agent-artifact) tinted toward black on hover, while the adjacent `--a-bg-selected` note already prescribed "hover = container-low". Now: hover = `container-low` (10%), active = `container` (20%, the selected/tonal tier). The fill ladder itself is untouched — filled buttons keep 600/650/700.
23
+ - **The unit suite can no longer touch real network** (`test-setup.js`) — happy-dom's `fetch` does real I/O against its default `http://localhost:3000` base URL, so test outcomes depended on which dev servers happened to be running (the v0.8.17 cut lost a gate run to an ECONNREFUSED flake in `onboarding-checklist.test.js`). A fast-rejecting guard now replaces `globalThis.fetch`; tests exercising fetch paths keep installing their own per-file mocks, which override it.
24
+
25
+ ### Maintenance
26
+ - **`components/` touched in this release window** (6 file(s), e.g. `select/select.a2ui.json`) — carried by the entries above.
27
+ - **`dist/` bundles rebuilt** in this cut's window (6 file(s)) — regenerated from the source changes described above, not independent edits.
28
+
3
29
  ## [0.8.18] — 2026-07-27
4
30
 
5
31
  ### Maintenance
@@ -36,6 +36,11 @@
36
36
  "type": "boolean",
37
37
  "default": false
38
38
  },
39
+ "selected": {
40
+ "description": "Marks the group containing the selected route (consumers set it from active-route logic). Drives the filled header glyph — selection, never mere expansion (gh#459).",
41
+ "type": "boolean",
42
+ "default": false
43
+ },
39
44
  "text": {
40
45
  "description": "Visible group label.",
41
46
  "type": "string",
@@ -35,6 +35,10 @@ export class UINavGroup extends UIElement {
35
35
  icon: { type: String, default: '', reflect: true },
36
36
  badge: { type: String, default: '', reflect: true },
37
37
  open: { type: Boolean, default: false, reflect: true },
38
+ // gh#459: marks the group that CONTAINS the selected route — drives the
39
+ // filled header glyph (selection, never mere expansion) and the CSS
40
+ // selected treatment. Consumers set it from their active-route logic.
41
+ selected: { type: Boolean, default: false, reflect: true },
38
42
  collapsible: { type: Boolean, default: true, reflect: true },
39
43
  variant: { type: String, default: '', reflect: true },
40
44
  };
@@ -87,13 +91,17 @@ export class UINavGroup extends UIElement {
87
91
 
88
92
  this.setAttribute('aria-expanded', String(this.open));
89
93
 
90
- // Mirrors nav-item-ui's selected → fill convention: an open group
91
- // reads as the active section via the same filled-glyph treatment
92
- // [open] already drives (nav-group.css's [open] > [slot=header] color
93
- // rule) a state-driven weight swap, not a CSS color change, since
94
- // Phosphor ships filled/regular as distinct SVGs.
94
+ // Mirrors nav-item-ui's selected → fill convention: the filled glyph
95
+ // marks SELECTION, not expansion (gh#459, operator ruling 2026-07-27)
96
+ // the group CONTAINING the selected route fills (consumers set [selected]
97
+ // on it), while a merely-expanded group stays regular. Keyed on the
98
+ // attribute so it works whether or not a consumer uses the property
99
+ // path; [open] keeps driving aria-expanded only. A state-driven weight
100
+ // swap, not a CSS color change, since Phosphor ships filled/regular as
101
+ // distinct SVGs.
95
102
  const iconEl = this.querySelector('[slot="header"] [slot="icon"] icon-ui');
96
- if (iconEl) iconEl.setAttribute('weight', this.open ? 'fill' : 'regular');
103
+ if (iconEl)
104
+ iconEl.setAttribute('weight', this.hasAttribute('selected') ? 'fill' : 'regular');
97
105
  }
98
106
 
99
107
  showPopover() {
@@ -49,8 +49,11 @@
49
49
  background: var(--nav-group-indent-rail-bg);
50
50
  pointer-events: none;
51
51
  }
52
- :scope[open] > [slot="header"],
53
- :scope[open] > [slot="header"] [slot="icon"] {
52
+ /* Selected-look header treatment keys on [selected] the CSS half of
53
+ gh issue 459 (PR 460 corrected the icon weight): the group CONTAINING
54
+ the selected route reads strong; a merely-expanded group does not. */
55
+ :scope[selected] > [slot="header"],
56
+ :scope[selected] > [slot="header"] [slot="icon"] {
54
57
  color: var(--nav-group-fg-selected);
55
58
  }
56
59
 
@@ -38,6 +38,8 @@ export class UINavGroup extends UIElement {
38
38
  icon: string;
39
39
  /** Inline-expanded state. Toggled by header click when [collapsible]. */
40
40
  open: boolean;
41
+ /** Marks the group containing the selected route (consumers set it from active-route logic). Drives the filled header glyph — selection, never mere expansion (gh#459). */
42
+ selected: boolean;
41
43
  /** Visible group label. */
42
44
  text: string;
43
45
  /** Visual treatment. Default ('') renders as a primary-rail group (icon row, caret, collapsible). 'section' renders the header as a static kicker label with always-visible children. When the parent <nav-ui> carries variant="section", this group inherits it via CSS cascade unless an explicit variant is set on the group. */
@@ -37,6 +37,10 @@ props:
37
37
  type: boolean
38
38
  default: false
39
39
  description: "Inline-expanded state. Toggled by header click when [collapsible]."
40
+ selected:
41
+ type: boolean
42
+ default: false
43
+ description: "Marks the group containing the selected route (consumers set it from active-route logic). Drives the filled header glyph — selection, never mere expansion (gh#459)."
40
44
  collapsible:
41
45
  type: boolean
42
46
  default: true
@@ -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/Select.json",
4
4
  "title": "Select",
5
- "description": "Single- and multi-select dropdown primitive — the canonical AdiaUI\nselect control. Form-bearing via UIFormElement: [name], [value],\n[required], [disabled], fires `change`. Options via native\n<option> / <optgroup> children, programmatic `.options` array, or\nJSON [data-options] (hydrated by <editor-shell>'s wireSelects).\nUse for single-select with > 4 options; for ≤ 4 options use\n<segmented-ui> or <radio-ui>. Multi-select via [multiple]: trigger\nrenders <tag-ui> chips per selection, popover shows checkbox-style\noption list, Backspace removes the last chip, [select-all] adds\nbulk controls, [max-chips] caps the visible chip count with a\n\"+N more\" pill. Form value stays a comma-separated string under\n[name] for native <form> compatibility (parse with .split(',')).\n",
5
+ "description": "Single- and multi-select dropdown primitive — the canonical AdiaUI\nselect control. Form-bearing via UIFormElement: [name], [value],\n[required], [disabled], fires `change`. Options via native\n<option> / <optgroup> children, programmatic `.options` array, or\nJSON [data-options] (hydrated by <editor-shell>'s wireSelects).\nUse for single-select with > 4 options; for ≤ 4 options use\n<segmented-ui> or <radio-ui>. Multi-select via [multiple]: trigger\nrenders <tag-ui> chips per selection, popover shows checkbox-style\noption list, Backspace removes the last chip, [select-all] adds\nbulk controls, [max-chips] caps the visible chip count with a\n\"+N more\" pill, and [summary-label] swaps the chip trigger for one\ntext line — the single selection's label, or \"Label (N)\" for more.\nForm value stays a comma-separated string under\n[name] for native <form> compatibility (parse with .split(',')).\n",
6
6
  "type": "object",
7
7
  "allOf": [
8
8
  {
@@ -150,6 +150,11 @@
150
150
  ],
151
151
  "default": "md"
152
152
  },
153
+ "summary-label": {
154
+ "description": "Multi-select only (gh#442). Replaces the chip trigger with one\ntext line: a single selection shows its own label; N > 1 shows\n\"<summary-label> (N)\" — e.g. \"Organizations (2)\"; none shows the\nplaceholder. Popover checkbox rows, Backspace-removes-last, the\ncomma-separated form value, [select-all], and [clearable] are\nunchanged. Absent (default) = tag-ui chip trigger. Prefer this\nform for narrow triggers (filter bars) where wrapping chips\nwould balloon the control.\n",
155
+ "type": "string",
156
+ "default": null
157
+ },
153
158
  "value": {
154
159
  "description": "Currently selected option value. With declarative <option> children, a child carrying the native `selected` attribute sets the initial value when this attribute is absent (native <select> parity).",
155
160
  "type": "string",
@@ -60,6 +60,10 @@ export class UISelect extends UIFormElement {
60
60
  // These are no-ops without [multiple] but reflect so [maxChips]
61
61
  // selectors etc. work in CSS even when authored declaratively.
62
62
  maxChips: { type: Number, default: 0, reflect: true, attribute: 'max-chips' },
63
+ // gh#442: summary trigger for [multiple] — when set, the trigger shows
64
+ // one text line (a single selection's own label, or "Label (N)" for
65
+ // N > 1) instead of tag-ui chips. Absent = chip mode, unchanged.
66
+ summaryLabel: { type: String, default: '', reflect: true, attribute: 'summary-label' },
63
67
  min: { type: Number, default: 0, reflect: true },
64
68
  max: { type: Number, default: 0, reflect: true },
65
69
  selectAll: { type: Boolean, default: false, reflect: true, attribute: 'select-all' },
@@ -404,7 +408,9 @@ export class UISelect extends UIFormElement {
404
408
  // optional clear-x + optional "+N more" pill alongside the usual
405
409
  // leading / display / caret. We toggle host attribute [data-multi-chips]
406
410
  // so CSS can switch layout from single-line text to flex-wrap chip row.
407
- if (this.multiple) this.setAttribute('data-multi-chips', '');
411
+ // gh#442 — [summary-label] keeps the single-line text layout: the
412
+ // summary renders through [slot="display"], no chip row.
413
+ if (this.multiple && !this.summaryLabel) this.setAttribute('data-multi-chips', '');
408
414
  else this.removeAttribute('data-multi-chips');
409
415
 
410
416
  // A11y (FB-88): name a placeholder-only select from its placeholder, like
@@ -859,17 +865,18 @@ export class UISelect extends UIFormElement {
859
865
  #stampChips() {
860
866
  const slot = this.querySelector('[slot="trigger"] [data-chips]');
861
867
  if (!slot) return;
868
+ // gh#442 — summary mode renders no chips; clear any stamped ones so a
869
+ // dynamic switch out of chip mode leaves no stale tag-ui behind.
870
+ if (this.summaryLabel) {
871
+ while (slot.firstChild) slot.removeChild(slot.firstChild);
872
+ return;
873
+ }
862
874
  const values = this.#values();
863
875
  const cap = Number(this.maxChips) || 0;
864
876
  const cappedValues = cap > 0 && values.length > cap ? values.slice(0, cap) : values;
865
877
  const overflow = cap > 0 && values.length > cap ? values.length - cap : 0;
866
878
  // Label lookup from internal options model (handles grouped form too).
867
- const flat = this.#options.flatMap((o) => o.options || [o])
868
- .filter((o) => !o.separator && !o.header && o.value != null);
869
- const labelOf = (v) => {
870
- const found = flat.find((o) => String(o.value) === String(v));
871
- return found ? found.label : String(v);
872
- };
879
+ const labelOf = (v) => this.#labelOf(v);
873
880
  // Rebuild — chips are cheap and reconciliation gets us correct order
874
881
  // + correct labels with no per-chip tracking state.
875
882
  while (slot.firstChild) slot.removeChild(slot.firstChild);
@@ -893,8 +900,28 @@ export class UISelect extends UIFormElement {
893
900
  }
894
901
  }
895
902
 
903
+ /**
904
+ * Label for one value from the internal options model (grouped form
905
+ * included); falls back to the raw value string. Shared by the chip
906
+ * stamper and the gh#442 summary trigger.
907
+ */
908
+ #labelOf(v) {
909
+ const flat = this.#options.flatMap((o) => o.options || [o])
910
+ .filter((o) => !o.separator && !o.header && o.value != null);
911
+ const found = flat.find((o) => String(o.value) === String(v));
912
+ return found ? found.label : String(v);
913
+ }
914
+
896
915
  #displayText() {
897
916
  if (this.multiple) {
917
+ // gh#442 — [summary-label]: one selection shows its own label, more
918
+ // show "Label (N)", none shows the placeholder.
919
+ if (this.summaryLabel) {
920
+ const values = this.#values();
921
+ if (values.length === 0) return this.placeholder;
922
+ if (values.length === 1) return this.#labelOf(values[0]);
923
+ return `${this.summaryLabel} (${values.length})`;
924
+ }
898
925
  // SPEC-040 — chips render the selection; display element holds the
899
926
  // placeholder when empty and is otherwise empty (CSS sees [data-multi-chips]).
900
927
  return this.value ? '' : this.placeholder;
@@ -58,6 +58,8 @@ export class UISelect extends UIFormElement {
58
58
  divider: boolean;
59
59
  /** SPEC-040 — cap visible chips in multi-select trigger. 0 = unlimited. */
60
60
  maxChips: number;
61
+ /** gh#442 — multi-select summary trigger: one selection shows its label, N > 1 shows "Label (N)"; empty string = chip mode. */
62
+ summaryLabel: string;
61
63
  /** SPEC-040 — minimum required selections in multi-select mode. */
62
64
  min: number;
63
65
  /** SPEC-040 — maximum allowed selections in multi-select mode. 0 = unlimited. */
@@ -18,7 +18,9 @@ description: |
18
18
  renders <tag-ui> chips per selection, popover shows checkbox-style
19
19
  option list, Backspace removes the last chip, [select-all] adds
20
20
  bulk controls, [max-chips] caps the visible chip count with a
21
- "+N more" pill. Form value stays a comma-separated string under
21
+ "+N more" pill, and [summary-label] swaps the chip trigger for one
22
+ text line — the single selection's label, or "Label (N)" for more.
23
+ Form value stays a comma-separated string under
22
24
  [name] for native <form> compatibility (parse with .split(',')).
23
25
  # Per ADR-0027 — primitives that programmatically create other primitives
24
26
  # do NOT auto-import them. Consumer (or demo shell) must explicitly import.
@@ -120,6 +122,19 @@ props:
120
122
  type: number
121
123
  default: 0
122
124
  reflect: true
125
+ summary-label:
126
+ description: |
127
+ Multi-select only (gh#442). Replaces the chip trigger with one
128
+ text line: a single selection shows its own label; N > 1 shows
129
+ "<summary-label> (N)" — e.g. "Organizations (2)"; none shows the
130
+ placeholder. Popover checkbox rows, Backspace-removes-last, the
131
+ comma-separated form value, [select-all], and [clearable] are
132
+ unchanged. Absent (default) = tag-ui chip trigger. Prefer this
133
+ form for narrow triggers (filter bars) where wrapping chips
134
+ would balloon the control.
135
+ type: string
136
+ default: null
137
+ reflect: true
123
138
  min:
124
139
  description: |
125
140
  Multi-select only. Minimum required selections. Below this floor