@adia-ai/web-components 0.8.37 → 0.8.39
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 +36 -0
- package/MIGRATION.md +9 -7
- package/README.md +3 -3
- package/bin/doc.mjs +27 -5
- package/components/calendar-picker/calendar-picker.css +4 -1
- package/components/card/card.css +1 -1
- package/components/combobox/combobox.css +6 -0
- package/components/command/command.a2ui.json +3 -0
- package/components/command/command.class.js +28 -6
- package/components/command/command.css +14 -3
- package/components/command/command.yaml +5 -0
- package/components/date-range-picker/date-range-picker.css +6 -0
- package/components/datetime-picker/datetime-picker.css +4 -0
- package/components/drilldown/drilldown.a2ui.json +244 -0
- package/components/drilldown/drilldown.class.js +550 -0
- package/components/drilldown/drilldown.css +304 -0
- package/components/drilldown/drilldown.d.ts +68 -0
- package/components/drilldown/drilldown.examples.md +20 -0
- package/components/drilldown/drilldown.js +17 -0
- package/components/drilldown/drilldown.yaml +273 -0
- package/components/index.js +1 -0
- package/components/modal/modal.class.js +68 -4
- package/components/nav/nav.a2ui.json +5 -0
- package/components/nav/nav.class.js +35 -12
- package/components/nav/nav.d.ts +2 -0
- package/components/nav/nav.yaml +18 -0
- package/components/nav-item/nav-item.class.js +9 -6
- package/components/page/page.a2ui.json +13 -1
- package/components/page/page.css +113 -0
- package/components/page/page.yaml +30 -3
- package/components/select/select.class.js +30 -12
- package/components/select/select.css +11 -2
- package/components/swatch/swatch.css +6 -4
- package/components/toggle-group/toggle-group.class.js +21 -11
- package/components/toggle-group/toggle-group.css +16 -8
- package/components/toggle-group/toggle-group.d.ts +6 -0
- package/components/toggle-group/toggle-group.yaml +10 -0
- package/components/toggle-group/toggle-option.a2ui.json +5 -0
- package/components/toggle-group/toggle-option.yaml +18 -2
- package/custom-elements.json +277 -100
- package/dist/theme-provider.min.js +3 -3
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +110 -88
- package/dist/web-components.sheet.js +1 -1
- package/package.json +1 -1
- package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.html +1 -1
- package/styles/components.css +1 -0
- package/traits/view-transition/view-transition.js +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog — @adia-ai/web-components
|
|
2
2
|
|
|
3
|
+
## [0.8.39] — 2026-08-15
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- **`toggle-option-ui` converges onto `segment-ui`'s declared, parent-managed, reflected `[selected]` prop (ADR-0056, gh#1303).** Found by `framework-alignment-agent`'s corpus-wide component-state-naming sweep: the two same-role selection-item primitives named their "currently selected" state differently — `segment-ui` declared a reflected `selected: Boolean` prop (yaml → catalog → generative grammar), while `toggle-option-ui` had no declared state at all; the parent (`toggle-group-ui`) instead stamped a private `data-selected` attribute invisible to the yaml SoT, catalog, and A2UI grammar. `toggle-option-ui` gains `selected` (`Boolean`, default `false`, reflected) in `toggle-option.yaml` + `toggle-group.class.js`; `toggle-group-ui` now sets `selected` on its children (the `UIToggleOption` render pass stamps `aria-pressed` from it, mirroring `segment-ui`'s `aria-checked`-from-`selected` pattern); `toggle-group.css`'s `:scope[data-selected]` style rule becomes `toggle-option-ui[selected]` (moved outside `@scope`, same Safari-17.x attribute-restyle workaround `segment.css` already uses). Behavior, keyboard model, and the group's `value` as the single source of truth are unchanged. **SCOPING RULING (additive only, not the full ADR):** `toggle-group-ui` keeps stamping the legacy `data-selected` attribute on options in parallel, alongside the new `selected` prop — marked **DEPRECATED** in `toggle-option.yaml`/`toggle-group.yaml` — because it was an undocumented hook external CSS may already target; removing it is the breaking half of ADR-0056 and rides the ratified **0.9.0** breaking wave (alongside ADR-0052/0053/0054) rather than landing in this patch. `toggle-group.test.js` (new) covers the reflected `[selected]` + `aria-pressed` pairing, group-managed single-selection, multi-select, and the `data-selected` parallel-stamp deprecation case (named so it's obvious which test flips at 0.9.0 removal); `segmented.test.js` gains a regression check that `segment-ui`'s reference `[selected]`/`aria-checked` pairing is unchanged.
|
|
7
|
+
### Fixed
|
|
8
|
+
- **Trigger-caret sizing — the universal caret-size ruling (`--a-icon-size: var(--a-caret-size)`, encoded on `select-ui`'s trigger caret) is now applied consistently across the input/picker family (gh#1314).** `date-range-picker-ui`'s trigger caret never applied the rule at all, so it rode the ambient icon size and rendered visibly larger than `select-ui`'s caret in mixed control rows (e.g. the admin Finance Batches filter bar; consumer defect ADIA2-8729). Sweeping the rest of the trigger-caret family for the same class of miss found three more members: `datetime-picker-ui`'s trigger caret was missing the rule the same way; `combobox-ui`'s default caret-down suffix icon was sized off `--combobox-font-size` (the same value its leading/prefix icon correctly uses) instead of `--a-caret-size`; `calendar-picker-ui`'s `[slot="caret"]` was likewise sized off its own `--calendar-picker-font-size`. All four now resolve `--a-icon-size` to `--a-caret-size` on the caret glyph specifically (name-guarded on `combobox-ui`/date-picker family so a consumer-slotted non-caret glyph is unaffected), so every trigger caret in the family reads as one size. `time-picker-ui` was audited and excluded — it has no chevron/trigger caret (segmented inline input only). New/updated regression coverage in `date-range-picker.test.js`, `datetime-picker.test.js`, `combobox.test.js`, and a new `calendar-picker.test.js` asserts each caret rule's declared `--a-icon-size` computed value (CSS-source assertion — happy-dom doesn't evaluate `@scope` via `getComputedStyle`, same documented limitation as `badge.test.js`/`tag.test.js`).
|
|
9
|
+
- **ADR-0055 — `command-ui`, `select-ui`'s searchable trigger, and `drilldown-ui`'s filter row no longer stamp a native `<input>` with no recorded ADR-0025 exception (gh#1302).** A corpus sweep (gh#1302) found three uncatalogued native `<input>` stamps: `command-ui`'s palette search field, `select-ui`'s searchable-trigger branch (ADR-0025's own compliant-list entry predated/overlooked this branch), and `drilldown-ui`'s filter row (found while decomposing the issue, not in the original report). ADR-0055 rules no new exceptions get added — all three are replaced with patterns already in-tree: `command-ui` and `select-ui`'s searchable branch now use a `contenteditable="plaintext-only"` span carrying `role="combobox"` (the `combobox-ui` pattern — placeholder via the `[data-empty]::before` pseudo, same mechanism as `input-ui`/`combobox-ui`); `drilldown-ui`'s filter row now composes `<input-ui raw>` (the `search-ui` precedent). Behavior parity verified by a per-surface browser probe (typing, filtering, Escape-clears-query, Backspace-doesn't-navigate-back for drilldown) — not tests alone. `agent-trace`'s native `<details>`/`<summary>` disclosure toggle is ruled explicitly out of ADR-0025's scope by the same ADR (carries no submittable value). `command.yaml` gains the `input` slot entry the CSS rewrite now positions (`audit-slot-vocab-vs-css`). `drilldown.yaml`'s `composes:` list gains `input-ui` (+ `button-ui`, previously undeclared for the back affordance).
|
|
10
|
+
- **`scripts/dev/audit-stamped-native-buttons.mjs` extended to cover native `<input>` stamps, not just `<button>` (ADR-0055 follow-up, gh#1302).** Mirrors the existing button gate: no `createElement('input')` / `<input` in component source outside a `SANCTIONED_INPUT` map with a per-file reason. `input.class.js`'s `type="password"` branch is the one ADR-0025-named exception. Widening the scan surfaced pre-existing native `<input>` stamps outside gh#1302's audited scope — three `type="file"` OS-file-picker triggers (`chat-input.js`, `upload.class.js`, `admin-roster.js`) and two text-gathering surfaces (`otp-input.class.js`'s digit boxes, `table.class.js`'s filter-popover inputs) — all recorded as `PENDING` findings for a follow-up maintainer call, not fixed here (outside this issue's named scope).
|
|
11
|
+
- **`modal-ui` — `close` now fires reliably on every programmatic dismissal, including a parent-driven teardown race that was the actual root cause (gh#1295, adiav2 ADIA2-8581).** `close` previously depended entirely on the native `<dialog>` element's own queued `close` task re-dispatching it, which a consumer's own re-render could tear down before it ran. Real-browser reproduction (a vitest/happy-dom harness alone can't see this) traced the true root cause further: EVERY internal close path only ever runs from `<modal-ui>`'s own render effect, which reacts to the `open` property write via a microtask (signals.js) — `disconnectedCallback()` disposes that effect unconditionally. A consumer whose Confirm/Cancel handler flips `open = false` and, on the SAME tick, unmounts the modal (an ordinary framework re-render shape) tears the effect down before `#animateClose`'s exit-animation timer is ever even created, so `close` never fires and any caller awaiting it (`showConfirmDialog`-style APIs) hangs forever — confirmed reproducing, and NOT fixed, against PR #1296's authoritative-timer-dispatch approach, since that timer is one of the things the teardown race skips creating. `disconnected()` now emits `close` as a last-resort teardown fallback whenever the modal was open or mid-close at disconnect time, guarded by a once-only dispatch flag shared with the two other paths (the native `<dialog>` `close` listener, kept as a redundant net; `#animateClose`'s own timer, now also an authoritative direct dispatch — both absorbed from PR #1296, which correctly diagnosed the duration/unit bug below but couldn't reproduce this teardown race). `#getDuration()` also no longer silently drops a `s` vs `ms` unit on `--modal-duration` (`parseFloat('.25s')` read as `0.25`, a 0.25ms timer instead of 250ms — PR #1296's fix, kept as-is). `modal.test.js` gains per-dismissal-path close-emission coverage (button, Escape, backdrop, direct `.open=false`, raw `dialog.close()`, no-double-fire, re-open reset) plus the teardown-race repro itself.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- **`nav-ui` — selecting a page now collapses every expanded group except the one containing the newly selected item (gh#1306).** Previously, `select()` only moved `[selected]` between items and never touched group `[open]` state, so any group a consumer or the collapsed-rail popover had expanded stayed expanded indefinitely, even after navigating elsewhere. Default-on: `select()` now closes every top-level `<nav-group-ui>` except the selected item's ancestor group (all groups, when the selection is ungrouped) — manual multi-expansion is still allowed right up until the next selection. New `[multi-expand]` attribute opts a consumer out, keeping every manually expanded group open across selections. Applies to every `select()` entrypoint: click, keyboard, programmatic, and the collapsed-rail popover option click (`nav-group.class.js`'s `showPopover()`, which itself calls `nav.select()`).
|
|
15
|
+
|
|
16
|
+
### Maintenance
|
|
17
|
+
- **`components/` touched in this release window** (39 file(s), e.g. `calendar-picker/calendar-picker.css`) — carried by the entries above.
|
|
18
|
+
- **`dist/` bundles rebuilt** in this cut's window (4 file(s)) — regenerated from the source changes described above, not independent edits.
|
|
19
|
+
|
|
20
|
+
## [0.8.38] — 2026-08-15
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- **`drilldown-ui` — a new single-panel, multi-level drill-in menu primitive (gh#1285).** Replaces the hand-rolled miller-columns shape (two stacked list columns) that adiav2's Schema Manager had to build because the kit had no drill-down primitive — cleared the pre-build audit against `tree-ui` (inline expand/collapse, wrong for wide/deep >2-level data), `segmented-ui` (no overflow behavior for 18 namespaces), `tag-ui`-as-menu (no keyboard path), `list-ui`/`nav-ui`/`menu-ui`/`context-menu`/`breadcrumb-ui` (flat, not hierarchical drill-in) — none provide single-panel level-swap navigation with back/focus-restoration. Data-driven (`.items` tree of `{value, label, meta?, dot?, counts?, children?}` nodes; `children` may be sync or an async `(item) => array | Promise<array>` lazy loader, rendering a `<skeleton-ui>` loading level while pending). Full keyboard path (roving-tabindex listbox per level; ArrowRight/Enter drills in, ArrowLeft/Backspace goes back with focus restored to the item drilled from), `select` + `navigate` events, a `path` property that reflects to a JSON-encoded attribute both ways (hand-managed, not the generic `reflect:` mechanism — array values don't round-trip through `String(v)`) for deep-link/URL restore, `[filterable]` filter input, `[select-on-drill]` mode flag, a `breadcrumb` slot, row-native trailing count chips (not full `<badge-ui>`, which reads oversized at row density), and a horizontal slide transition that honors `prefers-reduced-motion: reduce` automatically (also suppressible via `[static]`).
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- **`swatch-ui` — auto-contrast label/detail colors no longer fall back to raw hex, and detail-line dimming no longer uses `color-mix()`.** Found by a new `framework-alignment-agent` cohesion sweep: `--a-chrome-light`/`--a-chrome-dark` are always-defined foundational tokens (`styles/colors/semantics/features.css:124-125`), so the `var(--a-chrome-light, #fafafa)` / `var(--a-chrome-dark, #111)` fallbacks on the `[auto-contrast]` label rules were dead weight and a raw-CSS-color violation. The detail-line dimming also swapped `color-mix(in oklab, …, transparent)` for the plain token color plus `opacity` — not a browser-support fix (`color-mix()` is this repo's documented, Baseline-supported convention per `ADR-0007`/`BROWSER-COMPAT.md`), but a deliberate preference for a literal token over a computed color derivation.
|
|
27
|
+
- **`page-ui` header now ships the slot-gated grid its docs promised (gh#1253).** ADR-0009 recorded the named-slot vocabulary (`slot="icon"` / `slot="heading"` / `slot="action"`) as wired into four containers — Card, Drawer, Modal, and Page — but `page.css` never shipped its half; the slot attributes on a page header's children were dead metadata, so a block-level action child (e.g. `<toggle-scheme-ui>`) dropped to its own row instead of clustering. `page.css` gains the same `:has(> [slot])`-gated header grid as `card.css` (icon/heading/action columns, direct-child guard against nested false-positives), plus the `--page-header-gap` / `--page-heading-*` tokens `card-ui`'s pair already had. `page.yaml`'s `slots.default` and `page.examples.html` (new "Header slot-gated grid" demo) now describe the contract that's actually implemented.
|
|
28
|
+
- **`nav-ui` / `nav-item-ui` — `nav-select` now fires exactly once per click, and zero times re-clicking the already-selected item (gh#1254).** Root cause: two layered click handlers both drove selection for the same physical click — `nav-item-ui`'s own click handler called `nav.select(this)` *and* dispatched its own `nav-select`, while `nav-ui`'s click-delegation listener matched the same item on bubble and called `select()` again — three dispatches per click. `nav.select()` is now the single source of the event (idempotent: a no-op, including no hover-flush, when the item is already selected); `nav-item-ui` no longer dispatches its own copy, and `nav-ui`'s click listener no longer delegates item selection (it now only handles group expand/popover).
|
|
29
|
+
- **`view-transition` trait — no longer leaks unhandled `AbortError` rejections when a transition is superseded (gh#1255).** Only `transition.finished` had a `.catch()`; `ready` and `updateCallbackDone` are distinct promises that also reject (with `AbortError`) when a newer transition supersedes this one, and were previously uncaught. All three are now swallowed on abort — expected behavior for rapid navigation, not an error — with `fireEnd()` still driven solely by `finished`.
|
|
30
|
+
|
|
31
|
+
### Maintenance
|
|
32
|
+
- **`bin/` touched in this release window** (1 file(s), e.g. `bin/doc.mjs`) — carried by the entries above.
|
|
33
|
+
- **`components/` touched in this release window** (18 file(s), e.g. `card/card.css`) — carried by the entries above.
|
|
34
|
+
- **`dist/` bundles rebuilt** in this cut's window (4 file(s)) — regenerated from the source changes described above, not independent edits.
|
|
35
|
+
- **`patterns/` touched in this release window** (1 file(s), e.g. `bulk-action-toolbar/bulk-action-toolbar.examples.html`) — carried by the entries above.
|
|
36
|
+
- **`styles/` touched in this release window** (1 file(s), e.g. `styles/components.css`) — carried by the entries above.
|
|
37
|
+
- **`traits/` touched in this release window** (2 file(s), e.g. `view-transition/view-transition.js`) — carried by the entries above.
|
|
38
|
+
|
|
3
39
|
## [0.8.37] — 2026-08-14
|
|
4
40
|
|
|
5
41
|
### Maintenance
|
package/MIGRATION.md
CHANGED
|
@@ -79,8 +79,9 @@ The split is by concept, ruled in `docs/adr/adr-0048-a2ui-gen-ui-estate-split.md
|
|
|
79
79
|
`@adia-ai/a2ui-protocol-mcp`, before either ever published) ships one bin,
|
|
80
80
|
`adia-mcp`, dispatching two subcommands as separate processes:
|
|
81
81
|
`adia-mcp gen-ui` (30 tools — the generation surface, direct successor of
|
|
82
|
-
`@adia-ai/a2ui-mcp`) and `adia-mcp protocol` (4 tools — `validate_document`,
|
|
83
|
-
`get_registry_map`, `get_wiring_registry`, `protocol_status
|
|
82
|
+
`@adia-ai/a2ui-mcp`) and `adia-mcp protocol` (4 tools at the fold — `validate_document`,
|
|
83
|
+
`get_registry_map`, `get_wiring_registry`, `protocol_status`, joined since by
|
|
84
|
+
`get_catalog_ladder`; the first four renamed off
|
|
84
85
|
their original same-named-as-`gen-ui`-surface forms by gh#1248 before this
|
|
85
86
|
server ever published). The name `@adia-ai/a2ui-mcp` is **burned, never
|
|
86
87
|
reused**: it means "the generation MCP" to every consumer that has it
|
|
@@ -130,8 +131,8 @@ failed at load. If you were writing them, they were already broken.
|
|
|
130
131
|
| `@adia-ai/a2ui-compose/<sub>` (`core`, `strategies/*`, `transpiler`, `evals`, `shared/*`; incl. deprecated `engine`/`engines/*` aliases) | `@adia-ai/gen-ui/compose/<sub>` (aliases NOT carried over — they were already deprecated one cycle) |
|
|
131
132
|
| `@adia-ai/a2ui-retrieval` | `@adia-ai/gen-ui/retrieval` |
|
|
132
133
|
| `@adia-ai/a2ui-retrieval/<sub>` (e.g. `domain-router`, `intent/*`, `embedding/*`, `feedback/*`, `authoring/*`) | `@adia-ai/gen-ui/retrieval/<sub>` (e.g. `@adia-ai/gen-ui/retrieval/domain-router`) |
|
|
133
|
-
| `@adia-ai/a2ui-corpus` | `@adia-ai/gen-ui/corpus` |
|
|
134
|
-
| `@adia-ai/a2ui-corpus/<sub>` (`chunks/*`, `chunk-library`, `chunk-embeddings`, `manifest`) | `@adia-ai/gen-ui/corpus/<sub>` |
|
|
134
|
+
| `@adia-ai/a2ui-corpus` | **`@adia-ai/a2ui/catalog`** — this export WAS the catalog document, and the catalog moved protocol-side at ADR-0050 (gh#1243). `@adia-ai/gen-ui/corpus` no longer resolves as a root export |
|
|
135
|
+
| `@adia-ai/a2ui-corpus/<sub>` (`chunks/*`, `chunk-library`, `chunk-embeddings`, `manifest`) | `@adia-ai/gen-ui/corpus/<sub>` (unchanged — corpus fuel stays in gen-ui) |
|
|
135
136
|
| `@adia-ai/a2ui-mcp` (bin `adiaui-mcp`) | `@adia-ai/mcp` (bin `adia-mcp`, subcommand `gen-ui`) — retargeted gh#1240; the `@adia-ai/gen-ui-mcp` name this row originally named never published |
|
|
136
137
|
| — (new) | `@adia-ai/mcp` (bin `adia-mcp`, subcommand `protocol`) — folded into the same package as the row above by gh#1240; the `@adia-ai/a2ui-protocol-mcp` name this row originally named never published |
|
|
137
138
|
|
|
@@ -194,7 +195,8 @@ sweep '@adia-ai/a2ui-validator' '@adia-ai/a2ui/validate'
|
|
|
194
195
|
sweep '@adia-ai/a2ui-compose/' '@adia-ai/gen-ui/compose/'
|
|
195
196
|
sweep '@adia-ai/a2ui-compose' '@adia-ai/gen-ui'
|
|
196
197
|
sweep '@adia-ai/a2ui-retrieval' '@adia-ai/gen-ui/retrieval'
|
|
197
|
-
sweep '@adia-ai/a2ui-corpus'
|
|
198
|
+
sweep '@adia-ai/a2ui-corpus/' '@adia-ai/gen-ui/corpus/'
|
|
199
|
+
sweep '@adia-ai/a2ui-corpus' '@adia-ai/a2ui/catalog' # bare corpus import WAS the catalog (ADR-0050)
|
|
198
200
|
|
|
199
201
|
# protocol
|
|
200
202
|
sweep '@adia-ai/a2ui-runtime' '@adia-ai/a2ui'
|
|
@@ -218,7 +220,7 @@ between a dependency and a peer:
|
|
|
218
220
|
MCP, into one package, `@adia-ai/mcp`, before either ever published — the
|
|
219
221
|
bin gained a subcommand accordingly). Tool names and their input/output
|
|
220
222
|
shapes are **unchanged**; only the package that serves them is renamed. The
|
|
221
|
-
30-tool surface is documented at `packages/mcp/TOOLS.md` (the `gen-ui`
|
|
223
|
+
30-tool surface is documented at `packages/gen-ui/mcp/TOOLS.md` (the `gen-ui`
|
|
222
224
|
section).
|
|
223
225
|
|
|
224
226
|
### After the sweep, check
|
|
@@ -1182,7 +1184,7 @@ git grep -nlz '<grid-ui[^>]*\bcols=' \
|
|
|
1182
1184
|
done
|
|
1183
1185
|
```
|
|
1184
1186
|
|
|
1185
|
-
Then re-run `npm run harvest:chunks` if any of the touched pages live under `site/pages/` and are tagged with `data-chunk` (extracts to `packages/a2ui/corpus/chunks/`).
|
|
1187
|
+
Then re-run `npm run harvest:chunks` if any of the touched pages live under `site/pages/` and are tagged with `data-chunk` (extracts to `packages/gen-ui/a2ui/corpus/chunks/`).
|
|
1186
1188
|
|
|
1187
1189
|
---
|
|
1188
1190
|
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Vanilla web components + A2UI runtime for AdiaUI. **95 light-DOM custom
|
|
4
4
|
elements** + **56 composable traits**, a reactive core (signals + tagged-template renderer), form-associated form controls, and integration into the A2UI generation pipeline.
|
|
5
5
|
|
|
6
|
-
> This package ships UI atoms only. Composite shells (admin / chat / editor / simple / theme) live in [`@adia-ai/web-modules`](../web-modules). The generation pipeline lives in [`@adia-ai/a2ui-compose`](../gen-ui/compose); the pattern corpus in [`@adia-ai/a2ui-corpus`](../gen-ui/corpus); the MCP server in [`@adia-ai/mcp`](../mcp) (its `gen-ui` surface).
|
|
6
|
+
> This package ships UI atoms only. Composite shells (admin / chat / editor / simple / theme) live in [`@adia-ai/web-modules`](../web-modules). The generation pipeline lives in [`@adia-ai/a2ui-compose`](../gen-ui/engine/compose); the pattern corpus in [`@adia-ai/a2ui-corpus`](../gen-ui/engine/corpus); the MCP server in [`@adia-ai/mcp`](../gen-ui/mcp) (its `gen-ui` surface).
|
|
7
7
|
>
|
|
8
8
|
> **Consumer guide:** [`USAGE.md`](./USAGE.md) — property reactivity, event contract, form participation, lifecycle, registration, TypeScript. Start here if you're integrating AdiaUI into an app.
|
|
9
9
|
>
|
|
@@ -125,7 +125,7 @@ web-components/
|
|
|
125
125
|
│ All actual A2UI runtime code (renderer,
|
|
126
126
|
│ registry, streams, surface manifest,
|
|
127
127
|
│ wiring, dockables, controllers) lives in
|
|
128
|
-
│ `@adia-ai/a2ui` at packages/a2ui/.
|
|
128
|
+
│ `@adia-ai/a2ui` at packages/gen-ui/a2ui/.
|
|
129
129
|
│
|
|
130
130
|
└── styles/ — Global tokens and CSS layering
|
|
131
131
|
├── tokens.css all --a-* design tokens
|
|
@@ -329,7 +329,7 @@ attribute table + live demos:
|
|
|
329
329
|
npm run build:components # regenerate all .a2ui.json from YAML
|
|
330
330
|
```
|
|
331
331
|
|
|
332
|
-
The build also writes `packages/gen-ui/corpus/catalog-a2ui_0_9.json` and
|
|
332
|
+
The build also writes `packages/gen-ui/engine/corpus/catalog-a2ui_0_9.json` and
|
|
333
333
|
`catalog-a2ui_0_9_rules.txt` — the flat-file catalog the MCP server and
|
|
334
334
|
generation engine consume.
|
|
335
335
|
|
package/bin/doc.mjs
CHANGED
|
@@ -45,6 +45,32 @@ const DEMO_BASE = 'https://ui-kit.exe.xyz/site/components';
|
|
|
45
45
|
* Defensive: any parse error returns a partial object plus an `_errors`
|
|
46
46
|
* array — the caller decides how to surface failures.
|
|
47
47
|
*/
|
|
48
|
+
/**
|
|
49
|
+
* Strips a trailing YAML comment from a single raw line. A `#` only
|
|
50
|
+
* starts a comment when it's at column 0 or preceded by whitespace (the
|
|
51
|
+
* YAML plain-scalar rule) — a bare mid-word `#` (e.g. `gh#1253` inside a
|
|
52
|
+
* folded description block) is content, not a comment marker. Quote
|
|
53
|
+
* state is tracked (paired ' / ") so a `#` inside an actual quoted
|
|
54
|
+
* string is never treated as a comment start either.
|
|
55
|
+
*
|
|
56
|
+
* Fixes a truncation bug (CodeRabbit, PR #1284): the previous regex
|
|
57
|
+
* (`/^([^#"']*)(#.*)?$/`) cut ANY line at its first bare `#` regardless
|
|
58
|
+
* of context, silently truncating descriptions containing `gh#NNNN`.
|
|
59
|
+
*/
|
|
60
|
+
function stripYamlComment(line) {
|
|
61
|
+
let inSingle = false;
|
|
62
|
+
let inDouble = false;
|
|
63
|
+
for (let i = 0; i < line.length; i++) {
|
|
64
|
+
const ch = line[i];
|
|
65
|
+
if (ch === "'" && !inDouble) inSingle = !inSingle;
|
|
66
|
+
else if (ch === '"' && !inSingle) inDouble = !inDouble;
|
|
67
|
+
else if (ch === '#' && !inSingle && !inDouble && (i === 0 || /\s/.test(line[i - 1]))) {
|
|
68
|
+
return line.slice(0, i).replace(/\s+$/, '');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return line.replace(/\s+$/, '');
|
|
72
|
+
}
|
|
73
|
+
|
|
48
74
|
function parseYaml(source) {
|
|
49
75
|
const out = {
|
|
50
76
|
name: '',
|
|
@@ -59,11 +85,7 @@ function parseYaml(source) {
|
|
|
59
85
|
|
|
60
86
|
// Strip comments + trailing whitespace, keep blank lines for block detection
|
|
61
87
|
const rawLines = source.split(/\r?\n/);
|
|
62
|
-
const lines = rawLines.map(
|
|
63
|
-
// strip line comments unless inside a quoted string (rough heuristic)
|
|
64
|
-
const m = l.match(/^([^#"']*)(#.*)?$/);
|
|
65
|
-
return m ? m[1].replace(/\s+$/, '') : l;
|
|
66
|
-
});
|
|
88
|
+
const lines = rawLines.map(stripYamlComment);
|
|
67
89
|
|
|
68
90
|
/** Find the line range of a top-level key block (indent === 0). */
|
|
69
91
|
function blockRange(key) {
|
|
@@ -178,7 +178,10 @@
|
|
|
178
178
|
|
|
179
179
|
[slot="caret"] {
|
|
180
180
|
flex-shrink: 0;
|
|
181
|
-
|
|
181
|
+
/* Universal caret size (gh#1314 sweep) — was riding the trigger's
|
|
182
|
+
font-size instead of the shared --a-caret-size token select-ui's
|
|
183
|
+
trigger caret already reads. */
|
|
184
|
+
--a-icon-size: var(--a-caret-size);
|
|
182
185
|
}
|
|
183
186
|
}
|
|
184
187
|
|
package/components/card/card.css
CHANGED
|
@@ -264,7 +264,7 @@
|
|
|
264
264
|
/* Heading — row 1.
|
|
265
265
|
Matches native h1-h6 AND the text-ui variants the A2UI transpiler emits
|
|
266
266
|
for them (display|title|heading|subsection — see HTML_TAG_MAP in
|
|
267
|
-
packages/a2ui/compose/transpiler/transpiler-maps.js).
|
|
267
|
+
packages/gen-ui/a2ui/compose/transpiler/transpiler-maps.js).
|
|
268
268
|
The text-ui variants are matched only when UNSLOTTED so an explicit
|
|
269
269
|
`slot="heading"` / `slot="description"` / `slot="action"` always wins. */
|
|
270
270
|
& > header > :is([slot="heading"], h1, h2, h3, h4, h5, h6),
|
|
@@ -127,6 +127,12 @@
|
|
|
127
127
|
cursor: pointer;
|
|
128
128
|
}
|
|
129
129
|
[data-suffix] icon-ui[name="caret-down"] {
|
|
130
|
+
/* Universal caret size (gh#1314 sweep) — overrides the shared
|
|
131
|
+
[data-prefix]/[data-suffix] --a-icon-size (font-size) above so the
|
|
132
|
+
default caret matches select-ui's trigger caret instead of riding
|
|
133
|
+
the field's text size. Name-guarded: a consumer-slotted non-caret
|
|
134
|
+
suffix glyph keeps the font-size default. */
|
|
135
|
+
--a-icon-size: var(--a-caret-size);
|
|
130
136
|
transition: rotate var(--a-duration-fast) var(--a-easing);
|
|
131
137
|
}
|
|
132
138
|
/* Clear OPEN state: the default caret flips while the listbox is open.
|
|
@@ -105,6 +105,9 @@
|
|
|
105
105
|
"footer": {
|
|
106
106
|
"description": "Keyboard hint bar"
|
|
107
107
|
},
|
|
108
|
+
"input": {
|
|
109
|
+
"description": "The auto-stamped search field — a contenteditable=\"plaintext-only\" span carrying role=\"combobox\" (per ADR-0025/ADR-0055; no native <input>). Internal only; not consumer-slottable."
|
|
110
|
+
},
|
|
108
111
|
"list": {
|
|
109
112
|
"description": "Container for command items and groups"
|
|
110
113
|
}
|
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
import { UIElement } from '../../core/element.js';
|
|
15
15
|
import { logicalSlotted } from '../../core/logical-children.js';
|
|
16
16
|
|
|
17
|
+
function escapeHTML(s) {
|
|
18
|
+
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let cmdInstanceSeq = 0;
|
|
22
|
+
|
|
17
23
|
/**
|
|
18
24
|
* <command-ui> — Searchable command palette.
|
|
19
25
|
*
|
|
@@ -58,6 +64,7 @@ export class UICommand extends UIElement {
|
|
|
58
64
|
#bound = false;
|
|
59
65
|
#itemByEl = new WeakMap();
|
|
60
66
|
#emptyNodes = [];
|
|
67
|
+
#instanceId = `command-${++cmdInstanceSeq}`;
|
|
61
68
|
|
|
62
69
|
static #RECENTS_MAX = 3;
|
|
63
70
|
|
|
@@ -96,12 +103,22 @@ export class UICommand extends UIElement {
|
|
|
96
103
|
}
|
|
97
104
|
this.#emptyNodes = logicalSlotted(this, 'empty');
|
|
98
105
|
|
|
106
|
+
const listId = `${this.#instanceId}-list`;
|
|
107
|
+
|
|
108
|
+
// No native <input> wrap (per ADR-0055 / ADR-0025): the search field
|
|
109
|
+
// is a `contenteditable="plaintext-only"` surface carrying
|
|
110
|
+
// `role="combobox"`, matching combobox-ui's editable-surface pattern.
|
|
111
|
+
// Placeholder text renders via the [data-empty]::before pseudo (see
|
|
112
|
+
// command.css), same mechanism as input-ui / combobox-ui.
|
|
99
113
|
this.innerHTML = `
|
|
100
114
|
<header>
|
|
101
115
|
<icon-ui name="magnifying-glass" slot="icon"></icon-ui>
|
|
102
|
-
<
|
|
116
|
+
<span contenteditable="plaintext-only" role="combobox" tabindex="0"
|
|
117
|
+
aria-autocomplete="list" aria-expanded="false" aria-controls="${listId}"
|
|
118
|
+
data-placeholder="${escapeHTML(this.placeholder)}" data-empty
|
|
119
|
+
slot="input"></span>
|
|
103
120
|
</header>
|
|
104
|
-
<section slot="list"></section>
|
|
121
|
+
<section slot="list" id="${listId}"></section>
|
|
105
122
|
<footer>
|
|
106
123
|
<span data-hint><kbd>↑</kbd><kbd>↓</kbd> Navigate</span>
|
|
107
124
|
<span data-hint><kbd>↵</kbd> Select</span>
|
|
@@ -109,7 +126,7 @@ export class UICommand extends UIElement {
|
|
|
109
126
|
</footer>
|
|
110
127
|
`;
|
|
111
128
|
|
|
112
|
-
this.#inputEl = this.querySelector('input');
|
|
129
|
+
this.#inputEl = this.querySelector('[slot="input"]');
|
|
113
130
|
this.#listEl = this.querySelector('[slot="list"]');
|
|
114
131
|
this.#footerEl = this.querySelector('footer');
|
|
115
132
|
|
|
@@ -120,6 +137,8 @@ export class UICommand extends UIElement {
|
|
|
120
137
|
this.#renderItems();
|
|
121
138
|
}
|
|
122
139
|
|
|
140
|
+
if (this.#inputEl) this.#inputEl.setAttribute('aria-expanded', String(this.open));
|
|
141
|
+
|
|
123
142
|
if (this.open) {
|
|
124
143
|
this.#focusRaf = requestAnimationFrame(() => {
|
|
125
144
|
this.#focusRaf = null;
|
|
@@ -130,11 +149,12 @@ export class UICommand extends UIElement {
|
|
|
130
149
|
|
|
131
150
|
// ── Public API ──
|
|
132
151
|
|
|
133
|
-
get value() { return this.#inputEl?.
|
|
152
|
+
get value() { return this.#inputEl?.textContent || ''; }
|
|
134
153
|
set value(v) {
|
|
135
154
|
if (!this.#inputEl) return;
|
|
136
155
|
const next = v ?? '';
|
|
137
|
-
this.#inputEl.
|
|
156
|
+
this.#inputEl.textContent = next;
|
|
157
|
+
this.#inputEl.toggleAttribute('data-empty', !next);
|
|
138
158
|
// Keep the rendered list in sync with the visible input — the host
|
|
139
159
|
// commonly clears value on (re)open, and consumers expect to see the
|
|
140
160
|
// full list (plus recents) rather than the previous filter's residue.
|
|
@@ -421,7 +441,9 @@ export class UICommand extends UIElement {
|
|
|
421
441
|
// ── Events ──
|
|
422
442
|
|
|
423
443
|
#onInput = () => {
|
|
424
|
-
this.#
|
|
444
|
+
const text = this.#inputEl.textContent || '';
|
|
445
|
+
this.#inputEl.toggleAttribute('data-empty', !text);
|
|
446
|
+
this.#renderItems(text);
|
|
425
447
|
};
|
|
426
448
|
|
|
427
449
|
#onKeydown = (e) => {
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
--a-icon-size: 1rem;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
& > header input {
|
|
105
|
+
& > header [slot="input"] {
|
|
106
106
|
flex: 1;
|
|
107
107
|
min-width: 0;
|
|
108
108
|
border: none;
|
|
@@ -113,14 +113,25 @@
|
|
|
113
113
|
font-size: var(--command-input-font-size);
|
|
114
114
|
line-height: 1.4;
|
|
115
115
|
padding: 0;
|
|
116
|
+
white-space: nowrap;
|
|
117
|
+
overflow: hidden;
|
|
118
|
+
/* Positioning context for the [data-empty]::before placeholder pseudo
|
|
119
|
+
below — out-of-flow, same rationale as input-ui/combobox-ui: an
|
|
120
|
+
in-flow pseudo would render the caret after the placeholder text
|
|
121
|
+
instead of at content-start. */
|
|
122
|
+
position: relative;
|
|
116
123
|
}
|
|
117
124
|
|
|
118
|
-
& > header input::
|
|
125
|
+
& > header [slot="input"][data-empty]::before {
|
|
126
|
+
content: attr(data-placeholder);
|
|
119
127
|
color: var(--command-fg-muted);
|
|
128
|
+
pointer-events: none;
|
|
129
|
+
position: absolute;
|
|
130
|
+
inset: 0;
|
|
120
131
|
}
|
|
121
132
|
|
|
122
133
|
/* Suppress focus ring on the input — the palette itself is the focused surface */
|
|
123
|
-
& > header input:focus-visible {
|
|
134
|
+
& > header [slot="input"]:focus-visible {
|
|
124
135
|
outline: none;
|
|
125
136
|
box-shadow: none;
|
|
126
137
|
}
|
|
@@ -67,6 +67,11 @@ slots:
|
|
|
67
67
|
description: Empty state shown when no items match
|
|
68
68
|
footer:
|
|
69
69
|
description: Keyboard hint bar
|
|
70
|
+
input:
|
|
71
|
+
description: >-
|
|
72
|
+
The auto-stamped search field — a contenteditable="plaintext-only"
|
|
73
|
+
span carrying role="combobox" (per ADR-0025/ADR-0055; no native
|
|
74
|
+
<input>). Internal only; not consumer-slottable.
|
|
70
75
|
list:
|
|
71
76
|
description: Container for command items and groups
|
|
72
77
|
states:
|
|
@@ -90,6 +90,12 @@
|
|
|
90
90
|
equal specificity so the picker trigger reads as part of the
|
|
91
91
|
input/picker family rather than a generic button. */
|
|
92
92
|
:scope [slot="trigger"] icon-ui[name="caret-down"] {
|
|
93
|
+
/* Universal caret size (gh#1314) — without this the caret rides the
|
|
94
|
+
ambient icon size instead of the shared --a-caret-size token that
|
|
95
|
+
select-ui's trigger caret already reads, so mixed control rows
|
|
96
|
+
(select-ui next to date-range-picker-ui) showed visibly different
|
|
97
|
+
chevron sizes. */
|
|
98
|
+
--a-icon-size: var(--a-caret-size);
|
|
93
99
|
transition: rotate var(--a-duration-fast) var(--a-easing);
|
|
94
100
|
}
|
|
95
101
|
/* Clear OPEN state: the default trigger's trailing caret flips while the
|
|
@@ -62,6 +62,10 @@
|
|
|
62
62
|
left-aligned with the caret pushed to the trailing edge, not centered
|
|
63
63
|
like a default button. */
|
|
64
64
|
:scope [slot="trigger"] icon-ui[name="caret-down"] {
|
|
65
|
+
/* Universal caret size (gh#1314 sweep) — mirrors select-ui's trigger
|
|
66
|
+
caret so mixed control rows read as one family instead of showing
|
|
67
|
+
the ambient (larger) icon size. */
|
|
68
|
+
--a-icon-size: var(--a-caret-size);
|
|
65
69
|
transition: rotate var(--a-duration-fast) var(--a-easing);
|
|
66
70
|
}
|
|
67
71
|
/* Clear OPEN state: the default trigger's trailing caret flips while the
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/Drilldown.json",
|
|
4
|
+
"title": "Drilldown",
|
|
5
|
+
"description": "Single-panel, multi-level drill-in menu. One list panel slides between\nlevels (a level header with back navigation replaces stacking a column\nper level) — for hierarchical data where miller columns don't fit the\nviewport (>2 levels, >12 top-level entries, or narrow layouts). Rows\ncompose a leading dot/icon, primary label, optional meta line, and\ntrailing row-native count chips (not full <badge-ui>, which reads\noversized at row density) — plus a drill-in chevron on branch items.\nSet `.items` to a tree of `{value, label, meta?, dot?, counts?, children?}`\nnodes; `children` may be an array (sync) or a function returning an\narray/Promise (lazy, fetched on drill-in — a loading level is shown via\n<skeleton-ui> while it resolves). Distinct from <tree-ui> (inline\nexpand/collapse — right for file-tree shapes, wrong for wide/deep data\nwhere the expanded tree becomes a wall of rows), <nav-ui> (flat app\nnavigation, not hierarchical drill-in), and <context-menu> (transient\npopover submenus, not a persistent content panel).\n",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"allOf": [
|
|
8
|
+
{
|
|
9
|
+
"$ref": "common_types.json#/$defs/ComponentCommon"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"$ref": "common_types.json#/$defs/CatalogComponentCommon"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"items": {
|
|
17
|
+
"description": "Root-level array of node objects: {value, label, meta?, dot?, counts?, children?, leaf?}. `value` is the stable id used in `path` and event details. `dot` names a semantic tone (info, success, warning, danger, primary, neutral) rendered as a small leading color swatch. `counts` is an array of {variant?, value} rendered as compact trailing chips. `children` is either an array of child nodes (resolved synchronously) or a function `(item) => array | Promise<array>` for lazy per-level fetch — a loading level renders while the promise is pending. `leaf: true` forces no drill chevron even when `children` is present (rare — normally omit `children` instead).",
|
|
18
|
+
"$ref": "common_types.json#/$defs/DynamicObjectList"
|
|
19
|
+
},
|
|
20
|
+
"component": {
|
|
21
|
+
"const": "Drilldown"
|
|
22
|
+
},
|
|
23
|
+
"filterable": {
|
|
24
|
+
"description": "Show a filter input pinned above the list, filtering the current level's rows by label/meta.",
|
|
25
|
+
"type": "boolean",
|
|
26
|
+
"default": false
|
|
27
|
+
},
|
|
28
|
+
"path": {
|
|
29
|
+
"description": "Current drilled-in level, as an array of ancestor item `value`s (root = `[]`). Setting `.path` restores directly to that nested level without animating through every intermediate level — the deep-link/URL-restore path. Hand-managed (not the generic `reflect:` mechanism, which can't JSON-encode arrays) — it still genuinely reflects to a JSON-encoded `path` attribute both ways (property → attribute and attribute → property), unlike most array props on this kit.",
|
|
30
|
+
"$ref": "common_types.json#/$defs/DynamicStringList"
|
|
31
|
+
},
|
|
32
|
+
"placeholder": {
|
|
33
|
+
"description": "Placeholder text for the filter input (only rendered when `[filterable]`).",
|
|
34
|
+
"type": "string",
|
|
35
|
+
"default": "Filter…"
|
|
36
|
+
},
|
|
37
|
+
"rootLabel": {
|
|
38
|
+
"description": "Level-header title shown at the root level (depth 0). Empty by default (no title row).",
|
|
39
|
+
"type": "string",
|
|
40
|
+
"default": ""
|
|
41
|
+
},
|
|
42
|
+
"selectOnDrill": {
|
|
43
|
+
"description": "When true, clicking/activating a branch item (one with children) BOTH fires `select` and drills into it — the Schema Manager shape, where picking a namespace is both a selection and a navigation. Default false: branch activation only drills in; only leaf activation fires `select`.",
|
|
44
|
+
"type": "boolean",
|
|
45
|
+
"default": false
|
|
46
|
+
},
|
|
47
|
+
"static": {
|
|
48
|
+
"description": "Suppress the horizontal slide transition between levels — instant swap. Also honored automatically under `prefers-reduced-motion: reduce`.",
|
|
49
|
+
"type": "boolean",
|
|
50
|
+
"default": false
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"required": [
|
|
54
|
+
"component"
|
|
55
|
+
],
|
|
56
|
+
"unevaluatedProperties": false,
|
|
57
|
+
"x-adiaui": {
|
|
58
|
+
"anti_patterns": [],
|
|
59
|
+
"category": "navigation",
|
|
60
|
+
"composes": [
|
|
61
|
+
"icon-ui",
|
|
62
|
+
"skeleton-ui",
|
|
63
|
+
"empty-state-ui",
|
|
64
|
+
"input-ui",
|
|
65
|
+
"button-ui"
|
|
66
|
+
],
|
|
67
|
+
"events": {
|
|
68
|
+
"navigate": {
|
|
69
|
+
"description": "Fired whenever the shown level changes — drill-in, back, breadcrumb jump, or a programmatic `.path` assignment. detail: { path }. Hosts mirror this to the URL instead of listening to `select`.\n",
|
|
70
|
+
"detail": {
|
|
71
|
+
"path": {
|
|
72
|
+
"description": "The newly-shown level's ancestor value chain.",
|
|
73
|
+
"type": "array",
|
|
74
|
+
"items": {
|
|
75
|
+
"type": "string"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"select": {
|
|
81
|
+
"description": "Fired when a leaf item is activated (click/Enter), or when a branch item is activated with `[select-on-drill]` set. detail: { path, item }. `path` is the drilled-in level the item lives at (the current `.path` at the time of activation); `item` is the raw node object.\n",
|
|
82
|
+
"detail": {
|
|
83
|
+
"item": {
|
|
84
|
+
"description": "The activated node object.",
|
|
85
|
+
"type": "object"
|
|
86
|
+
},
|
|
87
|
+
"path": {
|
|
88
|
+
"description": "Ancestor value chain the selected item lives at.",
|
|
89
|
+
"type": "array",
|
|
90
|
+
"items": {
|
|
91
|
+
"type": "string"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"examples": [
|
|
98
|
+
{
|
|
99
|
+
"description": "Basic Drilldown usage",
|
|
100
|
+
"a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Card\",\n \"children\": [\n \"sec\"\n ]\n },\n {\n \"id\": \"sec\",\n \"component\": \"Section\",\n \"children\": [\n \"comp\"\n ]\n },\n {\n \"id\": \"comp\",\n \"component\": \"Drilldown\"\n }\n]",
|
|
101
|
+
"name": "basic-drilldown"
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
"keywords": [
|
|
105
|
+
"drilldown",
|
|
106
|
+
"drill-down",
|
|
107
|
+
"miller",
|
|
108
|
+
"miller-columns",
|
|
109
|
+
"namespace",
|
|
110
|
+
"hierarchy",
|
|
111
|
+
"navigator",
|
|
112
|
+
"catalog-browser"
|
|
113
|
+
],
|
|
114
|
+
"name": "UIDrilldown",
|
|
115
|
+
"related": [
|
|
116
|
+
"Tree",
|
|
117
|
+
"List",
|
|
118
|
+
"Nav",
|
|
119
|
+
"Breadcrumb",
|
|
120
|
+
"Menu"
|
|
121
|
+
],
|
|
122
|
+
"slots": {
|
|
123
|
+
"title": {
|
|
124
|
+
"description": "Auto-stamped title cluster (current-level label + item count) inside the header."
|
|
125
|
+
},
|
|
126
|
+
"back": {
|
|
127
|
+
"description": "Auto-stamped back-affordance <button> (chevron + parent-level label). Hidden at the root level."
|
|
128
|
+
},
|
|
129
|
+
"breadcrumb": {
|
|
130
|
+
"description": "Optional slot for a consumer-supplied <breadcrumb-ui> in the level header, for deep hierarchies. Not managed by drilldown-ui — the consumer wires jump clicks to `.path = [...]` itself."
|
|
131
|
+
},
|
|
132
|
+
"counts": {
|
|
133
|
+
"description": "Auto-stamped per-row trailing count-chip cluster, rendered when the row's data item declares `counts`."
|
|
134
|
+
},
|
|
135
|
+
"dot": {
|
|
136
|
+
"description": "Auto-stamped per-row leading color swatch, rendered when the row's data item declares a `dot` tone."
|
|
137
|
+
},
|
|
138
|
+
"filter": {
|
|
139
|
+
"description": "Auto-stamped filter-input row, shown only when `[filterable]` is set."
|
|
140
|
+
},
|
|
141
|
+
"header": {
|
|
142
|
+
"description": "Auto-stamped level-header container (back affordance + breadcrumb + title/count). Structural — not consumer-overridable."
|
|
143
|
+
},
|
|
144
|
+
"main": {
|
|
145
|
+
"description": "Auto-stamped per-row content column (label + optional meta line)."
|
|
146
|
+
},
|
|
147
|
+
"meta": {
|
|
148
|
+
"description": "Auto-stamped per-row secondary meta line, rendered when the row's data item declares `meta`."
|
|
149
|
+
},
|
|
150
|
+
"viewport": {
|
|
151
|
+
"description": "Auto-stamped clipping container for the current level's panel (hosts the slide-transition overflow clip)."
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"states": [
|
|
155
|
+
{
|
|
156
|
+
"description": "Default, ready for interaction.",
|
|
157
|
+
"name": "idle"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"description": "A lazy level's children are being resolved (async `children` function pending).",
|
|
161
|
+
"name": "loading"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"description": "The current level has no items.",
|
|
165
|
+
"name": "empty"
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
"status": "beta",
|
|
169
|
+
"synonyms": {
|
|
170
|
+
"hierarchy": [
|
|
171
|
+
"tree",
|
|
172
|
+
"nav",
|
|
173
|
+
"drilldown"
|
|
174
|
+
],
|
|
175
|
+
"miller": [
|
|
176
|
+
"drilldown",
|
|
177
|
+
"tree",
|
|
178
|
+
"column"
|
|
179
|
+
],
|
|
180
|
+
"navigator": [
|
|
181
|
+
"tree",
|
|
182
|
+
"nav",
|
|
183
|
+
"drilldown",
|
|
184
|
+
"sidebar"
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
"tag": "drilldown-ui",
|
|
188
|
+
"tokens": {
|
|
189
|
+
"--drilldown-bg-active": {
|
|
190
|
+
"description": "Row background when focused/active (roving tabindex)."
|
|
191
|
+
},
|
|
192
|
+
"--drilldown-bg-hover": {
|
|
193
|
+
"description": "Row background on hover."
|
|
194
|
+
},
|
|
195
|
+
"--drilldown-count-chip-font-size": {
|
|
196
|
+
"description": "Font size of a trailing count chip."
|
|
197
|
+
},
|
|
198
|
+
"--drilldown-count-chip-px": {
|
|
199
|
+
"description": "Inline padding of a trailing count chip."
|
|
200
|
+
},
|
|
201
|
+
"--drilldown-count-chip-radius": {
|
|
202
|
+
"description": "Border radius of a trailing count chip."
|
|
203
|
+
},
|
|
204
|
+
"--drilldown-dot-size": {
|
|
205
|
+
"description": "Diameter of the leading color dot."
|
|
206
|
+
},
|
|
207
|
+
"--drilldown-duration": {
|
|
208
|
+
"description": "Slide-transition duration."
|
|
209
|
+
},
|
|
210
|
+
"--drilldown-easing": {
|
|
211
|
+
"description": "Slide-transition easing function."
|
|
212
|
+
},
|
|
213
|
+
"--drilldown-fg": {
|
|
214
|
+
"description": "Primary row text color."
|
|
215
|
+
},
|
|
216
|
+
"--drilldown-fg-muted": {
|
|
217
|
+
"description": "Muted text color (meta line, chevron, back affordance)."
|
|
218
|
+
},
|
|
219
|
+
"--drilldown-focus-ring": {
|
|
220
|
+
"description": "Focus ring box-shadow for rows and the back button."
|
|
221
|
+
},
|
|
222
|
+
"--drilldown-header-px": {
|
|
223
|
+
"description": "Inline padding of the level header."
|
|
224
|
+
},
|
|
225
|
+
"--drilldown-header-py": {
|
|
226
|
+
"description": "Block padding of the level header."
|
|
227
|
+
},
|
|
228
|
+
"--drilldown-row-gap": {
|
|
229
|
+
"description": "Gap between row elements (dot / main / counts / chevron)."
|
|
230
|
+
},
|
|
231
|
+
"--drilldown-row-height": {
|
|
232
|
+
"description": "Height of each row."
|
|
233
|
+
},
|
|
234
|
+
"--drilldown-row-px": {
|
|
235
|
+
"description": "Inline padding of each row."
|
|
236
|
+
},
|
|
237
|
+
"--drilldown-row-radius": {
|
|
238
|
+
"description": "Border radius of each row."
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
"traits": [],
|
|
242
|
+
"version": 1
|
|
243
|
+
}
|
|
244
|
+
}
|