@adia-ai/web-components 0.8.23 → 0.8.25

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 (69) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/components/agent-questions/agent-questions.yaml +0 -2
  3. package/components/alert/alert.class.js +5 -0
  4. package/components/alert/alert.yaml +2 -0
  5. package/components/anchor-bar/anchor-bar.a2ui.json +139 -0
  6. package/components/anchor-bar/anchor-bar.class.js +124 -0
  7. package/components/anchor-bar/anchor-bar.css +140 -0
  8. package/components/anchor-bar/anchor-bar.d.ts +35 -0
  9. package/components/anchor-bar/anchor-bar.examples.md +32 -0
  10. package/components/anchor-bar/anchor-bar.js +17 -0
  11. package/components/anchor-bar/anchor-bar.yaml +167 -0
  12. package/components/calendar-grid/calendar-grid.yaml +3 -0
  13. package/components/card/card.css +12 -0
  14. package/components/chart/chart.a2ui.json +16 -0
  15. package/components/chart/chart.class.js +105 -27
  16. package/components/chart/chart.css +27 -0
  17. package/components/chart/chart.d.ts +4 -0
  18. package/components/chart/chart.yaml +28 -0
  19. package/components/combobox/combobox.class.js +1 -1
  20. package/components/combobox/combobox.yaml +0 -1
  21. package/components/date-range-picker/date-range-picker.class.js +1 -1
  22. package/components/date-range-picker/date-range-picker.yaml +0 -1
  23. package/components/datetime-picker/datetime-picker.class.js +1 -1
  24. package/components/datetime-picker/datetime-picker.yaml +0 -1
  25. package/components/drawer/drawer.class.js +5 -0
  26. package/components/drawer/drawer.yaml +2 -0
  27. package/components/feed/feed.class.js +5 -0
  28. package/components/feed/feed.yaml +2 -0
  29. package/components/index.js +1 -0
  30. package/components/input/input.class.js +5 -0
  31. package/components/input/input.yaml +3 -0
  32. package/components/list/list.css +13 -4
  33. package/components/modal/modal.class.js +5 -0
  34. package/components/modal/modal.yaml +2 -0
  35. package/components/nav-group/nav-group.class.js +5 -0
  36. package/components/nav-group/nav-group.yaml +2 -0
  37. package/components/pagination/pagination.yaml +3 -0
  38. package/components/select/select.class.js +2 -2
  39. package/components/select/select.yaml +0 -1
  40. package/components/stat/stat.a2ui.json +8 -0
  41. package/components/stat/stat.css +39 -0
  42. package/components/stat/stat.d.ts +2 -0
  43. package/components/stat/stat.js +5 -0
  44. package/components/stat/stat.yaml +11 -0
  45. package/components/swiper/swiper.class.js +5 -0
  46. package/components/swiper/swiper.yaml +3 -0
  47. package/components/toolbar/toolbar.class.js +20 -0
  48. package/components/toolbar/toolbar.yaml +2 -0
  49. package/components/tooltip/tooltip.class.js +17 -11
  50. package/dist/host.min.css +1 -1
  51. package/dist/host.sheet.js +1 -1
  52. package/dist/theme-provider.min.js +2 -2
  53. package/dist/web-components.min.css +1 -1
  54. package/dist/web-components.min.js +117 -117
  55. package/dist/web-components.sheet.js +1 -1
  56. package/package.json +1 -1
  57. package/patterns/admin-keys-and-export/admin-keys-and-export.examples.html +3 -3
  58. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.html +81 -93
  59. package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.js +12 -25
  60. package/patterns/bulk-action-toolbar/bulk-action-toolbar.html +8 -52
  61. package/patterns/changelog-feed/changelog-feed.examples.html +3 -3
  62. package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +4 -4
  63. package/patterns/data-tables-inline-edit-and-tree/data-tables-inline-edit-and-tree.examples.html +2 -2
  64. package/patterns/marketing-engagement/marketing-engagement.examples.html +8 -8
  65. package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +1 -1
  66. package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +2 -2
  67. package/styles/api/parametric-scope.css +81 -0
  68. package/styles/components.css +1 -0
  69. package/styles/tokens.css +1 -0
@@ -66,6 +66,11 @@ export class UIInput extends UIFormElement {
66
66
  // motivated the deprecation.
67
67
  static labelDeprecated = false;
68
68
 
69
+ // Phosphor icons this primitive auto-stamps (without consumer markup).
70
+ // Aggregated by installIconLoadersForRegistered() across all defined
71
+ // elements. Audited by check-required-icons.mjs.
72
+ static requiredIcons = ['caret-up', 'caret-down'];
73
+
69
74
  static properties = {
70
75
  ...UIFormElement.properties,
71
76
  placeholder: { type: String, default: '', reflect: true },
@@ -238,6 +238,9 @@ tokens:
238
238
  description: Override border radius
239
239
  --input-transition:
240
240
  description: Override transition timing
241
+ requiredIcons:
242
+ - caret-up
243
+ - caret-down
241
244
  a2ui:
242
245
  rules:
243
246
  - >-
@@ -107,11 +107,11 @@
107
107
  }
108
108
 
109
109
  /* Anatomy (header-row model):
110
- row 1 = icon (col 1) · text/title (col 2) · action (col 3), all aligned
111
- on the title row. row 2 = description, spanning col 2 / -1 (under the
110
+ row 1 = text/title (col 2) · action (col 3), aligned on the title row.
111
+ icon (col 1) spans both rows, centered on the whole item (gh#558).
112
+ row 2 = description, spanning col 2 / -1 (under the
112
113
  action) so multi-line supporting text uses the full content width rather
113
- than being boxed into col 2 beside the action. For icon+text-only items
114
- there is a single row, so `grid-row: 1` is identical to the old `1 / -1`.
114
+ than being boxed into col 2 beside the action.
115
115
  `[slot="content"]` (used when consumer provides custom rendering)
116
116
  spans all columns. */
117
117
  :scope {
@@ -147,6 +147,15 @@
147
147
  /* carries the old column-gap (gh#524) */
148
148
  margin-inline-end: var(--list-item-gap-column);
149
149
  }
150
+ /* Two-line items center the glyph on the WHOLE item (gh#558) — Material's
151
+ two-line convention; pinned to row 1 it hung above the item center.
152
+ The span must be explicit (`span 2`): with no grid-template-rows the
153
+ explicit grid has zero rows, so `1 / -1` collapses back to one row.
154
+ Scoped by :has() so single-line items don't mint an empty second row
155
+ (which would charge row-gap below the content). */
156
+ :scope:has([slot="description"]) [slot="icon"] {
157
+ grid-row: 1 / span 2;
158
+ }
150
159
  /* An empty stamped icon span must not resurrect the ghost track. */
151
160
  :scope [slot="icon"]:empty {
152
161
  display: none;
@@ -51,6 +51,11 @@ export class UIModal extends UIElement {
51
51
  #closeTimer = null;
52
52
  #dialogRef = null;
53
53
 
54
+ // Phosphor icons this primitive auto-stamps (without consumer markup).
55
+ // Aggregated by installIconLoadersForRegistered() across all defined
56
+ // elements. Audited by check-required-icons.mjs.
57
+ static requiredIcons = ['x'];
58
+
54
59
  static properties = {
55
60
  text: { type: String, default: '', reflect: true },
56
61
  size: { type: String, default: 'md', reflect: true },
@@ -79,6 +79,8 @@ tokens:
79
79
  description: Override surface border radius
80
80
  --modal-shadow:
81
81
  description: Override surface box shadow
82
+ requiredIcons:
83
+ - x
82
84
  a2ui:
83
85
  rules:
84
86
  - >-
@@ -30,6 +30,11 @@ import { anchorPopover } from '../../core/anchor.js';
30
30
  import { logicalSlotted } from '../../core/logical-children.js';
31
31
 
32
32
  export class UINavGroup extends UIElement {
33
+ // Phosphor icons this primitive auto-stamps (without consumer markup).
34
+ // Aggregated by installIconLoadersForRegistered() across all defined
35
+ // elements. Audited by check-required-icons.mjs.
36
+ static requiredIcons = ['caret-right'];
37
+
33
38
  static properties = {
34
39
  text: { type: String, default: '', reflect: true },
35
40
  icon: { type: String, default: '', reflect: true },
@@ -81,6 +81,8 @@ states:
81
81
 
82
82
  traits: []
83
83
  tokens: {}
84
+ requiredIcons:
85
+ - caret-right
84
86
  a2ui:
85
87
  rules:
86
88
  - >-
@@ -64,6 +64,9 @@ states:
64
64
  description: Default, ready for interaction.
65
65
  traits: []
66
66
  tokens: {}
67
+ requiredIcons:
68
+ - caret-left
69
+ - caret-right
67
70
  a2ui:
68
71
  rules:
69
72
  - rule: 'Renders below tables, card grids, or list views for paginated data. Emits page-change events.'
@@ -26,8 +26,8 @@ export class UISelect extends UIFormElement {
26
26
  // across all defined elements. Audited by check-required-icons.mjs
27
27
  // (slot 11). Per FEEDBACK-06 §4 + FEEDBACK-07 §4.
28
28
  // SPEC-040: [multiple] mode stamps <tag-ui> chips (own `x`), checkbox
29
- // indicator (`check`), and search-input prefix (`magnifying-glass`).
30
- static requiredIcons = ['caret-down', 'check', 'x', 'magnifying-glass'];
29
+ // indicator (`check`).
30
+ static requiredIcons = ['caret-down', 'check', 'x'];
31
31
 
32
32
  // §225 (v0.5.9, FEEDBACK-10 §3): once-per-element console.warn dedup when
33
33
  // consumer authors children that aren't native <option>/<optgroup>. The
@@ -274,7 +274,6 @@ requiredIcons:
274
274
  - caret-down
275
275
  - check # checkbox indicator on each option in [multiple] mode
276
276
  - x # chip dismiss + clear-all affordance ([multiple] + [clearable])
277
- - magnifying-glass # search-input prefix ([searchable])
278
277
  a2ui:
279
278
  rules:
280
279
  - >-
@@ -13,6 +13,11 @@
13
13
  }
14
14
  ],
15
15
  "properties": {
16
+ "band": {
17
+ "description": "Chart-band KPI layout. With a `slot=\"chart\"` child, label / value / change stack full width top to bottom and the chart forms a short full-width band between the value and the change row (the Gmail/Vercel/shadcn stat-card shape). Composes with `icon` exactly as the base layout does. No effect without a chart slot.",
18
+ "type": "boolean",
19
+ "default": false
20
+ },
16
21
  "bleed": {
17
22
  "description": "Horizontal-bleed KPI layout. With a `slot=\"chart\"` child, the value / label / change stack on the left while the chart fills the right column at full height and bleeds to the card's top / right / bottom edges (the horizontal counterpart to a chart in a card `<section bleed>`). No effect without a chart slot.",
18
23
  "type": "boolean",
@@ -233,6 +238,9 @@
233
238
  "--stat-change-size": {
234
239
  "description": "Font size for the change badge"
235
240
  },
241
+ "--stat-chart-band-height": {
242
+ "description": "Fixed height of the full-width chart band in `band` layout"
243
+ },
236
244
  "--stat-column-gap": {
237
245
  "description": "Horizontal gap between grid columns"
238
246
  },
@@ -16,6 +16,9 @@
16
16
  --stat-column-gap: var(--a-gap-self, var(--a-gap));
17
17
  --stat-row-gap: var(--a-gap-sm);
18
18
  text-align: start; /* §text-align-reset — blocks inheritance from centered ancestors */
19
+
20
+ /* ── Chart-band mode ── */
21
+ --stat-chart-band-height: 3rem;
19
22
  }
20
23
 
21
24
  :scope {
@@ -127,6 +130,42 @@
127
130
  aspect-ratio: auto;
128
131
  }
129
132
 
133
+ /* ── Chart-band KPI tile ──
134
+ label / value / chart-band / change stack full width, top to bottom —
135
+ the Gmail/Vercel/shadcn stat-card shape: a short full-width trend band
136
+ sits between the hero number and the delta footer, instead of the
137
+ inline chart resting beside the value (compact) or the bleed panel
138
+ replacing the text column (wide). Text rows stay baseline-aligned;
139
+ the band row stretches to its own fixed height. Compose inside a card
140
+ section:
141
+ <stat-ui band value=… label=… change=… trend=…>
142
+ <chart-ui slot="chart" type="sparkline" …></chart-ui>
143
+ </stat-ui> */
144
+ :scope[band]:has([slot="chart"]) {
145
+ grid-template-columns: minmax(0, 1fr) auto;
146
+ grid-template-areas:
147
+ "label icon"
148
+ "value value"
149
+ "chart chart"
150
+ "change change";
151
+ align-items: baseline;
152
+ }
153
+ /* Restore full-width value span — the inline-chart reflow above confines
154
+ value to column 1 to make room for the beside-value chart; band has no
155
+ such column, so the value spans both again. */
156
+ :scope[band] [slot="value"] {
157
+ grid-column: 1 / -1;
158
+ }
159
+ :scope[band] [slot="chart"] {
160
+ grid-area: chart;
161
+ /* Band row stretches to its own fixed short height rather than the
162
+ inline-chart's baseline-anchored 4:3 box — aspect-ratio released. */
163
+ align-self: stretch;
164
+ width: 100%;
165
+ height: var(--stat-chart-band-height);
166
+ aspect-ratio: auto;
167
+ }
168
+
130
169
  /* ── Label (eyebrow) ── */
131
170
  [slot="label"] {
132
171
  grid-area: label;
@@ -13,6 +13,8 @@
13
13
  import { UIElement } from '../../core/element.js';
14
14
 
15
15
  export class UIStat extends UIElement {
16
+ /** Chart-band KPI layout. With a `slot="chart"` child, label / value / change stack full width top to bottom and the chart forms a short full-width band between the value and the change row (the Gmail/Vercel/shadcn stat-card shape). Composes with `icon` exactly as the base layout does. No effect without a chart slot. */
17
+ band: boolean;
16
18
  /** Horizontal-bleed KPI layout. With a `slot="chart"` child, the value / label / change stack on the left while the chart fills the right column at full height and bleeds to the card's top / right / bottom edges (the horizontal counterpart to a chart in a card `<section bleed>`). No effect without a chart slot. */
17
19
  bleed: boolean;
18
20
  /** Change indicator text (e.g. '+12%', '-3%') */
@@ -23,6 +23,11 @@ class UIStat extends UIElement {
23
23
  // slot="chart" child fills the right column and bleeds to the card edges.
24
24
  // Reflected so the [bleed] CSS selector gates the layout.
25
25
  bleed: { type: Boolean, default: false, reflect: true },
26
+ // Chart-band layout: label/value/chart/change stack full width, top to
27
+ // bottom, with the slot="chart" child forming a short full-width band
28
+ // between the value and the change row. Reflected so the [band] CSS
29
+ // selector gates the layout.
30
+ band: { type: Boolean, default: false, reflect: true },
26
31
  };
27
32
 
28
33
  static template = () => null;
@@ -58,6 +58,15 @@ props:
58
58
  without a chart slot.
59
59
  type: boolean
60
60
  default: false
61
+ band:
62
+ description: >-
63
+ Chart-band KPI layout. With a `slot="chart"` child, label / value /
64
+ change stack full width top to bottom and the chart forms a short
65
+ full-width band between the value and the change row (the
66
+ Gmail/Vercel/shadcn stat-card shape). Composes with `icon` exactly as
67
+ the base layout does. No effect without a chart slot.
68
+ type: boolean
69
+ default: false
61
70
  events: {}
62
71
  slots:
63
72
  change:
@@ -81,6 +90,8 @@ traits: []
81
90
  tokens:
82
91
  --stat-change-size:
83
92
  description: Font size for the change badge
93
+ --stat-chart-band-height:
94
+ description: Fixed height of the full-width chart band in `band` layout
84
95
  --stat-column-gap:
85
96
  description: Horizontal gap between grid columns
86
97
  --stat-down-fg:
@@ -37,6 +37,11 @@
37
37
  import { UIElement } from '../../core/element.js';
38
38
 
39
39
  export class UISwiper extends UIElement {
40
+ // Phosphor icons this primitive auto-stamps (without consumer markup).
41
+ // Aggregated by installIconLoadersForRegistered() across all defined
42
+ // elements. Audited by check-required-icons.mjs.
43
+ static requiredIcons = ['caret-left', 'caret-right'];
44
+
40
45
  static properties = {
41
46
  autoplay: { type: Boolean, default: false, reflect: true },
42
47
  interval: { type: Number, default: 5000, reflect: true },
@@ -95,6 +95,9 @@ states:
95
95
  description: Default, ready for interaction.
96
96
  traits: []
97
97
  tokens: {}
98
+ requiredIcons:
99
+ - caret-left
100
+ - caret-right
98
101
  a2ui:
99
102
  rules:
100
103
  - rule: 'Horizontal slide carousel with touch + arrow nav. Hosts arbitrary children as slides.'
@@ -90,6 +90,11 @@ function outerWidth(el) {
90
90
  }
91
91
 
92
92
  export class UIToolbar extends UIElement {
93
+ // Phosphor icons this primitive auto-stamps (without consumer markup).
94
+ // Aggregated by installIconLoadersForRegistered() across all defined
95
+ // elements. Audited by check-required-icons.mjs.
96
+ static requiredIcons = ['dots-three'];
97
+
93
98
  static properties = {
94
99
  gap: { type: String, default: 'sm', reflect: true },
95
100
  align: { type: String, default: 'start', reflect: true },
@@ -320,6 +325,12 @@ export class UIToolbar extends UIElement {
320
325
  trigger.hidden = true;
321
326
 
322
327
  // 2. Measure.
328
+ // Hidden-ancestor guard (gh#528): a display:none mount reads
329
+ // clientWidth 0 — committing a spill from that reading strands the
330
+ // bar collapsed. Step 1 already restored everything visible, so
331
+ // leaving now is the correct state; the ResizeObserver fires again
332
+ // with a real size when the ancestor becomes visible.
333
+ if (this.clientWidth === 0) return;
323
334
  const style = getComputedStyle(this);
324
335
  const padL = parseFloat(style.paddingLeft) || 0;
325
336
  const padR = parseFloat(style.paddingRight) || 0;
@@ -329,7 +340,16 @@ export class UIToolbar extends UIElement {
329
340
  // Include horizontal margins — components like divider-ui add their own
330
341
  // margin-inline which getBoundingClientRect() does NOT include but flex
331
342
  // layout DOES count toward the row width.
343
+ // Grow-item guard (gh#528): a flex-grow child (a spacer, a stretched
344
+ // group) measures at its EXPANDED width — the row then always reads
345
+ // "full" and the last real action spills unrecoverably (the pattern
346
+ // bug gh#494's build hit). Measure grow items at their natural size
347
+ // by zeroing flex-grow for the read, then restoring.
348
+ const grown = all.filter(el => parseFloat(getComputedStyle(el).flexGrow) > 0);
349
+ const savedGrow = grown.map(el => el.style.flexGrow);
350
+ for (const el of grown) el.style.flexGrow = '0';
332
351
  const widths = all.map(el => outerWidth(el));
352
+ grown.forEach((el, i) => { el.style.flexGrow = savedGrow[i]; });
333
353
  const total = widths.reduce((a, w) => a + w, 0) + Math.max(0, all.length - 1) * gap;
334
354
 
335
355
  // Reserve OVERFLOW_BUFFER worth of headroom — start spilling sooner
@@ -41,6 +41,8 @@ states:
41
41
  description: Default, ready for interaction.
42
42
  traits: []
43
43
  tokens: {}
44
+ requiredIcons:
45
+ - dots-three
44
46
  a2ui:
45
47
  rules:
46
48
  - rule: 'Horizontal action bar — hosts <button-ui>, <toolbar-group-ui>, and <divider-ui> children.'
@@ -192,22 +192,28 @@ export class UITooltip extends UIElement {
192
192
  /* Copy per-series `--color-{key}` custom properties from the target chart
193
193
  to the popover so inner rows can reference them. The popover lives in
194
194
  the top layer and does NOT inherit custom properties from the host,
195
- so we bridge them explicitly at show time. */
195
+ so we bridge them explicitly at show time.
196
+
197
+ gh#561 — chart-ui no longer writes `--color-{key}` as an inline style
198
+ on itself (that shadowed ancestor overrides — the bug this issue
199
+ fixed). Resolve each declared series key's *effective* value via
200
+ getComputedStyle instead, which correctly picks up a value set on
201
+ the chart itself OR any ancestor. `legendData` (chart-ui's public
202
+ getter) enumerates every declared series key; targets without it
203
+ (non-chart-ui `for` targets, or charts with no y-keys) have nothing
204
+ to copy. */
196
205
  #copySeriesColorsFromTarget() {
197
206
  if (!this.#target || !this.#popover) return;
198
- /* Prefer inline style on the target (what chart-ui's #injectSeriesColors
199
- writes). Fall back to computed style resolution for any --color-* vars
200
- the author set on ancestors. */
201
- const inline = this.#target.style;
202
- for (let i = 0; i < inline.length; i++) {
203
- const name = inline[i];
204
- if (name.startsWith('--color-')) {
205
- this.#popover.style.setProperty(name, inline.getPropertyValue(name));
206
- }
207
+ const cs = getComputedStyle(this.#target);
208
+ const keys = Array.isArray(this.#target.legendData)
209
+ ? this.#target.legendData.map((it) => it.key).filter(Boolean)
210
+ : [];
211
+ for (const key of keys) {
212
+ const v = cs.getPropertyValue(`--color-${key}`).trim();
213
+ if (v) this.#popover.style.setProperty(`--color-${key}`, v);
207
214
  }
208
215
  /* Also copy the 10 categorical palette slots so the fallback
209
216
  `var(--color-{key}, var(--chart-{slot}))` resolves. */
210
- const cs = getComputedStyle(this.#target);
211
217
  for (let i = 0; i < 10; i++) {
212
218
  const v = cs.getPropertyValue(`--chart-${i}`).trim();
213
219
  if (v) this.#popover.style.setProperty(`--chart-${i}`, v);