@adia-ai/web-components 0.8.25 → 0.8.27
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 +18 -0
- package/components/menu/menu-item.a2ui.json +12 -1
- package/components/menu/menu-item.yaml +9 -1
- package/components/menu/menu.class.js +33 -1
- package/components/menu/menu.css +43 -0
- package/components/menu/menu.d.ts +2 -0
- package/components/richtext/richtext.css +7 -2
- package/dist/theme-provider.min.js +1 -1
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +83 -83
- package/dist/web-components.sheet.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog — @adia-ai/web-components
|
|
2
2
|
|
|
3
|
+
## [0.8.27] — 2026-08-05
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **`menu-item-ui` gains `subtitle` (+ a trailing slot)** — menu rows can carry a secondary line, unblocking composite menu rows without native `<button>` stamps (gh#616, PR #623).
|
|
7
|
+
|
|
8
|
+
### Maintenance
|
|
9
|
+
- **`components/` touched in this release window** (5 file(s), e.g. `menu/menu-item.a2ui.json`) — carried by the entries above.
|
|
10
|
+
- **`dist/` bundles rebuilt** in this cut's window (4 file(s)) — regenerated from the source changes described above, not independent edits.
|
|
11
|
+
|
|
12
|
+
## [0.8.26] — 2026-08-05
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **richtext link focus had no indicator at all** — the focus rule assigned the box-shadow-shaped `--a-focus-ring` token to `outline`, invalid CSS that silently resolves to `none` (gh#594, PR #595). Fixed to the canonical `outline: none` + `box-shadow: var(--a-focus-ring)` shape. The focus-handoff rule is now written into `component-token-contract.md` (never-zero / never-two indicators) and enforced by the new `check:focus-handoff` gate (negative-control proven).
|
|
16
|
+
|
|
17
|
+
### Maintenance
|
|
18
|
+
- **`components/` touched in this release window** (1 file(s), e.g. `richtext/richtext.css`) — carried by the entries above.
|
|
19
|
+
- **`dist/` bundles rebuilt** in this cut's window (4 file(s)) — regenerated from the source changes described above, not independent edits.
|
|
20
|
+
|
|
3
21
|
## [0.8.25] — 2026-07-31
|
|
4
22
|
|
|
5
23
|
### Fixed
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://adiaui.dev/a2ui/v0_9/components/MenuItem.json",
|
|
4
4
|
"title": "MenuItem",
|
|
5
|
-
"description": "Child of `<menu-ui>`. One actionable row inside a menu, with optional leading icon + label + value.",
|
|
5
|
+
"description": "Child of `<menu-ui>`. One actionable row inside a menu, with optional leading icon + label + subtitle + value.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"allOf": [
|
|
8
8
|
{
|
|
@@ -26,6 +26,11 @@
|
|
|
26
26
|
"type": "string",
|
|
27
27
|
"default": ""
|
|
28
28
|
},
|
|
29
|
+
"subtitle": {
|
|
30
|
+
"description": "Optional secondary description line rendered under the label.",
|
|
31
|
+
"type": "string",
|
|
32
|
+
"default": ""
|
|
33
|
+
},
|
|
29
34
|
"text": {
|
|
30
35
|
"description": "Item label text.",
|
|
31
36
|
"type": "string",
|
|
@@ -73,8 +78,14 @@
|
|
|
73
78
|
"icon": {
|
|
74
79
|
"description": "Custom leading element override; falls through to `[icon]` prop if not slotted."
|
|
75
80
|
},
|
|
81
|
+
"subtitle": {
|
|
82
|
+
"description": "Custom secondary-line content override; falls through to `[subtitle]` prop if not slotted."
|
|
83
|
+
},
|
|
76
84
|
"text": {
|
|
77
85
|
"description": "Custom label content; falls through to `[text]` prop if not slotted."
|
|
86
|
+
},
|
|
87
|
+
"trailing": {
|
|
88
|
+
"description": "Trailing element pinned to the far edge (shortcut hint, check mark)."
|
|
78
89
|
}
|
|
79
90
|
},
|
|
80
91
|
"states": [],
|
|
@@ -13,7 +13,7 @@ component: MenuItem
|
|
|
13
13
|
category: navigation
|
|
14
14
|
version: 1
|
|
15
15
|
description: |-
|
|
16
|
-
Child of `<menu-ui>`. One actionable row inside a menu, with optional leading icon + label + value.
|
|
16
|
+
Child of `<menu-ui>`. One actionable row inside a menu, with optional leading icon + label + subtitle + value.
|
|
17
17
|
|
|
18
18
|
props:
|
|
19
19
|
icon:
|
|
@@ -24,6 +24,10 @@ props:
|
|
|
24
24
|
description: Item label text.
|
|
25
25
|
type: string
|
|
26
26
|
default: ""
|
|
27
|
+
subtitle:
|
|
28
|
+
description: Optional secondary description line rendered under the label.
|
|
29
|
+
type: string
|
|
30
|
+
default: ""
|
|
27
31
|
value:
|
|
28
32
|
description: Stable identifier emitted via the parent menu's `select` event.
|
|
29
33
|
type: string
|
|
@@ -44,6 +48,10 @@ slots:
|
|
|
44
48
|
description: Custom leading element override; falls through to `[icon]` prop if not slotted.
|
|
45
49
|
text:
|
|
46
50
|
description: Custom label content; falls through to `[text]` prop if not slotted.
|
|
51
|
+
subtitle:
|
|
52
|
+
description: Custom secondary-line content override; falls through to `[subtitle]` prop if not slotted.
|
|
53
|
+
trailing:
|
|
54
|
+
description: Trailing element pinned to the far edge (shortcut hint, check mark).
|
|
47
55
|
|
|
48
56
|
a2ui:
|
|
49
57
|
rules:
|
|
@@ -258,6 +258,7 @@ export class UIMenuItem extends UIElement {
|
|
|
258
258
|
static properties = {
|
|
259
259
|
icon: { type: String, default: '', reflect: true },
|
|
260
260
|
text: { type: String, default: '', reflect: true },
|
|
261
|
+
subtitle: { type: String, default: '', reflect: true },
|
|
261
262
|
value: { type: String, default: '', reflect: true },
|
|
262
263
|
variant: { type: String, default: 'default', reflect: true },
|
|
263
264
|
disabled: { type: Boolean, default: false, reflect: true },
|
|
@@ -265,6 +266,11 @@ export class UIMenuItem extends UIElement {
|
|
|
265
266
|
|
|
266
267
|
static template = () => null;
|
|
267
268
|
|
|
269
|
+
// Set when connected() stamped role="menuitem" itself; a consumer-provided
|
|
270
|
+
// role (e.g. admin-roster's role="menuitemradio", gh#616) is never touched
|
|
271
|
+
// — that consumer also owns tabindex and any checked-state aria.
|
|
272
|
+
#roleStamped = false;
|
|
273
|
+
|
|
268
274
|
connected() {
|
|
269
275
|
// role="menuitem" ONLY when actually inside a menu container. A menu-ui
|
|
270
276
|
// closes by moving its items back into its own light DOM (see #hide()),
|
|
@@ -273,12 +279,16 @@ export class UIMenuItem extends UIElement {
|
|
|
273
279
|
// open popover (role="menu") adopts items via appendChild, which re-fires
|
|
274
280
|
// connected(), so the role re-applies when the menu opens and is dropped
|
|
275
281
|
// again on close.
|
|
276
|
-
if (this.
|
|
282
|
+
if (this.hasAttribute('role') && !this.#roleStamped) {
|
|
283
|
+
// Consumer-owned role — leave role/tabindex alone.
|
|
284
|
+
} else if (this.closest('[role="menu"], [role="menubar"]')) {
|
|
277
285
|
this.setAttribute('role', 'menuitem');
|
|
286
|
+
this.#roleStamped = true;
|
|
278
287
|
if (!this.hasAttribute('tabindex')) this.setAttribute('tabindex', '-1');
|
|
279
288
|
} else {
|
|
280
289
|
this.removeAttribute('role');
|
|
281
290
|
this.removeAttribute('tabindex');
|
|
291
|
+
this.#roleStamped = false;
|
|
282
292
|
}
|
|
283
293
|
this.#stamp();
|
|
284
294
|
this.#syncAria();
|
|
@@ -313,6 +323,13 @@ export class UIMenuItem extends UIElement {
|
|
|
313
323
|
span.textContent = this.text;
|
|
314
324
|
this.appendChild(span);
|
|
315
325
|
}
|
|
326
|
+
|
|
327
|
+
if (this.subtitle && !this.#ownChild('[slot="subtitle"]')) {
|
|
328
|
+
const sub = this.#stampMark(document.createElement('span'));
|
|
329
|
+
sub.setAttribute('slot', 'subtitle');
|
|
330
|
+
sub.textContent = this.subtitle;
|
|
331
|
+
this.appendChild(sub);
|
|
332
|
+
}
|
|
316
333
|
}
|
|
317
334
|
|
|
318
335
|
#syncAria() {
|
|
@@ -340,6 +357,21 @@ export class UIMenuItem extends UIElement {
|
|
|
340
357
|
const textEl = this.#ownChild('[slot="text"]');
|
|
341
358
|
if (this.#wasStamped(textEl)) textEl.textContent = this.text;
|
|
342
359
|
|
|
360
|
+
// Sync subtitle — only touch elements we stamped.
|
|
361
|
+
const subEl = this.#ownChild('[slot="subtitle"]');
|
|
362
|
+
if (this.subtitle) {
|
|
363
|
+
if (subEl) {
|
|
364
|
+
if (this.#wasStamped(subEl)) subEl.textContent = this.subtitle;
|
|
365
|
+
} else {
|
|
366
|
+
const el = this.#stampMark(document.createElement('span'));
|
|
367
|
+
el.setAttribute('slot', 'subtitle');
|
|
368
|
+
el.textContent = this.subtitle;
|
|
369
|
+
this.appendChild(el);
|
|
370
|
+
}
|
|
371
|
+
} else if (this.#wasStamped(subEl)) {
|
|
372
|
+
subEl.remove();
|
|
373
|
+
}
|
|
374
|
+
|
|
343
375
|
this.#syncAria();
|
|
344
376
|
}
|
|
345
377
|
}
|
package/components/menu/menu.css
CHANGED
|
@@ -136,6 +136,8 @@ menu-item-ui[variant="danger"]:hover [slot="icon"] {
|
|
|
136
136
|
|
|
137
137
|
/* ── Typography ── */
|
|
138
138
|
--menu-item-font-size: var(--a-ui-size);
|
|
139
|
+
--menu-item-subtitle-size: var(--a-ui-sm);
|
|
140
|
+
--menu-item-subtitle-fg: var(--a-fg-muted);
|
|
139
141
|
|
|
140
142
|
/* ── Transition ── */
|
|
141
143
|
--menu-item-duration: var(--a-duration-fast);
|
|
@@ -178,6 +180,47 @@ menu-item-ui[variant="danger"]:hover [slot="icon"] {
|
|
|
178
180
|
white-space: nowrap;
|
|
179
181
|
}
|
|
180
182
|
|
|
183
|
+
/* Trailing (shortcut hint, check mark) — pinned to the far edge */
|
|
184
|
+
[slot="trailing"] {
|
|
185
|
+
flex-shrink: 0;
|
|
186
|
+
margin-left: auto;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* ── Subtitle — two-line anatomy ──
|
|
190
|
+
Content-driven: a present [slot="subtitle"] switches the row to a
|
|
191
|
+
grid so the subtitle sits under the label while icon and trailing
|
|
192
|
+
content span both lines. */
|
|
193
|
+
:scope:has(> [slot="subtitle"]) {
|
|
194
|
+
display: grid;
|
|
195
|
+
grid-template-columns: auto 1fr auto;
|
|
196
|
+
align-items: center;
|
|
197
|
+
column-gap: var(--menu-item-gap);
|
|
198
|
+
row-gap: 0;
|
|
199
|
+
}
|
|
200
|
+
:scope:has(> [slot="subtitle"]) > [slot="icon"] {
|
|
201
|
+
grid-column: 1;
|
|
202
|
+
grid-row: 1 / span 2;
|
|
203
|
+
}
|
|
204
|
+
:scope:has(> [slot="subtitle"]) > [slot="text"] {
|
|
205
|
+
grid-column: 2;
|
|
206
|
+
grid-row: 1;
|
|
207
|
+
}
|
|
208
|
+
:scope:has(> [slot="subtitle"]) > [slot="trailing"] {
|
|
209
|
+
grid-column: 3;
|
|
210
|
+
grid-row: 1 / span 2;
|
|
211
|
+
margin-left: 0;
|
|
212
|
+
}
|
|
213
|
+
:scope > [slot="subtitle"] {
|
|
214
|
+
grid-column: 2;
|
|
215
|
+
grid-row: 2;
|
|
216
|
+
min-width: 0;
|
|
217
|
+
font-size: var(--menu-item-subtitle-size);
|
|
218
|
+
color: var(--menu-item-subtitle-fg);
|
|
219
|
+
white-space: nowrap;
|
|
220
|
+
overflow: hidden;
|
|
221
|
+
text-overflow: ellipsis;
|
|
222
|
+
}
|
|
223
|
+
|
|
181
224
|
/* ── Variant: danger ── */
|
|
182
225
|
:scope[variant="danger"] {
|
|
183
226
|
--menu-item-fg: var(--a-danger-bg);
|
|
@@ -45,6 +45,8 @@ export class UIMenuItem extends UIElement {
|
|
|
45
45
|
disabled: boolean;
|
|
46
46
|
/** Optional leading Phosphor icon name. */
|
|
47
47
|
icon: string;
|
|
48
|
+
/** Optional secondary description line rendered under the label. */
|
|
49
|
+
subtitle: string;
|
|
48
50
|
/** Item label text. */
|
|
49
51
|
text: string;
|
|
50
52
|
/** Stable identifier emitted via the parent menu's `select` event. */
|
|
@@ -381,8 +381,13 @@
|
|
|
381
381
|
}
|
|
382
382
|
|
|
383
383
|
[data-richtext-body] a:focus-visible {
|
|
384
|
-
|
|
385
|
-
|
|
384
|
+
/* --a-focus-ring is a box-shadow value (two stacked shadow layers), not a
|
|
385
|
+
valid `outline` value — the prior `outline: var(--a-focus-ring)` was
|
|
386
|
+
invalid at computed-value time, which resolves outline to its initial
|
|
387
|
+
`none` and left focused links with ZERO indicator (gh#594). Canonical
|
|
388
|
+
box-shadow ring + explicit outline suppression, matching link.css. */
|
|
389
|
+
outline: none;
|
|
390
|
+
box-shadow: var(--a-focus-ring);
|
|
386
391
|
border-radius: var(--a-radius-sm);
|
|
387
392
|
}
|
|
388
393
|
|