@aceshooting/lyra-ui 21.0.0 → 21.1.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 +56 -0
- package/README.md +6 -3
- package/custom-elements.json +1 -1
- package/dist/ai/runtime.js +1 -1
- package/dist/ai/types.d.ts +13 -5
- package/dist/cli/lyra-rename-ledger.mjs +1 -0
- package/dist/cli/migrate-wa.mjs +33 -13
- package/dist/cli/migration-contract.json +1 -1
- package/dist/components/agent-tools/agent-trace/agent-trace.class.d.ts +5 -3
- package/dist/components/agent-tools/agent-trace/agent-trace.class.js +3 -2
- package/dist/components/agent-tools/tool-call-block/tool-call-block.class.d.ts +15 -3
- package/dist/components/agent-tools/tool-call-block/tool-call-block.class.js +3 -3
- package/dist/components/agent-tools/tool-call-chip/tool-call-chip.class.d.ts +24 -8
- package/dist/components/agent-tools/tool-call-chip/tool-call-chip.class.js +3 -3
- package/dist/components/agent-tools/tool-param-form/tool-param-form.class.js +4 -0
- package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.class.d.ts +9 -5
- package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.class.js +2 -2
- package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.styles.js +1 -1
- package/dist/components/agent-tools/tool-status.js +2 -2
- package/dist/components/agent-tools/tool-timeline/tool-timeline.class.d.ts +8 -0
- package/dist/components/agent-tools/tool-timeline/tool-timeline.class.js +3 -2
- package/dist/components/agent-tools/tool-timeline/tool-timeline.styles.js +1 -1
- package/dist/components/agent-tools/trace-tree/span.d.ts +2 -2
- package/dist/components/charts/chart/box-plot.class.d.ts +3 -1
- package/dist/components/charts/chart/box-plot.class.js +9 -9
- package/dist/components/charts/chart/chart-bidi.d.ts +109 -0
- package/dist/components/charts/chart/chart-bidi.js +1 -0
- package/dist/components/charts/chart/chart.class.d.ts +17 -2
- package/dist/components/charts/chart/chart.class.js +10 -8
- package/dist/components/charts/chart/histogram.class.js +1 -1
- package/dist/components/charts/chart/lite-chart.class.d.ts +24 -5
- package/dist/components/charts/chart/lite-chart.class.js +9 -9
- package/dist/components/charts/chart/lite-chart.styles.js +1 -1
- package/dist/components/conversation/code-block/shiki-dark-theme.d.ts +2 -2
- package/dist/components/conversation/code-block/shiki-dark-theme.js +1 -1
- package/dist/components/conversation/conversation-item/conversation-item.class.d.ts +18 -2
- package/dist/components/conversation/conversation-item/conversation-item.class.js +3 -3
- package/dist/components/conversation/conversation-item/conversation-item.styles.js +1 -1
- package/dist/components/conversation/markdown/markdown-base.class.js +1 -1
- package/dist/components/conversation/markdown/markdown-core.class.d.ts +3 -1
- package/dist/components/conversation/markdown/markdown.class.d.ts +3 -1
- package/dist/components/conversation/message-actions/message-actions.class.d.ts +7 -2
- package/dist/components/conversation/message-actions/message-actions.class.js +2 -2
- package/dist/components/conversation/message-parts/message-parts.class.d.ts +6 -2
- package/dist/components/conversation/message-parts/message-parts.class.js +5 -3
- package/dist/components/conversation/selection-toolbar/selection-toolbar.class.d.ts +3 -2
- package/dist/components/conversation/selection-toolbar/selection-toolbar.class.js +1 -1
- package/dist/components/conversation/streaming-text/streaming-text-base.class.d.ts +1 -5
- package/dist/components/conversation/streaming-text/streaming-text-base.class.js +2 -2
- package/dist/components/conversation/streaming-text/streaming-text-core.class.d.ts +7 -1
- package/dist/components/conversation/streaming-text/streaming-text-core.class.js +3 -4
- package/dist/components/conversation/streaming-text/streaming-text.class.d.ts +7 -1
- package/dist/components/conversation/streaming-text/streaming-text.class.js +3 -4
- package/dist/components/conversation/thread-list/thread-list.class.d.ts +13 -2
- package/dist/components/conversation/thread-list/thread-list.class.js +3 -3
- package/dist/components/data/data-grid/data-grid.class.js +1 -1
- package/dist/components/data/document-library/document-library.class.js +2 -0
- package/dist/components/data/flow-canvas/flow-canvas.class.js +1 -1
- package/dist/components/data/flow-minimap/flow-minimap.class.js +1 -1
- package/dist/components/data/flow-node/flow-node.class.d.ts +6 -2
- package/dist/components/data/flow-node/flow-node.class.js +6 -2
- package/dist/components/data/flow-node/flow-node.styles.js +1 -1
- package/dist/components/data/gauge/gauge.styles.js +1 -1
- package/dist/components/data/heatmap/heatmap.class.d.ts +3 -0
- package/dist/components/data/heatmap/heatmap.class.js +2 -2
- package/dist/components/data/heatmap/heatmap.styles.js +1 -1
- package/dist/components/data/sparkline/sparkline.class.d.ts +5 -3
- package/dist/components/data/stat/stat.class.d.ts +4 -3
- package/dist/components/data/table/table.class.d.ts +32 -5
- package/dist/components/data/table/table.class.js +3 -2
- package/dist/components/forms/button/button.class.d.ts +7 -1
- package/dist/components/forms/button/button.styles.js +1 -1
- package/dist/components/forms/date-picker/date-input.styles.js +1 -1
- package/dist/components/forms/input/input.styles.js +1 -1
- package/dist/components/forms/rubric-form/rubric-form.class.js +1 -0
- package/dist/components/layout/app-rail/app-rail.class.d.ts +50 -11
- package/dist/components/layout/app-rail/app-rail.class.js +2 -2
- package/dist/components/layout/command-palette/command-palette.class.js +1 -1
- package/dist/components/layout/menu/menu.class.d.ts +6 -5
- package/dist/components/layout/split-panel/split-panel.class.d.ts +2 -1
- package/dist/components/media/flag/language-map.d.ts +8 -0
- package/dist/components/media/flag/language-map.js +1 -1
- package/dist/components/media/map/map-loader.d.ts +7 -1
- package/dist/components/media/map/map.class.d.ts +111 -5
- package/dist/components/media/map/map.class.js +3 -3
- package/dist/components/media/media-card/media-card.class.d.ts +18 -5
- package/dist/components/media/media-card/media-card.class.js +1 -1
- package/dist/components/overlays/overlay/popover.class.d.ts +20 -0
- package/dist/components/overlays/overlay/popover.class.js +2 -2
- package/dist/components/retrieval/graph/graph.class.d.ts +3 -3
- package/dist/components/retrieval/graph/graph.class.js +1 -1
- package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.d.ts +5 -3
- package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.js +3 -2
- package/dist/components/retrieval/mind-map/mind-map.class.d.ts +3 -3
- package/dist/components/retrieval/mind-map/mind-map.class.js +1 -1
- package/dist/components/retrieval/retrieval-results/retrieval-results.class.d.ts +4 -1
- package/dist/components/retrieval/retrieval-results/retrieval-results.class.js +5 -1
- package/dist/components/retrieval/source-picker/source-picker.class.d.ts +4 -1
- package/dist/components/retrieval/source-picker/source-picker.class.js +5 -1
- package/dist/components/utility/icon/icon.class.d.ts +9 -3
- package/dist/components/utility/icon/icon.class.js +1 -1
- package/dist/components/utility/mutation-observer/mutation-observer.class.d.ts +14 -8
- package/dist/components/utility/mutation-observer/mutation-observer.class.js +1 -1
- package/dist/components/viewers/document-viewer/document-viewer.class.d.ts +6 -1
- package/dist/components/viewers/document-viewer/document-viewer.class.js +7 -1
- package/dist/components/viewers/document-viewer/registry.d.ts +12 -3
- package/dist/components/viewers/geojson-view/geojson-view.class.d.ts +13 -5
- package/dist/components/viewers/geojson-view/geojson-view.class.js +1 -1
- package/dist/components/viewers/geojson-view/geojson-viewer.class.d.ts +3 -2
- package/dist/custom-elements-jsx.d.ts +1 -1
- package/dist/events.d.ts +644 -1242
- package/dist/internal/anchored-overlay-runtime.d.ts +2 -2
- package/dist/internal/cell-range.d.ts +0 -6
- package/dist/internal/cell-range.js +1 -1
- package/dist/internal/css-token-length.d.ts +7 -0
- package/dist/internal/css-token-length.js +1 -0
- package/dist/internal/default-strings.generated.d.ts +1 -1
- package/dist/internal/default-strings.generated.js +1 -1
- package/dist/internal/deferred-focus-return.d.ts +7 -4
- package/dist/internal/deferred-focus-return.js +1 -1
- package/dist/internal/dev-mode-attribute-warning.d.ts +35 -0
- package/dist/internal/dev-mode-attribute-warning.js +1 -1
- package/dist/internal/localization-runtime.js +1 -1
- package/dist/internal/localization-types.d.ts +1 -1
- package/dist/internal/localization.js +2 -2
- package/dist/internal/package-metadata.d.ts +1 -1
- package/dist/internal/package-metadata.js +1 -1
- package/dist/internal/papaparse-loader.d.ts +1 -1
- package/dist/internal/papaparse-loader.js +1 -1
- package/dist/internal/positioner.d.ts +10 -0
- package/dist/internal/positioner.js +1 -1
- package/dist/internal/shared-unions.d.ts +2 -1
- package/dist/internal/slotted-overlay-controller.d.ts +20 -0
- package/dist/internal/slotted-overlay-controller.js +1 -0
- package/dist/internal/text-quote.d.ts +1 -1
- package/dist/internal/text-quote.js +1 -1
- package/dist/internal/theme-observation.d.ts +4 -0
- package/dist/internal/theme-observation.js +1 -0
- package/dist/internal/theme-watcher.js +1 -1
- package/dist/localization.d.ts +6 -1
- package/dist/localization.js +1 -1
- package/dist/lyra.d.ts +1 -1
- package/dist/lyra.js +1 -1
- package/dist/svelte.d.ts +1 -1
- package/dist/testing/lyra-tag-event-map.js +1 -1
- package/dist/translations/ar/agent-tools.js +1 -1
- package/dist/translations/cs/agent-tools.js +1 -1
- package/dist/translations/da/agent-tools.js +1 -1
- package/dist/translations/de/agent-tools.js +1 -1
- package/dist/translations/de-CH/agent-tools.js +1 -1
- package/dist/translations/es/agent-tools.js +1 -1
- package/dist/translations/fa/agent-tools.js +1 -1
- package/dist/translations/fi/agent-tools.js +1 -1
- package/dist/translations/fr/agent-tools.js +1 -1
- package/dist/translations/he/agent-tools.js +1 -1
- package/dist/translations/hi/agent-tools.js +1 -1
- package/dist/translations/hr/agent-tools.js +1 -1
- package/dist/translations/hu/agent-tools.js +1 -1
- package/dist/translations/id/agent-tools.js +1 -1
- package/dist/translations/it/agent-tools.js +1 -1
- package/dist/translations/ja/agent-tools.js +1 -1
- package/dist/translations/kk/agent-tools.js +1 -1
- package/dist/translations/ko/agent-tools.js +1 -1
- package/dist/translations/nb/agent-tools.js +1 -1
- package/dist/translations/nl/agent-tools.js +1 -1
- package/dist/translations/nn/agent-tools.js +1 -1
- package/dist/translations/pl/agent-tools.js +1 -1
- package/dist/translations/pt-BR/agent-tools.js +1 -1
- package/dist/translations/pt-PT/agent-tools.js +1 -1
- package/dist/translations/pt-PT/charts.js +1 -1
- package/dist/translations/pt-PT/conversation.js +1 -1
- package/dist/translations/pt-PT/data.js +1 -1
- package/dist/translations/pt-PT/forms.js +1 -1
- package/dist/translations/pt-PT/layout.js +1 -1
- package/dist/translations/pt-PT/media.js +1 -1
- package/dist/translations/pt-PT/retrieval.js +1 -1
- package/dist/translations/pt-PT/shared.js +1 -1
- package/dist/translations/pt-PT/utility.js +1 -1
- package/dist/translations/pt-PT/viewers.js +2 -2
- package/dist/translations/ro/agent-tools.js +1 -1
- package/dist/translations/ru/agent-tools.js +1 -1
- package/dist/translations/sl/agent-tools.js +1 -1
- package/dist/translations/sv/agent-tools.js +1 -1
- package/dist/translations/tr/agent-tools.js +1 -1
- package/dist/translations/uk/agent-tools.js +1 -1
- package/dist/translations/zh-CN/agent-tools.js +1 -1
- package/dist/translations/zh-TW/agent-tools.js +1 -1
- package/dist/utilities/index.d.ts +1 -1
- package/dist/utilities/index.js +1 -1
- package/dist/utilities/localization.d.ts +28 -10
- package/dist/vue.d.ts +1 -1
- package/llms/components/lr-accordion-item.md +2 -344
- package/llms/components/lr-accordion.md +2 -344
- package/llms/components/lr-app-rail-group.md +2 -721
- package/llms/components/lr-app-rail-item.md +2 -721
- package/llms/components/lr-app-rail.md +29 -4
- package/llms/components/lr-bar-chart.md +2 -87
- package/llms/components/lr-box-plot.md +1 -1
- package/llms/components/lr-breadcrumb-item.md +2 -67
- package/llms/components/lr-bubble-chart.md +2 -87
- package/llms/components/lr-button.md +7 -0
- package/llms/components/lr-chart.md +4 -5
- package/llms/components/lr-chip-group.md +2 -272
- package/llms/components/lr-conversation-item.md +20 -2
- package/llms/components/lr-date-input.md +2 -457
- package/llms/components/lr-date-picker.md +6 -2
- package/llms/components/lr-document-library.md +3 -3
- package/llms/components/lr-document-viewer.md +14 -5
- package/llms/components/lr-doughnut-chart.md +2 -87
- package/llms/components/lr-dropdown-item.md +2 -306
- package/llms/components/lr-dropdown.md +6 -0
- package/llms/components/lr-flag.md +9 -1
- package/llms/components/lr-flow-node.md +4 -1
- package/llms/components/lr-geojson-view.md +3 -90
- package/llms/components/lr-geojson-viewer.md +13 -5
- package/llms/components/lr-icon-button.md +4 -1
- package/llms/components/lr-icon.md +14 -6
- package/llms/components/lr-input.md +9 -2
- package/llms/components/lr-lite-chart.md +17 -9
- package/llms/components/lr-map.md +87 -2
- package/llms/components/lr-markdown-core.md +5 -4
- package/llms/components/lr-markdown.md +4 -2
- package/llms/components/lr-media-card.md +10 -5
- package/llms/components/lr-menu-item.md +2 -306
- package/llms/components/lr-menu.md +9 -6
- package/llms/components/lr-menubar-item.md +2 -125
- package/llms/components/lr-message-actions.md +4 -1
- package/llms/components/lr-message-parts.md +14 -5
- package/llms/components/lr-mutation-observer.md +12 -6
- package/llms/components/lr-navigation-menu-item.md +2 -237
- package/llms/components/lr-number-input.md +4 -0
- package/llms/components/lr-option.md +2 -712
- package/llms/components/lr-pie-chart.md +2 -87
- package/llms/components/lr-polar-area-chart.md +2 -87
- package/llms/components/lr-popover.md +10 -0
- package/llms/components/lr-prompt-input.md +5 -1
- package/llms/components/lr-radar-chart.md +2 -87
- package/llms/components/lr-reorder-item.md +2 -157
- package/llms/components/lr-result-field.md +2 -117
- package/llms/components/lr-retrieval-results.md +3 -1
- package/llms/components/lr-rubric-form.md +3 -2
- package/llms/components/lr-scatter-chart.md +2 -87
- package/llms/components/lr-selection-toolbar.md +4 -2
- package/llms/components/lr-source-card.md +2 -186
- package/llms/components/lr-source-picker.md +3 -1
- package/llms/components/lr-sparkline.md +9 -4
- package/llms/components/lr-split-panel.md +5 -3
- package/llms/components/lr-stat.md +4 -3
- package/llms/components/lr-streaming-text-core.md +7 -5
- package/llms/components/lr-streaming-text.md +9 -6
- package/llms/components/lr-swatch-picker.md +130 -0
- package/llms/components/lr-tag.md +2 -177
- package/llms/components/lr-thread-list.md +27 -4
- package/llms/components/lr-timeline-item.md +2 -120
- package/llms/components/lr-toast-item.md +2 -223
- package/llms/components/lr-tool-call-block.md +12 -3
- package/llms/components/lr-tool-call-chip.md +12 -6
- package/llms/components/lr-tool-param-form.md +3 -2
- package/llms/components/lr-tool-result-dialog.md +7 -3
- package/llms/components/lr-tool-result-view.md +4 -3
- package/llms/components/lr-tool-timeline.md +12 -5
- package/llms/components/lr-tree-item.md +2 -354
- package/llms/index.md +22 -4
- package/llms/migration.md +75 -1
- package/llms/peers.md +1 -1
- package/llms/shared.md +86 -39
- package/llms-full.txt +739 -163
- package/package.json +6 -6
- package/registrations.json +1 -1
- package/vscode-css-data.json +1 -1
- package/vscode-html-data.json +1 -1
- package/web-types.json +1 -1
|
@@ -9,725 +9,6 @@
|
|
|
9
9
|
- **Release history** [CHANGELOG.md](../../CHANGELOG.md); family-wide breaking-change summaries: [llms-full.txt](../../llms-full.txt)
|
|
10
10
|
- **Deprecations** none
|
|
11
11
|
- **Optional peers** none
|
|
12
|
-
- **Themeable via** 10 parts, 22 custom properties — see
|
|
13
|
-
- **Documented with** `lr-app-rail
|
|
12
|
+
- **Themeable via** 10 parts, 22 custom properties — see `lr-app-rail.md`
|
|
13
|
+
- **Documented with** `lr-app-rail`: see [lr-app-rail.md](./lr-app-rail.md).
|
|
14
14
|
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## `lr-app-rail`
|
|
19
|
-
|
|
20
|
-
A responsive navigation rail, and the library's application sidebar, that adapts across three presentations as the _viewport_ narrows (not
|
|
21
|
-
this element's own inline size): `'full'` (nav items show icon + label, inline), `'icon-only'` (a
|
|
22
|
-
narrower inline rail, icons only), and `'mobile'` (hidden behind a toggle button; opening it shows a
|
|
23
|
-
focus-trapped floating overlay over the page). First-party invention (no `wa-*`/`sl-*` counterpart).
|
|
24
|
-
Breakpoints are viewport-width `matchMedia()` queries against `icon-only-breakpoint`/
|
|
25
|
-
`mobile-breakpoint`, not a `ResizeObserver` on this element — presentation tracks the actual device/
|
|
26
|
-
window width the way a native OS shell's navigation does, not however much horizontal space a
|
|
27
|
-
particular layout happens to give it. `[part="base"]` (the inline `'full'`/`'icon-only'`
|
|
28
|
-
presentation) and `[part="panel"]` (the mobile overlay) are the _same_ element promoted in place
|
|
29
|
-
across modes (mirrors `<lr-widget>`'s fullscreen mode) — never both at once, and slotted nav
|
|
30
|
-
content is never duplicated.
|
|
31
|
-
|
|
32
|
-
In mobile mode the closed `[part="panel"]` has no shadow and parks one pixel beyond the inline-start
|
|
33
|
-
edge, including at fractional widths. The open-only `--lr-app-rail-panel-shadow` defaults to
|
|
34
|
-
`var(--lr-shadow-l)`. Elevation snaps with the open state while the panel slides. Only real open/close
|
|
35
|
-
changes animate: runtime direction changes and entering mobile mode leave a closed panel parked
|
|
36
|
-
without a sweep. Closed content remains inert. Keep the rail outside ancestors establishing a fixed
|
|
37
|
-
containing block (`transform`, `filter`, `will-change: transform`, or layout/paint containment),
|
|
38
|
-
because geometric parking otherwise follows that ancestor's edge instead of the viewport.
|
|
39
|
-
|
|
40
|
-
Opting in to `resizable` adds a continuously draggable width for the `'full'` state: a
|
|
41
|
-
`[part="resizer"]` handle (pointer-drag and Left/Right-arrow keyboard stepping, RTL-aware) clamped to
|
|
42
|
-
`[minRailWidthPx, maxRailWidthPx]`. Set `storageKey` (attribute `storage-key`) to persist the fields
|
|
43
|
-
selected by `persist` to `localStorage` under `lr-app-rail:${storageKey}` and restore them on the
|
|
44
|
-
next mount (mirrors `lr-multi-split`'s `storage-key`; effective `mode` is breakpoint-derived and never
|
|
45
|
-
persisted). The backward-compatible allowlist is `open width`; use
|
|
46
|
-
`persist="width preferred-mode"` for durable layout preference without restoring the transient
|
|
47
|
-
mobile overlay. Without a `storageKey` there is no persistence — listen for `lr-rail-resize` and
|
|
48
|
-
persist its committed `widthPx` yourself. Listen for the preceding cancelable
|
|
49
|
-
`lr-rail-resize-request` event when a host needs to veto a proposed width.
|
|
50
|
-
`preferredMode` separately lets a host manually prefer `'full'`/`'icon-only'` for the non-mobile
|
|
51
|
-
breakpoint axis (e.g. a user's own collapse toggle) while `mobile-breakpoint` continues to be tracked
|
|
52
|
-
automatically regardless — it's only consulted while `forceMode` is `'auto'` or unset; an explicit
|
|
53
|
-
`forceMode` value takes full priority. A `preferredMode` restored from `localStorage` on mount is
|
|
54
|
-
observable the same way a live change is: it fires `lr-mode-change` too (see **Events** below),
|
|
55
|
-
letting a consumer that syncs app chrome to the rail's mode pick up the restored value on load.
|
|
56
|
-
|
|
57
|
-
**Properties:**
|
|
58
|
-
|
|
59
|
-
- `frame?: LyraFrame` — optional `'card'` floating frame or `'plain'` edgeless inline surface.
|
|
60
|
-
Unset retains the flush rail. Unknown values clear the attribute. In mobile mode framing is inert.
|
|
61
|
-
- `triggerCollapses: boolean = false` (attribute `trigger-collapses`) — extend the `trigger`/`for`
|
|
62
|
-
association to full/icon-only modes, managing `aria-expanded`, `aria-controls` and
|
|
63
|
-
`aria-keyshortcuts`. Wire the trigger's click to `toggle()`. Independent of `collapsible`, which
|
|
64
|
-
alone decides whether the built-in collapse control renders.
|
|
65
|
-
- `hotkey: string = ''` — optional chord such as `mod+b`, `ctrl+b`, `meta+b`, or `alt+b`.
|
|
66
|
-
Requires a non-Shift modifier. Removing the attribute restores the empty default. See the shortcut
|
|
67
|
-
behavior below.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
- `mode: LyraAppRailMode` (custom accessor, reflected, read-only as of 9.0.0) — always resolves to
|
|
71
|
-
one of the three real modes (`'full'|'icon-only'|'mobile'`), never `'auto'`; assigning it now
|
|
72
|
-
throws (`el.mode = 'icon-only'` -> TypeError).
|
|
73
|
-
- `forceMode?: 'full' | 'icon-only' | 'auto'` (attribute `force-mode`, reflected) — replaces `mode`'s
|
|
74
|
-
former write side as of 9.0.0. Assigning `'full'`/`'icon-only'` pins that mode and stops the
|
|
75
|
-
element responding to breakpoint changes; assigning `'auto'` (or leaving it unset) releases the
|
|
76
|
-
pin and resumes automatic viewport tracking. `'mobile'` can never be force-pinned here — the
|
|
77
|
-
mobile breakpoint is always tracked automatically regardless, mirroring `preferredMode`'s scope
|
|
78
|
-
below; widen `mobile-breakpoint` for a guaranteed-mobile state instead. Settable via the
|
|
79
|
-
`force-mode` attribute too (`force-mode="icon-only"`, `force-mode="auto"`).
|
|
80
|
-
- `iconOnlyBreakpoint: string = '960px'` (attribute `icon-only-breakpoint`) — any valid CSS length,
|
|
81
|
-
used directly in a `(max-width: ...)` media query; below it the rail switches from `'full'` to
|
|
82
|
-
`'icon-only'`.
|
|
83
|
-
- `mobileBreakpoint: string = '600px'` (attribute `mobile-breakpoint`) — same mechanism; below it the
|
|
84
|
-
rail switches from `'icon-only'` to `'mobile'`. Should be smaller than `iconOnlyBreakpoint` to
|
|
85
|
-
produce all three states as the viewport narrows.
|
|
86
|
-
- `open: boolean = false` (reflected) — whether the mobile floating overlay is shown. Only meaningful
|
|
87
|
-
while `mode` is `'mobile'`; leaving mobile mode closes it so a later mobile transition cannot
|
|
88
|
-
restore a stale modal. Set this directly, or use the built-in toggle button — there is no separate
|
|
89
|
-
`show()`/`hide()` pair.
|
|
90
|
-
- `label?: string` — optional accessible name for the rail's navigation landmark and mobile dialog.
|
|
91
|
-
Every nonempty supplied string is honored literally; only absence/empty uses the localized
|
|
92
|
-
navigation fallback. A host-level `aria-label` attribute (including an explicit empty value)
|
|
93
|
-
takes precedence.
|
|
94
|
-
- `preferredMode?: 'full' | 'icon-only' | null` (attribute `preferred-mode`) — manually prefers
|
|
95
|
-
`'full'` or `'icon-only'` for the non-mobile breakpoint axis, while `mobile-breakpoint` continues to
|
|
96
|
-
be tracked automatically regardless — e.g. a user's manual collapse toggle that should still yield
|
|
97
|
-
to a genuinely too-narrow-for-any-inline-rail viewport. Only consulted while `forceMode` is
|
|
98
|
-
`'auto'` or unset (see above); an explicit `forceMode` value takes full priority.
|
|
99
|
-
Unset (the default, `null`) reproduces the original breakpoint-only behavior exactly.
|
|
100
|
-
- `collapsible: boolean = false` (reflected) — opts in a desktop collapse control rendered inside
|
|
101
|
-
`[part="header"]`. It flips the rail between its `'full'` and `'icon-only'` presentations by
|
|
102
|
-
writing `preferredMode`, so the `mobile-breakpoint` keeps being tracked automatically and a
|
|
103
|
-
genuinely too-narrow viewport still wins. The control is not rendered at all while `mode` is
|
|
104
|
-
`'mobile'`, and `[part="header"]`'s layout is unchanged when this is unset. The collapse survives
|
|
105
|
-
a reload only when `storage-key` is set AND `persist` includes `preferred-mode` — the default
|
|
106
|
-
`persist` is `open width`, which does not. Pair them: `persist="width preferred-mode"`. Either
|
|
107
|
-
route announces itself through the existing `lr-mode-change` event; there is no new event.
|
|
108
|
-
- `hideToggle: boolean = false` (reflected, attribute `hide-toggle`) — suppresses the built-in mobile
|
|
109
|
-
`[part='toggle']` hamburger/OPEN button, for a consumer that already owns an external mobile-menu
|
|
110
|
-
trigger wired to this rail's own `open` property (pair it with `trigger`/`for` below so focus
|
|
111
|
-
still returns to that external trigger on close). `false` (the default) reproduces the exact
|
|
112
|
-
existing output. This does not remove the button once the overlay is open: at that point it has
|
|
113
|
-
been reparented inside the trapped `[part="panel"]` (see the CSS parts entry below) as the
|
|
114
|
-
panel's only in-panel dismiss control, and hiding it there too would leave the open panel with no
|
|
115
|
-
in-panel way to close it at all besides Escape/backdrop.
|
|
116
|
-
- `trigger: HTMLElement | null = null` (attribute: false) — direct reference to an external element
|
|
117
|
-
that opens this rail's mobile overlay (e.g. an application-chrome hamburger button used together
|
|
118
|
-
with `hideToggle`). When set (or resolved through `for`), closing the overlay by any path —
|
|
119
|
-
Escape, backdrop click, a nav-item click, or the built-in toggle itself — returns focus to it, the
|
|
120
|
-
same guarantee the built-in toggle's own click already gets. The return is attempted as the
|
|
121
|
-
overlay closes and, if that attempt could not land (a host commonly keeps its own menu button
|
|
122
|
-
`visibility: hidden` while the drawer is open and re-shows it from its own render in response to
|
|
123
|
-
`lr-toggle`), again once the close's update has completed and one animation frame has passed, so
|
|
124
|
-
the host's re-render has applied. That second pass only acts while focus is still inside the rail
|
|
125
|
-
or has fallen to `<body>` — focus moved elsewhere in the meantime is never taken back — and
|
|
126
|
-
focuses the first candidate that can actually hold focus: the trigger, then the element that held
|
|
127
|
-
focus when the overlay opened, then the built-in `[part='toggle']` (unavailable under
|
|
128
|
-
`hideToggle`), and finally the rail host; when even the host cannot take focus (hidden, or inert —
|
|
129
|
-
under an inert ancestor or behind a stacked modal), focus is left where it is. The host
|
|
130
|
-
temporarily receives `tabindex="-1"` only when it has no authored tabindex; the temporary
|
|
131
|
-
attribute is removed once focus moves off the host or the rail disconnects, while a blur caused
|
|
132
|
-
only by the window or tab losing system focus (the host stays the focused element) leaves it in
|
|
133
|
-
place, so focus is still on the host when the window regains focus. Needed because a consumer's
|
|
134
|
-
own JS-driven `open = true` never focuses anything, and even a real click does not reliably focus
|
|
135
|
-
its target in every browser. Resolved when the overlay opens and again when it closes, so
|
|
136
|
-
reassigning it (or `for`) while the overlay is open changes where focus returns; when the
|
|
137
|
-
association no longer resolves at close, the target resolved at open still applies. Read alongside
|
|
138
|
-
`for`; this direct reference wins when both resolve to different elements. Unset (the default,
|
|
139
|
-
`null`), no external element is a return candidate: a close still returns focus to the built-in
|
|
140
|
-
toggle when its own click opened the overlay, otherwise to the element that held focus at open,
|
|
141
|
-
and the deferred pass still runs over its remaining candidates (that element, the built-in toggle,
|
|
142
|
-
then the rail host) — only the trigger candidate depends on the association. The resolved trigger
|
|
143
|
-
also receives `aria-expanded` (rendered in both states) and `aria-controls` pointing at the rail's
|
|
144
|
-
own panel, so an external control announces the overlay's state across the shadow boundary.
|
|
145
|
-
Because a light-DOM element cannot hold a raw reference into another element's shadow tree,
|
|
146
|
-
engines resolve `aria-controls` to the `<lr-app-rail>` host itself; either resolution is correct.
|
|
147
|
-
The association applies while `mode` is `'mobile'`, or in full/icon-only when `trigger-collapses`
|
|
148
|
-
is set; otherwise it is released. Disconnect also releases it, and reassigning `trigger` moves the
|
|
149
|
-
state to the new element.
|
|
150
|
-
- `for: string = ''` — id of an external element that opens this rail's mobile overlay, the
|
|
151
|
-
label/`htmlFor`-style alternative to assigning `trigger` directly (mirrors `<lr-page-rail>`'s
|
|
152
|
-
`for`). Resolved against this element's own root (shadow root or document) when the overlay opens
|
|
153
|
-
and again when it closes. Ignored once `trigger` is itself set.
|
|
154
|
-
- `resizable: boolean = false` (reflected) — opts a continuously draggable width in for the `'full'`
|
|
155
|
-
state, exposing a `[part='resizer']` handle clamped to `[minRailWidthPx, maxRailWidthPx]`. `false`
|
|
156
|
-
(the default) renders no resizer and leaves the fixed-width `--lr-app-rail-width` CSS token
|
|
157
|
-
exactly as before this property existed.
|
|
158
|
-
- `railWidthPx?: number` (attribute `rail-width-px`) — the rail's current width in px while
|
|
159
|
-
`resizable`; settable/gettable directly. Unset defers to `--lr-app-rail-width`'s own resolved
|
|
160
|
-
width.
|
|
161
|
-
- `storageKey?: string` (attribute `storage-key`) — when set, persists the fields selected by
|
|
162
|
-
`persist` to `localStorage` under `lr-app-rail:${storageKey}` and restores them on the next
|
|
163
|
-
mount. Each field is restored only when the consumer has not assigned it on that same mount: an
|
|
164
|
-
`open`/`rail-width-px`/`preferred-mode` attribute, or a `.open=${false}`-style binding, wins over
|
|
165
|
-
stored state, and a restored `open` fires no `lr-toggle`. Effective `mode` is breakpoint-derived
|
|
166
|
-
and never persisted. Unset means no persistence.
|
|
167
|
-
- `persist: string = 'open width'` — whitespace-separated field allowlist used with `storageKey`.
|
|
168
|
-
Valid `LyraAppRailPersistField` tokens are `open`, `width` (`railWidthPx`), and `preferred-mode`
|
|
169
|
-
(`preferredMode`). The default preserves the existing open+width behavior. Use
|
|
170
|
-
`persist="width preferred-mode"` when overlay-open state is controlled or should stay
|
|
171
|
-
session-only.
|
|
172
|
-
- `minRailWidthPx: number = 190` (attribute `min-rail-width-px`) — minimum `railWidthPx` a
|
|
173
|
-
drag/keyboard resize can reach.
|
|
174
|
-
- `maxRailWidthPx: number = 440` (attribute `max-rail-width-px`) — maximum `railWidthPx` a
|
|
175
|
-
drag/keyboard resize can reach.
|
|
176
|
-
- `dragging: boolean = false` (reflected, read-only as of 9.0.0) — `true` for the duration of an
|
|
177
|
-
active pointer-driven resize drag (not a keyboard step); reflected so a consumer (or this
|
|
178
|
-
component's own styles) can suppress `[part='base']`'s `transition: inline-size` during the drag,
|
|
179
|
-
which otherwise visibly "chases" the pointer instead of tracking it 1:1. This component always
|
|
180
|
-
owned every drag transition itself; assigning it now throws (`el.dragging = true` -> TypeError).
|
|
181
|
-
|
|
182
|
-
Also settable as a plain `aria-label` attribute (not a reactive property): overrides the computed
|
|
183
|
-
`label`/localized-default accessible name on both the navigation landmark and the mobile dialog
|
|
184
|
-
role, matching `<lr-date-input>`'s `accessibleLabel`.
|
|
185
|
-
|
|
186
|
-
**Methods:** `toggle(): void` opens/closes the mobile overlay through cancelable `lr-toggle`, or
|
|
187
|
-
flips the full/icon-only preference. It is a no-op while disconnected. While pinned, it records
|
|
188
|
-
only the preference; releasing `forceMode` applies it.
|
|
189
|
-
|
|
190
|
-
`toggleCollapse(): void` performs the same `'full'`/`'icon-only'` flip `collapsible`'s
|
|
191
|
-
built-in control does, for a consumer rendering its own control (app chrome, a command palette, a
|
|
192
|
-
keyboard shortcut). A no-op while `mode` is `'mobile'`. While `forceMode` pins the mode the
|
|
193
|
-
preference alternates relative to `preferredMode ?? mode` on every call and takes effect once the pin is released.
|
|
194
|
-
|
|
195
|
-
**Events:** `lr-mode-change` (`detail: LyraAppRailModeChangeDetail` = `{ mode: LyraAppRailMode }`; the
|
|
196
|
-
effective mode changed, whether from a breakpoint crossing, a `forceMode` assignment, or a
|
|
197
|
-
persisted `preferred-mode` restored on mount (`storage-key` + `persist="preferred-mode"`) — the
|
|
198
|
-
restored-on-mount case fires once, from the first `updated()` after that mount's render and
|
|
199
|
-
attribute reflection have both landed, rather than synchronously during the mount itself; it is
|
|
200
|
-
not fired for a redundant reassignment to the mode already in effect, nor when no preferred mode
|
|
201
|
-
was persisted), `lr-toggle`
|
|
202
|
-
(`detail: LyraAppRailToggleDetail` = `{ open: boolean }`; the mobile overlay is opening or closing — via
|
|
203
|
-
the built-in toggle button, Escape, a backdrop click, a nav-item click while open, or a
|
|
204
|
-
breakpoint/forced mode change leaving `'mobile'` while open — not fired when a consumer sets `open`
|
|
205
|
-
directly. Cancelable for every trigger except the forced mode-change close, which always applies —
|
|
206
|
-
vetoing that one would leave `open` stuck `true` in a mode where it's meaningless; call
|
|
207
|
-
`preventDefault()` to keep the overlay as it is for the other triggers),
|
|
208
|
-
`lr-rail-resize-request` (`detail: LyraAppRailResizeDetail` = `{ widthPx: number }`; a cancelable
|
|
209
|
-
proposed width from drag or keyboard stepping, emitted before the component assigns
|
|
210
|
-
`railWidthPx` — call `preventDefault()` to keep the current width. A synchronous request listener
|
|
211
|
-
that disables resizing, leaves full mode, or disconnects the rail also cancels the proposal,
|
|
212
|
-
preserving any width the listener assigned and publishing no accepted resize. Assigning only
|
|
213
|
-
`railWidthPx` does not veto the proposal. It is not fired when a consumer sets `railWidthPx` directly), and `lr-rail-resize` (`detail: LyraAppRailResizeDetail` =
|
|
214
|
-
`{ widthPx: number }`; non-cancelable committed width, emitted immediately for a genuine keyboard
|
|
215
|
-
step and once at pointerup for a genuine drag. Clamped/no-op steps, canceled/lost gestures, and
|
|
216
|
-
consumer property writes emit no committed event).
|
|
217
|
-
|
|
218
|
-
**Slots:** default (nav items — generic slotted content, e.g. `<a>`/`<button>` elements the consumer
|
|
219
|
-
builds with its own icon+label structure; clicking anywhere in this slot closes the mobile overlay if
|
|
220
|
-
open), `header` (logo/brand content, shown above the nav items in every mode), `footer` (a trailing
|
|
221
|
-
user/settings trigger, shown below the nav items).
|
|
222
|
-
|
|
223
|
-
**CSS parts:** `base`, `header`, `nav`, `footer`, `toggle` (hidden via CSS outside `'mobile'` mode, or
|
|
224
|
-
-- while it is not also serving as the panel's only in-panel dismiss control -- via `hideToggle`;
|
|
225
|
-
reparented to be the first child of `[part="panel"]` for exactly as long as the mobile overlay is
|
|
226
|
-
open, so the shared focus trap, scoped to the panel alone, can reach it and Tab cycles through it
|
|
227
|
-
like `<lr-dialog>`'s in-panel close button, then moved back to its resting position, a sibling
|
|
228
|
-
immediately ahead of `[part="panel"]`, once closed; the same button element is reused throughout,
|
|
229
|
-
never destroyed/recreated, so a reference captured before opening remains valid after closing, and
|
|
230
|
-
it renders as its own reserved row ahead of the `header` slot rather than an absolute overlay on
|
|
231
|
-
top of it, so a wide/slotted header is never obscured), `backdrop`, `panel` (`base`/`panel` are mutually exclusive on the same
|
|
232
|
-
underlying element — see above), `resizer` (the `resizable` opt-in's drag handle, only rendered while
|
|
233
|
-
`resizable` and `mode` is `'full'`; its hit target is `--lr-icon-button-size`-wide), `resizer-track`
|
|
234
|
-
(the slim 3px visible drag line centered inside that hit target, tinted `--lr-color-brand` on hover),
|
|
235
|
-
`collapse-toggle` (the opt-in desktop collapse control, rendered inside `[part="header"]` only while
|
|
236
|
-
`collapsible` is set and `mode` is not `'mobile'`; it renders `aria-expanded` in both states, points
|
|
237
|
-
`aria-controls` at `[part="nav"]` — the item list whose presentation actually changes, never the
|
|
238
|
-
containing `[part="base"]`/`[part="panel"]` — and takes a localized name from the
|
|
239
|
-
`appRailCollapse`/`appRailExpand` keys) and `collapse-icon` (the chevron wrapper, mirrored by its own
|
|
240
|
-
`transform` under RTL). Collapsing to `'icon-only'` keeps each item's own accessible name and
|
|
241
|
-
clips its `label`/`meta` visually, while hiding nested disclosure controls and child lists — so `aria-expanded` reports which of the two
|
|
242
|
-
presentations is on screen, for magnifier and braille users, rather than announcing hidden content.
|
|
243
|
-
|
|
244
|
-
**Themeable custom properties:**
|
|
245
|
-
`--lr-app-rail-frame-gap` (default `var(--lr-space-s)`), `--lr-app-rail-frame-radius` (default
|
|
246
|
-
`var(--lr-radius)`), and `--lr-app-rail-frame-shadow` (default `var(--lr-shadow-s)`) customize a
|
|
247
|
-
card frame. The card host uses `display: flow-root` to contain its margins; a consumer overriding
|
|
248
|
-
host `display` owns that formatting context. Plain frames default to a transparent background,
|
|
249
|
-
while an explicit `--lr-app-rail-background` still wins. Forced colors restores the plain frame's
|
|
250
|
-
boundary. `--lr-app-rail-panel-shadow` (default `var(--lr-shadow-l)`) affects only the open mobile
|
|
251
|
-
panel; no value can paint elevation while closed. Prefer that token over an unqualified
|
|
252
|
-
`::part(panel)` shadow rule, which would also paint while closed; state-scope a part override with
|
|
253
|
-
`lr-app-rail[mode="mobile"][open]::part(panel)`.
|
|
254
|
-
|
|
255
|
-
Other hooks: `--lr-app-rail-width` (default `15rem` — the inline rail width in
|
|
256
|
-
`'full'` mode), `--lr-app-rail-icon-width` (default `4rem` — the inline rail width in `'icon-only'`
|
|
257
|
-
mode), `--lr-app-rail-mobile-width` (default `18rem`, capped at `85vw` — the mobile overlay panel
|
|
258
|
-
width), `--lr-app-rail-overlay-color` (default `var(--lr-color-overlay)` — the mobile backdrop scrim
|
|
259
|
-
color; component-specific since no shared token exists), `--lr-app-rail-panel-inset-block-start`
|
|
260
|
-
(default `0`, applied to both `[part="panel"]` and `[part="backdrop"]` — raise it to leave room for
|
|
261
|
-
a fixed app bar/status area above the drawer instead of the panel/scrim starting flush with the
|
|
262
|
-
viewport top), `--lr-app-rail-panel-radius` (default `0` — uniform corner radius of `[part="panel"]`;
|
|
263
|
-
pairs naturally with a nonzero `--lr-app-rail-panel-inset-block-start`, which exposes the panel's top
|
|
264
|
-
corners). Four direction-aware per-corner tokens each default to `--lr-app-rail-panel-radius`, so
|
|
265
|
-
setting only the uniform token still rounds all four corners exactly as before:
|
|
266
|
-
`--lr-app-rail-panel-radius-start-start` and `--lr-app-rail-panel-radius-end-start` (logical
|
|
267
|
-
`border-start-start-radius`/`border-end-start-radius` — the two corners at the panel's own flush
|
|
268
|
-
inline-start edge, since the drawer always sits flush against `inset-inline-start: 0`) and
|
|
269
|
-
`--lr-app-rail-panel-radius-start-end`/`--lr-app-rail-panel-radius-end-end` (logical
|
|
270
|
-
`border-start-end-radius`/`border-end-end-radius` — the two corners away from that flush edge, the
|
|
271
|
-
pair a flush-against-one-edge drawer typically rounds). All four are logical, so which physical
|
|
272
|
-
corner each one paints swaps under `dir="rtl"` with no second consumer rule.
|
|
273
|
-
`--lr-app-rail-panel-overflow-block` (default `auto`) and
|
|
274
|
-
`--lr-app-rail-panel-overflow-inline` (default `clip`) — `[part="panel"]`'s logical overflow axes;
|
|
275
|
-
either non-`visible` value clips a `position: fixed` popup opened by a slotted/nav-item control
|
|
276
|
-
(e.g. a slotted `<lr-select>`/`<lr-menu>`) whenever its rendered box extends past the panel,
|
|
277
|
-
regardless of that popup's own containing block. Per the CSS overflow spec, a lone `visible` axis
|
|
278
|
-
paired with a non-`visible` other axis computes as `auto` instead (still clipping) — set **both**
|
|
279
|
-
tokens to `visible` together to actually stop the clipping, accepting that wide header/footer
|
|
280
|
-
content can then scroll/bleed both ways. `--lr-app-rail-background` (default
|
|
281
|
-
`var(--lr-color-surface)` — `[part="base"]`'s background, the docked non-overlay presentation) and
|
|
282
|
-
`--lr-app-rail-panel-background` (default `var(--lr-color-surface-overlay)` — `[part="panel"]`'s
|
|
283
|
-
background, the mobile overlay presentation; kept separate from `--lr-app-rail-background`/
|
|
284
|
-
`--lr-app-rail-overlay-color` since the panel is deliberately themed as a modal surface, not the
|
|
285
|
-
docked rail chrome). `--lr-app-rail-header-padding` and `--lr-app-rail-footer-padding` (both default
|
|
286
|
-
`var(--lr-space-m)`) retune `[part="header"]`/`[part="footer"]`'s padding independently.
|
|
287
|
-
`--lr-app-rail-header-min-block-size` (default `auto`, the property's own initial value, so unset
|
|
288
|
-
reproduces today's exact height) reserves a minimum height for `[part="header"]`, for content that
|
|
289
|
-
mounts or resizes asynchronously. `--lr-app-rail-nav-padding` and `--lr-app-rail-nav-gap` (default
|
|
290
|
-
`var(--lr-space-s)`/`var(--lr-space-xs)`, the values this rule hard-coded before either token
|
|
291
|
-
existed) retune `[part="nav"]`'s own padding and inter-item gap — the rail's vertical rhythm,
|
|
292
|
-
previously reachable only through `::part(nav)`. Plus shared
|
|
293
|
-
tokens (`--lr-color-border`, `--lr-color-border-subtle`,
|
|
294
|
-
`--lr-color-surface`, `--lr-color-text`, `--lr-color-brand`, `--lr-color-brand-quiet`,
|
|
295
|
-
`--lr-space-*`, `--lr-radius`, `--lr-shadow-l` (the mobile overlay panel's default elevation, while
|
|
296
|
-
open only), `--lr-shadow-s` (the `frame="card"` default elevation), `--lr-icon-button-size`,
|
|
297
|
-
`--lr-focus-ring-*`, `--lr-transition-base`). The rail's inline-end edge and its header/footer rules
|
|
298
|
-
use the decorative `--lr-color-border-subtle`, except while the resizer renders (`resizable` in
|
|
299
|
-
`'full'` mode): its track is transparent at rest, so the edge is then the separator's only visible
|
|
300
|
-
mark and stays on `--lr-color-border`. `resizable`'s width is driven entirely by
|
|
301
|
-
`railWidthPx`'s inline `inline-size` style rather than a new custom property.
|
|
302
|
-
The mobile toggle's hover/pressed background and foreground are independently inheritable through
|
|
303
|
-
`--lr-app-rail-toggle-hover-bg`, `--lr-app-rail-toggle-hover-color`,
|
|
304
|
-
`--lr-app-rail-toggle-active-bg`, and `--lr-app-rail-toggle-active-color`. `[part="collapse-toggle"]`
|
|
305
|
-
has the matching set: `--lr-app-rail-collapse-toggle-hover-bg` (default
|
|
306
|
-
`var(--lr-color-brand-quiet)`), `--lr-app-rail-collapse-toggle-hover-color` (default
|
|
307
|
-
`var(--lr-color-brand)`), `--lr-app-rail-collapse-toggle-active-bg` (no default) and
|
|
308
|
-
`--lr-app-rail-collapse-toggle-active-color` (default `var(--lr-color-brand)`). The resizer track uses
|
|
309
|
-
`--lr-app-rail-resizer-hover-bg` and `--lr-app-rail-resizer-active-bg`. Each hook is an inline
|
|
310
|
-
fallback at its exact state rule and preserves the previous brand or active-mix value when unset.
|
|
311
|
-
|
|
312
|
-
**Optional peer deps:** none.
|
|
313
|
-
|
|
314
|
-
```html
|
|
315
|
-
<lr-app-rail
|
|
316
|
-
label="Main navigation"
|
|
317
|
-
icon-only-breakpoint="960px"
|
|
318
|
-
mobile-breakpoint="600px"
|
|
319
|
-
resizable
|
|
320
|
-
>
|
|
321
|
-
<span slot="header"><img src="/logo.svg" alt="Acme" /></span>
|
|
322
|
-
<a href="/inbox" aria-label="Inbox"
|
|
323
|
-
><svg aria-hidden="true">...</svg><span>Inbox</span></a
|
|
324
|
-
>
|
|
325
|
-
<a href="/settings" aria-label="Settings"
|
|
326
|
-
><svg aria-hidden="true">...</svg><span>Settings</span></a
|
|
327
|
-
>
|
|
328
|
-
<span slot="footer"><button>Profile</button></span>
|
|
329
|
-
</lr-app-rail>
|
|
330
|
-
<script type="module">
|
|
331
|
-
const rail = document.querySelector("lr-app-rail");
|
|
332
|
-
rail.addEventListener("lr-rail-resize-request", (e) => {
|
|
333
|
-
if (e.detail.widthPx > 360) e.preventDefault();
|
|
334
|
-
});
|
|
335
|
-
rail.addEventListener("lr-rail-resize", (e) =>
|
|
336
|
-
localStorage.setItem("railWidthPx", String(e.detail.widthPx))
|
|
337
|
-
);
|
|
338
|
-
</script>
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
```ts
|
|
342
|
-
rail.forceMode = "icon-only"; // force a presentation regardless of viewport width
|
|
343
|
-
rail.forceMode = "auto"; // release the force, resume live breakpoint tracking
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
The package root also exports a pure `computeAppRailMode(iconOnlyMatches: boolean, mobileMatches:
|
|
347
|
-
boolean, preferredMode?: 'full' | 'icon-only' | null): LyraAppRailMode` resolver (plus the
|
|
348
|
-
`LyraAppRailMode`/`LyraAppRailModeInput`/`LyraAppRailPreferredMode`/`LyraAppRailPersistField`/
|
|
349
|
-
`LyraAppRailModeChangeDetail`/`LyraAppRailToggleDetail`/`LyraAppRailResizeDetail` types) — the same
|
|
350
|
-
logic the element's internal `matchMedia` listeners call, exposed standalone so a
|
|
351
|
-
consumer can compute or unit-test the same resolution without a real browser window. `mobileMatches`
|
|
352
|
-
wins over everything else when true (the viewport is narrower than both breakpoints at once);
|
|
353
|
-
otherwise `preferredMode` (when set) wins over `iconOnlyMatches`.
|
|
354
|
-
|
|
355
|
-
The mobile state keeps its own panel template rather than nesting `<lr-dialog>`, while its modal
|
|
356
|
-
behavior participates in the shared overlay stack. It is a plain `<div>` with an explicit
|
|
357
|
-
`role="navigation"` (swapping to `role="dialog"` while the overlay is open) rather than a literal
|
|
358
|
-
`<nav>` tag, since a
|
|
359
|
-
`<nav>`'s implicit role can't be overridden to `role="dialog"` without an `aria-allowed-role`
|
|
360
|
-
violation. In `'icon-only'` mode, slotted nav items lose their visible text label — give each one a
|
|
361
|
-
real accessible name (`aria-label`, visually hidden text, or `title`) regardless, since this
|
|
362
|
-
component only lays out whatever is slotted and can't inspect or fix up a consumer's own markup.
|
|
363
|
-
|
|
364
|
-
**Known gotchas:**
|
|
365
|
-
|
|
366
|
-
- `mode` is the readonly `LyraAppRailMode` result. `forceMode` accepts the non-mobile
|
|
367
|
-
`LyraAppRailPreferredMode` values plus the `'auto'` release sentinel, so whether the rail is
|
|
368
|
-
pinned or tracking the viewport remains directly observable.
|
|
369
|
-
- reassigning `icon-only-breakpoint`/`mobile-breakpoint` after first render tears down and rebuilds
|
|
370
|
-
the `matchMedia` listeners, but does not itself clear `forceMode` — if a consumer set
|
|
371
|
-
`forceMode = 'icon-only'`, changing the breakpoints won't resume auto-tracking until
|
|
372
|
-
`forceMode = 'auto'` is set explicitly.
|
|
373
|
-
- leaving `'mobile'` mode while `open` (via a breakpoint crossing or a `forceMode` reassignment)
|
|
374
|
-
auto-closes the overlay through the same path as the toggle button, so `lr-toggle` still fires
|
|
375
|
-
and the scroll lock/focus trap still release normally — a consumer listening only for explicit
|
|
376
|
-
toggle-button clicks would miss this closure.
|
|
377
|
-
- the mobile panel is also given `inert` whenever `mode === 'mobile'` and `open` is `false` — it's
|
|
378
|
-
removed from the accessibility tree and tab order via `inert` at the same time it's hidden visually
|
|
379
|
-
via `transform: translateX(calc(-100% - var(--lr-size-1px)))`, both applied simultaneously rather than one implying the other.
|
|
380
|
-
- the offscreen slide direction for the mobile panel is flipped for RTL via a `:dir(rtl)` CSS
|
|
381
|
-
selector (`translateX(calc(100% + var(--lr-size-1px)))`), not through the shared `internal/rtl.ts` JS helper used for pointer/
|
|
382
|
-
keyboard math elsewhere in this library — a physical `transform` isn't expressible with logical
|
|
383
|
-
properties, so this one case is handled purely in CSS.
|
|
384
|
-
- a reconnect that preserves the same element instance (e.g. a drag-and-drop reparent) resumes its
|
|
385
|
-
shared overlay registration and re-acquires the scroll lock if the overlay was still active
|
|
386
|
-
across the move — `disconnectedCallback`/`connectedCallback` fire back-to-back with no update in
|
|
387
|
-
between, so `willUpdate()` alone wouldn't otherwise notice.
|
|
388
|
-
- `resizable`'s drag handle is pointer-only (`pointerdown`/`pointermove`/`pointerup`/
|
|
389
|
-
`pointercancel`/`lostpointercapture`) plus discrete Left/Right-arrow keyboard stepping (8px per
|
|
390
|
-
press, RTL-aware) — there's no dedicated touch gesture beyond what Pointer Events already unify.
|
|
391
|
-
- reassigning `railWidthPx` while `resizable` is unset has no visible effect on the rendered width —
|
|
392
|
-
the fixed-width `--lr-app-rail-width` token still governs `'full'`-mode width until `resizable`
|
|
393
|
-
is also set.
|
|
394
|
-
- reassigning `icon-only-breakpoint`/`mobile-breakpoint`/`preferredMode` does not itself un-force a
|
|
395
|
-
previously-forced `mode` — same caveat as above, `preferredMode` is only consulted while `mode`
|
|
396
|
-
isn't force-pinned.
|
|
397
|
-
- the built-in toggle physically moves in the DOM: it is a sibling immediately ahead of
|
|
398
|
-
`[part="base"]`/`[part="panel"]` while closed (or outside mobile mode), and the first child of
|
|
399
|
-
`[part="panel"]` while the mobile overlay is open. This is a real reparent via `insertBefore`
|
|
400
|
-
(not a template-conditional recreate), so an event listener or a captured element reference
|
|
401
|
-
stays valid across the transition — a MutationObserver watching a specific fixed container would
|
|
402
|
-
still need to account for the move.
|
|
403
|
-
- opening the mobile overlay moves initial focus to the first focusable nav item (or the panel
|
|
404
|
-
itself when nothing in the slotted content is focusable), never to the toggle — even now that
|
|
405
|
-
the toggle lives inside the panel as its structurally-first child. Tab still reaches it as part
|
|
406
|
-
of the trap's normal cycle.
|
|
407
|
-
|
|
408
|
-
### Building an app sidebar
|
|
409
|
-
|
|
410
|
-
The rail's shortcut uses the same grammar and per-window owner registry as `lr-command-palette`.
|
|
411
|
-
The last-connected eligible owner acts once. Rails ignore repeat/composition/key-less events,
|
|
412
|
-
`defaultPrevented`, text-entry and contenteditable targets, invisible/inert rails, and pinned modes.
|
|
413
|
-
A pinned rail still accepts explicit `toggle()` calls to record a preference. Chords match the
|
|
414
|
-
printed key; ASCII letter/digit chords fall back to `event.code` only when the event key is
|
|
415
|
-
non-ASCII, preserving Dvorak/AZERTY printed-letter behavior. Mod resolves to Meta on macOS and
|
|
416
|
-
Control elsewhere. Built-in controls and applicable external triggers expose `aria-keyshortcuts`.
|
|
417
|
-
`lr-button` and `lr-icon-button` forward this and `aria-expanded` to their focused control; other
|
|
418
|
-
custom triggers must implement forwarding themselves. Cross-shadow element-reference
|
|
419
|
-
`aria-controls` forwarding by those custom triggers is not claimed.
|
|
420
|
-
|
|
421
|
-
A hidden rail is not a desktop-offcanvas mode: `toggle()` never clears `hidden`, hotkeys ignore it,
|
|
422
|
-
and a `trigger-collapses` trigger reports the last inline presentation. Collapse normally preserves
|
|
423
|
-
focus. A focused resizer returns focus to the rail, and a hidden nested control returns it to the
|
|
424
|
-
visible parent item.
|
|
425
|
-
|
|
426
|
-
**Standalone recipe: viewport offcanvas, one trigger.**
|
|
427
|
-
|
|
428
|
-
```html
|
|
429
|
-
<div class="shell">
|
|
430
|
-
<lr-app-rail id="sidebar" label="Workspace" frame="card" hide-toggle trigger-collapses
|
|
431
|
-
for="sidebar-trigger" hotkey="mod+b" storage-key="app" persist="preferred-mode">
|
|
432
|
-
<lr-app-rail-group heading="Platform">
|
|
433
|
-
<lr-app-rail-item href="/projects" current tooltip><span slot="icon">▦</span>Projects</lr-app-rail-item>
|
|
434
|
-
</lr-app-rail-group>
|
|
435
|
-
<lr-divider></lr-divider>
|
|
436
|
-
</lr-app-rail>
|
|
437
|
-
<main><button id="sidebar-trigger" type="button">Toggle sidebar</button></main>
|
|
438
|
-
</div>
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
```css
|
|
442
|
-
.shell { display: flex; block-size: 100dvh; }
|
|
443
|
-
.shell main { flex: 1; min-inline-size: 0; }
|
|
444
|
-
lr-app-rail lr-divider { align-self: stretch; }
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
```js
|
|
448
|
-
const rail = document.getElementById('sidebar');
|
|
449
|
-
document.getElementById('sidebar-trigger').addEventListener('click', () => rail.toggle());
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
Import the rail, group, item and divider from their granular `components/lr-*.js` entries. The
|
|
453
|
-
rail owns mobile offcanvas at its viewport breakpoint (600px default). For an inset composition,
|
|
454
|
-
use `frame="plain"` and style the sibling main region as a card with surface, radius, border and
|
|
455
|
-
shadow tokens. A raised sidebar tint is opt-in with
|
|
456
|
-
`--lr-app-rail-background: var(--lr-color-surface-raised)`.
|
|
457
|
-
|
|
458
|
-
**Page recipe: allocation offcanvas, two view-scoped controls.**
|
|
459
|
-
|
|
460
|
-
```html
|
|
461
|
-
<lr-page id="workspace">
|
|
462
|
-
<lr-app-rail slot="navigation" id="workspace-nav" label="Workspace" frame="plain"
|
|
463
|
-
mobile-breakpoint="0px" icon-only-breakpoint="0px" collapsible hotkey="mod+b"
|
|
464
|
-
storage-key="app" persist="preferred-mode">
|
|
465
|
-
<lr-app-rail-item href="/projects"><span slot="icon">▦</span>Projects</lr-app-rail-item>
|
|
466
|
-
</lr-app-rail>
|
|
467
|
-
<p>Main content</p>
|
|
468
|
-
</lr-page>
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
```css
|
|
472
|
-
lr-page[view='mobile'] lr-app-rail::part(collapse-toggle) { display: none; }
|
|
473
|
-
lr-app-rail lr-divider { align-self: stretch; }
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
```js
|
|
477
|
-
const page = document.getElementById('workspace');
|
|
478
|
-
const nav = document.getElementById('workspace-nav');
|
|
479
|
-
const syncRail = () => { nav.forceMode = page.view === 'mobile' ? 'full' : 'auto'; };
|
|
480
|
-
const observer = new MutationObserver(syncRail);
|
|
481
|
-
observer.observe(page, { attributes: true, attributeFilter: ['view'] });
|
|
482
|
-
syncRail();
|
|
483
|
-
// Call observer.disconnect() when disposing this application composition.
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
`lr-page` owns the sole drawer at narrow allocations and its built-in or slotted
|
|
487
|
-
`navigation-toggle` owns mobile trigger ARIA. The rail's built-in collapse control owns desktop
|
|
488
|
-
collapse. An external desktop control may use `trigger-collapses` if hidden in mobile view.
|
|
489
|
-
The pin shows the full rail inside the drawer and preserves its desktop preference. The shortcut
|
|
490
|
-
acts only on desktop; it cannot open the page drawer. Give the rail a distinct `label`, since the
|
|
491
|
-
page and rail navigation landmarks nest. This recipe uses the page's default start-side placement.
|
|
492
|
-
|
|
493
|
-
### `lr-app-rail-item`
|
|
494
|
-
|
|
495
|
-
An explicit navigation item for `<lr-app-rail>`. It renders an accessible link when `href` is
|
|
496
|
-
set and enabled, otherwise a button; the rail can add its `icon-only` presentation state without
|
|
497
|
-
removing the label from the accessibility tree.
|
|
498
|
-
|
|
499
|
-
In icon-only presentation the nested disclosure and children list are hidden while `expanded`
|
|
500
|
-
is preserved. Returning full restores the list. If a disclosure or descendant had focus, it moves
|
|
501
|
-
to the outermost visible parent item's base control. The parent link remains operable. The `end`
|
|
502
|
-
slot stays visible; reserve an icon-only rail width of at least twice the nav padding plus the icon
|
|
503
|
-
square, item gap and end content. A 1.5rem badge requires 5.5rem with default tokens. Keep end actions
|
|
504
|
-
out of default-width compact rails, or use a rail that never collapses.
|
|
505
|
-
|
|
506
|
-
**Properties:**
|
|
507
|
-
|
|
508
|
-
- `href: string = ''` — optional destination. Without it, the item renders as a button.
|
|
509
|
-
- `target: string = ''` — optional link target.
|
|
510
|
-
- `disabled: boolean = false` (reflected) — prevents activation while retaining the item in the rail.
|
|
511
|
-
- `current: boolean = false` (reflected) — marks this as the destination for the current page/view;
|
|
512
|
-
reflects `aria-current="page"` on `[part='base']` and drives the current visual treatment. The rail
|
|
513
|
-
has no built-in routing, so the consumer sets this per item (e.g. by comparing `href` against the
|
|
514
|
-
current location). `active`, a deprecated alias in both property and attribute form, was removed
|
|
515
|
-
in 16.0.0 (available since 11.2.0; eligible for removal from 13.0.0) — use `current`.
|
|
516
|
-
- `tooltip: boolean = false` (reflected) — opt-in hover or keyboard-focus flyout (the focused control matches `:focus-visible` and no pointer press preceded it)
|
|
517
|
-
(`[part='tooltip']`) showing
|
|
518
|
-
this item's label text while the rail's `icon-only` mode (set externally by the parent
|
|
519
|
-
`<lr-app-rail>` as the viewport narrows) hides it from view. No effect outside icon-only mode,
|
|
520
|
-
since the label is already visible there. `false` (the default) reproduces the exact existing
|
|
521
|
-
output.
|
|
522
|
-
- `expanded: boolean = false` (reflected) — whether this item's own `children` are shown. `false`
|
|
523
|
-
reproduces exactly what an item without this property rendered before this feature existed.
|
|
524
|
-
Driven through the same request/commit pair as `<lr-app-rail-group>`'s `open`, see Events below.
|
|
525
|
-
|
|
526
|
-
A host `aria-label` is copied to the rendered native link or button by attribute presence,
|
|
527
|
-
including an explicitly empty value; without it, the default slot supplies the native name. The
|
|
528
|
-
same precedence supplies the tooltip text when that opt-in flyout is visible, and the disclosure's
|
|
529
|
-
interpolated `{label}` (see Events below).
|
|
530
|
-
|
|
531
|
-
**Events:** `lr-toggle-request` — cancelable, emitted before `expanded` changes from the built-in
|
|
532
|
-
disclosure (`detail: { open }` — the field is named `open`, matching `<lr-app-rail-group>`'s
|
|
533
|
-
identical event name and detail shape exactly). Call `preventDefault()` to keep the current state,
|
|
534
|
-
or assign `expanded` from the listener to resolve it yourself; a write during the dispatch
|
|
535
|
-
suppresses the default commit even when it assigns the value the property already held. Not
|
|
536
|
-
emitted for a direct `expanded` write. `lr-toggle` — non-cancelable, emitted after `expanded` is
|
|
537
|
-
written, never for a vetoed or listener-resolved request (`detail: { open }`).
|
|
538
|
-
|
|
539
|
-
**Methods:** `click(): void` activates the internal native link or button; it is a no-op while
|
|
540
|
-
`disabled`.
|
|
541
|
-
|
|
542
|
-
If an `href`/`disabled` update replaces a focused link or button, focus follows an available native
|
|
543
|
-
replacement. When that replacement is disabled or inert, focus returns to the available element
|
|
544
|
-
that led into the item, or to the stable owning rail surface when no return target exists. A newer
|
|
545
|
-
external focus move is always preserved, and this repair dispatches no activation event.
|
|
546
|
-
|
|
547
|
-
**Slots:** default (the visible label), `icon` (the leading decorative icon, always hidden from
|
|
548
|
-
assistive technology and inert across its flattened subtree; the default slot or host `aria-label`
|
|
549
|
-
names the native control, which remains the sole action).
|
|
550
|
-
|
|
551
|
-
- `meta` slot — secondary trailing text (an unread count, a keyboard shortcut). Rendered as a
|
|
552
|
-
SIBLING of the item's own link/button, so its text is not part of the item's accessible name and a
|
|
553
|
-
pointer landing on it does not activate the item. Visually clipped in `icon-only` mode exactly as
|
|
554
|
-
the label is, staying available to assistive technology.
|
|
555
|
-
- `end` slot — trailing controls or adornments (an overflow-menu trigger, a status badge). Also a
|
|
556
|
-
sibling of the link/button — the shape `<lr-details>` uses for `header-actions` — so a slotted
|
|
557
|
-
control keeps its own click, keyboard activation and focus order instead of being swallowed.
|
|
558
|
-
Unlike `meta` it stays visible in `icon-only` mode, where it shares the narrow rail's width with
|
|
559
|
-
the icon.
|
|
560
|
-
- `children` slot — nested `<lr-app-rail-item>`s disclosed beneath this item (the
|
|
561
|
-
treeitem-with-link pattern: the row itself navigates, a separate disclosure expands its own
|
|
562
|
-
child rows). Slotting anything into it grows a built-in `[part="toggle"]` disclosure button as a
|
|
563
|
-
SIBLING of the item's own link/button, never nested inside it, so the link keeps navigating on
|
|
564
|
-
its own and the disclosure keeps toggling on its own — clicking one never triggers the other.
|
|
565
|
-
Leaving `children` empty renders neither the disclosure nor `[part="children"]` at all: an item
|
|
566
|
-
authored without any `children` content renders byte-identically to one authored before this
|
|
567
|
-
slot existed. The disclosure carries `aria-expanded` (both states) and `aria-controls` pointing
|
|
568
|
-
at `[part="children"]`'s id, and a localized accessible name interpolating this item's own label
|
|
569
|
-
(`Expand {label}`/`Collapse {label}` in the default locale — no literal fallback, so a
|
|
570
|
-
`registerLyraLocale()` translation or a `.strings` override always reaches it). `<lr-app-rail-group>`
|
|
571
|
-
cannot express this pattern: its collapsible heading *is* the toggle, so a navigable link cannot
|
|
572
|
-
live inside it without nesting an interactive element inside a button.
|
|
573
|
-
|
|
574
|
-
`icon-only` forwards from this item onto every `<lr-app-rail-item>` it directly owns through
|
|
575
|
-
`children` — including ones appended later — exactly how `<lr-app-rail-group>` forwards onto the
|
|
576
|
-
items and nested groups it owns. The disclosure is a fixed icon-button-sized square in full
|
|
577
|
-
presentation, sharing the link/button state tokens. Icon-only hides it and the nested list while
|
|
578
|
-
preserving expansion and recovering focus to the visible parent item. There is no ancestor-current treatment — `<lr-app-rail-group>` has
|
|
579
|
-
no equivalent concept for a group containing the current item, so none is invented here either; a
|
|
580
|
-
current descendant stays perceivable only through its own `current` property.
|
|
581
|
-
|
|
582
|
-
Both wrappers (`[part="meta"]`, `[part="end"]`) are hidden while empty, so an item using neither
|
|
583
|
-
renders exactly as before. Note that while the mobile overlay is open, a click anywhere in the
|
|
584
|
-
rail's default slot closes it — including a click on an `end` control; that is the rail's documented
|
|
585
|
-
nav-slot behaviour, not new to these slots.
|
|
586
|
-
|
|
587
|
-
**CSS parts:** `base`, `icon`, `label`, `current-indicator` (a decorative inline indicator rendered
|
|
588
|
-
only while the item is `current`/`aria-current="page"`, mirroring `<lr-conversation-item>`'s
|
|
589
|
-
shipped `active-indicator` part — suppressed by default while `icon-only`, see the current-ring
|
|
590
|
-
tokens below), `tooltip` (the hover or keyboard-focus flyout (the focused control matches `:focus-visible` and no pointer press preceded it), only rendered while
|
|
591
|
-
`tooltip` is set, the item is `icon-only`, and it is hovered or keyboard-focused), `meta` (the wrapper around the `meta` slot,
|
|
592
|
-
hidden while empty), `end` (the wrapper around the `end` slot, hidden while empty), `toggle` (the
|
|
593
|
-
`children` disclosure, rendered only while something is slotted into `children`; a sibling of
|
|
594
|
-
`base`, never nested inside it), `toggle-icon` (the wrapper around the disclosure chevron,
|
|
595
|
-
direction-aware through this wrapper's own `transform` — mirrors `<lr-app-rail-group>`'s own
|
|
596
|
-
`[part="toggle-icon"]`) and `children` (the wrapper around the `children` slot, rendered only
|
|
597
|
-
alongside `toggle`; hidden — but present, so `aria-controls` keeps resolving — while `expanded` is
|
|
598
|
-
`false`).
|
|
599
|
-
|
|
600
|
-
**Themeable custom properties:** `--lr-app-rail-item-current-bg` (default
|
|
601
|
-
`var(--lr-color-brand-quiet)`), `--lr-app-rail-item-current-color` (default
|
|
602
|
-
`var(--lr-color-brand)`), and `--lr-app-rail-item-current-font-weight` (default
|
|
603
|
-
`var(--lr-font-weight-semibold)`) — background, text/icon color, and font weight of the `current`
|
|
604
|
-
(`aria-current="page"`) item. All three are scoped to `[aria-current='page']` only and declared as
|
|
605
|
-
inline `var()` fallbacks at the point of use, never on `:host`, so any can be set on the item itself
|
|
606
|
-
_or on any ancestor_ — including on `<lr-app-rail>` or a wrapper above it, to retheme every item's
|
|
607
|
-
current state at once. `::part(base)[aria-current='page']` is invalid CSS (Shadow Parts forbids an
|
|
608
|
-
attribute selector after `::part()`), so before these hooks the only lever was overriding the
|
|
609
|
-
library-wide `--lr-color-brand-quiet`/`--lr-color-brand`/`--lr-font-weight-semibold` tokens, which
|
|
610
|
-
repainted every other element reading them. Unset, each falls back to the token its rule used
|
|
611
|
-
before. `--lr-app-rail-item-current-font-weight` mirrors `<lr-stepper>`'s
|
|
612
|
-
`--lr-stepper-current-font-weight` and `<lr-segmented>`'s `--lr-segmented-selected-font-weight`.
|
|
613
|
-
`--lr-app-rail-item-current-indicator-color` (default `var(--lr-color-brand)`),
|
|
614
|
-
`--lr-app-rail-item-current-indicator-width` (default `var(--lr-size-2px)`), and
|
|
615
|
-
`--lr-app-rail-item-current-indicator-inset-inline` (default `0 auto`; set `auto 0` to place the
|
|
616
|
-
indicator at the inline-end edge instead) theme `[part="current-indicator"]`.
|
|
617
|
-
`--lr-app-rail-item-current-indicator-display` (no default; unset resolves to `none` while
|
|
618
|
-
`icon-only`) restores the indicator bar in icon-only presentation — a full-height edge bar reads
|
|
619
|
-
as a rendering glitch on the square icon-only tile, so it is suppressed there by default; full
|
|
620
|
-
presentation is unaffected either way, since its own `[part="current-indicator"]` rule declares no
|
|
621
|
-
`display` at all. `--lr-app-rail-item-current-ring` (no default; unset resolves to `none` in full
|
|
622
|
-
presentation and an inset ring in icon-only presentation) sets `box-shadow` on `[part="base"]`
|
|
623
|
-
while current: unset, icon-only gets an inset ring automatically — the non-color-only signal
|
|
624
|
-
(WCAG 1.4.1) that replaces the bar suppressed there, since full presentation already conveys
|
|
625
|
-
current state through the indicator bar and `--lr-app-rail-item-current-font-weight`. Setting this
|
|
626
|
-
token explicitly applies the same value in both presentations.
|
|
627
|
-
Ordinary interaction states are independently inheritable through
|
|
628
|
-
`--lr-app-rail-item-hover-bg`, `--lr-app-rail-item-hover-color`,
|
|
629
|
-
`--lr-app-rail-item-active-bg`, and `--lr-app-rail-item-active-color`, again retaining the former
|
|
630
|
-
brand/active-mix values as fallbacks.
|
|
631
|
-
`--lr-app-rail-item-min-block-size` (default `var(--lr-icon-button-size)`, floor-clamped to that
|
|
632
|
-
same token regardless of the override so the row's own hit target can never shrink below the WCAG
|
|
633
|
-
2.5.8 minimum), `--lr-app-rail-item-padding` (default `var(--lr-space-s)`),
|
|
634
|
-
`--lr-app-rail-item-gap` (default `var(--lr-space-s)`, the gap between `[part="icon"]` and
|
|
635
|
-
`[part="label"]`, and now also between the item's control and the `meta`/`end` adornments),
|
|
636
|
-
`--lr-app-rail-item-meta-color` (default `var(--lr-color-text-quiet)`),
|
|
637
|
-
`--lr-app-rail-item-meta-font-size` (default `var(--lr-font-size-sm)`),
|
|
638
|
-
`--lr-app-rail-item-icon-size` (default `var(--lr-icon-button-size)`, not
|
|
639
|
-
floor-clamped since the icon is decorative, not itself a pointer target), and
|
|
640
|
-
`--lr-app-rail-item-font-size` (default `inherit`, set after the `font` shorthand so only the size
|
|
641
|
-
is retuned while family/weight/line-height stay inherited) retune the row's geometry.
|
|
642
|
-
While `icon-only`, `[part="base"]` resolves to a square hit target matching the icon-button
|
|
643
|
-
footprint used elsewhere in this library (`aspect-ratio: 1` against its already floor-clamped
|
|
644
|
-
block size) instead of stretching across the rail's icon column. `--lr-app-rail-item-icon-only-size`
|
|
645
|
-
(no default) sizes that square directly — both `inline-size` and `block-size`, and the row's own
|
|
646
|
-
`min-block-size` floor — independent of `--lr-app-rail-item-min-block-size`, so a taller expanded
|
|
647
|
-
row and an icon-only square pinned to `--lr-icon-button-size` can coexist. Unset, the square is
|
|
648
|
-
still derived via `aspect-ratio: 1` against the row's block size exactly as before.
|
|
649
|
-
|
|
650
|
-
**`--lr-positioning-strategy`** (16.0.0) — the icon-only flyout tooltip reads this same cascading
|
|
651
|
-
`absolute`/`fixed` override documented on `<lr-popover>` when it is (re)positioned, falling back to
|
|
652
|
-
its own `fixed` default when nothing is set. There is no per-instance `positioning-strategy`
|
|
653
|
-
property on `<lr-app-rail-item>`; set the custom property on `:root`, a theme, or one clipping
|
|
654
|
-
ancestor to change every unset rail item's flyout beneath it.
|
|
655
|
-
|
|
656
|
-
`--lr-app-rail-item-indent` (default `var(--lr-space-l)`) sets `[part="children"]`'s
|
|
657
|
-
`padding-inline-start`. Applied once per nesting level — a doubly-nested `children` list compounds
|
|
658
|
-
two insets automatically, since each level's own `[part="children"]` applies the token again.
|
|
659
|
-
Logical, so it mirrors under `dir="rtl"` with no separate rule.
|
|
660
|
-
|
|
661
|
-
**Optional peer deps:** none.
|
|
662
|
-
|
|
663
|
-
---
|
|
664
|
-
|
|
665
|
-
### `lr-app-rail-group`
|
|
666
|
-
|
|
667
|
-
Titles, and optionally collapses, a section of `<lr-app-rail-item>`s. Grouping is by composition —
|
|
668
|
-
the group holds whatever it is given; there is no items array and no renderer callback, so it can
|
|
669
|
-
never disagree with what is rendered inside it.
|
|
670
|
-
|
|
671
|
-
```html
|
|
672
|
-
<script type="module">
|
|
673
|
-
import '@aceshooting/lyra-ui/components/layout/app-rail-group/app-rail-group.js';
|
|
674
|
-
</script>
|
|
675
|
-
|
|
676
|
-
<lr-app-rail>
|
|
677
|
-
<lr-app-rail-group heading="Workspaces" collapsible>
|
|
678
|
-
<button slot="header-actions" aria-label="Add workspace">+</button>
|
|
679
|
-
<lr-app-rail-item href="/atlas" current>Atlas</lr-app-rail-item>
|
|
680
|
-
<lr-app-rail-item href="/beacon">Beacon</lr-app-rail-item>
|
|
681
|
-
</lr-app-rail-group>
|
|
682
|
-
</lr-app-rail>
|
|
683
|
-
```
|
|
684
|
-
|
|
685
|
-
**Properties:**
|
|
686
|
-
|
|
687
|
-
- `heading: string = ''` — the section title. The `heading` slot replaces it when populated.
|
|
688
|
-
- `headingLevel: number = 3` (attribute `heading-level`) — the `aria-level` the heading landmark
|
|
689
|
-
reports. Clamped to 1-6 and rounded; a non-finite value falls back to `3`. Settable because a rail
|
|
690
|
-
sits at a different depth in every page that embeds it.
|
|
691
|
-
- `collapsible: boolean = false` (reflected) — opts in the built-in collapse control. The heading's
|
|
692
|
-
own text becomes the button carrying `aria-expanded` and `aria-controls`, which is the accordion
|
|
693
|
-
pattern; an unnamed group falls back to a localized `Collapse`/`Expand` name.
|
|
694
|
-
- `open: boolean = true` (reflected) — whether the content is shown. Carries a true-default
|
|
695
|
-
converter, so `open="false"` parses from markup (a plain presence-based boolean cannot). `open`
|
|
696
|
-
governs visibility whether or not `collapsible` is set, so a consumer can drive collapse entirely
|
|
697
|
-
from its own chrome.
|
|
698
|
-
|
|
699
|
-
**Events:** `lr-toggle-request` — cancelable, emitted before `open` changes from the built-in
|
|
700
|
-
control (`detail: { open }`). Call `preventDefault()` to keep the current state, or assign `open`
|
|
701
|
-
from the listener to resolve it yourself; a write during the dispatch suppresses the default commit
|
|
702
|
-
even when it assigns the value the property already held. Not emitted for a direct `open` write.
|
|
703
|
-
`lr-toggle` — non-cancelable, emitted after `open` is written, never for a vetoed or
|
|
704
|
-
listener-resolved request (`detail: { open }`).
|
|
705
|
-
|
|
706
|
-
**Slots:** default — the group's items, and any nested `<lr-app-rail-group>`s; `heading` — rich
|
|
707
|
-
heading content; `header-actions` — controls beside the heading, rendered as a sibling of the
|
|
708
|
-
collapse control so activating one never toggles the group.
|
|
709
|
-
|
|
710
|
-
**CSS parts:** `base`, `header`, `heading`, `heading-text`, `toggle`, `toggle-icon`,
|
|
711
|
-
`header-actions`, `content`.
|
|
712
|
-
|
|
713
|
-
**Themeable custom properties:** `--lr-app-rail-group-gap` (default `var(--lr-space-xs)`),
|
|
714
|
-
`--lr-app-rail-group-padding-block` (default `var(--lr-space-xs)`),
|
|
715
|
-
`--lr-app-rail-group-heading-color` (default `var(--lr-color-text-quiet)`),
|
|
716
|
-
`--lr-app-rail-group-heading-font-size` (default `var(--lr-font-size-sm)`),
|
|
717
|
-
`--lr-app-rail-group-hover-bg` (default `var(--lr-color-brand-quiet)`),
|
|
718
|
-
`--lr-app-rail-group-hover-color` (default `var(--lr-color-brand)`),
|
|
719
|
-
`--lr-app-rail-group-active-bg` (no default), `--lr-app-rail-group-active-color` (default
|
|
720
|
-
`var(--lr-color-brand)`).
|
|
721
|
-
|
|
722
|
-
The owning rail marks a slotted group `icon-only` exactly as it marks a slotted item, and the group
|
|
723
|
-
forwards that to the items and nested groups it *directly* owns — including ones appended later —
|
|
724
|
-
so grouping survives the rail's icon-only presentation. A nested group re-forwards in turn, so
|
|
725
|
-
exactly one element ever writes `icon-only` onto any given node and a nested group clips its own
|
|
726
|
-
heading too. In that mode the heading text is clipped out of layout — whether or not the group is
|
|
727
|
-
collapsible — but stays in the accessibility tree. While `collapsible` too, `[part="toggle"]`
|
|
728
|
-
resolves to a square hit target matching the icon-button footprint used elsewhere in this library,
|
|
729
|
-
instead of stretching across the header row (mirroring `<lr-app-rail-item>`'s own `[part="base"]`).
|
|
730
|
-
|
|
731
|
-
**Optional peer deps:** none.
|
|
732
|
-
|
|
733
|
-
---
|