@adia-ai/web-modules 0.8.39 → 0.8.41

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 (120) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +6 -6
  3. package/agent-admin/admin-roster/admin-roster.a2ui.json +1 -0
  4. package/agent-admin/admin-roster/admin-roster.css +10 -2
  5. package/agent-admin/admin-roster/admin-roster.js +70 -22
  6. package/agent-admin/admin-settings/admin-settings.a2ui.json +1 -0
  7. package/agent-admin/agent-admin/agent-admin.a2ui.json +1 -0
  8. package/billing/billing-overview/billing-overview.a2ui.json +10 -1
  9. package/billing/billing-overview/billing-overview.class.js +142 -73
  10. package/billing/billing-overview/billing-overview.css +70 -57
  11. package/billing/billing-overview/billing-overview.yaml +5 -0
  12. package/billing/invoice-detail/invoice-detail.a2ui.json +9 -4
  13. package/billing/invoice-detail/invoice-detail.class.js +22 -5
  14. package/billing/invoice-detail/invoice-detail.yaml +9 -4
  15. package/billing/invoice-history/invoice-history.a2ui.json +7 -4
  16. package/billing/invoice-history/invoice-history.class.js +45 -12
  17. package/billing/invoice-history/invoice-history.css +18 -15
  18. package/billing/invoice-history/invoice-history.yaml +10 -4
  19. package/billing/payment-method-form/payment-method-form.a2ui.json +1 -0
  20. package/billing/payment-method-form/payment-method-form.class.js +3 -3
  21. package/billing/payment-method-form/payment-method-form.css +4 -4
  22. package/billing/payment-method-list/payment-method-list.a2ui.json +4 -0
  23. package/billing/payment-method-list/payment-method-list.class.js +31 -11
  24. package/billing/payment-method-list/payment-method-list.css +13 -10
  25. package/billing/payment-method-list/payment-method-list.yaml +3 -0
  26. package/billing/plan-picker/plan-picker.a2ui.json +6 -0
  27. package/billing/plan-picker/plan-picker.class.js +70 -22
  28. package/billing/plan-picker/plan-picker.css +21 -38
  29. package/billing/plan-picker/plan-picker.yaml +5 -0
  30. package/chat/chat-composer/chat-composer.a2ui.json +1 -0
  31. package/chat/chat-empty/chat-empty.a2ui.json +4 -0
  32. package/chat/chat-empty/chat-empty.yaml +2 -0
  33. package/chat/chat-header/chat-header.a2ui.json +5 -0
  34. package/chat/chat-header/chat-header.yaml +3 -0
  35. package/chat/chat-shell/chat-shell.a2ui.json +4 -0
  36. package/chat/chat-shell/chat-shell.yaml +2 -0
  37. package/chat/chat-shell/css/chat-shell.tokens.css +1 -1
  38. package/chat/chat-sidebar/chat-sidebar.a2ui.json +4 -0
  39. package/chat/chat-sidebar/chat-sidebar.yaml +2 -0
  40. package/chat/chat-status/chat-status.a2ui.json +4 -0
  41. package/chat/chat-status/chat-status.yaml +2 -0
  42. package/chat/chat-thread/chat-thread.a2ui.json +4 -0
  43. package/chat/chat-thread/chat-thread.yaml +2 -0
  44. package/dashboard/dashboard-layout/dashboard-layout.a2ui.json +8 -8
  45. package/dashboard/dashboard-layout/dashboard-layout.css +4 -4
  46. package/dashboard/dashboard-layout/dashboard-layout.js +10 -9
  47. package/dashboard/dashboard-layout/dashboard-layout.yaml +19 -7
  48. package/dashboard/date-range-selector/date-range-selector.a2ui.json +4 -3
  49. package/dashboard/date-range-selector/date-range-selector.yaml +6 -4
  50. package/dist/chat/chat-shell.min.css +1 -1
  51. package/dist/chat/chat-shell.min.js +25 -25
  52. package/dist/editor/editor-shell.min.js +3 -3
  53. package/dist/everything.min.js +118 -131
  54. package/dist/shell/admin-shell.min.css +1 -1
  55. package/dist/shell/admin-shell.min.js +6 -6
  56. package/dist/simple/simple-shell.min.js +5 -5
  57. package/dist/web-modules.min.css +1 -1
  58. package/dist/web-modules.sheet.js +1 -1
  59. package/editor/editor-canvas/editor-canvas.a2ui.json +4 -0
  60. package/editor/editor-canvas/editor-canvas.yaml +2 -0
  61. package/editor/editor-canvas-empty/editor-canvas-empty.a2ui.json +4 -0
  62. package/editor/editor-canvas-empty/editor-canvas-empty.yaml +2 -0
  63. package/editor/editor-canvas-toolbar/editor-canvas-toolbar.a2ui.json +4 -0
  64. package/editor/editor-canvas-toolbar/editor-canvas-toolbar.yaml +2 -0
  65. package/editor/editor-shell/editor-shell.a2ui.json +5 -0
  66. package/editor/editor-shell/editor-shell.yaml +3 -0
  67. package/editor/editor-sidebar/editor-sidebar.a2ui.json +4 -0
  68. package/editor/editor-sidebar/editor-sidebar.yaml +2 -0
  69. package/editor/editor-statusbar/editor-statusbar.a2ui.json +4 -0
  70. package/editor/editor-statusbar/editor-statusbar.yaml +2 -0
  71. package/editor/editor-toolbar/editor-toolbar.a2ui.json +4 -0
  72. package/editor/editor-toolbar/editor-toolbar.yaml +2 -0
  73. package/feedback/confirm-dialog/confirm-dialog.a2ui.json +1 -0
  74. package/form/form-popover/form-popover.a2ui.json +1 -0
  75. package/onboarding/onboarding-checklist/onboarding-checklist.a2ui.json +2 -1
  76. package/onboarding/onboarding-checklist/onboarding-checklist.js +2 -2
  77. package/onboarding/onboarding-checklist/onboarding-checklist.yaml +1 -1
  78. package/package.json +6 -6
  79. package/runtime/a2ui-root/a2ui-root.a2ui.json +6 -5
  80. package/runtime/a2ui-root/a2ui-root.css +75 -0
  81. package/runtime/a2ui-root/a2ui-root.js +118 -10
  82. package/runtime/a2ui-root/a2ui-root.yaml +7 -2
  83. package/runtime/gen-root/gen-root.a2ui.json +6 -5
  84. package/runtime/gen-root/gen-root.yaml +10 -2
  85. package/settings/integrations-page/integrations-page.a2ui.json +3 -2
  86. package/settings/integrations-page/integrations-page.js +8 -8
  87. package/settings/integrations-page/integrations-page.yaml +2 -2
  88. package/settings/notification-preferences/notification-preferences.a2ui.json +1 -0
  89. package/shell/admin-command/admin-command.a2ui.json +5 -1
  90. package/shell/admin-command/admin-command.yaml +3 -1
  91. package/shell/admin-content/admin-content.a2ui.json +4 -0
  92. package/shell/admin-content/admin-content.yaml +2 -0
  93. package/shell/admin-entity-item/admin-entity-item.a2ui.json +1 -0
  94. package/shell/admin-page/admin-page.a2ui.json +5 -0
  95. package/shell/admin-page/admin-page.yaml +3 -0
  96. package/shell/admin-page-body/admin-page-body.a2ui.json +4 -0
  97. package/shell/admin-page-body/admin-page-body.yaml +2 -0
  98. package/shell/admin-page-footer/admin-page-footer.a2ui.json +4 -0
  99. package/shell/admin-page-footer/admin-page-footer.yaml +2 -0
  100. package/shell/admin-page-header/admin-page-header.a2ui.json +4 -0
  101. package/shell/admin-page-header/admin-page-header.yaml +2 -0
  102. package/shell/admin-scroll/admin-scroll.a2ui.json +4 -0
  103. package/shell/admin-scroll/admin-scroll.yaml +2 -0
  104. package/shell/admin-shell/admin-shell.a2ui.json +7 -3
  105. package/shell/admin-shell/admin-shell.yaml +3 -1
  106. package/shell/admin-shell/css/admin-shell.sidebar.css +2 -2
  107. package/shell/admin-sidebar/admin-sidebar.a2ui.json +4 -0
  108. package/shell/admin-sidebar/admin-sidebar.yaml +2 -0
  109. package/shell/admin-statusbar/admin-statusbar.a2ui.json +6 -0
  110. package/shell/admin-statusbar/admin-statusbar.yaml +4 -0
  111. package/shell/admin-topbar/admin-topbar.a2ui.json +6 -0
  112. package/shell/admin-topbar/admin-topbar.yaml +4 -0
  113. package/shell/embed-shell/embed-shell.a2ui.json +1 -0
  114. package/simple/simple-content/simple-content.a2ui.json +4 -0
  115. package/simple/simple-content/simple-content.yaml +2 -0
  116. package/simple/simple-hero/simple-hero.a2ui.json +4 -0
  117. package/simple/simple-hero/simple-hero.yaml +2 -0
  118. package/simple/simple-shell/simple-shell.a2ui.json +4 -0
  119. package/simple/simple-shell/simple-shell.yaml +2 -0
  120. package/theme/theme-panel/theme-panel.a2ui.json +1 -0
@@ -86,13 +86,18 @@ props:
86
86
  density:
87
87
  description: |
88
88
  Line-items table row density. `compact` for tightly-packed
89
- invoices with many line items; `standard` for typical invoices.
90
- Forwarded to the embedded <table-ui density>.
89
+ invoices with many line items; unset (default) for typical
90
+ invoices. Forwarded to the embedded <table-ui density>.
91
+ Converged onto the global `[density]` Enum·preset vocabulary
92
+ (gh#1335) — `standard` retired (it was never a real state; the
93
+ forwarding logic already treated it as unset) and `spacious`
94
+ added for parity with the global grammar.
91
95
  type: string
92
- default: "standard"
96
+ default: ""
93
97
  enum:
98
+ - ""
94
99
  - compact
95
- - standard
100
+ - spacious
96
101
  reflect: true
97
102
 
98
103
  loading:
@@ -27,14 +27,14 @@
27
27
  "default": ""
28
28
  },
29
29
  "density": {
30
- "description": "Row density, forwarded to <table-ui>. `compact` for dense lists,\n`standard` for default rows, `comfortable` for spacious rows.\n",
30
+ "description": "Row density, forwarded to <table-ui>. `compact` for dense lists,\n`spacious` for airier rows, unset (default) for standard rows.\nConverged onto the global `[density]` Enum·preset vocabulary\n(gh#1335) — `standard` retired (never a real state) and\n`comfortable` renamed to `spacious` (ADR-0054's own migration\nmapping).\n",
31
31
  "type": "string",
32
32
  "enum": [
33
+ "",
33
34
  "compact",
34
- "standard",
35
- "comfortable"
35
+ "spacious"
36
36
  ],
37
- "default": "standard"
37
+ "default": ""
38
38
  },
39
39
  "exportable": {
40
40
  "description": "When set, the default toolbar carries an \"Export CSV\" button.\nConsumers overriding the toolbar via slot=\"toolbar\" can omit\nthis and stamp their own export affordance.\n",
@@ -96,6 +96,8 @@
96
96
  "composes": [
97
97
  "table-ui",
98
98
  "table-toolbar-ui",
99
+ "card-ui",
100
+ "row-ui",
99
101
  "badge-ui",
100
102
  "button-ui",
101
103
  "empty-state-ui",
@@ -210,6 +212,7 @@
210
212
  "export-invoices"
211
213
  ],
212
214
  "name": "InvoiceHistory",
215
+ "parts": {},
213
216
  "related": [
214
217
  "Table",
215
218
  "TableToolbar",
@@ -12,9 +12,12 @@
12
12
  *
13
13
  * Architecture (light-DOM, ADR-0033):
14
14
  * This composite stamps a three-region skeleton once on first connect:
15
- * [data-toolbar] (slot toolbar / default toolbar)
16
- * [data-table] (the <table-ui>)
17
- * [data-empty] (default <empty-state-ui> branch)
15
+ * [data-toolbar] (slot toolbar / default toolbar; filter chips
16
+ * live in a <row-ui data-invoice-filter>)
17
+ * [data-table-host] (a <card-ui> wrapping <section bleed> wrapping
18
+ * the <table-ui> — the canonical invoices-card
19
+ * grammar from billing.contents.html, gh#1519)
20
+ * [data-empty] (default <empty-state-ui> branch)
18
21
  * Then mutates the table's `.data` / `.columns` on every render. The
19
22
  * table primitive owns row stamping; this composite only wires its
20
23
  * inputs and converts cell renderers.
@@ -120,11 +123,28 @@ function csvEscape(val) {
120
123
  return str;
121
124
  }
122
125
 
126
+ /**
127
+ * Translate invoice-history-ui's own `density` onto table-ui's global
128
+ * [density] grammar (ADR-0054). gh#1335 — the vocabulary now IS the
129
+ * global one (compact/spacious/unset): identity passthrough for a real
130
+ * value, anything else is the absent attribute. Returns null for "no
131
+ * attribute".
132
+ */
133
+ function mapDensity(density) {
134
+ return density === 'compact' || density === 'spacious' ? density : null;
135
+ }
136
+
137
+ function forwardDensity(tableEl, density) {
138
+ const attr = mapDensity(density);
139
+ if (attr) tableEl.setAttribute('density', attr);
140
+ else tableEl.removeAttribute('density');
141
+ }
142
+
123
143
  export class InvoiceHistory extends UIElement {
124
144
  static properties = {
125
145
  invoices: { type: Array, default: [] },
126
146
  currency: { type: String, default: 'USD', reflect: true },
127
- density: { type: String, default: 'standard', reflect: true },
147
+ density: { type: String, default: '', reflect: true },
128
148
  loading: { type: Boolean, default: false, reflect: true },
129
149
  selectable: { type: Boolean, default: false, reflect: true },
130
150
  hrefPattern: { type: String, default: '/invoices/{number}', reflect: true, attribute: 'href-pattern' },
@@ -145,10 +165,10 @@ export class InvoiceHistory extends UIElement {
145
165
  #cleanups = [];
146
166
  #toolbarHost = null; // <div data-toolbar>
147
167
  #defaultToolbar = null; // <table-toolbar-ui> (when no slot=toolbar provided)
148
- #filterChipsHost = null; // <div data-invoice-filter>
168
+ #filterChipsHost = null; // <row-ui data-invoice-filter>
149
169
  #exportBtn = null; // <button-ui data-export>
150
- #tableHost = null; // <div data-table-host>
151
- #table = null; // <table-ui>
170
+ #tableHost = null; // <card-ui data-table-host>
171
+ #table = null; // <table-ui> (inside <section bleed>)
152
172
  #emptyHost = null; // <empty-state-ui data-empty>
153
173
  #internalUpdate = false;
154
174
  #loadCount = 0; // monotonic guard against stale fetches
@@ -301,7 +321,11 @@ export class InvoiceHistory extends UIElement {
301
321
  // cross-element subscription; invoice-detail.class.js avoids the same
302
322
  // hazard by comparing via getAttribute() instead.
303
323
  untracked(() => {
304
- if (this.#table.density !== this.density) this.#table.density = this.density;
324
+ // table-ui has no local `density` prop (ADR-0054) forward via
325
+ // attribute, translated through the global grammar.
326
+ if (this.#table.getAttribute('density') !== mapDensity(this.density)) {
327
+ forwardDensity(this.#table, this.density);
328
+ }
305
329
  if (!!this.#table.loading !== !!this.loading) this.#table.loading = !!this.loading;
306
330
  if (Number(this.#table.paginate || 0) !== Number(this.paginate || 0)) {
307
331
  this.#table.paginate = Number(this.paginate || 0);
@@ -355,9 +379,12 @@ export class InvoiceHistory extends UIElement {
355
379
  tt.setAttribute('data-default-toolbar', '');
356
380
  tt.setAttribute('text', 'Invoices');
357
381
  // Status-filter chips live in the [slot="actions"] region so they
358
- // sit alongside the toolbar's title + search affordances.
359
- const chips = document.createElement('div');
382
+ // sit alongside the toolbar's title + search affordances. The
383
+ // cluster is a <row-ui> (framework layout primitive, gh#1519).
384
+ const chips = document.createElement('row-ui');
360
385
  chips.setAttribute('data-invoice-filter', '');
386
+ chips.setAttribute('gap', '2');
387
+ chips.setAttribute('wrap', '');
361
388
  chips.setAttribute('role', 'group');
362
389
  chips.setAttribute('aria-label', 'Filter invoices by status');
363
390
  chips.setAttribute('slot', 'actions');
@@ -379,16 +406,22 @@ export class InvoiceHistory extends UIElement {
379
406
  }
380
407
 
381
408
  // ── Table region ───────────────────────────────────────────────
382
- const tableHost = document.createElement('div');
409
+ // Canonical invoices-card grammar (billing.contents.html):
410
+ // <card-ui><section bleed><table-ui sortable></table-ui></section></card-ui>
411
+ const tableHost = document.createElement('card-ui');
383
412
  tableHost.setAttribute('data-table-host', '');
384
413
  this.appendChild(tableHost);
385
414
  this.#tableHost = tableHost;
386
415
 
416
+ const tableSection = document.createElement('section');
417
+ tableSection.setAttribute('bleed', '');
418
+ tableHost.appendChild(tableSection);
419
+
387
420
  const table = document.createElement('table-ui');
388
421
  table.id = this.id ? `${this.id}__table` : `invoice-history-${Math.random().toString(36).slice(2, 8)}`;
389
422
  table.setAttribute('data-invoice-table', '');
390
423
  table.setAttribute('sortable', '');
391
- tableHost.appendChild(table);
424
+ tableSection.appendChild(table);
392
425
  this.#table = table;
393
426
 
394
427
  // Wire the default toolbar to this table by id.
@@ -1,10 +1,12 @@
1
1
  /* ═══════════════════════════════════════════════════════════════
2
2
  <invoice-history-ui> — Canonical billing-list composite.
3
3
 
4
- Composite over <table-ui> + <table-toolbar-ui> + <badge-ui> +
5
- <button-ui> + <empty-state-ui>. The host stacks three regions in
6
- DOM order: toolbar · table · empty-state. Each row in the table
7
- is owned by <table-ui>; this composite only styles its own
4
+ Composite over <table-ui> + <table-toolbar-ui> + <card-ui> +
5
+ <row-ui> + <badge-ui> + <button-ui> + <empty-state-ui>. The host
6
+ stacks three regions in DOM order: toolbar · table card · empty-
7
+ state. The table card is the canonical invoices grammar —
8
+ <card-ui><section bleed><table-ui> (gh#1519). Each row in the
9
+ table is owned by <table-ui>; this composite only styles its own
8
10
  region scaffolding + cell-internal billing chrome (status badge,
9
11
  amount alignment, action cluster).
10
12
 
@@ -40,9 +42,10 @@ invoice-history-ui {
40
42
  background: var(--invoice-history-bg);
41
43
  }
42
44
 
43
- /* ── Region stacking (toolbar · table · empty) ── */
45
+ /* ── Region stacking (toolbar · table card · empty) ──
46
+ [data-table-host] is a <card-ui> since gh#1519 — card-ui's own
47
+ @scope owns its display/chrome; don't override it here. */
44
48
  :scope > [data-toolbar],
45
- :scope > [data-table-host],
46
49
  :scope > [data-empty] {
47
50
  display: block;
48
51
  }
@@ -51,12 +54,11 @@ invoice-history-ui {
51
54
  margin-block-end: var(--invoice-history-gap);
52
55
  }
53
56
 
54
- /* Status-filter chip cluster lives in the toolbar's actions slot. */
57
+ /* Status-filter chip cluster a <row-ui wrap> in the toolbar's
58
+ actions slot; forward this composite's gap token into row-ui's
59
+ own contract. */
55
60
  :scope [data-invoice-filter] {
56
- display: inline-flex;
57
- flex-wrap: wrap;
58
- gap: var(--invoice-history-filter-gap);
59
- align-items: center;
61
+ --row-gap: var(--invoice-history-filter-gap);
60
62
  }
61
63
 
62
64
  /* ── Underlying table cascade ── */
@@ -64,7 +66,7 @@ invoice-history-ui {
64
66
  @scope assigns `display: grid` + sets `grid-template-columns` via
65
67
  JS; overriding the display collapses the grid into a single
66
68
  column. (Caught in v0.6.34 dogfood-sweep visual probe.) */
67
- :scope > [data-table-host] > table-ui {
69
+ :scope > [data-table-host] table-ui {
68
70
  --table-row-h: var(--invoice-history-row-min-height);
69
71
  width: 100%;
70
72
  }
@@ -120,11 +122,12 @@ invoice-history-ui {
120
122
  --empty-state-icon-fg: var(--a-danger-strong, var(--md-sys-color-danger));
121
123
  }
122
124
 
123
- /* ── Compact density forwards to the table primitive ── */
125
+ /* ── Density forwards to the table primitive (gh#1335 — `comfortable`
126
+ renamed to `spacious`, converged onto the global [density] grammar) ── */
124
127
  :scope[density="compact"] {
125
128
  --invoice-history-row-min-height: var(--table-row-h-compact, 2.25rem);
126
129
  }
127
- :scope[density="comfortable"] {
128
- --invoice-history-row-min-height: var(--table-row-h-comfortable, 3.5rem);
130
+ :scope[density="spacious"] {
131
+ --invoice-history-row-min-height: var(--table-row-h-spacious, 3.5rem);
129
132
  }
130
133
  }
@@ -31,6 +31,8 @@ description: |
31
31
  composes:
32
32
  - table-ui
33
33
  - table-toolbar-ui
34
+ - card-ui
35
+ - row-ui
34
36
  - badge-ui
35
37
  - button-ui
36
38
  - empty-state-ui
@@ -59,13 +61,17 @@ props:
59
61
  density:
60
62
  description: |
61
63
  Row density, forwarded to <table-ui>. `compact` for dense lists,
62
- `standard` for default rows, `comfortable` for spacious rows.
64
+ `spacious` for airier rows, unset (default) for standard rows.
65
+ Converged onto the global `[density]` Enum·preset vocabulary
66
+ (gh#1335) — `standard` retired (never a real state) and
67
+ `comfortable` renamed to `spacious` (ADR-0054's own migration
68
+ mapping).
63
69
  type: string
64
- default: standard
70
+ default: ""
65
71
  enum:
72
+ - ""
66
73
  - compact
67
- - standard
68
- - comfortable
74
+ - spacious
69
75
  reflect: true
70
76
 
71
77
  loading:
@@ -195,6 +195,7 @@
195
195
  "add-card"
196
196
  ],
197
197
  "name": "UIPaymentMethodForm",
198
+ "parts": {},
198
199
  "related": [
199
200
  "Form",
200
201
  "Field",
@@ -22,7 +22,7 @@
22
22
  *
23
23
  * Architecture (light-DOM, ADR-0033):
24
24
  * Stamps a fixed skeleton once on first connect:
25
- * [data-alert] · [data-summary] · [data-grid > field rows] · [data-legal]
25
+ * [data-alert] · [data-summary] · [data-field-grid > field rows] · [data-legal]
26
26
  * Mutates per-field state via reflected attributes on each sub-control.
27
27
  * No template rebuilds across renders — field nodes keep identity so
28
28
  * focus + IME composition state survive.
@@ -207,7 +207,7 @@ export class UIPaymentMethodForm extends UIFormElement {
207
207
  #alertHost = null; // <alert-ui data-alert>
208
208
  #summaryHost = null; // <div data-summary>
209
209
  #editButton = null; // <button-ui> inside summary
210
- #gridHost = null; // <div data-grid>
210
+ #gridHost = null; // <div data-field-grid>
211
211
 
212
212
  // Per-row <field-ui> hosts (keyed by field name).
213
213
  #fields = {
@@ -434,7 +434,7 @@ export class UIPaymentMethodForm extends UIFormElement {
434
434
 
435
435
  // Grid host.
436
436
  const grid = document.createElement('div');
437
- grid.setAttribute('data-grid', '');
437
+ grid.setAttribute('data-field-grid', '');
438
438
  this.appendChild(grid);
439
439
  this.#gridHost = grid;
440
440
 
@@ -82,23 +82,23 @@ payment-method-form-ui {
82
82
  }
83
83
 
84
84
  /* ── Field grid ── */
85
- :scope > [data-grid] {
85
+ :scope > [data-field-grid] {
86
86
  display: grid;
87
87
  grid-template-columns: 1fr 1fr;
88
88
  grid-template-areas: var(--payment-method-form-grid-template);
89
89
  gap: var(--payment-method-form-gap);
90
90
  }
91
- :scope > [data-grid][hidden] {
91
+ :scope > [data-field-grid][hidden] {
92
92
  display: none;
93
93
  }
94
94
 
95
- :scope[require-billing-address] > [data-grid] {
95
+ :scope[require-billing-address] > [data-field-grid] {
96
96
  grid-template-areas: var(--payment-method-form-grid-template-billing);
97
97
  }
98
98
 
99
99
  /* Hidden rows (address lines when require-billing-address is unset)
100
100
  collapse out of the grid without affecting sibling cell flow. */
101
- :scope > [data-grid] > field-ui[hidden] {
101
+ :scope > [data-field-grid] > field-ui[hidden] {
102
102
  display: none;
103
103
  }
104
104
 
@@ -80,6 +80,9 @@
80
80
  "category": "input",
81
81
  "composes": [
82
82
  "card-ui",
83
+ "col-ui",
84
+ "row-ui",
85
+ "divider-ui",
83
86
  "button-ui",
84
87
  "icon-ui",
85
88
  "tag-ui",
@@ -161,6 +164,7 @@
161
164
  "method-list"
162
165
  ],
163
166
  "name": "PaymentMethodList",
167
+ "parts": {},
164
168
  "related": [
165
169
  "PlanPicker",
166
170
  "Card",
@@ -12,9 +12,12 @@
12
12
  * Sibling: <plan-picker-ui> (same id-keyed-diff pattern)
13
13
  *
14
14
  * Architecture (light-DOM, ADR-0033):
15
- * This composite stamps a three-region skeleton once on first
16
- * connect:
17
- * [data-rows] · [data-empty] · [data-add]
15
+ * This composite stamps a region skeleton once on first connect:
16
+ * [data-rows] (a <col-ui> stack) · [data-empty] ·
17
+ * [data-add-divider] · [data-add] (a <row-ui>)
18
+ * The rows host and add row are framework layout primitives
19
+ * (col-ui / row-ui / divider-ui — gh#1519), mirroring the canonical
20
+ * billing page's layout-primitive grammar instead of raw divs.
18
21
  * Then mutates per-row DOM by id-keyed in-place diff on every
19
22
  * `methods` / `value` write. The diff preserves identity across
20
23
  * re-renders (id-keyed map) so focus survives unrelated row updates.
@@ -162,9 +165,10 @@ export class PaymentMethodList extends UIFormElement {
162
165
  // ── Field refs ────────────────────────────────────────────────────
163
166
  #stamped = false;
164
167
  #cleanups = [];
165
- #rowsHost = null; // <div data-rows role=presentation>
168
+ #rowsHost = null; // <col-ui data-rows role=presentation>
166
169
  #emptyHost = null; // <empty-state-ui data-empty>
167
- #addHost = null; // <div data-add>
170
+ #addHost = null; // <row-ui data-add>
171
+ #addDivider = null; // <divider-ui data-add-divider>
168
172
  #addButton = null; // <button-ui> inside #addHost
169
173
  #rowsById = new Map(); // id → <card-ui>
170
174
  #internalUpdate = false;
@@ -288,10 +292,14 @@ export class PaymentMethodList extends UIFormElement {
288
292
  this.removeAttribute('empty');
289
293
  }
290
294
 
291
- // Add-CTA visibility.
295
+ // Add-CTA visibility (the divider tracks the add row, and also
296
+ // hides when the rows list is empty — nothing to divide).
292
297
  if (this.#addHost) {
293
298
  this.#addHost.hidden = !!this['no-add'];
294
299
  }
300
+ if (this.#addDivider) {
301
+ this.#addDivider.hidden = !!this['no-add'] || methods.length === 0;
302
+ }
295
303
  if (this.#addButton) {
296
304
  this.#addButton.setAttribute('text', this['add-label'] || 'Add payment method');
297
305
  const icon = this['add-icon'] || 'plus';
@@ -332,10 +340,12 @@ export class PaymentMethodList extends UIFormElement {
332
340
  }
333
341
 
334
342
  #stamp() {
335
- // Rows host — the radiogroup container for the method cards.
336
- // role="presentation" because the outer host carries radiogroup.
337
- const rows = document.createElement('div');
343
+ // Rows host — a <col-ui> stack (framework layout primitive) that
344
+ // the method cards live in as DIRECT children. role="presentation"
345
+ // because the outer host carries radiogroup.
346
+ const rows = document.createElement('col-ui');
338
347
  rows.setAttribute('data-rows', '');
348
+ rows.setAttribute('gap', '2');
339
349
  rows.setAttribute('role', 'presentation');
340
350
  this.appendChild(rows);
341
351
  this.#rowsHost = rows;
@@ -351,9 +361,19 @@ export class PaymentMethodList extends UIFormElement {
351
361
  this.#emptyHost = empty;
352
362
  }
353
363
 
354
- // Add CTA host overridable via slot="add".
355
- const addHost = document.createElement('div');
364
+ // Divider between the rows list and the add CTA — the canonical
365
+ // grammar divides a list from its trailing action group
366
+ // (billing.contents.html drawer footers). Hidden alongside the
367
+ // add row via CSS ([empty] / [no-add]).
368
+ const addDivider = document.createElement('divider-ui');
369
+ addDivider.setAttribute('data-add-divider', '');
370
+ this.appendChild(addDivider);
371
+ this.#addDivider = addDivider;
372
+
373
+ // Add CTA host — a <row-ui>; overridable via slot="add".
374
+ const addHost = document.createElement('row-ui');
356
375
  addHost.setAttribute('data-add', '');
376
+ addHost.setAttribute('gap', '2');
357
377
  this.appendChild(addHost);
358
378
  this.#addHost = addHost;
359
379
 
@@ -2,8 +2,9 @@
2
2
  <payment-method-list-ui> — Saved payment methods (cards / bank /
3
3
  wallet) rendered as a 1-of-N radiogroup list.
4
4
 
5
- Composite over <card-ui> + <icon-ui> + <tag-ui> + <button-ui> +
6
- <empty-state-ui>. Each row is a <card-ui data-row> with a single
5
+ Composite over <card-ui> + <col-ui> + <row-ui> + <divider-ui> +
6
+ <icon-ui> + <tag-ui> + <button-ui> + <empty-state-ui>. Each row is
7
+ a <card-ui data-row> with a single
7
8
  <header> child carrying the canonical card-ui slot composition:
8
9
  slot=icon brand mark
9
10
  slot=heading "Visa ···· 4242" + inline <tag-ui> when default
@@ -43,11 +44,10 @@ payment-method-list-ui {
43
44
  background: var(--payment-method-list-bg);
44
45
  }
45
46
 
46
- /* ── Rows host — column stack of <card-ui> rows ── */
47
+ /* ── Rows host — a <col-ui> stack of <card-ui> rows; forward this
48
+ composite's gap token into col-ui's own contract. ── */
47
49
  :scope > [data-rows] {
48
- display: flex;
49
- flex-direction: column;
50
- gap: var(--payment-method-list-gap);
50
+ --col-gap: var(--payment-method-list-gap);
51
51
  }
52
52
 
53
53
  /* ── Per-row card — chrome only; card-ui owns layout via <header>. ── */
@@ -87,12 +87,15 @@ payment-method-list-ui {
87
87
  display: none;
88
88
  }
89
89
 
90
- /* ── Add CTA row ── */
90
+ /* ── Add CTA row — a <row-ui>; forward the justify token. ── */
91
91
  :scope > [data-add] {
92
- display: flex;
93
- justify-content: var(--payment-method-list-add-justify);
92
+ --row-justify: var(--payment-method-list-add-justify);
93
+ }
94
+ :scope > [data-add][hidden],
95
+ :scope > [data-add-divider][hidden] {
96
+ display: none;
94
97
  }
95
- :scope > [data-add][hidden] {
98
+ :scope[empty] > [data-add-divider] {
96
99
  display: none;
97
100
  }
98
101
 
@@ -27,6 +27,9 @@ description: |
27
27
  # .html demo shell) must explicitly side-effect-import each one.
28
28
  composes:
29
29
  - card-ui
30
+ - col-ui
31
+ - row-ui
32
+ - divider-ui
30
33
  - button-ui
31
34
  - icon-ui
32
35
  - tag-ui
@@ -100,6 +100,11 @@
100
100
  "option-card-ui",
101
101
  "segmented-ui",
102
102
  "segment-ui",
103
+ "field-ui",
104
+ "grid-ui",
105
+ "col-ui",
106
+ "row-ui",
107
+ "divider-ui",
103
108
  "button-ui",
104
109
  "icon-ui",
105
110
  "text-ui",
@@ -201,6 +206,7 @@
201
206
  "plan-selection"
202
207
  ],
203
208
  "name": "PlanPicker",
209
+ "parts": {},
204
210
  "related": [
205
211
  "OptionCard",
206
212
  "Segmented",