@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.
Files changed (150) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/DESIGN-SYSTEM.md +96 -0
  3. package/README.md +29 -39
  4. package/dist/types/components/account-menu/account-menu.d.ts +144 -0
  5. package/dist/types/components/app-sidebar/app-sidebar.d.ts +243 -0
  6. package/dist/types/components/avatar/avatar.d.ts +100 -0
  7. package/dist/types/components/card/card.d.ts +128 -0
  8. package/dist/types/components/chart/chart.d.ts +322 -0
  9. package/dist/types/components/date-picker/month-picker.d.ts +6 -0
  10. package/dist/types/components/dialog/dialog.d.ts +9 -0
  11. package/dist/types/components/dock/dock.d.ts +495 -0
  12. package/dist/types/components/dropdown/dropdown.d.ts +2 -2
  13. package/dist/types/components/field/field.d.ts +14 -0
  14. package/dist/types/components/field-upload/field-upload.d.ts +37 -4
  15. package/dist/types/components/form/form.d.ts +25 -3
  16. package/dist/types/components/grid/grid.d.ts +172 -0
  17. package/dist/types/components/launcher/launcher.d.ts +359 -0
  18. package/dist/types/components/layout/layout.d.ts +4 -4
  19. package/dist/types/components/menu-button/menu-button.d.ts +10 -2
  20. package/dist/types/components/modal/modal.d.ts +25 -0
  21. package/dist/types/components/multi-value-editor/multi-value-editor.d.ts +7 -1
  22. package/dist/types/components/questionnaire/items.d.ts +327 -0
  23. package/dist/types/components/questionnaire/questionnaire.d.ts +449 -0
  24. package/dist/types/components/select/select.d.ts +51 -0
  25. package/dist/types/components/sheet/sheet-stack.d.ts +167 -0
  26. package/dist/types/components/sheet/sheet.d.ts +294 -0
  27. package/dist/types/components/split-view/split-view.d.ts +2 -21
  28. package/dist/types/components/table/table.d.ts +223 -15
  29. package/dist/types/components/tooltip/tooltip.d.ts +2 -2
  30. package/dist/types/components/value-list/value-list.d.ts +24 -3
  31. package/dist/types/core/dom.d.ts +122 -0
  32. package/dist/types/core/drag-axis.d.ts +111 -0
  33. package/dist/types/core/position.d.ts +4 -2
  34. package/dist/types/index.d.ts +13 -2
  35. package/dist/types/internal/app-rail.d.ts +265 -0
  36. package/dist/types/zeyos/icons.d.ts +5 -0
  37. package/dist/types/zeyos/index.d.ts +2 -1
  38. package/dist/types/zeyos/launcher.d.ts +147 -0
  39. package/dist/types/zeyos/select.d.ts +39 -0
  40. package/dist/zx-zeyos.esm.js +1870 -133
  41. package/dist/zx-zeyos.esm.min.js +1 -1
  42. package/dist/zx.css +2006 -267
  43. package/dist/zx.esm.js +8987 -2757
  44. package/dist/zx.esm.min.js +3 -3
  45. package/dist/zx.global.js +8987 -2757
  46. package/dist/zx.global.min.js +3 -3
  47. package/dist/zx.min.css +1 -1
  48. package/docs/api.json +4396 -2344
  49. package/docs/llms.md +463 -86
  50. package/docs/llms.txt +32 -19
  51. package/package.json +10 -17
  52. package/src/components/account-menu/account-menu.css +70 -0
  53. package/src/components/account-menu/account-menu.js +208 -0
  54. package/src/components/app-rail/app-rail.css +107 -0
  55. package/src/components/app-sidebar/app-sidebar.css +80 -0
  56. package/src/components/app-sidebar/app-sidebar.js +588 -0
  57. package/src/components/avatar/avatar.css +61 -0
  58. package/src/components/avatar/avatar.js +222 -0
  59. package/src/components/button/button.css +0 -1
  60. package/src/components/card/card.css +149 -0
  61. package/src/components/card/card.js +285 -0
  62. package/src/components/chart/chart.css +61 -0
  63. package/src/components/chart/chart.js +523 -0
  64. package/src/components/date-picker/month-picker.js +9 -0
  65. package/src/components/dialog/dialog.js +11 -4
  66. package/src/components/dock/dock.css +228 -0
  67. package/src/components/dock/dock.js +977 -0
  68. package/src/components/dropdown/dropdown.js +1 -1
  69. package/src/components/field/field.js +2 -0
  70. package/src/components/field-upload/field-upload.js +9 -4
  71. package/src/components/form/form.js +6 -3
  72. package/src/components/grid/grid.css +6 -0
  73. package/src/components/grid/grid.js +220 -0
  74. package/src/components/launcher/launcher.css +210 -0
  75. package/src/components/launcher/launcher.js +765 -0
  76. package/src/components/layout/layout.js +2 -2
  77. package/src/components/menu-button/menu-button.js +27 -2
  78. package/src/components/modal/modal.css +5 -1
  79. package/src/components/modal/modal.js +37 -2
  80. package/src/components/multi-value-editor/multi-value-editor.js +2 -1
  81. package/src/components/questionnaire/items.js +370 -0
  82. package/src/components/questionnaire/questionnaire.css +387 -0
  83. package/src/components/questionnaire/questionnaire.js +1033 -0
  84. package/src/components/select/field-adapter.js +9 -4
  85. package/src/components/select/select.css +144 -11
  86. package/src/components/select/select.js +259 -27
  87. package/src/components/sheet/sheet-stack.js +304 -0
  88. package/src/components/sheet/sheet.css +351 -0
  89. package/src/components/sheet/sheet.js +551 -0
  90. package/src/components/split-view/split-view.js +76 -161
  91. package/src/components/table/table.css +43 -0
  92. package/src/components/table/table.js +531 -52
  93. package/src/components/tooltip/tooltip.js +5 -2
  94. package/src/components/value-list/value-list.js +6 -3
  95. package/src/core/dom.js +322 -0
  96. package/src/core/drag-axis.js +203 -0
  97. package/src/core/position.js +68 -22
  98. package/src/index.js +20 -2
  99. package/src/internal/app-rail.js +718 -0
  100. package/src/zeyos/icons.js +8 -3
  101. package/src/zeyos/index.js +7 -1
  102. package/src/zeyos/launcher.js +338 -0
  103. package/src/zeyos/modules.js +1 -0
  104. package/src/zeyos/select.js +115 -1
  105. package/styles/tokens/dark.css +2 -3
  106. package/styles/tokens/global.css +6 -6
  107. package/styles/tokens/modules.css +1 -0
  108. package/styles/tokens/semantic.css +5 -3
  109. package/styles/tokens/themes.css +1 -1
  110. package/styles/zx.css +11 -1
  111. package/MIGRATION.md +0 -598
  112. package/dist/types/compat/base.d.ts +0 -156
  113. package/dist/types/compat/globals.d.ts +0 -20
  114. package/dist/types/compat/index.d.ts +0 -128
  115. package/dist/types/compat/map/basic.d.ts +0 -175
  116. package/dist/types/compat/map/data.d.ts +0 -102
  117. package/dist/types/compat/map/date-time.d.ts +0 -80
  118. package/dist/types/compat/map/factory.d.ts +0 -9
  119. package/dist/types/compat/map/form.d.ts +0 -98
  120. package/dist/types/compat/map/helpers.d.ts +0 -12
  121. package/dist/types/compat/map/http.d.ts +0 -36
  122. package/dist/types/compat/map/layout.d.ts +0 -106
  123. package/dist/types/compat/map/options.d.ts +0 -80
  124. package/dist/types/compat/map/select.d.ts +0 -95
  125. package/dist/types/compat/map/stubs.d.ts +0 -32
  126. package/dist/types/compat/map/utilities.d.ts +0 -37
  127. package/dist/types/compat-entry.d.ts +0 -5
  128. package/dist/types/components/permission/permission.d.ts +0 -96
  129. package/dist/zx-compat.esm.js +0 -17484
  130. package/dist/zx-compat.esm.min.js +0 -1
  131. package/dist/zx-compat.global.js +0 -23635
  132. package/dist/zx-compat.global.min.js +0 -3
  133. package/src/compat/base.js +0 -291
  134. package/src/compat/globals.js +0 -165
  135. package/src/compat/index.js +0 -84
  136. package/src/compat/map/basic.js +0 -464
  137. package/src/compat/map/data.js +0 -232
  138. package/src/compat/map/date-time.js +0 -177
  139. package/src/compat/map/factory.js +0 -45
  140. package/src/compat/map/form.js +0 -330
  141. package/src/compat/map/helpers.js +0 -46
  142. package/src/compat/map/http.js +0 -117
  143. package/src/compat/map/layout.js +0 -256
  144. package/src/compat/map/options.js +0 -267
  145. package/src/compat/map/select.js +0 -198
  146. package/src/compat/map/stubs.js +0 -26
  147. package/src/compat/map/utilities.js +0 -113
  148. package/src/compat-entry.js +0 -11
  149. package/src/components/permission/permission.css +0 -47
  150. 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
- > Zx is the dependency-free, vanilla-JavaScript UI component library for ZeyOS business
4
- > applications. ES2022 modules, WAI-ARIA-accessible native
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
- > gx-compatibility, and CSS bundles. Zero runtime dependencies.
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](../website/docs.html): getting started, every component with a live demo and its JavaScript source, and the application layouts. Machine index: [website/llms.txt](../website/llms.txt).
17
- - [README](../README.md): what Zx is, quick start (ESM + global script), component index, theming, browser support, gx compat.
18
- - [API reference](llms.md): per-component constructor options, methods, and events for all 30+ components plus the core helpers.
19
- - [Skill](../.claude/skills/zx/SKILL.md): task-oriented guide — mental model, recipes, ZeyOS HTTP client, theming, gotchas, commands.
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
- - [Migration guide](../MIGRATION.md): gx→zx class map, script-tag replacement, deliberate behavior changes, unsupported legacy APIs.
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`, `Toggle`, `Search`, `Select` (APG combobox; `filter: false|'local'|async`), `Checklist`, `TagPicker`, `NumberField`, `Rating`, `Slider`, `copyButton()`/`CopyInput`, `DatePicker`, `MonthPicker`, `TimePicker`, `Datebox`/`DateTimeBox`, `DateRangePicker`/`DateRangeBox`, `Timebox`.
26
- - Overlays: `Message` (toasts/inline/progress), `Modal`, `Dialog` (+ `alert`/`confirm`/`prompt` Promises), `Dropdown`, `MenuButton`, `ContextMenu` (right-click; `selector` for per-row menus), `Tooltip`/`tooltip()`/`describe()`.
27
- - Data: `Table` (sortable, selectable, sticky header, opt-in inline cell/row editing via `editMode`), `DataFilter`, `Pagination` (+ pure `paginationRange()`), `TreeView` (APG tree), `Finder` (Miller columns).
28
- - Forms: `Form`, `Fieldset`, `Field` (type registry), `ValueList`, `MultiValueEditor`, `FieldUpload`, `Permission`.
29
- - Layout: `stack()`/`grid()`/`aspect()` (spacing, an intrinsically reflowing grid, a fixed-ratio box), `Groupbox`, `Panel` (header/footer action buttons), `MasterPanel`, `Tabbox` (`variant: 'divided'|'bracket'|'line'|'segmented'`), `NavigationBar`, `Toolbar` (APG toolbar with overflow menu), `SplitView`, `Stepper`, `Breadcrumb`, `emptyState()`.
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`. It replaces the legacy `gx.zeyos.Client`/`gx.zeyos.Request`. 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.
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), `./compat`
42
- (gx namespace), `./zx.css` (styles), `./global` (classic script tag). Docs: https://zx.zeyos.com
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": "2.3.0",
4
- "description": "Dependency-free, accessible UI component library for ZeyOS business applications.",
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
- "MIGRATION.md",
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); }