@adia-ai/web-components 0.6.50 → 0.7.0
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.
- package/CHANGELOG.md +120 -0
- package/components/action-list/action-list.css +1 -1
- package/components/agent-artifact/agent-artifact.class.js +10 -10
- package/components/agent-artifact/agent-artifact.css +1 -1
- package/components/agent-reasoning/agent-reasoning.class.js +51 -0
- package/components/agent-reasoning/agent-reasoning.css +49 -22
- package/components/alert/alert.class.js +8 -1
- package/components/alert/alert.css +13 -1
- package/components/avatar/avatar.a2ui.json +2 -14
- package/components/avatar/avatar.class.js +3 -15
- package/components/avatar/avatar.d.ts +2 -4
- package/components/avatar/avatar.yaml +1 -18
- package/components/breadcrumb/breadcrumb.css +4 -1
- package/components/button/button.a2ui.json +3 -0
- package/components/button/button.css +14 -3
- package/components/button/button.yaml +5 -0
- package/components/calendar-grid/calendar-grid.css +1 -1
- package/components/calendar-picker/calendar-picker.css +5 -2
- package/components/chart/chart.a2ui.json +0 -18
- package/components/chart/chart.class.js +8 -50
- package/components/chart/chart.css +1 -15
- package/components/chart/chart.d.ts +0 -4
- package/components/chart/chart.yaml +0 -24
- package/components/color-input/color-input.css +4 -1
- package/components/combobox/combobox.class.js +11 -0
- package/components/combobox/combobox.css +8 -0
- package/components/date-range-picker/date-range-picker.class.js +5 -1
- package/components/date-range-picker/date-range-picker.css +12 -2
- package/components/datetime-picker/datetime-picker.class.js +3 -0
- package/components/datetime-picker/datetime-picker.css +16 -2
- package/components/empty-state/empty-state.css +11 -4
- package/components/field/field.css +17 -6
- package/components/grid/grid.a2ui.json +5 -0
- package/components/grid/grid.class.js +16 -6
- package/components/grid/grid.css +17 -3
- package/components/grid/grid.d.ts +2 -0
- package/components/grid/grid.yaml +9 -0
- package/components/heatmap/heatmap.class.js +9 -3
- package/components/heatmap/heatmap.css +19 -2
- package/components/image/image.css +4 -1
- package/components/input/input.css +1 -1
- package/components/integration-card/integration-card.class.js +31 -7
- package/components/integration-card/integration-card.test.js +12 -1
- package/components/kbd/kbd.a2ui.json +3 -2
- package/components/kbd/kbd.css +7 -4
- package/components/kbd/kbd.d.ts +2 -2
- package/components/kbd/kbd.yaml +2 -1
- package/components/list/list.class.js +8 -1
- package/components/menu/menu.css +4 -1
- package/components/modal/modal.class.js +10 -1
- package/components/modal/modal.css +9 -0
- package/components/option-card/option-card.a2ui.json +3 -0
- package/components/option-card/option-card.css +44 -19
- package/components/option-card/option-card.yaml +5 -0
- package/components/otp-input/otp-input.css +25 -10
- package/components/page/page.css +64 -11
- package/components/pagination/pagination.class.js +1 -1
- package/components/pagination/pagination.css +9 -1
- package/components/pipeline-status/pipeline-status.css +6 -0
- package/components/popover/popover.css +12 -1
- package/components/preview/preview.css +30 -3
- package/components/progress-row/progress-row.css +3 -1
- package/components/qr-code/qr-code.css +4 -1
- package/components/segmented/segmented.css +4 -1
- package/components/select/select.a2ui.json +1 -1
- package/components/select/select.class.js +63 -7
- package/components/select/select.css +18 -0
- package/components/select/select.yaml +9 -2
- package/components/stack/stack.a2ui.json +12 -1
- package/components/stack/stack.d.ts +2 -2
- package/components/stack/stack.yaml +13 -1
- package/components/stat/stat.a2ui.json +5 -0
- package/components/stat/stat.css +55 -0
- package/components/stat/stat.d.ts +2 -0
- package/components/stat/stat.js +4 -0
- package/components/stat/stat.yaml +9 -0
- package/components/swiper/swiper.class.js +14 -6
- package/components/switch/switch.css +13 -0
- package/components/table/table.a2ui.json +2 -2
- package/components/table/table.css +13 -1
- package/components/table/table.yaml +2 -2
- package/components/time-picker/time-picker.css +4 -1
- package/components/timeline/timeline.class.js +3 -3
- package/components/timeline/timeline.css +23 -5
- package/components/toggle-group/toggle-group.css +4 -1
- package/components/toggle-scheme/toggle-scheme.css +4 -1
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +81 -81
- package/package.json +3 -3
- package/styles/api/layout.css +7 -0
- package/styles/api/text.css +9 -5
- package/styles/index.css +11 -2
- package/styles/prose.css +8 -0
- package/styles/resets.css +5 -5
- package/styles/themes.css +8 -1
- package/styles/tokens.css +3 -3
- package/styles/type/elements.css +73 -0
- package/styles/type/roles.css +14 -49
- package/styles/type/scale.css +0 -5
- package/styles/typography.css +3 -3
package/components/grid/grid.css
CHANGED
|
@@ -28,9 +28,23 @@
|
|
|
28
28
|
:scope[columns="5"] { grid-auto-flow: row; grid-template-columns: repeat(5, 1fr); grid-auto-columns: auto; }
|
|
29
29
|
:scope[columns="6"] { grid-auto-flow: row; grid-template-columns: repeat(6, 1fr); grid-auto-columns: auto; }
|
|
30
30
|
|
|
31
|
-
/* Responsive presets
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
/* Responsive presets — the minmax() floor reads [min-column-width] via
|
|
32
|
+
--grid-min-col (set inline by grid.class.js), defaulting to 12rem.
|
|
33
|
+
MUST also flip to grid-auto-flow:row + grid-auto-columns:auto (like the
|
|
34
|
+
numeric rules) — the base :scope default is grid-auto-flow:column +
|
|
35
|
+
grid-auto-columns:1fr, which makes items flow into IMPLICIT 1fr columns
|
|
36
|
+
instead of wrapping into the auto-fit row tracks (the cause of the
|
|
37
|
+
"240px 77px 77px" sliver cram). */
|
|
38
|
+
:scope[columns="auto-fill"] {
|
|
39
|
+
grid-auto-flow: row;
|
|
40
|
+
grid-auto-columns: auto;
|
|
41
|
+
grid-template-columns: repeat(auto-fill, minmax(var(--grid-min-col, 12rem), 1fr));
|
|
42
|
+
}
|
|
43
|
+
:scope[columns="auto-fit"] {
|
|
44
|
+
grid-auto-flow: row;
|
|
45
|
+
grid-auto-columns: auto;
|
|
46
|
+
grid-template-columns: repeat(auto-fit, minmax(var(--grid-min-col, 12rem), 1fr));
|
|
47
|
+
}
|
|
34
48
|
|
|
35
49
|
/* Column span — children can span multiple columns */
|
|
36
50
|
& > [span="2"] { grid-column: span 2; }
|
|
@@ -19,6 +19,8 @@ export class UIGrid extends UIElement {
|
|
|
19
19
|
columns: string;
|
|
20
20
|
/** Grid gap. Accepts numeric space-scale values (0–12) or named sizes (xs/sm/md/lg/xl). Responsive notation supported: "2 4@md" = 2 below md, 4 from md upward. */
|
|
21
21
|
gap: string;
|
|
22
|
+
/** Minimum track width for columns="auto-fit"/"auto-fill" (any CSS length, e.g. "240px", "16rem"). Sets the minmax() floor so cards don't shrink below it before wrapping; unset uses the 12rem default. No effect on numeric columns. */
|
|
23
|
+
minColumnWidth: string;
|
|
22
24
|
/** Row gap override */
|
|
23
25
|
rowGap: string;
|
|
24
26
|
}
|
|
@@ -35,6 +35,15 @@ props:
|
|
|
35
35
|
md, 4 from md upward.
|
|
36
36
|
type: string
|
|
37
37
|
default: md
|
|
38
|
+
minColumnWidth:
|
|
39
|
+
description: >-
|
|
40
|
+
Minimum track width for columns="auto-fit"/"auto-fill" (any CSS length,
|
|
41
|
+
e.g. "240px", "16rem"). Sets the minmax() floor so cards don't shrink
|
|
42
|
+
below it before wrapping; unset uses the 12rem default. No effect on
|
|
43
|
+
numeric columns.
|
|
44
|
+
type: string
|
|
45
|
+
default: ""
|
|
46
|
+
attribute: min-column-width
|
|
38
47
|
rowGap:
|
|
39
48
|
description: Row gap override
|
|
40
49
|
type: string
|
|
@@ -181,15 +181,21 @@ export class UIHeatmap extends UIElement {
|
|
|
181
181
|
labels.appendChild(span);
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
-
labels
|
|
184
|
+
// day-grid month labels align to the fixed-size cell columns.
|
|
185
|
+
labels.style.gridTemplateColumns = `repeat(${cols}, var(--heatmap-cell-min-size, 0.75rem))`;
|
|
185
186
|
this.appendChild(labels);
|
|
186
187
|
}
|
|
187
188
|
|
|
188
189
|
// ── Grid of cells ──
|
|
190
|
+
// day-grid keeps FIXED cell tracks (host scrolls horizontally when the
|
|
191
|
+
// 52-week grid exceeds the container); matrix/density use minmax(0,1fr)
|
|
192
|
+
// so they fit-and-shrink to the container.
|
|
193
|
+
const isDayGrid = this.type === 'day-grid';
|
|
194
|
+
const cellTrack = isDayGrid ? 'var(--heatmap-cell-min-size, 0.75rem)' : 'minmax(0, 1fr)';
|
|
189
195
|
const grid = document.createElement('div');
|
|
190
196
|
grid.setAttribute('data-grid', '');
|
|
191
|
-
grid.style.gridTemplateColumns = `repeat(${cols},
|
|
192
|
-
grid.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
|
|
197
|
+
grid.style.gridTemplateColumns = `repeat(${cols}, ${cellTrack})`;
|
|
198
|
+
grid.style.gridTemplateRows = `repeat(${rows}, ${isDayGrid ? 'var(--heatmap-cell-min-size, 0.75rem)' : '1fr'})`;
|
|
193
199
|
for (let r = 0; r < rows; r++) {
|
|
194
200
|
for (let c = 0; c < cols; c++) {
|
|
195
201
|
const d = map.get(`${r},${c}`);
|
|
@@ -36,6 +36,19 @@
|
|
|
36
36
|
gap: var(--heatmap-gap, var(--heatmap-gap-default));
|
|
37
37
|
color: var(--heatmap-text, var(--heatmap-text-default));
|
|
38
38
|
font-size: var(--a-body-size);
|
|
39
|
+
/* A wide day-grid (52+ week columns) keeps a FIXED cell size and scrolls
|
|
40
|
+
horizontally (the GitHub-contributions model) rather than crushing
|
|
41
|
+
fixed-size cells into overlap. matrix/density fit-and-shrink instead. */
|
|
42
|
+
overflow-x: auto;
|
|
43
|
+
scrollbar-width: thin;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* day-grid: fixed-size cell tracks + intrinsic width so the host scrolls
|
|
47
|
+
instead of squishing. (class.js writes the fixed gridTemplateColumns
|
|
48
|
+
inline for day-grid; matrix/density keep minmax(0,1fr) to fit.) */
|
|
49
|
+
:scope[type="day-grid"] > [data-grid],
|
|
50
|
+
:scope[type="day-grid"] > [data-months] {
|
|
51
|
+
width: max-content;
|
|
39
52
|
}
|
|
40
53
|
|
|
41
54
|
/* Title slot */
|
|
@@ -73,8 +86,12 @@
|
|
|
73
86
|
:scope [data-cell] {
|
|
74
87
|
border-radius: var(--heatmap-cell-radius, var(--heatmap-cell-radius-default));
|
|
75
88
|
background: var(--heatmap-empty-bg, var(--heatmap-empty-bg-default));
|
|
76
|
-
min
|
|
77
|
-
|
|
89
|
+
/* No hard min on the cell box — it conflicted with matrix/density's
|
|
90
|
+
minmax(0,1fr) tracks (the floor refused to shrink, so cells overlapped
|
|
91
|
+
into a smear). day-grid keeps fixed size via its fixed tracks; the
|
|
92
|
+
fit-to-container modes shrink with the track. */
|
|
93
|
+
min-width: 0;
|
|
94
|
+
min-height: 0;
|
|
78
95
|
aspect-ratio: 1 / 1;
|
|
79
96
|
cursor: default;
|
|
80
97
|
transition: transform var(--a-duration-fast) var(--a-easing-out);
|
|
@@ -14,13 +14,16 @@
|
|
|
14
14
|
:scope {
|
|
15
15
|
/* ── Base ── */
|
|
16
16
|
box-sizing: border-box;
|
|
17
|
-
display:
|
|
17
|
+
display: block;
|
|
18
18
|
position: relative;
|
|
19
19
|
overflow: hidden;
|
|
20
20
|
background: var(--image-bg, var(--image-bg-default));
|
|
21
21
|
border-radius: var(--image-radius, var(--image-radius-default));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/* Display convention (ADR-0037): block-level by default; [inline] opts back to inline-level. */
|
|
25
|
+
:scope[inline] { display: inline-block; }
|
|
26
|
+
|
|
24
27
|
/* ── Image ── */
|
|
25
28
|
[slot="image"] {
|
|
26
29
|
display: block;
|
|
@@ -229,7 +229,7 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
|
|
|
229
229
|
past the half-column cell). Targeting icon-ui directly is required
|
|
230
230
|
because its own `:where(:scope)` declaration of --icon-size wins over
|
|
231
231
|
any value inherited from its parent button-ui. Tying it to
|
|
232
|
-
--input-height keeps the
|
|
232
|
+
--input-height keeps the caret proportional across sm/md/lg. */
|
|
233
233
|
[data-number] [slot="controls"] icon-ui {
|
|
234
234
|
--icon-size: calc(var(--input-height, var(--input-height-default)) * 0.4);
|
|
235
235
|
}
|
|
@@ -55,6 +55,25 @@ const BADGE_FOR_STATUS = Object.freeze({
|
|
|
55
55
|
'coming-soon':{ variant: 'muted', text: 'Coming soon', icon: '' },
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
+
// Fallback glyph per known provider when no explicit [logo] is supplied, so
|
|
59
|
+
// a card set up with just [provider] never renders a blank logo disc. All
|
|
60
|
+
// names are verified Phosphor icons. Unknown providers fall back to a generic
|
|
61
|
+
// plug glyph.
|
|
62
|
+
const PROVIDER_LOGO = Object.freeze({
|
|
63
|
+
slack: 'chat-circle',
|
|
64
|
+
github: 'git-branch',
|
|
65
|
+
gitlab: 'git-branch',
|
|
66
|
+
linear: 'kanban',
|
|
67
|
+
stripe: 'credit-card',
|
|
68
|
+
zapier: 'lightning',
|
|
69
|
+
figma: 'figma-logo',
|
|
70
|
+
notion: 'notion-logo',
|
|
71
|
+
webhook: 'lightning',
|
|
72
|
+
discord: 'discord-logo',
|
|
73
|
+
google: 'google-logo',
|
|
74
|
+
});
|
|
75
|
+
const DEFAULT_LOGO = 'plug';
|
|
76
|
+
|
|
58
77
|
export class UIIntegrationCard extends UIElement {
|
|
59
78
|
// Phosphor icons this primitive auto-stamps (without consumer markup).
|
|
60
79
|
// Aggregated by installIconLoadersForRegistered() across all defined
|
|
@@ -238,8 +257,13 @@ export class UIIntegrationCard extends UIElement {
|
|
|
238
257
|
// a "not found" warn for every {{p:4}} it receives on first connect.
|
|
239
258
|
if (logo.startsWith('{{p:')) return;
|
|
240
259
|
|
|
241
|
-
//
|
|
242
|
-
|
|
260
|
+
// Resolve the visual: an explicit [logo] wins; otherwise fall back to a
|
|
261
|
+
// per-provider glyph (or a generic plug) so a card with only [provider]
|
|
262
|
+
// never renders a blank disc. Hide only when there's neither.
|
|
263
|
+
const provider = (this.provider || '').trim().toLowerCase();
|
|
264
|
+
const resolved = logo || PROVIDER_LOGO[provider] || (provider ? DEFAULT_LOGO : '');
|
|
265
|
+
|
|
266
|
+
if (!resolved) {
|
|
243
267
|
this.#logoEl.replaceChildren();
|
|
244
268
|
this.#logoEl.hidden = true;
|
|
245
269
|
return;
|
|
@@ -247,18 +271,18 @@ export class UIIntegrationCard extends UIElement {
|
|
|
247
271
|
this.#logoEl.hidden = false;
|
|
248
272
|
|
|
249
273
|
// URL vs icon-name sniff: presence of '/' → URL.
|
|
250
|
-
const isUrl =
|
|
274
|
+
const isUrl = resolved.includes('/');
|
|
251
275
|
|
|
252
276
|
if (isUrl) {
|
|
253
277
|
// Reuse existing <img> if same src; otherwise re-stamp.
|
|
254
278
|
let img = this.#logoEl.querySelector(':scope > img');
|
|
255
|
-
if (img && img.getAttribute('src') ===
|
|
279
|
+
if (img && img.getAttribute('src') === resolved) {
|
|
256
280
|
img.setAttribute('alt', `${this.name || this.provider || 'Integration'} logo`);
|
|
257
281
|
return;
|
|
258
282
|
}
|
|
259
283
|
this.#logoEl.replaceChildren();
|
|
260
284
|
img = document.createElement('img');
|
|
261
|
-
img.setAttribute('src',
|
|
285
|
+
img.setAttribute('src', resolved);
|
|
262
286
|
img.setAttribute('alt', `${this.name || this.provider || 'Integration'} logo`);
|
|
263
287
|
img.setAttribute('data-integration-logo', '');
|
|
264
288
|
img.setAttribute('loading', 'lazy');
|
|
@@ -269,10 +293,10 @@ export class UIIntegrationCard extends UIElement {
|
|
|
269
293
|
|
|
270
294
|
// Icon name → <icon-ui>.
|
|
271
295
|
let icon = this.#logoEl.querySelector(':scope > icon-ui');
|
|
272
|
-
if (icon && icon.getAttribute('name') ===
|
|
296
|
+
if (icon && icon.getAttribute('name') === resolved) return;
|
|
273
297
|
this.#logoEl.replaceChildren();
|
|
274
298
|
icon = document.createElement('icon-ui');
|
|
275
|
-
icon.setAttribute('name',
|
|
299
|
+
icon.setAttribute('name', resolved);
|
|
276
300
|
icon.setAttribute('aria-hidden', 'true');
|
|
277
301
|
this.#logoEl.appendChild(icon);
|
|
278
302
|
}
|
|
@@ -256,11 +256,22 @@ describe('integration-card-ui — logo rendering', () => {
|
|
|
256
256
|
expect(icon.getAttribute('name')).toBe('lightning');
|
|
257
257
|
});
|
|
258
258
|
|
|
259
|
-
it('
|
|
259
|
+
it('renders a provider fallback glyph when logo prop is empty', async () => {
|
|
260
|
+
// Empty [logo] + a [provider] → the per-provider Phosphor glyph fallback
|
|
261
|
+
// (instead of a blank logo disc): wrapper stays visible with an <icon-ui>.
|
|
260
262
|
const c = mount('<integration-card-ui provider="slack" name="Slack"></integration-card-ui>');
|
|
261
263
|
await tick2();
|
|
262
264
|
const wrap = c.querySelector('[data-integration-card-logo]');
|
|
263
265
|
expect(wrap).not.toBeNull();
|
|
266
|
+
expect(wrap.hidden).toBe(false);
|
|
267
|
+
expect(wrap.querySelector('icon-ui')).not.toBeNull();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('hides logo wrapper when both logo and provider are empty', async () => {
|
|
271
|
+
const c = mount('<integration-card-ui name="Custom"></integration-card-ui>');
|
|
272
|
+
await tick2();
|
|
273
|
+
const wrap = c.querySelector('[data-integration-card-logo]');
|
|
274
|
+
expect(wrap).not.toBeNull();
|
|
264
275
|
expect(wrap.hidden).toBe(true);
|
|
265
276
|
});
|
|
266
277
|
});
|
|
@@ -17,11 +17,12 @@
|
|
|
17
17
|
"const": "Kbd"
|
|
18
18
|
},
|
|
19
19
|
"size": {
|
|
20
|
-
"description": "Sizing scale
|
|
20
|
+
"description": "Sizing scale: sm, md (default), lg.",
|
|
21
21
|
"type": "string",
|
|
22
22
|
"enum": [
|
|
23
23
|
"sm",
|
|
24
|
-
"md"
|
|
24
|
+
"md",
|
|
25
|
+
"lg"
|
|
25
26
|
],
|
|
26
27
|
"default": ""
|
|
27
28
|
}
|
package/components/kbd/kbd.css
CHANGED
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
--kbd-radius-default: var(--a-radius-sm);
|
|
8
8
|
--kbd-font-default: var(--a-font-family-code);
|
|
9
9
|
|
|
10
|
-
/* Size — defaults to md
|
|
11
|
-
|
|
10
|
+
/* Size — defaults to md. Glyph is --a-ui-xs (12px) so md reads distinctly
|
|
11
|
+
larger than sm (--a-ui-tiny 10px) inside its 20px cap — previously both
|
|
12
|
+
md and sm resolved to --a-ui-tiny, so the glyphs were identical. */
|
|
13
|
+
--kbd-font-size-default: var(--a-ui-xs);
|
|
12
14
|
--kbd-height-default: 1.25rem;
|
|
13
15
|
--kbd-min-width-default: 1.25rem;
|
|
14
16
|
--kbd-px-default: var(--a-space-1);
|
|
@@ -19,8 +21,9 @@
|
|
|
19
21
|
--kbd-height-sm-default: 1rem;
|
|
20
22
|
--kbd-min-width-sm-default: 1rem;
|
|
21
23
|
|
|
22
|
-
/* Size: lg
|
|
23
|
-
|
|
24
|
+
/* Size: lg — glyph --a-ui-md (14px), the top of the three-tier ramp
|
|
25
|
+
(sm 10 / md 12 / lg 14). */
|
|
26
|
+
--kbd-font-size-lg-default: var(--a-ui-md);
|
|
24
27
|
--kbd-height-lg-default: 1.5rem;
|
|
25
28
|
--kbd-min-width-lg-default: 1.5rem;
|
|
26
29
|
text-align: start; /* §text-align-reset — blocks inheritance from centered ancestors */
|
package/components/kbd/kbd.d.ts
CHANGED
package/components/kbd/kbd.yaml
CHANGED
|
@@ -11,12 +11,13 @@ description: >-
|
|
|
11
11
|
menu items, tooltips, command hints, and shortcut documentation.
|
|
12
12
|
props:
|
|
13
13
|
size:
|
|
14
|
-
description: Sizing scale
|
|
14
|
+
description: 'Sizing scale: sm, md (default), lg.'
|
|
15
15
|
type: string
|
|
16
16
|
default: ''
|
|
17
17
|
enum:
|
|
18
18
|
- sm
|
|
19
19
|
- md
|
|
20
|
+
- lg
|
|
20
21
|
reflect: true
|
|
21
22
|
events: {}
|
|
22
23
|
slots:
|
|
@@ -87,8 +87,15 @@ export class UIList extends UIElement {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
#items() {
|
|
90
|
+
// Match by TAG as well as role: a child <list-item-ui> sets role="listitem"
|
|
91
|
+
// in its OWN connected(), which (light-DOM upgrade order) has NOT run yet
|
|
92
|
+
// when the parent list first render()s. A role-only filter therefore found
|
|
93
|
+
// zero items on the initial selectable render → aria-selected was never
|
|
94
|
+
// stamped → the [aria-selected="true"] selection CSS never matched. The tag
|
|
95
|
+
// name is present pre-upgrade, so this finds the rows regardless of timing;
|
|
96
|
+
// the attribute (aria-selected) we set persists through their later upgrade.
|
|
90
97
|
return [...this.children].filter(
|
|
91
|
-
(el) => el.
|
|
98
|
+
(el) => el.tagName === 'LIST-ITEM-UI' || el.getAttribute?.('role') === 'listitem',
|
|
92
99
|
);
|
|
93
100
|
}
|
|
94
101
|
|
package/components/menu/menu.css
CHANGED
|
@@ -12,10 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
:scope {
|
|
14
14
|
box-sizing: border-box;
|
|
15
|
-
display:
|
|
15
|
+
display: flex;
|
|
16
16
|
position: relative;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/* Display convention (ADR-0037): block-level by default; [inline] opts back to inline-level. */
|
|
20
|
+
:scope[inline] { display: inline-flex; }
|
|
21
|
+
|
|
19
22
|
/* Items/dividers in Light DOM are hidden unless they've been adopted
|
|
20
23
|
into the popover on open. Popover API also hides the popover itself
|
|
21
24
|
when closed. */
|
|
@@ -122,7 +122,16 @@ export class UIModal extends UIElement {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
#getDuration() {
|
|
125
|
-
|
|
125
|
+
// Read the same fallback chain the CSS transitions use —
|
|
126
|
+
// var(--modal-duration, var(--modal-duration-default)). Reading only the
|
|
127
|
+
// public token (unset unless a consumer overrides) made this always fall
|
|
128
|
+
// back to 200ms while the CSS exit animation ran for --modal-duration-default
|
|
129
|
+
// (= --a-duration = 250ms), so the dialog closed 50ms early, clipping the
|
|
130
|
+
// animation. Same class as swiper bug-29 (the OD-5 -default token sweep left
|
|
131
|
+
// JS read-sites pointed at the now-unset public token).
|
|
132
|
+
const cs = getComputedStyle(this);
|
|
133
|
+
const raw = cs.getPropertyValue('--modal-duration').trim()
|
|
134
|
+
|| cs.getPropertyValue('--modal-duration-default').trim();
|
|
126
135
|
return parseFloat(raw) || 200;
|
|
127
136
|
}
|
|
128
137
|
|
|
@@ -142,6 +142,12 @@
|
|
|
142
142
|
flex: 1;
|
|
143
143
|
overflow: auto;
|
|
144
144
|
}
|
|
145
|
+
/* Form controls in the modal body fill the available width (a modal body is
|
|
146
|
+
a form column, not a content-width row) — matches the field/input family's
|
|
147
|
+
full-width behaviour in a real form. */
|
|
148
|
+
:scope [slot="body"] :is(field-ui, input-ui, select-ui, textarea-ui, combobox-ui, tags-input-ui, color-input-ui, slider-ui) {
|
|
149
|
+
width: 100%;
|
|
150
|
+
}
|
|
145
151
|
|
|
146
152
|
/* ═══════ Footer ═══════ */
|
|
147
153
|
:scope [slot="footer"] {
|
|
@@ -153,4 +159,7 @@
|
|
|
153
159
|
border-top: 1px solid var(--modal-border, var(--modal-border-default));
|
|
154
160
|
flex-shrink: 0;
|
|
155
161
|
}
|
|
162
|
+
/* No content → no footer chrome: an empty footer slot otherwise renders a
|
|
163
|
+
dead band (border-top + padding) under the body. */
|
|
164
|
+
:scope [slot="footer"]:empty { display: none; }
|
|
156
165
|
}
|
|
@@ -114,6 +114,9 @@
|
|
|
114
114
|
"default": {
|
|
115
115
|
"description": "Spillover content revealed only when the card is checked — typically a follow-up form field (e.g. a textarea on an \"Other\" option, conditional inputs that depend on the selection). Aligns with the heading/description column; hidden via `display: none` when not checked."
|
|
116
116
|
},
|
|
117
|
+
"action": {
|
|
118
|
+
"description": "CTA slot for `layout=\"tile\"` — a button-ui anchored to the card's bottom edge and stretched full-width, so a row of tile cards keeps their CTAs aligned regardless of body length (plan-picker-style pickers)."
|
|
119
|
+
},
|
|
117
120
|
"heading": {
|
|
118
121
|
"description": "Rich heading content. Overrides the `heading` attribute when present."
|
|
119
122
|
},
|
|
@@ -173,34 +173,59 @@ option-card-ui[checked] [slot="icon"] {
|
|
|
173
173
|
description below, all left-aligned. Used for hero pickers
|
|
174
174
|
(data source, role, plan tiles) where the icon is a primary
|
|
175
175
|
brand cue rather than secondary chrome. ── */
|
|
176
|
+
/* Tile layout — a vertical content stack (icon → heading → description →
|
|
177
|
+
body → action) with the radio indicator pinned top-right OUT of flow.
|
|
178
|
+
Flex (not grid) so content flows in DOM order with no reserved/empty
|
|
179
|
+
tracks: a no-icon plan card has no dead left column, and a slotted CTA
|
|
180
|
+
(slot="action") anchors to the card bottom regardless of how much body
|
|
181
|
+
content (price / feature list) precedes it. (Plan Picker option-card grid) */
|
|
176
182
|
:scope[layout="tile"] {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
display: flex;
|
|
184
|
+
flex-direction: column;
|
|
185
|
+
gap: var(--option-card-gap-y, var(--option-card-gap-y-default));
|
|
186
|
+
position: relative;
|
|
187
|
+
/* padding inherited from base :scope (token-driven, --option-card-padding-*) */
|
|
188
|
+
}
|
|
189
|
+
/* Radio pinned to the card's top-right corner, OUT of flow. Clear the
|
|
190
|
+
base grid-area:indicator so the absolute containing block is the card
|
|
191
|
+
itself (position:relative) rather than a collapsed grid cell — otherwise
|
|
192
|
+
a grid-forcing host (plan-picker-ui) strands the indicator in an 8px
|
|
193
|
+
gutter and inset-inline-end resolves negative. Inset by the active
|
|
194
|
+
padding token so the dot aligns with the content edge. */
|
|
195
|
+
:scope[layout="tile"]::before {
|
|
196
|
+
position: absolute;
|
|
197
|
+
grid-area: auto;
|
|
198
|
+
inset-block-start: var(--option-card-padding-block, var(--option-card-padding-block-default));
|
|
199
|
+
inset-inline-end: var(--option-card-padding-inline, var(--option-card-padding-inline-default));
|
|
200
|
+
margin-block-start: 0;
|
|
186
201
|
}
|
|
202
|
+
/* Visual order is fixed via `order`, NOT DOM order: composites
|
|
203
|
+
(plan-picker-ui) append default-slot body content BEFORE option-card
|
|
204
|
+
stamps the heading/description spans on connect, so a flex stack that
|
|
205
|
+
trusted DOM order would render the body above the title. */
|
|
187
206
|
:scope[layout="tile"] [slot="icon"] {
|
|
188
|
-
|
|
189
|
-
justify-self: start;
|
|
207
|
+
order: 1;
|
|
190
208
|
align-self: start;
|
|
191
209
|
--option-card-icon-size-default: 1.75rem;
|
|
192
210
|
}
|
|
193
|
-
:scope[layout="tile"]::before {
|
|
194
|
-
grid-area: indicator;
|
|
195
|
-
align-self: start;
|
|
196
|
-
justify-self: end;
|
|
197
|
-
margin-block-start: 0;
|
|
198
|
-
}
|
|
199
211
|
:scope[layout="tile"] [slot="heading"] {
|
|
200
|
-
|
|
212
|
+
order: 2;
|
|
213
|
+
/* Reserve the top-right radio's footprint so a long heading doesn't run
|
|
214
|
+
under it. */
|
|
215
|
+
padding-inline-end: var(--a-space-6);
|
|
216
|
+
}
|
|
217
|
+
:scope[layout="tile"] [slot="description"] {
|
|
218
|
+
order: 3;
|
|
201
219
|
}
|
|
202
220
|
:scope[layout="tile"] > :not([slot]) {
|
|
203
|
-
|
|
221
|
+
order: 4;
|
|
222
|
+
}
|
|
223
|
+
/* CTA anchors to the card bottom (auto top-margin) + full-width, so a row of
|
|
224
|
+
plan cards with different body lengths keeps their buttons aligned. */
|
|
225
|
+
:scope[layout="tile"] [slot="action"] {
|
|
226
|
+
order: 5;
|
|
227
|
+
width: 100%;
|
|
228
|
+
margin-block-start: auto;
|
|
204
229
|
}
|
|
205
230
|
|
|
206
231
|
/* ── State: disabled ── */
|
|
@@ -76,6 +76,11 @@ slots:
|
|
|
76
76
|
description: Rich description content. Overrides the `description` attribute when present.
|
|
77
77
|
icon:
|
|
78
78
|
description: Custom icon element. Overrides the `icon` attribute when present.
|
|
79
|
+
action:
|
|
80
|
+
description: >-
|
|
81
|
+
CTA slot for `layout="tile"` — a button-ui anchored to the card's bottom
|
|
82
|
+
edge and stretched full-width, so a row of tile cards keeps their CTAs
|
|
83
|
+
aligned regardless of body length (plan-picker-style pickers).
|
|
79
84
|
default:
|
|
80
85
|
description: >-
|
|
81
86
|
Spillover content revealed only when the card is checked — typically a
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
@scope (otp-input-ui) {
|
|
2
2
|
:where(:scope) {
|
|
3
3
|
/* ── Tokens ── */
|
|
4
|
-
|
|
4
|
+
/* Digit boxes grow to fill the row, capped at ~1.75× the base control size
|
|
5
|
+
(~52px) so they read as comfortable single-digit boxes. Radius is the
|
|
6
|
+
plain --a-radius-lg token (no min()/clamp expression — radius patterns
|
|
7
|
+
stay simple). Scales with [size] + density (relative to --a-size). */
|
|
8
|
+
--otp-input-size-default: calc(var(--a-size) * 1.75);
|
|
5
9
|
--otp-input-gap-default: var(--a-space-2);
|
|
6
|
-
--otp-input-radius-default: var(--a-radius-
|
|
10
|
+
--otp-input-radius-default: var(--a-radius-lg);
|
|
7
11
|
--otp-input-border-default: var(--a-ui-border);
|
|
8
12
|
--otp-input-border-hover-default: var(--a-ui-border-hover);
|
|
9
13
|
--otp-input-border-focus-default: var(--a-accent);
|
|
@@ -24,15 +28,27 @@
|
|
|
24
28
|
/* ── Base ── */
|
|
25
29
|
box-sizing: border-box;
|
|
26
30
|
display: flex;
|
|
27
|
-
justify-content:
|
|
31
|
+
justify-content: center;
|
|
28
32
|
gap: var(--otp-input-gap, var(--otp-input-gap-default));
|
|
33
|
+
/* Fill the container (a block-level control per ADR-0037) so the digit
|
|
34
|
+
boxes grow to use the available width instead of sitting content-width
|
|
35
|
+
on the left. Boxes flex to fill up to their cap; once capped (very wide
|
|
36
|
+
container) the group centers rather than packing left. (bug-37 follow-up) */
|
|
37
|
+
width: 100%;
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
/* ── Digit inputs ── */
|
|
41
|
+
/* Boxes grow equally to fill the row (flex), staying square (aspect-ratio)
|
|
42
|
+
and capped at --otp-input-size so they read as digit boxes — not huge on a
|
|
43
|
+
wide container, not cramped on a narrow one. The cap keeps --a-radius-md
|
|
44
|
+
proportional (a rounded square, ~25-30%), which is the whole point of the
|
|
45
|
+
bug-37 sizing. */
|
|
32
46
|
[slot="digit"] {
|
|
33
47
|
box-sizing: border-box;
|
|
34
|
-
|
|
35
|
-
|
|
48
|
+
flex: 1 1 0;
|
|
49
|
+
min-width: 0;
|
|
50
|
+
max-width: var(--otp-input-size, var(--otp-input-size-default));
|
|
51
|
+
aspect-ratio: 1;
|
|
36
52
|
text-align: center;
|
|
37
53
|
border: 1px solid var(--otp-input-border, var(--otp-input-border-default));
|
|
38
54
|
border-radius: var(--otp-input-radius, var(--otp-input-radius-default));
|
|
@@ -67,12 +83,11 @@
|
|
|
67
83
|
:scope[disabled] [slot="digit"] {
|
|
68
84
|
background: var(--otp-input-bg-disabled, var(--otp-input-bg-disabled-default));
|
|
69
85
|
color: var(--otp-input-fg-disabled, var(--otp-input-fg-disabled-default));
|
|
70
|
-
/*
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
border-
|
|
86
|
+
/* Native dashed border — it follows the box's border-radius (a rounded
|
|
87
|
+
dashed square, matching the enabled state). An SVG border-image would
|
|
88
|
+
give more uniform dashes but is clipped to a SQUARE — it ignores
|
|
89
|
+
border-radius — so the disabled corners wouldn't round. */
|
|
74
90
|
border: 1px dashed var(--otp-input-border-disabled, var(--otp-input-border-disabled-default));
|
|
75
|
-
border-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='28' height='28' preserveAspectRatio='none'><rect x='0.5' y='0.5' width='27' height='27' fill='none' stroke='%23999' stroke-width='1' stroke-dasharray='8 6'/></svg>") 1 repeat;
|
|
76
91
|
cursor: not-allowed;
|
|
77
92
|
}
|
|
78
93
|
}
|