@zeyos/zx 4.4.0 → 5.2.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 +447 -0
- package/README.md +44 -11
- package/dist/types/components/account-menu/account-menu.d.ts +8 -0
- package/dist/types/components/activity-list/activity-list.d.ts +31 -1
- package/dist/types/components/app-icon/app-icon.d.ts +14 -2
- package/dist/types/components/app-sidebar/app-sidebar.d.ts +293 -17
- package/dist/types/components/avatar/avatar.d.ts +46 -7
- package/dist/types/components/badge/badge.d.ts +21 -1
- package/dist/types/components/button/button.d.ts +15 -0
- package/dist/types/components/calendar/calendar.d.ts +67 -5
- package/dist/types/components/card-view/card-view.d.ts +7 -4
- package/dist/types/components/card-view/record-card.d.ts +11 -0
- package/dist/types/components/chart/chart.d.ts +10 -0
- package/dist/types/components/checklist/checklist.d.ts +9 -0
- package/dist/types/components/code-editor/code-editor.d.ts +6 -17
- package/dist/types/components/context-menu/context-menu.d.ts +6 -2
- package/dist/types/components/data-filter/data-filter.d.ts +4 -4
- package/dist/types/components/datebox/date-range-box.d.ts +5 -1
- package/dist/types/components/datebox/datebox.d.ts +7 -2
- package/dist/types/components/dialog/dialog.d.ts +46 -6
- package/dist/types/components/dropdown/dropdown.d.ts +24 -9
- package/dist/types/components/entity-ref/entity-ref.d.ts +2 -2
- package/dist/types/components/field/field.d.ts +54 -2
- package/dist/types/components/fieldset/external-adapter.d.ts +83 -0
- package/dist/types/components/fieldset/fieldset.d.ts +26 -1
- package/dist/types/components/file-item/file-item.d.ts +34 -6
- package/dist/types/components/file-list/file-list.d.ts +35 -3
- package/dist/types/components/filter/filter.d.ts +127 -3
- package/dist/types/components/filter-panel/filter-panel.d.ts +36 -7
- package/dist/types/components/form/form.d.ts +12 -1
- package/dist/types/components/kanban-view/kanban-card.d.ts +8 -0
- package/dist/types/components/launcher/launcher.d.ts +268 -15
- package/dist/types/components/list-toolbar/list-toolbar.d.ts +16 -7
- package/dist/types/components/menu-button/menu-button.d.ts +39 -5
- package/dist/types/components/message/message.d.ts +132 -17
- package/dist/types/components/modal/modal.d.ts +43 -2
- package/dist/types/components/module-bar/module-bar.d.ts +395 -0
- package/dist/types/components/pagination/pagination.d.ts +17 -3
- package/dist/types/components/select/color.d.ts +12 -0
- package/dist/types/components/select/glyphs.d.ts +124 -0
- package/dist/types/components/select/select.d.ts +17 -3
- package/dist/types/components/sheet/sheet.d.ts +15 -0
- package/dist/types/components/stat-tile/stat-tile.d.ts +16 -5
- package/dist/types/components/tabbox/tabbox.d.ts +9 -0
- package/dist/types/components/table/table.d.ts +311 -18
- package/dist/types/components/tag-picker/tag-picker.d.ts +21 -4
- package/dist/types/components/tree/tree.d.ts +13 -0
- package/dist/types/components/view/record-view.d.ts +59 -2
- package/dist/types/components/view-settings/view-settings.d.ts +879 -0
- package/dist/types/components/workspace-switcher/workspace-switcher.d.ts +327 -0
- package/dist/types/core/breakpoint.d.ts +1 -1
- package/dist/types/core/component.d.ts +12 -2
- package/dist/types/core/i18n.d.ts +27 -3
- package/dist/types/core/icons.d.ts +23 -25
- package/dist/types/core/keyboard.d.ts +98 -1
- package/dist/types/core/messages.d.ts +382 -0
- package/dist/types/core/motion.d.ts +59 -0
- package/dist/types/core/overlay-host.d.ts +42 -1
- package/dist/types/core/overlay-stack.d.ts +260 -0
- package/dist/types/index.d.ts +10 -5
- package/dist/types/internal/app-rail.d.ts +38 -0
- package/dist/types/internal/menu-items.d.ts +71 -3
- package/dist/types/zeyos/connect.d.ts +6 -0
- package/dist/types/zeyos/form.d.ts +50 -9
- package/dist/types/zeyos/index.d.ts +2 -1
- package/dist/types/zeyos/launcher.d.ts +9 -0
- package/dist/types/zeyos/module-bar.d.ts +176 -0
- package/dist/types/zeyos/query.d.ts +110 -5
- package/dist/types/zeyos/schema.d.ts +24 -1
- package/dist/types/zeyos/table.d.ts +345 -22
- package/dist/types/zeyos/view.d.ts +26 -2
- package/dist/zx-zeyos.esm.js +6178 -1008
- package/dist/zx-zeyos.esm.min.js +53 -5
- package/dist/zx.css +2861 -827
- package/dist/zx.esm.js +8909 -1480
- package/dist/zx.esm.min.js +55 -7
- package/dist/zx.global.js +8911 -1482
- package/dist/zx.global.min.js +55 -7
- package/dist/zx.min.css +1 -1
- package/docs/api.json +1079 -117
- package/docs/llms.md +956 -154
- package/docs/llms.txt +9 -5
- package/package.json +1 -1
- package/src/components/account-menu/account-menu.css +35 -10
- package/src/components/account-menu/account-menu.js +12 -0
- package/src/components/activity-item/activity-item.css +1 -1
- package/src/components/activity-item/activity-item.js +3 -0
- package/src/components/activity-list/activity-list.css +38 -3
- package/src/components/activity-list/activity-list.js +61 -1
- package/src/components/app-icon/app-icon.css +14 -3
- package/src/components/app-icon/app-icon.js +11 -1
- package/src/components/app-rail/app-rail.css +59 -79
- package/src/components/app-sidebar/app-sidebar.css +304 -168
- package/src/components/app-sidebar/app-sidebar.js +667 -46
- package/src/components/aurora/aurora.css +5 -3
- package/src/components/avatar/avatar.css +32 -7
- package/src/components/avatar/avatar.js +72 -8
- package/src/components/badge/badge.css +54 -2
- package/src/components/badge/badge.js +29 -2
- package/src/components/breadcrumb/breadcrumb.css +1 -1
- package/src/components/button/button.css +67 -9
- package/src/components/button/button.js +38 -0
- package/src/components/calendar/calendar.css +47 -47
- package/src/components/calendar/calendar.js +26 -52
- package/src/components/card/card.css +4 -4
- package/src/components/card-view/card-view.css +14 -12
- package/src/components/card-view/card-view.js +13 -9
- package/src/components/card-view/record-card.js +14 -3
- package/src/components/chart/chart.css +4 -1
- package/src/components/chart/chart.js +12 -0
- package/src/components/check-button/check-button.css +10 -2
- package/src/components/checklist/checklist.css +9 -2
- package/src/components/checklist/checklist.js +11 -5
- package/src/components/checklist/field-adapter.js +3 -1
- package/src/components/code-editor/code-editor.css +7 -6
- package/src/components/code-editor/code-editor.js +19 -1
- package/src/components/code-editor/field-adapter.js +9 -2
- package/src/components/context-menu/context-menu.css +9 -4
- package/src/components/context-menu/context-menu.js +8 -2
- package/src/components/copy/copy-button.css +6 -0
- package/src/components/copy/copy-input.css +9 -4
- package/src/components/data-filter/data-filter.css +5 -4
- package/src/components/data-filter/data-filter.js +2 -2
- package/src/components/date-picker/date-picker.css +6 -6
- package/src/components/date-picker/date-range-picker.css +3 -3
- package/src/components/date-picker/time-picker.css +3 -3
- package/src/components/datebox/date-range-box.css +79 -2
- package/src/components/datebox/date-range-box.js +13 -2
- package/src/components/datebox/datebox.css +79 -2
- package/src/components/datebox/datebox.js +13 -2
- package/src/components/dialog/dialog.css +26 -0
- package/src/components/dialog/dialog.js +99 -17
- package/src/components/dock/dock.css +5 -4
- package/src/components/dropdown/dropdown.css +1 -1
- package/src/components/dropdown/dropdown.js +159 -72
- package/src/components/empty-state/empty-state.css +2 -2
- package/src/components/entity-ref/entity-ref.css +1 -1
- package/src/components/entity-ref/entity-ref.js +1 -1
- package/src/components/field/field.css +28 -9
- package/src/components/field/field.js +101 -29
- package/src/components/field-upload/field-upload.css +1 -1
- package/src/components/fieldset/external-adapter.js +363 -0
- package/src/components/fieldset/fieldset.css +5 -3
- package/src/components/fieldset/fieldset.js +38 -64
- package/src/components/file-item/file-item.js +144 -10
- package/src/components/file-list/file-list.js +144 -21
- package/src/components/filter/filter.css +86 -2
- package/src/components/filter/filter.js +307 -11
- package/src/components/filter-panel/filter-panel.css +1 -1
- package/src/components/filter-panel/filter-panel.js +149 -32
- package/src/components/finder/finder.css +16 -14
- package/src/components/form/form.css +5 -4
- package/src/components/form/form.js +11 -2
- package/src/components/grid/grid.css +1 -1
- package/src/components/groupbox/groupbox.css +1 -1
- package/src/components/kanban-view/kanban-card.js +11 -3
- package/src/components/kanban-view/kanban-view.css +42 -18
- package/src/components/kanban-view/kanban-view.js +9 -6
- package/src/components/launcher/launcher.css +108 -26
- package/src/components/launcher/launcher.js +530 -42
- package/src/components/list-toolbar/list-toolbar.css +13 -8
- package/src/components/list-toolbar/list-toolbar.js +40 -14
- package/src/components/master-panel/master-panel.css +2 -0
- package/src/components/master-panel/master-panel.js +0 -1
- package/src/components/menu-button/menu-button.css +72 -4
- package/src/components/menu-button/menu-button.js +160 -18
- package/src/components/message/message.css +33 -3
- package/src/components/message/message.js +264 -22
- package/src/components/modal/modal.css +1 -1
- package/src/components/modal/modal.js +83 -15
- package/src/components/module-bar/module-bar.css +486 -0
- package/src/components/module-bar/module-bar.js +1074 -0
- package/src/components/multi-value-editor/multi-value-editor.css +1 -1
- package/src/components/navigation-bar/navigation-bar.css +18 -9
- package/src/components/number-field/field-adapter.js +3 -1
- package/src/components/number-field/number-field.css +70 -0
- package/src/components/number-field/number-field.js +2 -0
- package/src/components/pagination/pagination.css +13 -11
- package/src/components/pagination/pagination.js +20 -4
- package/src/components/panel/panel.css +4 -3
- package/src/components/questionnaire/questionnaire.css +4 -4
- package/src/components/rating/field-adapter.js +2 -1
- package/src/components/rating/rating.css +8 -1
- package/src/components/search/search.css +4 -1
- package/src/components/select/color.js +22 -0
- package/src/components/select/field-adapter.js +2 -0
- package/src/components/select/glyphs.js +165 -0
- package/src/components/select/select.css +138 -45
- package/src/components/select/select.js +92 -47
- package/src/components/sheet/sheet.css +12 -9
- package/src/components/sheet/sheet.js +74 -32
- package/src/components/slider/field-adapter.js +6 -1
- package/src/components/slider/slider.css +18 -4
- package/src/components/slider/slider.js +1 -1
- package/src/components/sort-control/sort-control.css +4 -1
- package/src/components/split-view/split-view.css +1 -1
- package/src/components/stat-tile/stat-tile.css +42 -47
- package/src/components/stat-tile/stat-tile.js +17 -6
- package/src/components/stepper/stepper.css +1 -1
- package/src/components/tabbox/tabbox.css +41 -32
- package/src/components/tabbox/tabbox.js +5 -1
- package/src/components/table/table.css +187 -37
- package/src/components/table/table.js +718 -73
- package/src/components/tag-picker/field-adapter.js +2 -1
- package/src/components/tag-picker/tag-picker.css +83 -5
- package/src/components/tag-picker/tag-picker.js +25 -8
- package/src/components/timebox/timebox.css +22 -2
- package/src/components/toggle/toggle.css +12 -1
- package/src/components/toolbar/toolbar.js +2 -2
- package/src/components/tooltip/tooltip.css +4 -0
- package/src/components/tooltip/tooltip.js +44 -19
- package/src/components/tree/tree.css +26 -17
- package/src/components/tree/tree.js +31 -0
- package/src/components/value-list/value-list.css +3 -3
- package/src/components/view/record-view.css +1 -1
- package/src/components/view/record-view.js +178 -18
- package/src/components/view-settings/view-settings.css +285 -0
- package/src/components/view-settings/view-settings.js +1830 -0
- package/src/components/workspace-switcher/workspace-switcher.css +188 -0
- package/src/components/workspace-switcher/workspace-switcher.js +463 -0
- package/src/core/breakpoint.js +1 -1
- package/src/core/component.js +88 -17
- package/src/core/fontawesome.js +5 -1
- package/src/core/i18n.js +47 -4
- package/src/core/icons.js +204 -61
- package/src/core/keyboard.js +206 -11
- package/src/core/messages.js +427 -0
- package/src/core/motion.js +147 -0
- package/src/core/overlay-host.js +77 -3
- package/src/core/overlay-stack.js +487 -0
- package/src/core/position.js +22 -3
- package/src/index.js +10 -5
- package/src/internal/app-rail.js +135 -55
- package/src/internal/menu-items.js +61 -3
- package/src/zeyos/connect.js +6 -2
- package/src/zeyos/form.js +69 -27
- package/src/zeyos/index.js +4 -1
- package/src/zeyos/launcher.js +8 -3
- package/src/zeyos/module-bar.js +181 -0
- package/src/zeyos/query.js +197 -13
- package/src/zeyos/schema.js +144 -27
- package/src/zeyos/table.js +287 -84
- package/src/zeyos/view.js +49 -93
- package/styles/base.css +72 -1
- package/styles/icons.css +11 -1
- package/styles/print.css +1 -1
- package/styles/tokens/dark.css +117 -37
- package/styles/tokens/global.css +121 -27
- package/styles/tokens/semantic.css +140 -43
- package/styles/tokens/themes.css +7 -6
- package/styles/zx.css +3 -1
- package/styles/tokens/density.css +0 -9
package/docs/llms.txt
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> Xenon is a dependency-free design system for browser applications; Zx is its product-agnostic
|
|
4
4
|
> vanilla-JavaScript implementation. ES2022 modules, WAI-ARIA-accessible native
|
|
5
|
-
> controls, semantic `--zx-*` design tokens with light/dark
|
|
6
|
-
> components. Source modules run directly in the browser; a build step emits ESM, IIFE-global,
|
|
5
|
+
> controls, semantic `--zx-*` design tokens with light/dark theming and one token-driven geometry,
|
|
6
|
+
> and lifecycle-safe components. Source modules run directly in the browser; a build step emits ESM, IIFE-global,
|
|
7
7
|
> ZeyOS binding, and CSS bundles. Zero runtime dependencies.
|
|
8
8
|
|
|
9
9
|
Instantiate every component as `new Component(target, options)` where `target` is an Element, a
|
|
@@ -35,7 +35,9 @@ exports and as `window.zx.*`.
|
|
|
35
35
|
drag and resize, `optimistic: true|false`), backend-neutral nested `Filter`, simple client-side
|
|
36
36
|
`DataFilter`, `FilterPanel` (typed filter form built from a server's own field metadata;
|
|
37
37
|
`mode: 'draft'|'live'`, `serialize`), `ListToolbar` (search, live result count, view switch, and
|
|
38
|
-
tools whose badge is part of the accessible name), `
|
|
38
|
+
tools whose badge is part of the accessible name), `ViewSettings` (view-settings drawer content:
|
|
39
|
+
reorder, hide, and add fields, grouping, board axes; `bind()` to a record view),
|
|
40
|
+
`Pagination` (+ pure `paginationRange()`),
|
|
39
41
|
`SortControl` (searchable field selector with a direction toggle, one atomic `{id,dir}`),
|
|
40
42
|
`TreeView` (APG tree), `Finder` (Miller columns).
|
|
41
43
|
- Forms: `Form`, `Fieldset`, `Questionnaire` (guided one-question-at-a-time flow with branching), `Field` (type registry), `CodeEditor` (structured text in a native textarea; never executes or injects the value), `ValueList`, `MultiValueEditor`, `FieldUpload`.
|
|
@@ -47,7 +49,9 @@ exports and as `window.zx.*`.
|
|
|
47
49
|
canvas light; shared material roles govern chrome and overlays), `Avatar`, `Panel` (header/footer action
|
|
48
50
|
buttons), `MasterPanel`, `Tabbox`
|
|
49
51
|
(`variant: 'divided'|'bracket'|'line'|'segmented'`), `NavigationBar`, `AppSidebar` (expanded and
|
|
50
|
-
minimized vertical states plus horizontal rail layouts), `
|
|
52
|
+
minimized vertical states plus horizontal rail layouts; sections; a modules layout), `ModuleBar`
|
|
53
|
+
(colour-coded module strip or bar with flyout menus and an "All modules" catalogue), `AccountMenu`,
|
|
54
|
+
`WorkspaceSwitcher` (workspace tile, name and menu at the head of a navigation column), `Launcher`,
|
|
51
55
|
`spinner()`/`ProgressBar`/`InlineLoading`, `skeleton()`/`skeletonText()`/`skeletonTable()`,
|
|
52
56
|
`Toolbar` (APG toolbar with overflow menu), `SplitView`, `Dock`
|
|
53
57
|
(collapsible resizable pane stack; tab groups, regions, adopts a `Sheet`), `Stepper`,
|
|
@@ -56,7 +60,7 @@ exports and as `window.zx.*`.
|
|
|
56
60
|
- Helpers: `formatNumber`/`formatCurrency`/`formatPercent`/`formatFileSize`/`formatRelativeTime` (locale-aware, cached `Intl`), `debounce`/`throttle`, `groupBy`/`sortBy`/`uniqueBy`, `escapeRegExp`/`highlightMatch`, `storage()` (namespaced JSON, degrades to memory), `toCsv`/`downloadBlob`/`copyToClipboard`, `truncate()`/`isTruncated()` (line clamp with the full value on hover).
|
|
57
61
|
|
|
58
62
|
## Theming
|
|
59
|
-
Load `zx.css` once; set `data-zx-theme` (`light`|`dark`|`auto`)
|
|
63
|
+
Load `zx.css` once; set `data-zx-theme` (`light`|`dark`|`auto`) on any ancestor. There is one geometry: spacing steps are multiples of `--zx-space-unit` (4px), radius steps are fractions of `--zx-radius` (4px: 2/3/4/6px), and controls are `--zx-control-height` (30px) tall — override those to tighten or loosen an application. Define a product theme by overriding semantic `--zx-color-*` / `--zx-control-*` tokens under `[data-zx-theme="name"]`. The theme studio controls accent, neutral tint, spacing, font, type size, radius, control height, and CSS-first glass material. Compact menus, pickers, tooltips, column menus, and toasts share responsive compact glass; Launcher, Modal, Dialog, and floating Sheets share a calmer reading panel. Both preserve scoped theme inheritance and refract Aurora/page content; docked Sheets and dense data surfaces remain opaque. Never use tier-1 palette tokens or raw color literals in application CSS.
|
|
60
64
|
|
|
61
65
|
## Talking to ZeyOS
|
|
62
66
|
Use the dedicated ZeyOS client library **`@zeyos/client`** (`npm install @zeyos/client`) for ZeyOS business data: `createZeyosClient({ platform, auth })`, typed `client.api.list/get/create/update<Resource>` over the full OpenAPI surface, and `normalizeListResult`. Zx accepts the client by injection and does not bundle it. See the `website/layouts/zeyos-invoices.layout.js` layout for a Zx-components-on-@zeyos/client reference. Zx also ships a minimal `Http`/`zeyosService` for ad-hoc in-app `remotecall`, but `@zeyos/client` is the recommended default.
|
package/package.json
CHANGED
|
@@ -3,26 +3,50 @@
|
|
|
3
3
|
min-inline-size: 0;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/*
|
|
7
|
+
* The trigger is a flat row, not a button. MenuButton makes it a `.zx-btn.zx-menu-button`, and
|
|
8
|
+
* both of those stylesheets load after this one — `menu-button.css` centres it as an inline-flex
|
|
9
|
+
* pill and `button.css` gives it a border, a glass fill, a backdrop blur and a shadow. Every rule
|
|
10
|
+
* here is scoped under the root so it out-specifies them instead of depending on load order, and
|
|
11
|
+
* takes back each property they set rather than only the ones that were visible at the time.
|
|
12
|
+
*/
|
|
13
|
+
.zx-account-menu .zx-account-menu__trigger {
|
|
7
14
|
display: grid;
|
|
8
15
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
9
16
|
align-items: center;
|
|
10
|
-
|
|
17
|
+
justify-content: stretch;
|
|
18
|
+
gap: var(--zx-space-2);
|
|
11
19
|
inline-size: 100%;
|
|
12
20
|
min-block-size: calc(var(--zx-control-height) + var(--zx-space-2));
|
|
13
|
-
border
|
|
21
|
+
border: 0;
|
|
22
|
+
border-radius: var(--zx-radius-md);
|
|
14
23
|
background: transparent;
|
|
15
|
-
|
|
24
|
+
-webkit-backdrop-filter: none;
|
|
25
|
+
backdrop-filter: none;
|
|
26
|
+
box-shadow: none;
|
|
27
|
+
padding: var(--zx-space-1) var(--zx-space-2);
|
|
16
28
|
color: inherit;
|
|
29
|
+
font-weight: var(--zx-weight-medium);
|
|
30
|
+
line-height: 1.3;
|
|
17
31
|
text-align: start;
|
|
18
32
|
}
|
|
19
33
|
|
|
20
|
-
.zx-account-menu__trigger:hover,
|
|
21
|
-
.zx-account-menu__trigger:focus-visible,
|
|
22
|
-
.zx-account-menu__trigger[aria-expanded="true"] {
|
|
34
|
+
.zx-account-menu .zx-account-menu__trigger:is(:hover, [aria-expanded="true"]):not(:disabled) {
|
|
23
35
|
background: var(--zx-color-bg-hover);
|
|
24
36
|
}
|
|
25
37
|
|
|
38
|
+
.zx-account-menu .zx-account-menu__trigger:active:not(:disabled) {
|
|
39
|
+
background: var(--zx-color-bg-current);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.zx-account-menu .zx-account-menu__trigger:focus-visible {
|
|
43
|
+
box-shadow: var(--zx-focus-ring);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.zx-account-menu__chevron {
|
|
47
|
+
color: var(--zx-color-text-subtle);
|
|
48
|
+
}
|
|
49
|
+
|
|
26
50
|
.zx-account-menu__identity {
|
|
27
51
|
display: grid;
|
|
28
52
|
min-inline-size: 0;
|
|
@@ -36,8 +60,8 @@
|
|
|
36
60
|
white-space: nowrap;
|
|
37
61
|
}
|
|
38
62
|
|
|
39
|
-
.zx-account-menu__name { font-weight:
|
|
40
|
-
.zx-account-menu__secondary { color: var(--zx-color-text-muted); font-size: var(--zx-text-xs); }
|
|
63
|
+
.zx-account-menu__name { font-weight: var(--zx-weight-semibold); }
|
|
64
|
+
.zx-account-menu__secondary { color: var(--zx-color-text-muted); font-size: var(--zx-text-xs); font-weight: 400; }
|
|
41
65
|
.zx-account-menu__chevron { display: inline-flex; }
|
|
42
66
|
|
|
43
67
|
.zx-account-menu[data-compact="true"] {
|
|
@@ -48,6 +72,7 @@
|
|
|
48
72
|
display: inline-grid;
|
|
49
73
|
grid-template-columns: auto;
|
|
50
74
|
inline-size: auto;
|
|
75
|
+
padding: var(--zx-space-1);
|
|
51
76
|
}
|
|
52
77
|
|
|
53
78
|
.zx-account-menu[data-compact="true"] .zx-account-menu__identity,
|
|
@@ -61,7 +86,7 @@
|
|
|
61
86
|
gap: 2px;
|
|
62
87
|
margin: calc(-1 * var(--zx-space-1)) calc(-1 * var(--zx-space-1)) var(--zx-space-1);
|
|
63
88
|
border-block-end: 1px solid var(--zx-color-overlay-divider);
|
|
64
|
-
padding: var(--zx-space-3);
|
|
89
|
+
padding: var(--zx-space-2) var(--zx-space-3);
|
|
65
90
|
}
|
|
66
91
|
|
|
67
92
|
.zx-account-menu__panel-identity span {
|
|
@@ -124,6 +124,18 @@ export class AccountMenu extends Component {
|
|
|
124
124
|
return this;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Shows the avatar alone or the avatar with the name and secondary line — for a navigation
|
|
129
|
+
* column that narrows to icons and widens again. The trigger keeps its full accessible name
|
|
130
|
+
* either way.
|
|
131
|
+
* @param {boolean} compact
|
|
132
|
+
* @returns {this}
|
|
133
|
+
*/
|
|
134
|
+
setCompact(compact) {
|
|
135
|
+
this.el.dataset.compact = String(Boolean(compact));
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
|
|
127
139
|
/** Replaces account actions. @param {MenuButtonItem[]} items @returns {this} */
|
|
128
140
|
setItems(items) {
|
|
129
141
|
this._menu.setItems(items);
|
|
@@ -200,6 +200,9 @@ export class ActivityItem extends Component {
|
|
|
200
200
|
headingChildren.push(h('span', { class: 'zx-activity-item__actor' }, actor));
|
|
201
201
|
}
|
|
202
202
|
if (title !== null) {
|
|
203
|
+
// The flex gap separates the two on screen; the space separates them in the heading's text,
|
|
204
|
+
// which is what a screen reader reads ("Jonas Weber set…", not "Jonas Weberset…").
|
|
205
|
+
if (headingChildren.length) headingChildren.push(' ');
|
|
203
206
|
headingChildren.push(h('span', { class: 'zx-activity-item__title' }, title));
|
|
204
207
|
}
|
|
205
208
|
const heading = headingChildren.length ? h(`h${normalizeHeadingLevel(activity.headingLevel)}`, {
|
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
margin: 0;
|
|
16
16
|
color: var(--zx-color-text-muted);
|
|
17
17
|
font-size: var(--zx-text-sm);
|
|
18
|
-
font-weight:
|
|
19
|
-
letter-spacing: 0.02em;
|
|
18
|
+
font-weight: var(--zx-weight-semibold);
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
.zx-activity-list__items {
|
|
@@ -28,7 +27,7 @@
|
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
.zx-activity-list__items > .zx-activity-item:not(:last-child) {
|
|
31
|
-
border-block-end: 1px solid var(--zx-color-border);
|
|
30
|
+
border-block-end: 1px solid var(--zx-color-border-subtle);
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
.zx-activity-list__empty,
|
|
@@ -62,3 +61,39 @@
|
|
|
62
61
|
.zx-activity-list__skeleton > .zx-skeleton-text {
|
|
63
62
|
padding-block-start: var(--zx-space-1);
|
|
64
63
|
}
|
|
64
|
+
|
|
65
|
+
/* ------------------------------------------------------------------ compact -- */
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* `variant: 'compact'` — one line per entry at any width, for a summary card narrower than 30rem:
|
|
69
|
+
* who and what read as one sentence that wraps as text, the time keeps the trailing end of the
|
|
70
|
+
* first line instead of stacking under it, and metadata follows as a muted second line. These
|
|
71
|
+
* rules outrank the entry's own narrow-container layout, which would stack the header.
|
|
72
|
+
*/
|
|
73
|
+
.zx-activity-list[data-variant="compact"] .zx-activity-item__article {
|
|
74
|
+
gap: var(--zx-space-2);
|
|
75
|
+
padding-block: var(--zx-space-2);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.zx-activity-list[data-variant="compact"] .zx-activity-item__main {
|
|
79
|
+
gap: calc(var(--zx-space-1) / 2);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.zx-activity-list[data-variant="compact"] .zx-activity-item__header {
|
|
83
|
+
display: flex;
|
|
84
|
+
flex-wrap: nowrap;
|
|
85
|
+
align-items: baseline;
|
|
86
|
+
justify-content: flex-start;
|
|
87
|
+
gap: var(--zx-space-2);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.zx-activity-list[data-variant="compact"] .zx-activity-item__heading {
|
|
91
|
+
display: block;
|
|
92
|
+
flex: 1 1 auto;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* The state badge sits on the first line with the time, not centred against a wrapped sentence. */
|
|
96
|
+
.zx-activity-list[data-variant="compact"] .zx-activity-item__state {
|
|
97
|
+
align-self: baseline;
|
|
98
|
+
margin-inline-start: 0;
|
|
99
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import { Component } from '../../core/component.js';
|
|
3
3
|
import { h, restoreTarget, snapshotTarget } from '../../core/dom.js';
|
|
4
|
+
import { captureItemFocus, restoreItemFocus } from '../../core/keyboard.js';
|
|
4
5
|
import { uid } from '../../core/util.js';
|
|
5
6
|
import { skeleton, skeletonText } from '../skeleton/skeleton.js';
|
|
6
7
|
import { ActivityItem } from '../activity-item/activity-item.js';
|
|
@@ -9,6 +10,13 @@ import { ActivityItem } from '../activity-item/activity-item.js';
|
|
|
9
10
|
/** @typedef {string|number|Node|Component|null} ActivityListContent */
|
|
10
11
|
/** @typedef {string|((item:ActivityItemOptions,index:number)=>unknown)|null} ActivityGroupReader */
|
|
11
12
|
/** @typedef {(group:unknown,items:ActivityItemOptions[])=>ActivityListContent} ActivityGroupLabel */
|
|
13
|
+
/**
|
|
14
|
+
* Entry layout. `'default'` gives each entry its avatar, a heading row, and room for content;
|
|
15
|
+
* below 30rem the time moves under the heading. `'compact'` is one line per entry at any width —
|
|
16
|
+
* actor and summary reading as one sentence, the time at the trailing end, a small avatar, and
|
|
17
|
+
* metadata as a muted second line — for a summary card narrower than 30rem.
|
|
18
|
+
* @typedef {'default'|'compact'} ActivityListVariant
|
|
19
|
+
*/
|
|
12
20
|
|
|
13
21
|
/**
|
|
14
22
|
* @typedef {Object} ActivityGroup
|
|
@@ -22,6 +30,8 @@ import { ActivityItem } from '../activity-item/activity-item.js';
|
|
|
22
30
|
* @property {ActivityGroupReader} [groupBy=null] Property or resolver used for stable grouping.
|
|
23
31
|
* @property {ActivityGroupLabel|null} [groupLabel=null] Group-heading renderer.
|
|
24
32
|
* @property {string} [label='Activity'] Accessible collection name.
|
|
33
|
+
* @property {ActivityListVariant} [variant='default'] Entry layout, reflected as `data-variant` on
|
|
34
|
+
* the root. `'compact'` also makes an avatar descriptor without a `size` small.
|
|
25
35
|
* @property {boolean} [loading=false] Whether initial or incremental activity is loading.
|
|
26
36
|
* @property {number} [loadingCount=3] Placeholder rows shown for an empty loading list.
|
|
27
37
|
* @property {string} [loadingText='Loading activity'] Loading status text.
|
|
@@ -48,6 +58,7 @@ export class ActivityList extends Component {
|
|
|
48
58
|
groupBy: null,
|
|
49
59
|
groupLabel: null,
|
|
50
60
|
label: 'Activity',
|
|
61
|
+
variant: 'default',
|
|
51
62
|
loading: false,
|
|
52
63
|
loadingCount: 3,
|
|
53
64
|
loadingText: 'Loading activity',
|
|
@@ -78,6 +89,8 @@ export class ActivityList extends Component {
|
|
|
78
89
|
this._itemComponents = [];
|
|
79
90
|
this._groupSequence = 0;
|
|
80
91
|
root.setAttribute('aria-label', String(this.options.label || 'Activity'));
|
|
92
|
+
this._variant = normalizeActivityListVariant(this.options.variant);
|
|
93
|
+
if (this._variant === 'compact') root.dataset.variant = 'compact';
|
|
81
94
|
try {
|
|
82
95
|
this._renderList();
|
|
83
96
|
return root;
|
|
@@ -196,8 +209,33 @@ export class ActivityList extends Component {
|
|
|
196
209
|
if (!this._createdRoot) restoreTarget(this.el, this._original);
|
|
197
210
|
}
|
|
198
211
|
|
|
199
|
-
/**
|
|
212
|
+
/**
|
|
213
|
+
* Rebuilds the list. Items are regrouped on every mutation, so the rows are new; focus that sat
|
|
214
|
+
* on an item's control is put back on the same control of the same activity (matched by `id`,
|
|
215
|
+
* else by position), so a live feed prepending entries does not throw the reader to `<body>`.
|
|
216
|
+
* @private
|
|
217
|
+
* @returns {void}
|
|
218
|
+
*/
|
|
200
219
|
_renderList() {
|
|
220
|
+
const root = /** @type {HTMLElement} */ (this.el);
|
|
221
|
+
const focus = captureItemFocus(root, (element) => {
|
|
222
|
+
const index = this._itemComponents.findIndex((item) => item.el?.contains(element));
|
|
223
|
+
if (index < 0) return null;
|
|
224
|
+
const component = this._itemComponents[index];
|
|
225
|
+
return { key: { id: component.getActivity().id ?? null, index }, element: component.el };
|
|
226
|
+
});
|
|
227
|
+
this._renderItems();
|
|
228
|
+
restoreItemFocus(root, focus, (key) => {
|
|
229
|
+
const memo = /** @type {{id: unknown, index: number}} */ (key);
|
|
230
|
+
const match = memo.id === null ? undefined
|
|
231
|
+
: this._itemComponents.find((item) => Object.is(item.getActivity().id ?? null, memo.id));
|
|
232
|
+
const fallback = match ?? this._itemComponents[Math.min(memo.index, this._itemComponents.length - 1)];
|
|
233
|
+
return fallback?.el ?? null;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** @private @returns {void} */
|
|
238
|
+
_renderItems() {
|
|
201
239
|
this._destroyItems();
|
|
202
240
|
this._groupSequence = 0;
|
|
203
241
|
const root = /** @type {HTMLElement} */ (this.el);
|
|
@@ -229,6 +267,7 @@ export class ActivityList extends Component {
|
|
|
229
267
|
for (const item of group.items) {
|
|
230
268
|
const component = new ActivityItem(null, {
|
|
231
269
|
...item,
|
|
270
|
+
avatar: this._variant === 'compact' ? compactAvatar(item.avatar) : item.avatar,
|
|
232
271
|
headingLevel: item.headingLevel ?? this.options.itemHeadingLevel
|
|
233
272
|
});
|
|
234
273
|
this._itemComponents.push(component);
|
|
@@ -362,6 +401,27 @@ function normalizeHeadingLevel(value, fallback) {
|
|
|
362
401
|
return /** @type {1|2|3|4|5|6} */ (level >= 1 && level <= 6 ? level : fallback);
|
|
363
402
|
}
|
|
364
403
|
|
|
404
|
+
/**
|
|
405
|
+
* Resolves the entry layout; anything but `'compact'` is the default layout.
|
|
406
|
+
* @param {unknown} value Requested variant.
|
|
407
|
+
* @returns {ActivityListVariant}
|
|
408
|
+
*/
|
|
409
|
+
export function normalizeActivityListVariant(value) {
|
|
410
|
+
return value === 'compact' ? 'compact' : 'default';
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* A compact entry is one line tall, so an avatar descriptor that leaves its size open gets the
|
|
415
|
+
* small one. An explicit size, an Element, or no avatar passes through untouched.
|
|
416
|
+
* @param {ActivityItemOptions['avatar']} avatar Avatar descriptor, Element, or null.
|
|
417
|
+
* @returns {ActivityItemOptions['avatar']}
|
|
418
|
+
*/
|
|
419
|
+
function compactAvatar(avatar) {
|
|
420
|
+
if (!isPlainObject(avatar)) return avatar;
|
|
421
|
+
const descriptor = /** @type {import('../avatar/avatar.js').AvatarOptions} */ (avatar);
|
|
422
|
+
return descriptor.size == null ? { ...descriptor, size: 'sm' } : descriptor;
|
|
423
|
+
}
|
|
424
|
+
|
|
365
425
|
/** @param {unknown} value @returns {number} */
|
|
366
426
|
function normalizeLoadingCount(value) {
|
|
367
427
|
const count = Math.trunc(Number(value));
|
|
@@ -26,6 +26,17 @@
|
|
|
26
26
|
border-radius: var(--zx-radius-full);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/* A tone is identity from the theme's categorical palette. An inline `color` overrides it. */
|
|
30
|
+
.zx-app-icon[data-tone="blue"] { --zx-app-icon-color: var(--zx-color-tone-blue-solid); }
|
|
31
|
+
.zx-app-icon[data-tone="teal"] { --zx-app-icon-color: var(--zx-color-tone-teal-solid); }
|
|
32
|
+
.zx-app-icon[data-tone="green"] { --zx-app-icon-color: var(--zx-color-tone-green-solid); }
|
|
33
|
+
.zx-app-icon[data-tone="amber"] { --zx-app-icon-color: var(--zx-color-tone-amber-solid); }
|
|
34
|
+
.zx-app-icon[data-tone="orange"] { --zx-app-icon-color: var(--zx-color-tone-orange-solid); }
|
|
35
|
+
.zx-app-icon[data-tone="red"] { --zx-app-icon-color: var(--zx-color-tone-red-solid); }
|
|
36
|
+
.zx-app-icon[data-tone="pink"] { --zx-app-icon-color: var(--zx-color-tone-pink-solid); }
|
|
37
|
+
.zx-app-icon[data-tone="violet"] { --zx-app-icon-color: var(--zx-color-tone-violet-solid); }
|
|
38
|
+
.zx-app-icon[data-tone="slate"] { --zx-app-icon-color: var(--zx-color-tone-slate-solid); }
|
|
39
|
+
|
|
29
40
|
.zx-app-icon__surface {
|
|
30
41
|
position: absolute;
|
|
31
42
|
z-index: 0;
|
|
@@ -62,7 +73,7 @@
|
|
|
62
73
|
.zx-app-icon[data-glass="none"] .zx-app-icon__surface::after { display: none; }
|
|
63
74
|
|
|
64
75
|
.zx-app-icon[data-selected] .zx-app-icon__surface {
|
|
65
|
-
outline:
|
|
76
|
+
outline-color: transparent;
|
|
66
77
|
box-shadow: 0 0 0 1px var(--zx-color-bg-page),
|
|
67
78
|
0 0 0 3px var(--zx-app-icon-selected-ring),
|
|
68
79
|
var(--zx-app-icon-shadow-hover),
|
|
@@ -153,7 +164,7 @@
|
|
|
153
164
|
background: var(--zx-color-danger);
|
|
154
165
|
color: var(--zx-color-text-invert);
|
|
155
166
|
font-size: 9px;
|
|
156
|
-
font-weight:
|
|
167
|
+
font-weight: var(--zx-weight-semibold);
|
|
157
168
|
line-height: 1;
|
|
158
169
|
}
|
|
159
170
|
|
|
@@ -166,7 +177,7 @@
|
|
|
166
177
|
|
|
167
178
|
.zx-app-icon:is(a, button, [role="button"]):not(:disabled):not([aria-disabled="true"]):focus-visible .zx-app-icon__surface,
|
|
168
179
|
:is(a, button, [role="button"]):not(:disabled):not([aria-disabled="true"]):focus-visible .zx-app-icon__surface {
|
|
169
|
-
outline:
|
|
180
|
+
outline-color: transparent;
|
|
170
181
|
box-shadow: var(--zx-focus-ring), var(--zx-app-icon-shadow-hover), var(--zx-app-icon-halo);
|
|
171
182
|
}
|
|
172
183
|
|
|
@@ -3,10 +3,16 @@ import { h, restoreTarget, snapshotTarget } from '../../core/dom.js';
|
|
|
3
3
|
import { icon as renderIcon } from '../../core/icons.js';
|
|
4
4
|
import { isCssColor } from '../../core/util.js';
|
|
5
5
|
|
|
6
|
+
/** @typedef {'blue'|'teal'|'green'|'amber'|'orange'|'red'|'pink'|'violet'|'slate'} AppIconTone */
|
|
7
|
+
/** The categorical tones a `tone` option accepts. @type {ReadonlyArray<AppIconTone>} */
|
|
8
|
+
export const APP_ICON_TONES = Object.freeze(['blue', 'teal', 'green', 'amber', 'orange', 'red', 'pink', 'violet', 'slate']);
|
|
9
|
+
|
|
6
10
|
/**
|
|
7
11
|
* @typedef {Object} AppIconOptions
|
|
8
12
|
* @property {string|Node|(()=>string|Node)|null} [icon=null] Icon name, node, or lazy renderer.
|
|
9
|
-
* @property {string|null} [color=null] Identity colour
|
|
13
|
+
* @property {string|null} [color=null] Identity colour, any CSS colour; takes precedence over `tone`.
|
|
14
|
+
* @property {AppIconTone|null} [tone=null] Categorical identity colour from the theme's tone palette,
|
|
15
|
+
* drawn with its `--zx-color-tone-<tone>-solid` fill. Without `color` or `tone` the icon takes the accent.
|
|
10
16
|
* @property {number|string} [size=36] Edge length in pixels or any CSS length.
|
|
11
17
|
* @property {number|string} [iconSize='52%'] Glyph size.
|
|
12
18
|
* @property {string|null} [label=null] Accessible image label; null makes the icon decorative.
|
|
@@ -28,6 +34,7 @@ export class AppIcon extends Component {
|
|
|
28
34
|
static defaults = {
|
|
29
35
|
icon: null,
|
|
30
36
|
color: null,
|
|
37
|
+
tone: null,
|
|
31
38
|
size: 36,
|
|
32
39
|
iconSize: '52%',
|
|
33
40
|
label: null,
|
|
@@ -80,6 +87,9 @@ export class AppIcon extends Component {
|
|
|
80
87
|
this.el.style.setProperty('--zx-app-icon-glyph-size', glyphSize);
|
|
81
88
|
if (isCssColor(state.color)) this.el.style.setProperty('--zx-app-icon-color', String(state.color));
|
|
82
89
|
else this.el.style.removeProperty('--zx-app-icon-color');
|
|
90
|
+
// An inline colour outranks the tone rule in the stylesheet, which is what makes `color` win.
|
|
91
|
+
if (APP_ICON_TONES.includes(state.tone)) this.el.dataset.tone = state.tone;
|
|
92
|
+
else delete this.el.dataset.tone;
|
|
83
93
|
this.el.dataset.glass = normalizeGlass(state.glass);
|
|
84
94
|
this.el.dataset.shape = normalizeShape(state.shape);
|
|
85
95
|
this.el.toggleAttribute('data-selected', Boolean(state.selected));
|
|
@@ -34,6 +34,16 @@
|
|
|
34
34
|
.zx-app-rail[data-orientation="horizontal"] .zx-app-rail__list { flex-direction: row; }
|
|
35
35
|
.zx-app-rail__row { display: flex; }
|
|
36
36
|
|
|
37
|
+
/* Where a section of the tree ends on a rail: a short rule, since a rail has no room for a heading. */
|
|
38
|
+
.zx-app-rail__separator {
|
|
39
|
+
flex: none;
|
|
40
|
+
align-self: center;
|
|
41
|
+
background: var(--zx-color-border-subtle);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.zx-app-rail[data-orientation="vertical"] .zx-app-rail__separator { inline-size: var(--zx-space-4); block-size: 1px; margin-block: var(--zx-space-1); }
|
|
45
|
+
.zx-app-rail[data-orientation="horizontal"] .zx-app-rail__separator { inline-size: 1px; block-size: var(--zx-space-4); margin-inline: var(--zx-space-1); }
|
|
46
|
+
|
|
37
47
|
.zx-app-rail__item {
|
|
38
48
|
position: relative;
|
|
39
49
|
display: inline-grid;
|
|
@@ -48,13 +58,19 @@
|
|
|
48
58
|
text-decoration: none;
|
|
49
59
|
}
|
|
50
60
|
|
|
61
|
+
/* Hover, an open flyout, and a branch over the active route are one light step; the current page
|
|
62
|
+
is the neutral current fill. Neither is the accent: where you are is not something you picked. */
|
|
51
63
|
.zx-app-rail__item:hover,
|
|
52
64
|
.zx-app-rail__item:focus-visible,
|
|
53
65
|
.zx-app-rail__item[aria-expanded="true"],
|
|
54
|
-
.zx-app-rail__item[data-active-descendant="true"]
|
|
55
|
-
.zx-app-rail__item[aria-current="page"] {
|
|
66
|
+
.zx-app-rail__item[data-active-descendant="true"] {
|
|
56
67
|
background: var(--zx-color-bg-hover);
|
|
57
|
-
color: var(--zx-color-
|
|
68
|
+
color: var(--zx-color-text);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.zx-app-rail__item[aria-current="page"] {
|
|
72
|
+
background: var(--zx-color-bg-current);
|
|
73
|
+
color: var(--zx-color-text);
|
|
58
74
|
}
|
|
59
75
|
|
|
60
76
|
.zx-app-rail__item[aria-disabled="true"] {
|
|
@@ -86,44 +102,53 @@
|
|
|
86
102
|
* A minimized sidebar is the same navigation as an expanded one, so the panel a rail item opens
|
|
87
103
|
* has to read as a slice of the tree rather than as a menu that happens to hang off it: the same
|
|
88
104
|
* icon gutter, the same item height, labels that wrap instead of truncating, and the same active
|
|
89
|
-
* recipe —
|
|
105
|
+
* recipe — the neutral current fill and a heavier label.
|
|
90
106
|
*
|
|
91
107
|
* The panel is portaled to the overlay host, so `.zx-app-sidebar` descendant selectors and the
|
|
92
108
|
* `--zx-app-sidebar-*` scale declared on that root cannot reach it. The scale is re-declared here
|
|
93
|
-
* against the same
|
|
94
|
-
* move together when a token moves rather than agreeing on a number frozen twice.
|
|
109
|
+
* against the same tier-2 control and spacing tokens the sidebar derives it from, so the two
|
|
110
|
+
* surfaces move together when a token moves rather than agreeing on a number frozen twice.
|
|
95
111
|
*/
|
|
96
112
|
.zx-app-rail__popover {
|
|
97
113
|
--zx-app-rail-gutter: var(--zx-space-2);
|
|
98
114
|
--zx-app-rail-icon: var(--zx-space-6);
|
|
99
|
-
--zx-app-rail-item-gap: var(--zx-space-
|
|
100
|
-
--zx-app-rail-item-height:
|
|
101
|
-
--zx-app-rail-pad-x: var(--zx-space-
|
|
102
|
-
--zx-app-rail-row-gap:
|
|
103
|
-
--zx-app-rail-radius: var(--zx-radius-
|
|
104
|
-
--zx-app-rail-
|
|
105
|
-
--zx-app-rail-
|
|
106
|
-
--zx-app-rail-press: color-mix(in srgb, var(--zx-color-text)
|
|
115
|
+
--zx-app-rail-item-gap: var(--zx-space-2);
|
|
116
|
+
--zx-app-rail-item-height: var(--zx-control-height);
|
|
117
|
+
--zx-app-rail-pad-x: var(--zx-space-2);
|
|
118
|
+
--zx-app-rail-row-gap: 2px;
|
|
119
|
+
--zx-app-rail-radius: var(--zx-radius-md);
|
|
120
|
+
--zx-app-rail-hover: var(--zx-color-bg-hover);
|
|
121
|
+
--zx-app-rail-current: var(--zx-color-bg-current);
|
|
122
|
+
--zx-app-rail-press: color-mix(in srgb, var(--zx-color-text) 12%, transparent);
|
|
107
123
|
min-inline-size: 240px;
|
|
108
124
|
}
|
|
109
125
|
|
|
110
126
|
/* `styles/zx.css` imports this file before `dropdown.css`, so the two properties the flyout has
|
|
111
|
-
to take back from the generic overlay surface — the padding, which is the
|
|
112
|
-
|
|
127
|
+
to take back from the generic overlay surface — the padding, which is the tree's own gutter,
|
|
128
|
+
and the width the panel may grow to, which is the tree's own 280px — have to
|
|
113
129
|
out-specify it instead of merely following it. The viewport clamp the dropdown guarantees is
|
|
114
130
|
carried over rather than dropped. */
|
|
115
131
|
.zx-dropdown.zx-app-rail__popover {
|
|
116
132
|
max-inline-size: min(280px, calc(100vw - (2 * var(--zx-space-3))));
|
|
117
|
-
padding: var(--zx-space-
|
|
133
|
+
padding: var(--zx-space-1) var(--zx-app-rail-gutter);
|
|
118
134
|
}
|
|
119
135
|
|
|
120
136
|
/* The branch's own label, naming what the panel is a slice of. It aligns with the row padding
|
|
121
137
|
rather than with the labels under it: a caption for the list, not a row in it. */
|
|
122
138
|
.zx-app-rail__flyout-title {
|
|
123
|
-
padding: var(--zx-space-1) var(--zx-app-rail-pad-x)
|
|
139
|
+
padding: var(--zx-space-1) var(--zx-app-rail-pad-x);
|
|
124
140
|
color: var(--zx-color-text-muted);
|
|
125
141
|
font-size: var(--zx-text-xs);
|
|
126
|
-
font-weight:
|
|
142
|
+
font-weight: var(--zx-weight-semibold);
|
|
143
|
+
line-height: 1.3;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* A section inside a flyout keeps its heading, in the same quiet caption as the panel's title. */
|
|
147
|
+
.zx-app-rail__flyout-section {
|
|
148
|
+
padding: var(--zx-space-2) var(--zx-app-rail-pad-x) var(--zx-space-1);
|
|
149
|
+
color: var(--zx-color-text-muted);
|
|
150
|
+
font-size: var(--zx-text-xs);
|
|
151
|
+
font-weight: var(--zx-weight-semibold);
|
|
127
152
|
line-height: 1.3;
|
|
128
153
|
}
|
|
129
154
|
|
|
@@ -152,7 +177,9 @@
|
|
|
152
177
|
border: 0;
|
|
153
178
|
border-radius: var(--zx-app-rail-radius);
|
|
154
179
|
background: transparent;
|
|
155
|
-
|
|
180
|
+
/* The icon column fills the row exactly; a row without one keeps a space-1 margin round its
|
|
181
|
+
label. Either way a single-line row is one control tall, and a wrapped one grows. */
|
|
182
|
+
padding-block: min(var(--zx-space-1), (var(--zx-app-rail-item-height) - var(--zx-app-rail-icon)) / 2);
|
|
156
183
|
padding-inline: var(--zx-app-rail-pad-x);
|
|
157
184
|
color: var(--zx-color-text);
|
|
158
185
|
font-size: var(--zx-text-md);
|
|
@@ -166,39 +193,6 @@
|
|
|
166
193
|
.zx-app-rail__flyout-item { min-block-size: 40px; }
|
|
167
194
|
}
|
|
168
195
|
|
|
169
|
-
/* The marker, docked to the panel's own leading edge exactly as the tree's is docked to the
|
|
170
|
-
sidebar's: it sits in the panel's gutter, so every row lines up however deep the flyout chain
|
|
171
|
-
runs. On a right-hand rail the panel opens toward the workspace and the marker moves to the
|
|
172
|
-
edge facing the screen, which is the rule `.zx-app-sidebar[data-side="right"]` states. */
|
|
173
|
-
.zx-app-rail__flyout-item::before {
|
|
174
|
-
content: "";
|
|
175
|
-
position: absolute;
|
|
176
|
-
inset-block: 50%;
|
|
177
|
-
inset-inline-start: calc(-1 * var(--zx-app-rail-gutter));
|
|
178
|
-
inline-size: var(--zx-app-rail-marker);
|
|
179
|
-
border-start-end-radius: var(--zx-radius-full);
|
|
180
|
-
border-end-end-radius: var(--zx-radius-full);
|
|
181
|
-
background: var(--zx-color-accent);
|
|
182
|
-
opacity: 0;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.zx-app-rail__popover[data-side="right"] .zx-app-rail__flyout-item::before {
|
|
186
|
-
inset-inline: auto calc(-1 * var(--zx-app-rail-gutter));
|
|
187
|
-
border-radius: 0;
|
|
188
|
-
border-start-start-radius: var(--zx-radius-full);
|
|
189
|
-
border-end-start-radius: var(--zx-radius-full);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.zx-app-rail__flyout-item[aria-current="page"]::before {
|
|
193
|
-
inset-block: 25%;
|
|
194
|
-
opacity: 1;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
.zx-app-rail__flyout-item[data-active-descendant="true"][aria-expanded="false"]::before {
|
|
198
|
-
inset-block: 38%;
|
|
199
|
-
opacity: .55;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
196
|
.zx-app-rail__flyout-item-icon {
|
|
203
197
|
grid-column: 1;
|
|
204
198
|
display: flex;
|
|
@@ -247,34 +241,28 @@
|
|
|
247
241
|
}
|
|
248
242
|
|
|
249
243
|
.zx-app-rail__flyout-item[data-active-descendant="true"][aria-expanded="false"] {
|
|
250
|
-
|
|
251
|
-
font-weight: 600;
|
|
244
|
+
font-weight: var(--zx-weight-semibold);
|
|
252
245
|
}
|
|
253
246
|
|
|
254
|
-
.zx-app-rail__flyout-item[aria-
|
|
255
|
-
|
|
256
|
-
color: var(--zx-color-on-accent);
|
|
257
|
-
font-weight: 600;
|
|
247
|
+
.zx-app-rail__flyout-item[data-active-descendant="true"][aria-expanded="false"] .zx-app-rail__flyout-item-icon {
|
|
248
|
+
color: var(--zx-color-text);
|
|
258
249
|
}
|
|
259
250
|
|
|
260
|
-
|
|
261
|
-
|
|
251
|
+
/* The current page, as in the tree: the neutral current fill, the full text colour and a heavier
|
|
252
|
+
label, with the shared focus ring drawn on top when a keyboard user arrives on it. */
|
|
253
|
+
.zx-app-rail__flyout-item[aria-current="page"] {
|
|
254
|
+
background: var(--zx-app-rail-current);
|
|
255
|
+
color: var(--zx-color-text);
|
|
256
|
+
font-weight: var(--zx-weight-semibold);
|
|
262
257
|
}
|
|
263
258
|
|
|
264
259
|
.zx-app-rail__flyout-item[aria-current="page"]:active {
|
|
265
|
-
background:
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/* An accent glow around an accent fill is a ring nobody can see, so the pill draws an inner line
|
|
269
|
-
in its own text colour first — the same repair the tree makes. */
|
|
270
|
-
.zx-app-rail__flyout-item[aria-current="page"]:focus-visible {
|
|
271
|
-
box-shadow: inset 0 0 0 2px var(--zx-color-on-accent), var(--zx-focus-ring);
|
|
260
|
+
background: var(--zx-app-rail-press);
|
|
272
261
|
}
|
|
273
262
|
|
|
274
263
|
.zx-app-rail__flyout-item[aria-current="page"] .zx-app-rail__flyout-item-icon,
|
|
275
|
-
.zx-app-rail__flyout-item[aria-current="page"] .zx-app-rail__flyout-item-badge,
|
|
276
264
|
.zx-app-rail__flyout-item[aria-current="page"] .zx-app-rail__flyout-item-disclosure {
|
|
277
|
-
color:
|
|
265
|
+
color: var(--zx-color-text);
|
|
278
266
|
}
|
|
279
267
|
|
|
280
268
|
/* Unavailable, and still readable: the icon carries the dimming so the label can keep the muted
|
|
@@ -296,23 +284,15 @@
|
|
|
296
284
|
box-shadow var(--zx-dur-fast) var(--zx-ease);
|
|
297
285
|
}
|
|
298
286
|
|
|
299
|
-
.zx-app-rail__flyout-item::before {
|
|
300
|
-
transition: opacity var(--zx-dur-base) var(--zx-ease),
|
|
301
|
-
inset-block var(--zx-dur-base) var(--zx-ease);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
287
|
.zx-app-rail__flyout-item:active { transform: scale(.985); }
|
|
305
288
|
}
|
|
306
289
|
|
|
307
|
-
/* Forced colours drop the fill
|
|
308
|
-
state there instead. */
|
|
290
|
+
/* Forced colours drop the fill; the system's selected pair carries the state there instead. */
|
|
309
291
|
@media (forced-colors: active) {
|
|
310
292
|
.zx-app-rail__flyout-item[aria-current="page"] {
|
|
311
293
|
background: Highlight;
|
|
312
294
|
color: HighlightText;
|
|
313
295
|
}
|
|
314
|
-
|
|
315
|
-
.zx-app-rail__flyout-item::before { background: Highlight; }
|
|
316
296
|
}
|
|
317
297
|
|
|
318
298
|
@media (prefers-reduced-transparency: reduce) {
|