@adia-ai/web-components 0.8.42 → 0.8.44

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 (186) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/MIGRATION.md +130 -80
  3. package/USAGE.md +1 -1
  4. package/color/index.js +1 -1
  5. package/components/action-list/action-item.a2ui.json +3 -8
  6. package/components/action-list/action-item.yaml +10 -16
  7. package/components/action-list/action-list.class.js +5 -61
  8. package/components/action-list/action-list.d.ts +1 -3
  9. package/components/avatar/avatar.class.js +12 -1
  10. package/components/button/button.d.ts +4 -0
  11. package/components/button/button.yaml +1 -1
  12. package/components/calendar-picker/calendar-picker.a2ui.json +1 -1
  13. package/components/calendar-picker/calendar-picker.class.js +2 -3
  14. package/components/calendar-picker/calendar-picker.css +16 -18
  15. package/components/calendar-picker/calendar-picker.yaml +2 -2
  16. package/components/card/card.css +3 -3
  17. package/components/chart/chart.a2ui.json +29 -18
  18. package/components/chart/chart.class.js +1308 -266
  19. package/components/chart/chart.css +151 -1
  20. package/components/chart/chart.d.ts +9 -9
  21. package/components/chart/chart.yaml +63 -31
  22. package/components/chart-legend/chart-legend.a2ui.json +20 -14
  23. package/components/chart-legend/chart-legend.class.js +79 -58
  24. package/components/chart-legend/chart-legend.css +65 -1
  25. package/components/chart-legend/chart-legend.d.ts +5 -5
  26. package/components/chart-legend/chart-legend.examples.md +4 -4
  27. package/components/chart-legend/chart-legend.yaml +50 -20
  28. package/components/check/check.css +9 -0
  29. package/components/color-area/color-area.a2ui.json +1 -1
  30. package/components/color-area/color-area.class.js +2 -2
  31. package/components/color-area/color-area.css +4 -7
  32. package/components/color-area/color-area.d.ts +1 -1
  33. package/components/color-area/color-area.yaml +3 -3
  34. package/components/color-input/color-input.a2ui.json +1 -1
  35. package/components/color-input/color-input.class.js +1 -1
  36. package/components/color-input/color-input.yaml +2 -2
  37. package/components/combobox/combobox.a2ui.json +3 -0
  38. package/components/combobox/combobox.class.js +9 -8
  39. package/components/combobox/combobox.css +25 -4
  40. package/components/combobox/combobox.yaml +6 -0
  41. package/components/command/command.css +17 -1
  42. package/components/context-menu/context-menu.d.ts +2 -0
  43. package/components/drilldown/drilldown.a2ui.json +3 -0
  44. package/components/drilldown/drilldown.css +8 -1
  45. package/components/drilldown/drilldown.yaml +2 -0
  46. package/components/field/field.class.js +5 -5
  47. package/components/heatmap/heatmap.a2ui.json +5 -0
  48. package/components/heatmap/heatmap.d.ts +2 -0
  49. package/components/heatmap/heatmap.yaml +8 -0
  50. package/components/index.js +0 -4
  51. package/components/input/input.a2ui.json +4 -4
  52. package/components/input/input.class.js +20 -7
  53. package/components/input/input.css +6 -0
  54. package/components/input/input.yaml +15 -11
  55. package/components/integration-card/integration-card.d.ts +2 -0
  56. package/components/menu/menu-item.a2ui.json +3 -8
  57. package/components/menu/menu-item.yaml +13 -21
  58. package/components/menu/menu.class.js +5 -27
  59. package/components/menu/menu.d.ts +1 -3
  60. package/components/noodles/noodles.a2ui.json +0 -15
  61. package/components/noodles/noodles.yaml +0 -12
  62. package/components/number-format/number-format.class.js +10 -0
  63. package/components/pagination/pagination.class.js +88 -4
  64. package/components/pane/pane.a2ui.json +2 -12
  65. package/components/pane/pane.class.js +5 -30
  66. package/components/pane/pane.d.ts +3 -5
  67. package/components/pane/pane.yaml +7 -19
  68. package/components/popover/popover.d.ts +2 -0
  69. package/components/progress/progress.a2ui.json +1 -1
  70. package/components/progress/progress.class.js +13 -8
  71. package/components/progress/progress.d.ts +1 -1
  72. package/components/progress/progress.yaml +2 -2
  73. package/components/radio/radio.css +7 -0
  74. package/components/range/range.class.js +9 -1
  75. package/components/search/search.css +10 -1
  76. package/components/segment/segment.a2ui.json +1 -2
  77. package/components/segment/segment.css +10 -0
  78. package/components/segment/segment.yaml +1 -2
  79. package/components/segmented/segmented.a2ui.json +1 -3
  80. package/components/segmented/segmented.class.js +2 -2
  81. package/components/segmented/segmented.yaml +2 -4
  82. package/components/select/select.a2ui.json +5 -1
  83. package/components/select/select.class.js +14 -14
  84. package/components/select/select.css +20 -1
  85. package/components/select/select.yaml +7 -1
  86. package/components/step-progress/step-progress.yaml +1 -1
  87. package/components/stream/stream.d.ts +2 -0
  88. package/components/switch/switch.css +8 -0
  89. package/components/table/table.a2ui.json +6 -1
  90. package/components/table/table.class.js +18 -1
  91. package/components/table/table.css +31 -6
  92. package/components/table/table.d.ts +3 -1
  93. package/components/table/table.examples.md +2 -2
  94. package/components/table/table.yaml +9 -1
  95. package/components/table-toolbar/table-toolbar.a2ui.json +67 -2
  96. package/components/table-toolbar/table-toolbar.class.js +492 -17
  97. package/components/table-toolbar/table-toolbar.css +311 -0
  98. package/components/table-toolbar/table-toolbar.d.ts +17 -1
  99. package/components/table-toolbar/table-toolbar.yaml +220 -15
  100. package/components/tag/tag.class.js +13 -1
  101. package/components/text/text.a2ui.json +3 -3
  102. package/components/text/text.d.ts +5 -3
  103. package/components/text/text.yaml +3 -3
  104. package/components/textarea/textarea.class.js +9 -1
  105. package/components/textarea/textarea.css +4 -0
  106. package/components/time-picker/time-picker.yaml +3 -2
  107. package/components/tour/tour.d.ts +10 -0
  108. package/components/tree/tree.yaml +1 -1
  109. package/components/upload/upload.class.js +16 -0
  110. package/components/upload/upload.d.ts +7 -0
  111. package/components/upload/upload.yaml +7 -3
  112. package/core/a11y.d.ts +22 -0
  113. package/core/a11y.js +64 -0
  114. package/core/anchor.js +9 -5
  115. package/core/element.js +1 -1
  116. package/core/icons.js +2 -5
  117. package/custom-elements.json +142 -594
  118. package/dist/host.min.css +1 -1
  119. package/dist/host.sheet.js +1 -1
  120. package/dist/theme-provider.min.js +1 -1
  121. package/dist/web-components.min.css +1 -1
  122. package/dist/web-components.min.js +122 -119
  123. package/dist/web-components.sheet.js +1 -1
  124. package/index.d.ts +0 -15
  125. package/package.json +1 -1
  126. package/patterns/access-requests/access-requests.examples.html +1 -1
  127. package/patterns/admin-keys-and-export/admin-keys-and-export.examples.html +7 -7
  128. package/patterns/agent-cost/agent-cost.examples.html +6 -6
  129. package/patterns/agent-memory/agent-memory.examples.html +3 -3
  130. package/patterns/agent-prompt-library/agent-prompt-library.examples.html +8 -8
  131. package/patterns/agent-tool-call/agent-tool-call.examples.html +1 -1
  132. package/patterns/approvals/approvals.examples.html +6 -6
  133. package/patterns/audit-log/audit-log.examples.html +1 -1
  134. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.html +4 -4
  135. package/patterns/chart-in-card/chart-in-card.examples.html +124 -10
  136. package/patterns/chart-in-card/chart-in-card.examples.js +37 -0
  137. package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +5 -5
  138. package/patterns/conversion-funnel/conversion-funnel.html +2 -2
  139. package/patterns/data-tables-inline-edit-and-tree/data-tables-inline-edit-and-tree.examples.html +2 -2
  140. package/patterns/diff-review/diff-review.examples.html +6 -6
  141. package/patterns/editor-shell/editor-shell.examples.html +3 -3
  142. package/patterns/export-flow/export-flow.examples.html +2 -2
  143. package/patterns/filter-bar/filter-bar.examples.html +2 -2
  144. package/patterns/forms-conditional-and-autocomplete/forms-conditional-and-autocomplete.examples.html +2 -2
  145. package/patterns/inline-dialog/inline-dialog.examples.html +2 -2
  146. package/patterns/marketing-engagement/marketing-engagement.examples.html +4 -4
  147. package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +1 -1
  148. package/patterns/permissions-matrix/permissions-matrix.examples.html +1 -1
  149. package/patterns/permissions-sharing/permissions-sharing.examples.html +2 -2
  150. package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +4 -4
  151. package/patterns/search-discovery/search-discovery.examples.html +2 -2
  152. package/styles/colors/semantics/features.css +8 -1
  153. package/styles/components.css +0 -4
  154. package/styles/type/roles.css +29 -9
  155. package/traits/arrow-grid-nav/arrow-grid-nav.js +1 -1
  156. package/traits/resize-observer/resize-observer.js +1 -1
  157. package/components/color-picker/color-picker.a2ui.json +0 -148
  158. package/components/color-picker/color-picker.class.js +0 -46
  159. package/components/color-picker/color-picker.css +0 -14
  160. package/components/color-picker/color-picker.d.ts +0 -99
  161. package/components/color-picker/color-picker.examples.md +0 -19
  162. package/components/color-picker/color-picker.js +0 -17
  163. package/components/color-picker/color-picker.yaml +0 -175
  164. package/components/datetime-picker/datetime-picker.a2ui.json +0 -256
  165. package/components/datetime-picker/datetime-picker.class.js +0 -58
  166. package/components/datetime-picker/datetime-picker.css +0 -11
  167. package/components/datetime-picker/datetime-picker.d.ts +0 -87
  168. package/components/datetime-picker/datetime-picker.examples.md +0 -25
  169. package/components/datetime-picker/datetime-picker.js +0 -17
  170. package/components/datetime-picker/datetime-picker.yaml +0 -254
  171. package/components/progress-row/progress-row.a2ui.json +0 -107
  172. package/components/progress-row/progress-row.class.js +0 -118
  173. package/components/progress-row/progress-row.css +0 -60
  174. package/components/progress-row/progress-row.d.ts +0 -24
  175. package/components/progress-row/progress-row.examples.md +0 -7
  176. package/components/progress-row/progress-row.js +0 -17
  177. package/components/progress-row/progress-row.yaml +0 -85
  178. package/components/toggle-group/toggle-group.a2ui.json +0 -148
  179. package/components/toggle-group/toggle-group.class.js +0 -195
  180. package/components/toggle-group/toggle-group.css +0 -127
  181. package/components/toggle-group/toggle-group.d.ts +0 -50
  182. package/components/toggle-group/toggle-group.examples.md +0 -33
  183. package/components/toggle-group/toggle-group.js +0 -18
  184. package/components/toggle-group/toggle-group.yaml +0 -135
  185. package/components/toggle-group/toggle-option.a2ui.json +0 -86
  186. package/components/toggle-group/toggle-option.yaml +0 -75
@@ -18,10 +18,38 @@
18
18
  --table-toolbar-title-weight: var(--a-weight-medium);
19
19
  --table-toolbar-title-gap: var(--a-space-2);
20
20
 
21
+ /* ── Range summary ("Showing X–Y of N") ── */
22
+ --table-toolbar-summary-fg: var(--md-sys-color-neutral-on-surface-variant);
23
+ --table-toolbar-summary-size: var(--a-ui-size);
24
+
21
25
  /* ── Search ── */
22
26
  --table-toolbar-search-min: 14rem;
23
27
  --table-toolbar-search-max: 22rem;
24
28
 
29
+ /* ── Search-tight stage (ADR-0076, REQ-S-002) ──
30
+ Stepped-down search field extents applied under the
31
+ search-tight/icon-only/overflow @container query below (or the
32
+ matching [stage] pin). Public so a consumer can retune the
33
+ compacted width without forking component CSS, same posture as
34
+ every other --table-toolbar-* token. */
35
+ --table-toolbar-search-tight-min: 9rem;
36
+ --table-toolbar-search-tight-max: 14rem;
37
+
38
+ /* ── Compaction breakpoints (ADR-0076, REQ-M-006) ──
39
+ Normative default pixel widths, token-indirected so a consumer can
40
+ retune without forking component CSS. Boundaries are inclusive on
41
+ the narrower side (max-width semantics) — a container at exactly
42
+ the boundary width renders the narrower of the two adjacent
43
+ stages. MUST stay monotonically decreasing
44
+ (bp-search-tight > bp-icon-only > bp-overflow); no stage-specific
45
+ @container rules exist yet (step 1 lands only this substrate —
46
+ steps 2/3/5 land each stage's own CSS). Same three thresholds
47
+ data-stage-resolved's ResizeObserver (step 7) will classify
48
+ against — one source of truth, never a second hardcoded copy. */
49
+ --table-toolbar-bp-search-tight: 56rem;
50
+ --table-toolbar-bp-icon-only: 40rem;
51
+ --table-toolbar-bp-overflow: 26rem;
52
+
25
53
  /* Popover tokens removed — popover escapes to top layer and
26
54
  cannot inherit @scope tokens. Raw --a-* tokens are used
27
55
  directly in the unscoped popover rules below (see comment
@@ -34,6 +62,151 @@
34
62
  box-sizing: border-box;
35
63
  display: block;
36
64
  color: var(--md-sys-color-neutral-on-surface);
65
+ /* Width-only compaction measurement substrate (ADR-0076, REQ-M-001).
66
+ inline-size containment only — never `size` — per chart.css's own
67
+ documented size-containment trap (container-type: size forces
68
+ block-size containment, collapsing any height-less element; chart
69
+ needs it only because it queries aspect-ratio, table-toolbar's axis
70
+ is width alone). Steps 3/5 land icon-only/overflow's own CSS
71
+ against this same container. */
72
+ container-type: inline-size;
73
+ container-name: table-toolbar;
74
+ }
75
+
76
+ /* ═══════ Search-tight stage ═══════ (ADR-0076, REQ-S-002/REQ-O-001, step 2)
77
+ Auto-snap: the container query fires the step-down once the toolbar's
78
+ own inline-size crosses --table-toolbar-bp-search-tight (56rem
79
+ default). `max-width` is inclusive-on-the-narrower-side by construction
80
+ (REQ-M-006) — a container at exactly the boundary width already renders
81
+ search-tight. `:not([stage])`/`[stage=""]` together cover "no pin" —
82
+ an explicit [stage="full"] pin must NOT auto-snap even inside a narrow
83
+ container (REQ-O-001, the pin wins), so the auto-snap selector
84
+ excludes every OTHER pinned value too.
85
+
86
+ BUG (caught in review, CodeRabbit on PR #1672, verified live in a real
87
+ browser before fixing): the token reassignment below targets
88
+ `[data-toolbar]`, a DESCENDANT of `:scope` — never `:scope` itself. A
89
+ container query only matches elements whose NEAREST ANCESTOR
90
+ establishes the named container; the container element is never its
91
+ own ancestor, so `@container table-toolbar (...) { :scope { ... } }`
92
+ can never match the host that declared `container-type` in the first
93
+ place — it's permanently-dead CSS, regardless of the container's
94
+ actual width. The custom properties still cascade correctly from
95
+ `[data-toolbar]` down to `[data-search]` (a further descendant), so
96
+ targeting the wrapper one level down is both the fix and the reason
97
+ it works: normal inheritance, no container-query involvement needed
98
+ for the property to reach its consumer.
99
+
100
+ CSS custom properties cannot be referenced inside an `@container` size
101
+ query's condition (only `style()` container queries read custom
102
+ properties, and those query computed values, not literal breakpoints)
103
+ — so the boundary below is the literal `56rem`, bound in this comment
104
+ to --table-toolbar-bp-search-tight's own default one line above; the
105
+ table-toolbar.test.js source-assertion suite pins the two values equal
106
+ so this literal can never silently drift from the token. */
107
+ @container table-toolbar (max-width: 56rem) {
108
+ :scope:is(:not([stage]), [stage=""]) > [data-toolbar] {
109
+ --table-toolbar-search-min: var(--table-toolbar-search-tight-min);
110
+ --table-toolbar-search-max: var(--table-toolbar-search-tight-max);
111
+ }
112
+ }
113
+
114
+ /* Explicit pin (REQ-O-001) — forces the search-tight extents regardless
115
+ of the container's actual measured width. Also applies at
116
+ [stage="icon-only"]/[stage="overflow"] (steps 3/5's own, MORE compact
117
+ pins): each later stage is strictly more compact than the last
118
+ (REQ-S), so the search field never widens back out just because a
119
+ tighter stage hasn't landed its own CSS yet. */
120
+ :scope:is([stage="search-tight"], [stage="icon-only"], [stage="overflow"]) {
121
+ --table-toolbar-search-min: var(--table-toolbar-search-tight-min);
122
+ --table-toolbar-search-max: var(--table-toolbar-search-tight-max);
123
+ }
124
+
125
+ /* ═══════ Icon-only stage ═══════ (ADR-0076, REQ-S-003/REQ-M-003/REQ-M-004,
126
+ step 3) — [data-controls] buttons drop their rendered label; the range
127
+ summary's own compaction/hide (REQ-S-003, step 4) rides the SAME
128
+ breakpoint just below, since both are decided by the same container
129
+ crossing --table-toolbar-bp-icon-only.
130
+
131
+ ::after content:none overrides button.css's own `:scope[text]::after {
132
+ content: attr(text) }` generated-content rule (REQ-M-003) — aria-label
133
+ is untouched (button-ui stamps it from [text] unconditionally in its
134
+ own render(), REQ-X-001), so the accessible name survives regardless of
135
+ the CSS-hidden visual label. The selector below is deliberately more
136
+ specific than button.css's own (adds the `:scope` ancestor + `[text]`)
137
+ so it wins the cascade outright rather than relying on scope-proximity
138
+ tie-breaking, which would resolve in button.css's OWN favor at equal
139
+ specificity (its rule is proximity-0 to its own host). Geometry mirrors
140
+ button-ui's own [data-icon-only] treatment exactly (REQ-M-004) —
141
+ --button-px/width are button-ui's own exposed tokens, set here from the
142
+ table-toolbar side the same way [data-sort-row][data-active] overrides
143
+ menu-item-ui's tokens above. Literal `40rem` bound to
144
+ --table-toolbar-bp-icon-only's default (same duplication rationale +
145
+ source-assertion test as the search-tight boundary above). */
146
+ @container table-toolbar (max-width: 40rem) {
147
+ :scope:is(:not([stage]), [stage=""]) [data-toolbar-btn][text]::after {
148
+ content: none;
149
+ }
150
+ :scope:is(:not([stage]), [stage=""]) [data-toolbar-btn] {
151
+ --button-px: 0;
152
+
153
+ width: var(--button-height);
154
+ }
155
+ }
156
+
157
+ :scope:is([stage="icon-only"], [stage="overflow"]) [data-toolbar-btn][text]::after {
158
+ content: none;
159
+ }
160
+ :scope:is([stage="icon-only"], [stage="overflow"]) [data-toolbar-btn] {
161
+ --button-px: 0;
162
+
163
+ width: var(--button-height);
164
+ }
165
+
166
+ /* ═══════ Range-summary conditional compact/hide ═══════ (ADR-0076
167
+ REQ-S-003, step 4) — at the SAME icon-only breakpoint as above:
168
+ - [data-page-size] present AND visible (no [hidden]) → its own
169
+ pagination context already carries the range info, so [data-summary]
170
+ hides entirely (its info is genuinely redundant here).
171
+ - [data-page-size] absent/hidden → [data-summary] is the toolbar's
172
+ ONLY visible range context, so it never fully disappears; instead
173
+ it swaps its full text for the short form (see [data-summary-compact]
174
+ below, "1–25/320" vs "Showing 1–25 of 320").
175
+ Which branch applies is a pure :has()/:not() container-width read — no
176
+ JS drives this (REQ-M-002); #updateSummary() populates BOTH
177
+ [data-summary-text] and [data-summary-compact] together whenever a
178
+ valid range exists, and CSS alone decides which one paints. */
179
+ @container table-toolbar (max-width: 40rem) {
180
+ :scope:is(:not([stage]), [stage=""]):has([data-page-size]:not([hidden])) [data-summary] {
181
+ display: none;
182
+ }
183
+ :scope:is(:not([stage]), [stage=""]):not(:has([data-page-size]:not([hidden]))) [data-summary-text] {
184
+ display: none;
185
+ }
186
+ /* :not([hidden]) — reviewer finding on PR #1674: without it, this
187
+ author-origin `display: inline` OUTRANKS the UA-origin
188
+ `[hidden] { display: none }` rule regardless of specificity (origin
189
+ comes before specificity in the cascade), so a JS-hidden
190
+ [data-summary-compact] (#updateSummary()'s empty-slot/loading
191
+ branches, which set compact.hidden = true) would still paint —
192
+ stale compact text alongside the empty-state content after a
193
+ positive range transitions to range-total="0" in icon-only mode.
194
+ Same class of gotcha this file already reinforces explicitly for
195
+ every other JS-toggled node ([data-toolbar-btn][hidden],
196
+ [data-search][hidden], [data-toolbar-overflow][hidden], etc). */
197
+ :scope:is(:not([stage]), [stage=""]):not(:has([data-page-size]:not([hidden]))) [data-summary-compact]:not([hidden]) {
198
+ display: inline;
199
+ }
200
+ }
201
+
202
+ :scope:is([stage="icon-only"], [stage="overflow"]):has([data-page-size]:not([hidden])) [data-summary] {
203
+ display: none;
204
+ }
205
+ :scope:is([stage="icon-only"], [stage="overflow"]):not(:has([data-page-size]:not([hidden]))) [data-summary-text] {
206
+ display: none;
207
+ }
208
+ :scope:is([stage="icon-only"], [stage="overflow"]):not(:has([data-page-size]:not([hidden]))) [data-summary-compact]:not([hidden]) {
209
+ display: inline;
37
210
  }
38
211
 
39
212
  [data-toolbar] {
@@ -68,6 +241,17 @@
68
241
  --table-toolbar-px: var(--a-space-4);
69
242
  }
70
243
 
244
+ /* ═══════ Scope slot ═══════ (gh#1615 — leading region before the title cluster) */
245
+
246
+ [data-scope] {
247
+ display: inline-flex;
248
+ align-items: center;
249
+ gap: var(--table-toolbar-cluster-gap);
250
+ flex: 0 0 auto;
251
+ }
252
+
253
+ [data-scope]:empty { display: none; }
254
+
71
255
  /* ═══════ Title cluster ═══════ */
72
256
 
73
257
  [data-title] {
@@ -87,6 +271,40 @@
87
271
 
88
272
  [data-toolbar-count-badge][hidden] { display: none; }
89
273
 
274
+ /* ═══════ Range summary ═══════ (gh#1615 — "Showing X–Y of N", independent of [data-title]) */
275
+
276
+ [data-summary] {
277
+ flex: 0 0 auto;
278
+ }
279
+
280
+ [data-summary][hidden] { display: none; }
281
+
282
+ [data-summary-text] {
283
+ font-size: var(--table-toolbar-summary-size);
284
+ color: var(--table-toolbar-summary-fg);
285
+ line-height: var(--a-font-leading-snug);
286
+ }
287
+
288
+ /* Short-form compact text ("1–25/320", ADR-0076 REQ-S-003) — hidden by
289
+ default (`display: none`, not just [hidden]) since it only ever paints
290
+ inside the icon-only-without-page-size branch above; that branch is the
291
+ ONLY place this flips to `display: inline`. Same typography as
292
+ [data-summary-text] — it's the same information, just terser. */
293
+ [data-summary-compact] {
294
+ display: none;
295
+ font-size: var(--table-toolbar-summary-size);
296
+ color: var(--table-toolbar-summary-fg);
297
+ line-height: var(--a-font-leading-snug);
298
+ }
299
+
300
+ /* ═══════ Empty-state slot ═══════ (ADR-0076 REQ-E — renders in
301
+ [data-summary]'s own position, alongside [data-summary-text]; only one
302
+ of the two is ever visible at a time, per #updateSummary(). Plain
303
+ author-supplied markup (REQ-E-003) — no forced typography here, unlike
304
+ [data-summary-text] above, which styles OUR OWN generated text. */
305
+
306
+ [data-summary-empty][hidden] { display: none; }
307
+
90
308
  /* ═══════ Controls cluster ═══════ */
91
309
 
92
310
  [data-controls] {
@@ -96,8 +314,63 @@
96
314
  flex: 0 0 auto;
97
315
  }
98
316
 
317
+ /* ADR-0076 REQ-C-003 — with [chrome-only] set, all three buttons carry
318
+ [hidden] (see #updateControlVisibility()); this collapses the cluster
319
+ to zero footprint via the same :empty{display:none} shape [data-scope]/
320
+ [data-actions]/[data-actions-leading] already use above, defensively
321
+ covering the case where no button children are stamped at all. */
322
+ [data-controls]:empty { display: none; }
323
+
99
324
  [data-toolbar-btn][hidden] { display: none; }
100
325
 
326
+ /* ═══════ Overflow stage ═══════ (ADR-0076 REQ-S-004/REQ-X-003/004,
327
+ PLAN step 5) — collapses Filter/Sort/Columns into one "More" trigger
328
+ at the tightest breakpoint. The three granular buttons STAY in the
329
+ DOM (never created/destroyed across a stage transition — the simplest
330
+ way to keep REQ-X-004's tab-order guarantee correct); CSS alone
331
+ decides which cluster paints (REQ-M-002). [data-toolbar-overflow] is
332
+ the <menu-ui> wrapper stamped by #mkOverflowTrigger() — hiding/showing
333
+ it hides/shows its [slot="trigger"] button together with its (closed)
334
+ menu-item-ui children in one rule, matching how every other stage
335
+ toggle in this file targets one wrapper element rather than each
336
+ child individually. Literal `26rem` bound to
337
+ --table-toolbar-bp-overflow's own default (same duplication +
338
+ source-assertion discipline as the search-tight/icon-only boundaries
339
+ above). */
340
+ [data-toolbar-overflow] { display: none; }
341
+
342
+ @container table-toolbar (max-width: 26rem) {
343
+ :scope:is(:not([stage]), [stage=""]) [data-toolbar-btn] {
344
+ display: none;
345
+ }
346
+ :scope:is(:not([stage]), [stage=""]) [data-toolbar-overflow] {
347
+ display: inline-flex;
348
+ }
349
+ }
350
+
351
+ :scope[stage="overflow"] [data-toolbar-btn] {
352
+ display: none;
353
+ }
354
+ :scope[stage="overflow"] [data-toolbar-overflow] {
355
+ display: inline-flex;
356
+ }
357
+
358
+ /* [hidden] (per-kind chrome-only/no-* suppression, #updateControlVisibility())
359
+ must win over the stage-driven `display: inline-flex` rules above —
360
+ both for the reason `[data-toolbar-btn][hidden]`/`[data-search][hidden]`
361
+ etc reinforce it above (the browser's own UA-origin `[hidden]{display:
362
+ none}` rule is LOWER origin priority than any author rule, so an
363
+ unreinforced [hidden] would lose to author CSS) AND because this
364
+ selector must match or exceed the (0,3,0) specificity of the
365
+ `:scope … [data-toolbar-overflow]` show-rules above to win the tie —
366
+ `:scope [data-toolbar-overflow][hidden]` (0,3,0), placed after both in
367
+ source order, satisfies that. This covers the whole-trigger-suppressed
368
+ case (chrome-only, or all three no-* set) — the per-menu-item-ui
369
+ hidden state inside the (closed) menu needs no CSS help since
370
+ [hidden]'s UA default applies cleanly to a plain child with no
371
+ conflicting author rule. */
372
+ :scope [data-toolbar-overflow][hidden] { display: none; }
373
+
101
374
  /* ═══════ Search ═══════ */
102
375
 
103
376
  [data-search] {
@@ -111,6 +384,44 @@
111
384
 
112
385
  [data-search][hidden] { display: none; }
113
386
 
387
+ /* ═══════ Actions-leading slot ═══════ (gh#1649 — app-owned triggers, e.g.
388
+ custom Filter/Columns buttons, that land BEFORE the page-size select)
389
+ Carries the SAME margin-inline-start:auto as [data-search]/[data-page-size]
390
+ so the trailing group still pushes to the far edge even when EVERY
391
+ native affordance ahead of it is dropped ([no-search] + [no-filter]/
392
+ [no-sort]/[no-columns], the adiav2 shape) and this slot becomes the
393
+ first visible member of the trailing cluster. Multiple auto-margin
394
+ siblings in one flex row is safe — only the first one with unclaimed
395
+ free space to its left actually receives it (same note as [data-page-size]
396
+ below). */
397
+
398
+ [data-actions-leading] {
399
+ display: inline-flex;
400
+ align-items: center;
401
+ gap: var(--table-toolbar-cluster-gap);
402
+ flex: 0 0 auto;
403
+ margin-inline-start: auto;
404
+ }
405
+
406
+ [data-actions-leading]:empty { display: none; }
407
+
408
+ /* ═══════ Page-size select ═══════ (gh#1615)
409
+ Carries the SAME margin-inline-start:auto as [data-search] so the
410
+ trailing group (controls/search/page-size/actions) still pushes to the
411
+ far edge when [no-search] drops search — without changing search's own
412
+ existing default for consumers that never set [page-size-options]
413
+ (the element doesn't render at all until then; see [hidden] below and
414
+ #updatePageSizeSelect). Multiple auto-margin siblings in one flex row
415
+ is safe — only the first one with unclaimed free space to its left
416
+ actually receives it. */
417
+
418
+ [data-page-size] {
419
+ flex: 0 0 auto;
420
+ margin-inline-start: auto;
421
+ }
422
+
423
+ [data-page-size][hidden] { display: none; }
424
+
114
425
  /* ═══════ Actions slot ═══════ */
115
426
 
116
427
  [data-actions] {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `<table-toolbar-ui>` — Header / companion bar for a sibling table-ui. Renders title + count badge, filter / sort / columns popovers, and a search input — all wired to the target table via an [for] id-ref. Modeled on chart-legend-ui's [for] binding pattern. Drop next to (or above) any table-ui to add the standard data-grid toolbar without re-implementing search, filter, sort, or column visibility. Filter rows auto-pick a primitive per column: ≤ 50 distinct values → multi- select (searchable when ≥ 12 options), id-like keys → free-text contains. The column descriptor's `filter` field overrides the auto-detect: `'select'` forces multi-select even on high-cardinality columns; `'text'` forces a contains input even on small enums.
2
+ * `<table-toolbar-ui>` — Header / companion bar for a sibling table-ui. Renders an optional leading [slot="scope"] region, title + count badge, an optional "Showing X–Y of N" range summary, filter / sort / columns popovers, a search input, an optional [slot="actions-leading"] region, an optional page-size select, and a trailing [slot="actions"] region — all wired to the target table via an [for] id-ref. Modeled on chart-legend-ui's [for] binding pattern. Drop next to (or above) any table-ui to add the standard data-grid toolbar without re-implementing search, filter, sort, column visibility, or pagination summary/page-size. Filter rows auto-pick a primitive per column: ≤ 50 distinct values → multi-select (searchable when ≥ 12 options), id-like keys → free-text contains. The column descriptor's `filter` field overrides the auto-detect: `'select'` forces multi-select even on high-cardinality columns; `'text'` forces a contains input even on small enums.
3
3
  *
4
4
  * @see https://ui-kit.exe.xyz/site/components/table-toolbar
5
5
  *
@@ -14,10 +14,13 @@ import { UIElement } from '../../core/element.js';
14
14
 
15
15
  export type TableToolbarColumnsChangeEvent = CustomEvent<unknown>;
16
16
  export type TableToolbarFilterChangeEvent = CustomEvent<unknown>;
17
+ export type TableToolbarPageSizeChangeEvent = CustomEvent<unknown>;
17
18
  export type TableToolbarSearchEvent = CustomEvent<unknown>;
18
19
  export type TableToolbarSortChangeEvent = CustomEvent<unknown>;
19
20
 
20
21
  export class UITableToolbar extends UIElement {
22
+ /** Suppress all four native controls/search at once (filter, sort, columns, search) as additive sugar over noFilter/noSort/noColumns/ noSearch (ADR-0076, ADIA2-9123). Precedence is pure, absolute OR — while set, all four stay off regardless of any individual no-* attribute's own value, with no partial re-enable path; to re-enable one control, remove chrome-only entirely and set the other three no-* attributes explicitly instead. The four granular attributes are not deprecated or removed — they remain the independently-addressable shipped API; chrome-only never replaces them, it's a convenience preset on top. */
23
+ chromeOnly: boolean;
21
24
  /** Optional count badge value shown next to the title. When unset, falls back to the row count of the bound table. */
22
25
  count: string;
23
26
  /** id-ref of the table-ui to control. Falls back to the first sibling table-ui within the same parent when omitted. */
@@ -30,8 +33,20 @@ export class UITableToolbar extends UIElement {
30
33
  noSearch: boolean;
31
34
  /** Hide the Sort popover button. Sort is shown by default; set to opt out. */
32
35
  noSort: boolean;
36
+ /** Current rows-per-page value, shown as the selected option in the page-size select. Falls back to the first entry of pageSizeOptions when unset (0) or not present among the options. Applied directly to the bound table's own [paginate] prop (rows-per-page), the same way filter/sort/columns changes are applied to the target — mirror this prop only to reflect state to URL / persistence / analytics. */
37
+ pageSize: number;
38
+ /** Rows-per-page choices rendered as `<option>`s in the page-size select (e.g. `[10, 25, 50, 100]`). JS property (`.pageSizeOptions = [...]`), or a declarative JSON-array `page-size-options="[10,25,50]"` attribute hydrated once at connect — the same dynamic-array pattern table-ui's own `columns`/`data` props use. Empty (the default) hides the page-size select entirely — additive opt-in, no footprint on existing consumers. */
39
+ pageSizeOptions: string;
33
40
  /** Placeholder text for the search input. */
34
41
  placeholder: string;
42
+ /** 1-indexed last row number of the current page, for the range summary (e.g. `25` in "Showing 1–25 of 320"). See rangeStart. */
43
+ rangeEnd: number;
44
+ /** 1-indexed first row number of the current page, for the "Showing X–Y of N" range summary (e.g. `1` in "Showing 1–25 of 320"). Renders alongside — never in place of — [text]/[count]; the range summary is its own cluster, independent of the title cluster. All three of rangeStart/rangeEnd/rangeTotal must be set (> 0) for the summary to render; omit all three (the default) to opt out entirely. */
45
+ rangeStart: number;
46
+ /** Total row count across all pages, for the range summary (e.g. `320` in "Showing 1–25 of 320"). Distinct from pagination-ui's `total` (total PAGES) — this is total ROWS; deliberately not spelled `total` to avoid that cross-sibling name collision (ADR-0063 B5). See rangeStart. */
47
+ rangeTotal: number;
48
+ /** Explicit compaction-stage override (ADR-0076). Unset (the default, empty string) means auto-snap: the toolbar's own inline-size container query picks the nearest stage from its live width against the three --table-toolbar-bp-* breakpoints, snapping discretely, never interpolating. Setting stage explicitly pins that stage's rendering regardless of the container's actual width — overriding the container query, mirroring chart-ui's ratio override shape. Primary use: visual-eval fixtures that need a deterministic stage without resizing a real container, and a consumer embedding the toolbar inside a known-narrow panel who wants to skip auto-detection entirely. All four stages' CSS is now live (ADR-0076): search-tight steps the search field down (step 2); icon-only drops control labels and compacts/hides the range summary (steps 3+4); overflow collapses Filter/Sort/Columns into one "More" trigger routed through the same menu-ui/menu-item-ui pattern used elsewhere in the corpus (step 5). The resolved stage — the pin if set, otherwise the live width classification — also reflects onto the host as `data-stage-resolved` (step 7, informational only, mirrors chart-ui's `data-ratio-resolved` convention — never drives table-toolbar's own rendering, which stays 100% CSS-driven). */
49
+ stage: '' | 'full' | 'search-tight' | 'icon-only' | 'overflow';
35
50
  /** Title text shown on the left. */
36
51
  text: string;
37
52
  /** Toolbar visual variant. `default` renders bare on parent surface; `card` adds the same chrome as a card-ui header. */
@@ -39,6 +54,7 @@ export class UITableToolbar extends UIElement {
39
54
 
40
55
  addEventListener(type: 'columns-change', listener: (ev: TableToolbarColumnsChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
41
56
  addEventListener(type: 'filter-change', listener: (ev: TableToolbarFilterChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
57
+ addEventListener(type: 'page-size-change', listener: (ev: TableToolbarPageSizeChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
42
58
  addEventListener(type: 'search', listener: (ev: TableToolbarSearchEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
43
59
  addEventListener(type: 'sort-change', listener: (ev: TableToolbarSortChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
44
60
  addEventListener<K extends keyof HTMLElementEventMap>(