@zeyos/zx 2.3.0 → 4.0.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 +138 -0
- package/DESIGN-SYSTEM.md +96 -0
- package/README.md +29 -39
- package/dist/types/components/account-menu/account-menu.d.ts +144 -0
- package/dist/types/components/app-sidebar/app-sidebar.d.ts +243 -0
- package/dist/types/components/avatar/avatar.d.ts +100 -0
- package/dist/types/components/card/card.d.ts +128 -0
- package/dist/types/components/chart/chart.d.ts +322 -0
- package/dist/types/components/date-picker/month-picker.d.ts +6 -0
- package/dist/types/components/dialog/dialog.d.ts +9 -0
- package/dist/types/components/dock/dock.d.ts +495 -0
- package/dist/types/components/dropdown/dropdown.d.ts +2 -2
- package/dist/types/components/field/field.d.ts +14 -0
- package/dist/types/components/field-upload/field-upload.d.ts +37 -4
- package/dist/types/components/form/form.d.ts +25 -3
- package/dist/types/components/grid/grid.d.ts +172 -0
- package/dist/types/components/launcher/launcher.d.ts +359 -0
- package/dist/types/components/layout/layout.d.ts +4 -4
- package/dist/types/components/menu-button/menu-button.d.ts +10 -2
- package/dist/types/components/modal/modal.d.ts +25 -0
- package/dist/types/components/multi-value-editor/multi-value-editor.d.ts +7 -1
- package/dist/types/components/questionnaire/items.d.ts +327 -0
- package/dist/types/components/questionnaire/questionnaire.d.ts +449 -0
- package/dist/types/components/select/select.d.ts +51 -0
- package/dist/types/components/sheet/sheet-stack.d.ts +167 -0
- package/dist/types/components/sheet/sheet.d.ts +294 -0
- package/dist/types/components/split-view/split-view.d.ts +2 -21
- package/dist/types/components/table/table.d.ts +223 -15
- package/dist/types/components/tooltip/tooltip.d.ts +2 -2
- package/dist/types/components/value-list/value-list.d.ts +24 -3
- package/dist/types/core/dom.d.ts +122 -0
- package/dist/types/core/drag-axis.d.ts +111 -0
- package/dist/types/core/position.d.ts +4 -2
- package/dist/types/index.d.ts +13 -2
- package/dist/types/internal/app-rail.d.ts +265 -0
- package/dist/types/zeyos/icons.d.ts +5 -0
- package/dist/types/zeyos/index.d.ts +2 -1
- package/dist/types/zeyos/launcher.d.ts +147 -0
- package/dist/types/zeyos/select.d.ts +39 -0
- package/dist/zx-zeyos.esm.js +1870 -133
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +2006 -267
- package/dist/zx.esm.js +8987 -2757
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +8987 -2757
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +4396 -2344
- package/docs/llms.md +463 -86
- package/docs/llms.txt +32 -19
- package/package.json +10 -17
- package/src/components/account-menu/account-menu.css +70 -0
- package/src/components/account-menu/account-menu.js +208 -0
- package/src/components/app-rail/app-rail.css +107 -0
- package/src/components/app-sidebar/app-sidebar.css +80 -0
- package/src/components/app-sidebar/app-sidebar.js +588 -0
- package/src/components/avatar/avatar.css +61 -0
- package/src/components/avatar/avatar.js +222 -0
- package/src/components/button/button.css +0 -1
- package/src/components/card/card.css +149 -0
- package/src/components/card/card.js +285 -0
- package/src/components/chart/chart.css +61 -0
- package/src/components/chart/chart.js +523 -0
- package/src/components/date-picker/month-picker.js +9 -0
- package/src/components/dialog/dialog.js +11 -4
- package/src/components/dock/dock.css +228 -0
- package/src/components/dock/dock.js +977 -0
- package/src/components/dropdown/dropdown.js +1 -1
- package/src/components/field/field.js +2 -0
- package/src/components/field-upload/field-upload.js +9 -4
- package/src/components/form/form.js +6 -3
- package/src/components/grid/grid.css +6 -0
- package/src/components/grid/grid.js +220 -0
- package/src/components/launcher/launcher.css +210 -0
- package/src/components/launcher/launcher.js +765 -0
- package/src/components/layout/layout.js +2 -2
- package/src/components/menu-button/menu-button.js +27 -2
- package/src/components/modal/modal.css +5 -1
- package/src/components/modal/modal.js +37 -2
- package/src/components/multi-value-editor/multi-value-editor.js +2 -1
- package/src/components/questionnaire/items.js +370 -0
- package/src/components/questionnaire/questionnaire.css +387 -0
- package/src/components/questionnaire/questionnaire.js +1033 -0
- package/src/components/select/field-adapter.js +9 -4
- package/src/components/select/select.css +144 -11
- package/src/components/select/select.js +259 -27
- package/src/components/sheet/sheet-stack.js +304 -0
- package/src/components/sheet/sheet.css +351 -0
- package/src/components/sheet/sheet.js +551 -0
- package/src/components/split-view/split-view.js +76 -161
- package/src/components/table/table.css +43 -0
- package/src/components/table/table.js +531 -52
- package/src/components/tooltip/tooltip.js +5 -2
- package/src/components/value-list/value-list.js +6 -3
- package/src/core/dom.js +322 -0
- package/src/core/drag-axis.js +203 -0
- package/src/core/position.js +68 -22
- package/src/index.js +20 -2
- package/src/internal/app-rail.js +718 -0
- package/src/zeyos/icons.js +8 -3
- package/src/zeyos/index.js +7 -1
- package/src/zeyos/launcher.js +338 -0
- package/src/zeyos/modules.js +1 -0
- package/src/zeyos/select.js +115 -1
- package/styles/tokens/dark.css +2 -3
- package/styles/tokens/global.css +6 -6
- package/styles/tokens/modules.css +1 -0
- package/styles/tokens/semantic.css +5 -3
- package/styles/tokens/themes.css +1 -1
- package/styles/zx.css +11 -1
- package/MIGRATION.md +0 -598
- package/dist/types/compat/base.d.ts +0 -156
- package/dist/types/compat/globals.d.ts +0 -20
- package/dist/types/compat/index.d.ts +0 -128
- package/dist/types/compat/map/basic.d.ts +0 -175
- package/dist/types/compat/map/data.d.ts +0 -102
- package/dist/types/compat/map/date-time.d.ts +0 -80
- package/dist/types/compat/map/factory.d.ts +0 -9
- package/dist/types/compat/map/form.d.ts +0 -98
- package/dist/types/compat/map/helpers.d.ts +0 -12
- package/dist/types/compat/map/http.d.ts +0 -36
- package/dist/types/compat/map/layout.d.ts +0 -106
- package/dist/types/compat/map/options.d.ts +0 -80
- package/dist/types/compat/map/select.d.ts +0 -95
- package/dist/types/compat/map/stubs.d.ts +0 -32
- package/dist/types/compat/map/utilities.d.ts +0 -37
- package/dist/types/compat-entry.d.ts +0 -5
- package/dist/types/components/permission/permission.d.ts +0 -96
- package/dist/zx-compat.esm.js +0 -17484
- package/dist/zx-compat.esm.min.js +0 -1
- package/dist/zx-compat.global.js +0 -23635
- package/dist/zx-compat.global.min.js +0 -3
- package/src/compat/base.js +0 -291
- package/src/compat/globals.js +0 -165
- package/src/compat/index.js +0 -84
- package/src/compat/map/basic.js +0 -464
- package/src/compat/map/data.js +0 -232
- package/src/compat/map/date-time.js +0 -177
- package/src/compat/map/factory.js +0 -45
- package/src/compat/map/form.js +0 -330
- package/src/compat/map/helpers.js +0 -46
- package/src/compat/map/http.js +0 -117
- package/src/compat/map/layout.js +0 -256
- package/src/compat/map/options.js +0 -267
- package/src/compat/map/select.js +0 -198
- package/src/compat/map/stubs.js +0 -26
- package/src/compat/map/utilities.js +0 -113
- package/src/compat-entry.js +0 -11
- package/src/components/permission/permission.css +0 -47
- package/src/components/permission/permission.js +0 -201
package/docs/llms.txt
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# Zx
|
|
1
|
+
# Xenon Design System (Zx)
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
3
|
+
> Xenon is a dependency-free design system for browser applications; Zx is its product-agnostic
|
|
4
|
+
> vanilla-JavaScript implementation. ES2022 modules, WAI-ARIA-accessible native
|
|
5
5
|
> controls, semantic `--zx-*` design tokens with light/dark/density theming, and lifecycle-safe
|
|
6
6
|
> 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
|
|
10
10
|
selector string, or `null` (the component creates and owns its root, retrieved via `.toElement()`).
|
|
@@ -13,33 +13,46 @@ Subscribe with `.on(type, fn)` or an `on<type>` option; handlers receive a `Cust
|
|
|
13
13
|
exports and as `window.zx.*`.
|
|
14
14
|
|
|
15
15
|
## Documentation
|
|
16
|
-
- [Documentation site](
|
|
17
|
-
- [README](../README.md): what Zx is, quick start (ESM + global script), component index, theming, browser support
|
|
18
|
-
- [API reference](llms.md): per-component constructor options, methods, and events for all
|
|
19
|
-
- [Skill](
|
|
16
|
+
- [Documentation site](https://zx.zeyos.com/docs.html): getting started, every component with a live demo and its JavaScript source, and the application layouts. Machine index: [llms.txt](https://zx.zeyos.com/llms.txt).
|
|
17
|
+
- [README](../README.md): what Zx is, quick start (ESM + global script), component index, theming, and browser support.
|
|
18
|
+
- [API reference](llms.md): per-component constructor options, methods, and events for all public components plus the core helpers.
|
|
19
|
+
- [Skill](https://zx.zeyos.com/skills/zx/SKILL.md): task-oriented guide — mental model, recipes, ZeyOS HTTP client, theming, gotchas, commands.
|
|
20
20
|
- [Changelog](../CHANGELOG.md): what changed in each release, including behaviour changes an upgrade has to account for.
|
|
21
|
-
- [
|
|
22
|
-
- [Contributor contract](../AGENTS.md): conventions for editing component source (token rules, render-time state, listeners, ARIA/APG).
|
|
21
|
+
- [Contributor contract](https://github.com/zeyos/zx/blob/main/AGENTS.md): conventions for editing component source (token rules, render-time state, listeners, ARIA/APG).
|
|
23
22
|
|
|
24
23
|
## Components
|
|
25
|
-
- Inputs: `button()`, `buttonGroup()`, `badge()`/`badgeGroup()`, `CheckButton`,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
- Inputs and identity: `button()`, `buttonGroup()`, `badge()`/`badgeGroup()`, `CheckButton`,
|
|
25
|
+
`Toggle`, `Search`, `Launcher`, `Avatar`, `AccountMenu`, `Select` (APG combobox;
|
|
26
|
+
`filter: false|'local'|async`; priority, status, permission, and optional ZeyOS entity presets),
|
|
27
|
+
`Checklist`, `TagPicker`, `NumberField`, `Rating`, `Slider`,
|
|
28
|
+
`copyButton()`/`CopyInput`, `DatePicker`, `MonthPicker`, `TimePicker`,
|
|
29
|
+
`Datebox`/`DateTimeBox`, `DateRangePicker`/`DateRangeBox`, `Timebox`.
|
|
30
|
+
- Overlays: `Message` (toasts/inline/progress), `Modal`, `Dialog` (+ `alert`/`confirm`/`prompt` Promises), `Sheet` (edge-anchored Dialog; `side`, `modal: true|'trap-focus'|false`, `backdrop`, `snap` detents, swipe-to-dismiss), `SheetStack` (drill-down; `stack`/`cascade`), `Dropdown`, `MenuButton`, `ContextMenu` (right-click; `selector` for per-row menus), `Tooltip`/`tooltip()`/`describe()`.
|
|
31
|
+
- Data: `Table` (sortable, selectable, sticky header, opt-in inline cell/row editing via
|
|
32
|
+
`editMode`), `Grid` with `Grid.BillingItems()`, engine-neutral `Chart` with injected
|
|
33
|
+
`ChartJsAdapter`, `DataFilter`, `Pagination` (+ pure `paginationRange()`), `TreeView` (APG tree),
|
|
34
|
+
`Finder` (Miller columns).
|
|
35
|
+
- Forms: `Form`, `Fieldset`, `Questionnaire` (guided one-question-at-a-time flow with branching), `Field` (type registry), `ValueList`, `MultiValueEditor`, `FieldUpload`.
|
|
36
|
+
- Layout: `stack()`/`grid()`/`aspect()` (spacing, an intrinsically reflowing grid, a fixed-ratio
|
|
37
|
+
box), `Groupbox`, `Card` (media/title link/actions/footer), `Panel` (header/footer action
|
|
38
|
+
buttons), `MasterPanel`, `Tabbox`
|
|
39
|
+
(`variant: 'divided'|'bracket'|'line'|'segmented'`), `NavigationBar`, `AppSidebar` (expanded and
|
|
40
|
+
minimized vertical states plus horizontal rail layouts), `Toolbar` (APG toolbar with overflow menu), `SplitView`, `Dock`
|
|
41
|
+
(collapsible resizable pane stack; tab groups, regions, adopts a `Sheet`), `Stepper`,
|
|
42
|
+
`Breadcrumb`, `emptyState()`.
|
|
30
43
|
- Feedback: `spinner()` (unknown duration), `ProgressBar` (known share; determinate or indeterminate), `InlineLoading` (a wait resolving into its outcome in place), `skeleton()`/`skeletonText()`/`skeletonTable()` (placeholders shaped like the content; `aria-hidden`, set `aria-busy` on the region).
|
|
31
|
-
- Core: `Component`, `h`/`icon`, `position`, `breakpoints`/`breakpointOf`/`matchBreakpoint`/`onBreakpoint` (named width bands, observed on an element or the window), `Http`/`zeyosService`, i18n, date utils, `defineElements` (declarative `<zx-*>` custom elements).
|
|
44
|
+
- Core: `Component`, `h`/`__`/`icon`, `position`, `breakpoints`/`breakpointOf`/`matchBreakpoint`/`onBreakpoint` (named width bands, observed on an element or the window), `Http`/`zeyosService`, i18n, date utils, `defineElements` (declarative `<zx-*>` custom elements).
|
|
32
45
|
- 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).
|
|
33
46
|
|
|
34
47
|
## Theming
|
|
35
48
|
Load `zx.css` once; set `data-zx-theme` (`light`|`dark`|`auto`) and `data-zx-density` (`cozy`|`compact`) on any ancestor. Define a product theme by overriding semantic `--zx-color-*` / `--zx-control-*` tokens under `[data-zx-theme="name"]`. Never use tier-1 palette tokens or raw color literals in application CSS.
|
|
36
49
|
|
|
37
50
|
## Talking to ZeyOS
|
|
38
|
-
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, `normalizeListResult`.
|
|
51
|
+
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.
|
|
39
52
|
|
|
40
53
|
## Installing
|
|
41
|
-
`npm install @zeyos/zx`. Entry points: `.` (components), `./zeyos` (ZeyOS binding), `./
|
|
42
|
-
(
|
|
54
|
+
`npm install @zeyos/zx`. Entry points: `.` (components), `./zeyos` (ZeyOS binding), `./zx.css`
|
|
55
|
+
(styles), and `./global` (classic script tag). Docs: https://zx.zeyos.com
|
|
43
56
|
|
|
44
57
|
## Commands
|
|
45
58
|
`npm run serve` (no-build documentation at http://127.0.0.1:8321/website/docs.html) · `npm test` (unit + token lint) · `npm run build` (dist bundles).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeyos/zx",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Dependency-free
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"description": "Dependency-free JavaScript implementation of the Xenon Design System.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://zx.zeyos.com",
|
|
@@ -25,9 +25,7 @@
|
|
|
25
25
|
"sideEffects": [
|
|
26
26
|
"*.css",
|
|
27
27
|
"./dist/zx.global.js",
|
|
28
|
-
"./dist/zx.global.min.js"
|
|
29
|
-
"./dist/zx-compat.global.js",
|
|
30
|
-
"./dist/zx-compat.global.min.js"
|
|
28
|
+
"./dist/zx.global.min.js"
|
|
31
29
|
],
|
|
32
30
|
"exports": {
|
|
33
31
|
".": {
|
|
@@ -38,23 +36,15 @@
|
|
|
38
36
|
"types": "./dist/types/zeyos/index.d.ts",
|
|
39
37
|
"default": "./dist/zx-zeyos.esm.js"
|
|
40
38
|
},
|
|
41
|
-
"./compat": {
|
|
42
|
-
"types": "./dist/types/compat/index.d.ts",
|
|
43
|
-
"default": "./dist/zx-compat.esm.js"
|
|
44
|
-
},
|
|
45
39
|
"./global": {
|
|
46
40
|
"types": "./dist/types/index.d.ts",
|
|
47
41
|
"default": "./dist/zx.global.js"
|
|
48
42
|
},
|
|
49
|
-
"./compat-global": {
|
|
50
|
-
"types": "./dist/types/compat/index.d.ts",
|
|
51
|
-
"default": "./dist/zx-compat.global.js"
|
|
52
|
-
},
|
|
53
43
|
"./zx.css": "./dist/zx.css",
|
|
54
44
|
"./zx.min.css": "./dist/zx.min.css",
|
|
55
|
-
"./src/*.js": {
|
|
56
|
-
"types": "./dist/types/*.d.ts",
|
|
57
|
-
"default": "./src/*.js"
|
|
45
|
+
"./src/components/*.js": {
|
|
46
|
+
"types": "./dist/types/components/*.d.ts",
|
|
47
|
+
"default": "./src/components/*.js"
|
|
58
48
|
},
|
|
59
49
|
"./styles/*": "./styles/*",
|
|
60
50
|
"./dist/*": "./dist/*",
|
|
@@ -68,12 +58,14 @@
|
|
|
68
58
|
"dist/types",
|
|
69
59
|
"styles",
|
|
70
60
|
"src",
|
|
61
|
+
"!src/compat",
|
|
62
|
+
"!src/compat-entry.js",
|
|
71
63
|
"docs/llms.md",
|
|
72
64
|
"docs/llms.txt",
|
|
73
65
|
"docs/api.json",
|
|
74
66
|
"CHANGELOG.md",
|
|
75
67
|
"README.md",
|
|
76
|
-
"
|
|
68
|
+
"DESIGN-SYSTEM.md",
|
|
77
69
|
"LICENSE"
|
|
78
70
|
],
|
|
79
71
|
"engines": {
|
|
@@ -94,6 +86,7 @@
|
|
|
94
86
|
"test:browser": "node tools/run-smoke.js"
|
|
95
87
|
},
|
|
96
88
|
"devDependencies": {
|
|
89
|
+
"chart.js": "4.5.1",
|
|
97
90
|
"esbuild": "latest",
|
|
98
91
|
"playwright-core": "^1.62.1",
|
|
99
92
|
"typescript": "^7.0.2"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
.zx-account-menu {
|
|
2
|
+
display: block;
|
|
3
|
+
min-inline-size: 0;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.zx-account-menu__trigger {
|
|
7
|
+
display: grid;
|
|
8
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: var(--zx-space-3);
|
|
11
|
+
inline-size: 100%;
|
|
12
|
+
min-block-size: calc(var(--zx-control-height) + var(--zx-space-2));
|
|
13
|
+
border-color: transparent;
|
|
14
|
+
background: transparent;
|
|
15
|
+
padding: var(--zx-space-2);
|
|
16
|
+
color: inherit;
|
|
17
|
+
text-align: start;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.zx-account-menu__trigger:hover,
|
|
21
|
+
.zx-account-menu__trigger:focus-visible,
|
|
22
|
+
.zx-account-menu__trigger[aria-expanded="true"] {
|
|
23
|
+
background: var(--zx-color-bg-hover);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.zx-account-menu__identity {
|
|
27
|
+
display: grid;
|
|
28
|
+
min-inline-size: 0;
|
|
29
|
+
gap: 1px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.zx-account-menu__name,
|
|
33
|
+
.zx-account-menu__secondary {
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
text-overflow: ellipsis;
|
|
36
|
+
white-space: nowrap;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.zx-account-menu__name { font-weight: 650; }
|
|
40
|
+
.zx-account-menu__secondary { color: var(--zx-color-text-muted); font-size: var(--zx-text-xs); }
|
|
41
|
+
.zx-account-menu__chevron { display: inline-flex; }
|
|
42
|
+
|
|
43
|
+
.zx-account-menu[data-compact="true"] {
|
|
44
|
+
inline-size: fit-content;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.zx-account-menu[data-compact="true"] .zx-account-menu__trigger {
|
|
48
|
+
display: inline-grid;
|
|
49
|
+
grid-template-columns: auto;
|
|
50
|
+
inline-size: auto;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.zx-account-menu[data-compact="true"] .zx-account-menu__identity,
|
|
54
|
+
.zx-account-menu[data-compact="true"] .zx-account-menu__chevron {
|
|
55
|
+
display: none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.zx-account-menu__panel-identity {
|
|
59
|
+
display: grid;
|
|
60
|
+
min-inline-size: 220px;
|
|
61
|
+
gap: 2px;
|
|
62
|
+
margin: calc(-1 * var(--zx-space-1)) calc(-1 * var(--zx-space-1)) var(--zx-space-1);
|
|
63
|
+
border-block-end: 1px solid var(--zx-color-border);
|
|
64
|
+
padding: var(--zx-space-3);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.zx-account-menu__panel-identity span {
|
|
68
|
+
color: var(--zx-color-text-muted);
|
|
69
|
+
font-size: var(--zx-text-xs);
|
|
70
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { Component } from '../../core/component.js';
|
|
2
|
+
import { h } from '../../core/dom.js';
|
|
3
|
+
import { icon } from '../../core/icons.js';
|
|
4
|
+
import { Avatar } from '../avatar/avatar.js';
|
|
5
|
+
import { MenuButton } from '../menu-button/menu-button.js';
|
|
6
|
+
|
|
7
|
+
/** @typedef {import('../menu-button/menu-button.js').MenuButtonItem} MenuButtonItem */
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {Object} AccountIdentity
|
|
10
|
+
* @property {string} [name=''] Primary identity.
|
|
11
|
+
* @property {string} [secondary=''] Email, company, or other secondary identity.
|
|
12
|
+
* @property {string|null} [src=null] Avatar image URL.
|
|
13
|
+
* @property {string|null} [initials=null] Explicit avatar initials.
|
|
14
|
+
* @property {'online'|'away'|'busy'|'offline'|null} [status=null] Presence state.
|
|
15
|
+
* @property {string|null} [statusLabel=null] Presence text alternative.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} AccountMenuOptions
|
|
19
|
+
* @property {AccountIdentity} [account={}] Account presentation supplied by the application.
|
|
20
|
+
* @property {MenuButtonItem[]} [items=[]] Account actions and `'-'` separators.
|
|
21
|
+
* @property {boolean} [compact=false] Whether only the avatar is visually shown in the trigger.
|
|
22
|
+
* @property {string|null} [label=null] Trigger label; defaults to `Account menu for <name>`.
|
|
23
|
+
* @property {'bottom-start'|'bottom-end'|'top-start'|'top-end'|'bottom'|'top'|'right-start'|'right-end'|'left-start'|'left-end'|'right'|'left'} [placement='top-start'] Popup placement.
|
|
24
|
+
* @property {(event: CustomEvent<{value: unknown, item: object}>) => void} [onselect] Preventable action listener.
|
|
25
|
+
* @property {(event: CustomEvent<Record<string, never>>) => void} [onopen] Open listener.
|
|
26
|
+
* @property {(event: CustomEvent<Record<string, never>>) => void} [onclose] Close listener.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Avatar-backed APG account action menu; session/authentication behavior remains application-owned.
|
|
31
|
+
* @fires AccountMenu#select
|
|
32
|
+
* @fires AccountMenu#open
|
|
33
|
+
* @fires AccountMenu#close
|
|
34
|
+
* @extends {Component<AccountMenuOptions>}
|
|
35
|
+
*/
|
|
36
|
+
export class AccountMenu extends Component {
|
|
37
|
+
static cssName = 'account-menu';
|
|
38
|
+
|
|
39
|
+
/** @type {Readonly<AccountMenuOptions>} */
|
|
40
|
+
static defaults = {
|
|
41
|
+
account: {},
|
|
42
|
+
items: [],
|
|
43
|
+
compact: false,
|
|
44
|
+
label: null,
|
|
45
|
+
placement: 'top-start'
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates or enhances an account-menu host.
|
|
50
|
+
* @param {Element|string|null} target Host target.
|
|
51
|
+
* @param {AccountMenuOptions} [options={}] Account-menu options.
|
|
52
|
+
*/
|
|
53
|
+
constructor(target = null, options = {}) {
|
|
54
|
+
super(target, options);
|
|
55
|
+
for (const type of ['select', 'open', 'close']) {
|
|
56
|
+
this.listen(this.refs.trigger, `zx-${type}`, (event) => event.stopPropagation());
|
|
57
|
+
}
|
|
58
|
+
this._avatar = new Avatar(this.refs.avatar, avatarOptions(this._account));
|
|
59
|
+
this._menu = new MenuButton(this.refs.trigger, {
|
|
60
|
+
items: this.options.items,
|
|
61
|
+
placement: this.options.placement
|
|
62
|
+
});
|
|
63
|
+
this._menu.on('select', (event) => {
|
|
64
|
+
const selected = this.emit('select', event.detail);
|
|
65
|
+
if (selected.defaultPrevented) event.preventDefault();
|
|
66
|
+
});
|
|
67
|
+
this._menu.on('open', () => this.emit('open'));
|
|
68
|
+
this._menu.on('close', () => this.emit('close'));
|
|
69
|
+
this._renderIdentity();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** @returns {HTMLElement} */
|
|
73
|
+
render() {
|
|
74
|
+
this._createdRoot = this.el === null;
|
|
75
|
+
const root = /** @type {HTMLElement} */ (this.el ?? h('div'));
|
|
76
|
+
this.el = root;
|
|
77
|
+
this._original = this._createdRoot ? null : {
|
|
78
|
+
attributes: Array.from(root.attributes, (attribute) => [attribute.name, attribute.value]),
|
|
79
|
+
children: Array.from(root.childNodes)
|
|
80
|
+
};
|
|
81
|
+
this._account = normalizeAccount(this.options.account);
|
|
82
|
+
root.dataset.compact = String(Boolean(this.options.compact));
|
|
83
|
+
root.replaceChildren(h('button', {
|
|
84
|
+
ref: 'trigger',
|
|
85
|
+
class: 'zx-account-menu__trigger',
|
|
86
|
+
type: 'button'
|
|
87
|
+
},
|
|
88
|
+
h('span', { ref: 'avatar', class: 'zx-account-menu__avatar' }),
|
|
89
|
+
h('span', { class: 'zx-account-menu__identity' },
|
|
90
|
+
h('span', { ref: 'name', class: 'zx-account-menu__name' }),
|
|
91
|
+
h('span', { ref: 'secondary', class: 'zx-account-menu__secondary' })),
|
|
92
|
+
h('span', { class: 'zx-account-menu__chevron', ariaHidden: 'true' }, icon('chevron-down'))));
|
|
93
|
+
return root;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Opens the account menu. @returns {this} */
|
|
97
|
+
open() {
|
|
98
|
+
this._menu.open();
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Closes the account menu. @returns {this} */
|
|
103
|
+
close() {
|
|
104
|
+
this._menu.close();
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Toggles the account menu. @returns {this} */
|
|
109
|
+
toggle() {
|
|
110
|
+
this._menu.toggle();
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Reports whether the menu is open. @returns {boolean} */
|
|
115
|
+
isOpen() {
|
|
116
|
+
return this._menu.isOpen();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Replaces account presentation without touching session state. @param {AccountIdentity} account @returns {this} */
|
|
120
|
+
setAccount(account) {
|
|
121
|
+
this._account = normalizeAccount(account);
|
|
122
|
+
this._avatar.set(avatarOptions(this._account));
|
|
123
|
+
this._renderIdentity();
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Replaces account actions. @param {MenuButtonItem[]} items @returns {this} */
|
|
128
|
+
setItems(items) {
|
|
129
|
+
this._menu.setItems(items);
|
|
130
|
+
this._renderPanelHeader();
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Focuses the account trigger. @returns {this} */
|
|
135
|
+
focus() {
|
|
136
|
+
this._menu.getTrigger().focus();
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Returns the underlying menu panel for shell hover/focus coordination. @returns {HTMLElement} */
|
|
141
|
+
getPanel() {
|
|
142
|
+
return this._menu.getPanel();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Destroys child components and restores/removes the host. @returns {void} */
|
|
146
|
+
destroy() {
|
|
147
|
+
const original = this._original;
|
|
148
|
+
this._menu.destroy();
|
|
149
|
+
this._avatar.destroy();
|
|
150
|
+
super.destroy();
|
|
151
|
+
if (!this._createdRoot && original) {
|
|
152
|
+
for (const attribute of Array.from(this.el.attributes)) this.el.removeAttribute(attribute.name);
|
|
153
|
+
for (const [name, value] of original.attributes) this.el.setAttribute(name, value);
|
|
154
|
+
this.el.replaceChildren(...original.children);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** @returns {void} */
|
|
159
|
+
_renderIdentity() {
|
|
160
|
+
this.refs.name.textContent = this._account.name;
|
|
161
|
+
this.refs.secondary.textContent = this._account.secondary;
|
|
162
|
+
this.refs.secondary.hidden = !this._account.secondary;
|
|
163
|
+
const hasStatus = ['online', 'away', 'busy', 'offline'].includes(this._account.status);
|
|
164
|
+
const status = hasStatus && this._account.statusLabel ? `, ${this._account.statusLabel}` : '';
|
|
165
|
+
const label = this.options.label || `Account menu${this._account.name ? ` for ${this._account.name}` : ''}${status}`;
|
|
166
|
+
this.refs.trigger.setAttribute('aria-label', label);
|
|
167
|
+
this._renderPanelHeader();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** @returns {void} */
|
|
171
|
+
_renderPanelHeader() {
|
|
172
|
+
const panel = this._menu.getPanel();
|
|
173
|
+
panel.querySelector('.zx-account-menu__panel-identity')?.remove();
|
|
174
|
+
panel.prepend(h('div', {
|
|
175
|
+
class: 'zx-account-menu__panel-identity',
|
|
176
|
+
role: 'presentation'
|
|
177
|
+
},
|
|
178
|
+
h('strong', {}, this._account.name || 'Account'),
|
|
179
|
+
this._account.secondary ? h('span', {}, this._account.secondary) : null));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** @param {unknown} value @returns {AccountIdentity & {name: string, secondary: string}} */
|
|
184
|
+
function normalizeAccount(value) {
|
|
185
|
+
const account = value && typeof value === 'object' ? value : {};
|
|
186
|
+
return {
|
|
187
|
+
...account,
|
|
188
|
+
name: String(account.name ?? ''),
|
|
189
|
+
secondary: String(account.secondary ?? '')
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** @param {AccountIdentity} account @returns {import('../avatar/avatar.js').AvatarOptions} */
|
|
194
|
+
function avatarOptions(account) {
|
|
195
|
+
return {
|
|
196
|
+
name: account.name,
|
|
197
|
+
src: account.src ?? null,
|
|
198
|
+
initials: account.initials ?? null,
|
|
199
|
+
status: account.status ?? null,
|
|
200
|
+
statusLabel: account.statusLabel ?? null,
|
|
201
|
+
label: null,
|
|
202
|
+
size: 'md'
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Fired when an action is activated. @event AccountMenu#select @type {CustomEvent<{value: unknown, item: object}>} */
|
|
207
|
+
/** Fired after the popup opens. @event AccountMenu#open @type {CustomEvent<Record<string, never>>} */
|
|
208
|
+
/** Fired after the popup closes. @event AccountMenu#close @type {CustomEvent<Record<string, never>>} */
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
.zx-app-rail {
|
|
2
|
+
display: flex;
|
|
3
|
+
min-inline-size: 0;
|
|
4
|
+
border: 1px solid var(--zx-color-border);
|
|
5
|
+
background: var(--zx-color-bg-raised);
|
|
6
|
+
color: var(--zx-color-text);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.zx-app-rail[data-orientation="vertical"] { flex-direction: column; inline-size: calc(var(--zx-control-height) + var(--zx-space-3)); }
|
|
10
|
+
.zx-app-rail[data-orientation="horizontal"] { flex-direction: row; block-size: calc(var(--zx-control-height) + var(--zx-space-3)); }
|
|
11
|
+
|
|
12
|
+
.zx-app-rail__header,
|
|
13
|
+
.zx-app-rail__footer {
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
padding: var(--zx-space-1);
|
|
18
|
+
}
|
|
19
|
+
.zx-app-rail__header[hidden], .zx-app-rail__footer[hidden] { display: none; }
|
|
20
|
+
.zx-app-rail[data-orientation="vertical"] .zx-app-rail__footer { margin-block-start: auto; }
|
|
21
|
+
.zx-app-rail[data-orientation="horizontal"] .zx-app-rail__footer { margin-inline-start: auto; }
|
|
22
|
+
|
|
23
|
+
.zx-app-rail__list {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex: 1;
|
|
26
|
+
gap: var(--zx-space-1);
|
|
27
|
+
margin: 0;
|
|
28
|
+
padding: var(--zx-space-1);
|
|
29
|
+
list-style: none;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.zx-app-rail[data-orientation="vertical"] .zx-app-rail__list { flex-direction: column; }
|
|
33
|
+
.zx-app-rail[data-orientation="horizontal"] .zx-app-rail__list { flex-direction: row; }
|
|
34
|
+
.zx-app-rail__row { display: flex; }
|
|
35
|
+
|
|
36
|
+
.zx-app-rail__item {
|
|
37
|
+
position: relative;
|
|
38
|
+
display: inline-grid;
|
|
39
|
+
place-items: center;
|
|
40
|
+
inline-size: var(--zx-control-height);
|
|
41
|
+
block-size: var(--zx-control-height);
|
|
42
|
+
border-color: transparent;
|
|
43
|
+
border-radius: var(--zx-radius-md);
|
|
44
|
+
background: transparent;
|
|
45
|
+
color: inherit;
|
|
46
|
+
padding: 0;
|
|
47
|
+
text-decoration: none;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.zx-app-rail__item:hover,
|
|
51
|
+
.zx-app-rail__item:focus-visible,
|
|
52
|
+
.zx-app-rail__item[aria-expanded="true"],
|
|
53
|
+
.zx-app-rail__item[data-active-descendant="true"],
|
|
54
|
+
.zx-app-rail__item[aria-current="page"] {
|
|
55
|
+
background: var(--zx-color-bg-hover);
|
|
56
|
+
color: var(--zx-color-accent);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.zx-app-rail__item[aria-disabled="true"],
|
|
60
|
+
.zx-app-rail__flyout-item[aria-disabled="true"] {
|
|
61
|
+
color: var(--zx-color-text-muted);
|
|
62
|
+
cursor: not-allowed;
|
|
63
|
+
opacity: .65;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.zx-app-rail__item-label { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }
|
|
67
|
+
.zx-app-rail__item-icon { display: inline-flex; }
|
|
68
|
+
.zx-app-rail__item-badge {
|
|
69
|
+
position: absolute;
|
|
70
|
+
inset-inline-end: 1px;
|
|
71
|
+
inset-block-start: 1px;
|
|
72
|
+
min-inline-size: 15px;
|
|
73
|
+
border-radius: var(--zx-radius-full);
|
|
74
|
+
background: var(--zx-color-accent);
|
|
75
|
+
padding-inline: 3px;
|
|
76
|
+
color: var(--zx-color-on-accent);
|
|
77
|
+
font-size: 10px;
|
|
78
|
+
line-height: 15px;
|
|
79
|
+
text-align: center;
|
|
80
|
+
}
|
|
81
|
+
.zx-app-rail__item-disclosure { position: absolute; inset-inline-end: 1px; inset-block-end: 1px; display: inline-flex; }
|
|
82
|
+
|
|
83
|
+
.zx-app-rail__popover { min-inline-size: 220px; padding: var(--zx-space-2); }
|
|
84
|
+
.zx-app-rail__flyout-title { padding: var(--zx-space-2) var(--zx-space-3); color: var(--zx-color-text-muted); font-size: var(--zx-text-xs); font-weight: 700; }
|
|
85
|
+
.zx-app-rail__flyout-list { display: grid; gap: var(--zx-space-1); margin: 0; padding: 0; list-style: none; }
|
|
86
|
+
.zx-app-rail__flyout-item {
|
|
87
|
+
display: grid;
|
|
88
|
+
grid-template-columns: auto minmax(0, 1fr) auto auto;
|
|
89
|
+
align-items: center;
|
|
90
|
+
gap: var(--zx-space-2);
|
|
91
|
+
inline-size: 100%;
|
|
92
|
+
min-block-size: var(--zx-control-height);
|
|
93
|
+
border-color: transparent;
|
|
94
|
+
border-radius: var(--zx-radius-md);
|
|
95
|
+
background: transparent;
|
|
96
|
+
padding-inline: var(--zx-control-pad-x);
|
|
97
|
+
color: inherit;
|
|
98
|
+
text-align: start;
|
|
99
|
+
text-decoration: none;
|
|
100
|
+
}
|
|
101
|
+
.zx-app-rail__flyout-item:hover,
|
|
102
|
+
.zx-app-rail__flyout-item:focus-visible,
|
|
103
|
+
.zx-app-rail__flyout-item[aria-current="page"] { background: var(--zx-color-bg-hover); color: var(--zx-color-accent); }
|
|
104
|
+
.zx-app-rail__flyout-item-icon { display: inline-flex; grid-column: 1; }
|
|
105
|
+
.zx-app-rail__flyout-item-label { grid-column: 2; }
|
|
106
|
+
.zx-app-rail__flyout-item-badge { grid-column: 3; color: var(--zx-color-text-muted); font-size: var(--zx-text-xs); }
|
|
107
|
+
.zx-app-rail__flyout-item-disclosure { display: inline-flex; grid-column: 4; color: var(--zx-color-text-muted); }
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
.zx-app-sidebar {
|
|
2
|
+
display: flex;
|
|
3
|
+
min-block-size: 0;
|
|
4
|
+
border: 1px solid var(--zx-color-border);
|
|
5
|
+
background: var(--zx-color-bg-raised);
|
|
6
|
+
color: var(--zx-color-text);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.zx-app-sidebar[data-orientation="horizontal"] {
|
|
10
|
+
inline-size: 100%;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.zx-app-sidebar[data-orientation="horizontal"] > .zx-app-sidebar__rail {
|
|
14
|
+
inline-size: 100%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.zx-app-sidebar__rail.zx-app-rail {
|
|
18
|
+
border: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.zx-app-sidebar__expanded {
|
|
22
|
+
display: grid;
|
|
23
|
+
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
24
|
+
inline-size: min(280px, 100vw);
|
|
25
|
+
min-block-size: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.zx-app-sidebar__expanded[hidden],
|
|
29
|
+
.zx-app-sidebar__rail[hidden] { display: none; }
|
|
30
|
+
|
|
31
|
+
.zx-app-sidebar__header {
|
|
32
|
+
display: grid;
|
|
33
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
34
|
+
align-items: center;
|
|
35
|
+
gap: var(--zx-space-2);
|
|
36
|
+
min-block-size: calc(var(--zx-control-height) + var(--zx-space-3));
|
|
37
|
+
border-block-end: 1px solid var(--zx-color-border);
|
|
38
|
+
padding: var(--zx-space-2);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.zx-app-sidebar__header-content { min-inline-size: 0; }
|
|
42
|
+
.zx-app-sidebar__nav { min-block-size: 0; overflow: auto; padding: var(--zx-space-2); }
|
|
43
|
+
.zx-app-sidebar__list,
|
|
44
|
+
.zx-app-sidebar__sublist { display: grid; gap: var(--zx-space-1); margin: 0; padding: 0; list-style: none; }
|
|
45
|
+
.zx-app-sidebar__sublist { margin-block-start: var(--zx-space-1); }
|
|
46
|
+
.zx-app-sidebar__sublist[hidden] { display: none; }
|
|
47
|
+
|
|
48
|
+
.zx-app-sidebar__item {
|
|
49
|
+
display: grid;
|
|
50
|
+
grid-template-columns: auto minmax(0, 1fr) auto auto;
|
|
51
|
+
align-items: center;
|
|
52
|
+
gap: var(--zx-space-2);
|
|
53
|
+
inline-size: 100%;
|
|
54
|
+
min-block-size: var(--zx-control-height);
|
|
55
|
+
border-color: transparent;
|
|
56
|
+
border-radius: var(--zx-radius-md);
|
|
57
|
+
background: transparent;
|
|
58
|
+
padding-inline: var(--zx-space-3);
|
|
59
|
+
color: inherit;
|
|
60
|
+
text-align: start;
|
|
61
|
+
text-decoration: none;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.zx-app-sidebar__row[data-level="2"] > .zx-app-sidebar__item { padding-inline-start: calc(var(--zx-space-3) + var(--zx-space-5)); }
|
|
65
|
+
.zx-app-sidebar__row[data-level="3"] > .zx-app-sidebar__item { padding-inline-start: calc(var(--zx-space-3) + var(--zx-space-7)); }
|
|
66
|
+
.zx-app-sidebar__item:hover,
|
|
67
|
+
.zx-app-sidebar__item:focus-visible,
|
|
68
|
+
.zx-app-sidebar__item[data-active-descendant="true"],
|
|
69
|
+
.zx-app-sidebar__item[aria-current="page"] { background: var(--zx-color-bg-hover); color: var(--zx-color-accent); }
|
|
70
|
+
.zx-app-sidebar__item[aria-disabled="true"] { color: var(--zx-color-text-muted); cursor: not-allowed; opacity: .65; }
|
|
71
|
+
.zx-app-sidebar__item-icon,
|
|
72
|
+
.zx-app-sidebar__item-disclosure { display: inline-flex; }
|
|
73
|
+
.zx-app-sidebar__item-badge { color: var(--zx-color-text-muted); font-size: var(--zx-text-xs); }
|
|
74
|
+
|
|
75
|
+
.zx-app-sidebar__footer {
|
|
76
|
+
border-block-start: 1px solid var(--zx-color-border);
|
|
77
|
+
padding: var(--zx-space-2);
|
|
78
|
+
}
|
|
79
|
+
.zx-app-sidebar__footer[hidden] { display: none; }
|
|
80
|
+
.zx-app-sidebar__rail-header { display: grid; gap: var(--zx-space-1); }
|