@zeyos/zx 4.4.0 → 5.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 +283 -0
- package/README.md +42 -10
- package/dist/types/components/activity-list/activity-list.d.ts +22 -0
- package/dist/types/components/app-sidebar/app-sidebar.d.ts +5 -3
- 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/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/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 +2 -2
- package/dist/types/components/entity-ref/entity-ref.d.ts +2 -2
- package/dist/types/components/field/field.d.ts +24 -2
- package/dist/types/components/fieldset/fieldset.d.ts +19 -1
- package/dist/types/components/file-item/file-item.d.ts +34 -6
- 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 +260 -15
- package/dist/types/components/list-toolbar/list-toolbar.d.ts +1 -1
- package/dist/types/components/menu-button/menu-button.d.ts +33 -5
- package/dist/types/components/message/message.d.ts +119 -17
- package/dist/types/components/modal/modal.d.ts +13 -2
- 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 +11 -3
- 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 +46 -1
- 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/core/breakpoint.d.ts +1 -1
- package/dist/types/core/icons.d.ts +23 -25
- package/dist/types/core/motion.d.ts +59 -0
- package/dist/types/core/overlay-host.d.ts +15 -1
- package/dist/types/index.d.ts +5 -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 +1 -1
- package/dist/types/zeyos/query.d.ts +79 -0
- package/dist/types/zeyos/schema.d.ts +24 -1
- package/dist/types/zeyos/table.d.ts +100 -14
- package/dist/types/zeyos/view.d.ts +10 -0
- package/dist/zx-zeyos.esm.js +1621 -443
- package/dist/zx-zeyos.esm.min.js +53 -5
- package/dist/zx.css +1411 -660
- package/dist/zx.esm.js +1976 -703
- package/dist/zx.esm.min.js +56 -8
- package/dist/zx.global.js +1978 -705
- package/dist/zx.global.min.js +56 -8
- package/dist/zx.min.css +1 -1
- package/docs/api.json +219 -34
- package/docs/llms.md +472 -110
- package/docs/llms.txt +3 -3
- package/package.json +1 -1
- package/src/components/account-menu/account-menu.css +35 -10
- 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 +34 -0
- package/src/components/app-icon/app-icon.css +1 -1
- package/src/components/app-rail/app-rail.css +40 -79
- package/src/components/app-sidebar/app-sidebar.css +109 -174
- package/src/components/app-sidebar/app-sidebar.js +21 -6
- 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/button/button.css +67 -9
- package/src/components/button/button.js +38 -0
- package/src/components/calendar/calendar.css +46 -46
- package/src/components/card/card.css +2 -2
- package/src/components/card-view/card-view.css +9 -7
- 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 +6 -5
- package/src/components/code-editor/code-editor.js +19 -1
- package/src/components/context-menu/context-menu.css +9 -4
- package/src/components/copy/copy-button.css +6 -0
- package/src/components/copy/copy-input.css +8 -3
- package/src/components/data-filter/data-filter.css +5 -4
- 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 +1 -1
- 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/dropdown/dropdown.css +1 -1
- package/src/components/dropdown/dropdown.js +5 -3
- 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 +68 -26
- package/src/components/field-upload/field-upload.css +1 -1
- package/src/components/fieldset/fieldset.css +5 -3
- package/src/components/fieldset/fieldset.js +28 -5
- package/src/components/file-item/file-item.js +144 -10
- package/src/components/filter/filter.js +7 -3
- package/src/components/filter-panel/filter-panel.css +1 -1
- package/src/components/finder/finder.css +15 -13
- 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 +39 -15
- package/src/components/kanban-view/kanban-view.js +4 -4
- package/src/components/launcher/launcher.css +107 -25
- package/src/components/launcher/launcher.js +503 -41
- package/src/components/list-toolbar/list-toolbar.css +13 -8
- package/src/components/list-toolbar/list-toolbar.js +1 -1
- package/src/components/master-panel/master-panel.css +2 -0
- package/src/components/menu-button/menu-button.css +9 -4
- package/src/components/menu-button/menu-button.js +65 -9
- package/src/components/message/message.css +33 -3
- package/src/components/message/message.js +217 -15
- package/src/components/modal/modal.js +18 -3
- package/src/components/navigation-bar/navigation-bar.css +18 -9
- package/src/components/number-field/field-adapter.js +1 -0
- 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 +8 -6
- package/src/components/panel/panel.css +1 -1
- package/src/components/rating/rating.css +7 -0
- 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 +83 -47
- package/src/components/sheet/sheet.css +11 -8
- package/src/components/slider/slider.css +16 -2
- package/src/components/slider/slider.js +1 -1
- package/src/components/sort-control/sort-control.css +4 -1
- package/src/components/stat-tile/stat-tile.css +41 -46
- package/src/components/stat-tile/stat-tile.js +17 -6
- package/src/components/tabbox/tabbox.css +32 -27
- package/src/components/tabbox/tabbox.js +5 -1
- package/src/components/table/table.css +104 -29
- package/src/components/table/table.js +90 -16
- 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 +10 -3
- package/src/components/tree/tree.css +25 -16
- package/src/components/tree/tree.js +31 -0
- package/src/components/value-list/value-list.css +3 -3
- package/src/components/view/record-view.js +1 -1
- package/src/core/breakpoint.js +1 -1
- package/src/core/icons.js +193 -60
- package/src/core/motion.js +147 -0
- package/src/core/overlay-host.js +21 -2
- package/src/index.js +5 -3
- package/src/internal/app-rail.js +1 -2
- package/src/zeyos/connect.js +6 -2
- package/src/zeyos/form.js +67 -27
- package/src/zeyos/index.js +1 -1
- package/src/zeyos/query.js +153 -0
- package/src/zeyos/schema.js +144 -27
- package/src/zeyos/table.js +68 -5
- package/src/zeyos/view.js +6 -0
- package/styles/base.css +56 -0
- package/styles/icons.css +11 -1
- package/styles/tokens/dark.css +113 -37
- package/styles/tokens/global.css +121 -27
- package/styles/tokens/semantic.css +100 -31
- package/styles/tokens/themes.css +7 -6
- package/styles/zx.css +0 -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
|
|
@@ -56,7 +56,7 @@ exports and as `window.zx.*`.
|
|
|
56
56
|
- 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
57
|
|
|
58
58
|
## Theming
|
|
59
|
-
Load `zx.css` once; set `data-zx-theme` (`light`|`dark`|`auto`)
|
|
59
|
+
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
60
|
|
|
61
61
|
## Talking to ZeyOS
|
|
62
62
|
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 {
|
|
@@ -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
|
+
}
|
|
@@ -9,6 +9,13 @@ import { ActivityItem } from '../activity-item/activity-item.js';
|
|
|
9
9
|
/** @typedef {string|number|Node|Component|null} ActivityListContent */
|
|
10
10
|
/** @typedef {string|((item:ActivityItemOptions,index:number)=>unknown)|null} ActivityGroupReader */
|
|
11
11
|
/** @typedef {(group:unknown,items:ActivityItemOptions[])=>ActivityListContent} ActivityGroupLabel */
|
|
12
|
+
/**
|
|
13
|
+
* Entry layout. `'default'` gives each entry its avatar, a heading row, and room for content;
|
|
14
|
+
* below 30rem the time moves under the heading. `'compact'` is one line per entry at any width —
|
|
15
|
+
* actor and summary reading as one sentence, the time at the trailing end, a small avatar, and
|
|
16
|
+
* metadata as a muted second line — for a summary card narrower than 30rem.
|
|
17
|
+
* @typedef {'default'|'compact'} ActivityListVariant
|
|
18
|
+
*/
|
|
12
19
|
|
|
13
20
|
/**
|
|
14
21
|
* @typedef {Object} ActivityGroup
|
|
@@ -22,6 +29,8 @@ import { ActivityItem } from '../activity-item/activity-item.js';
|
|
|
22
29
|
* @property {ActivityGroupReader} [groupBy=null] Property or resolver used for stable grouping.
|
|
23
30
|
* @property {ActivityGroupLabel|null} [groupLabel=null] Group-heading renderer.
|
|
24
31
|
* @property {string} [label='Activity'] Accessible collection name.
|
|
32
|
+
* @property {ActivityListVariant} [variant='default'] Entry layout, reflected as `data-variant` on
|
|
33
|
+
* the root. `'compact'` also makes an avatar descriptor without a `size` small.
|
|
25
34
|
* @property {boolean} [loading=false] Whether initial or incremental activity is loading.
|
|
26
35
|
* @property {number} [loadingCount=3] Placeholder rows shown for an empty loading list.
|
|
27
36
|
* @property {string} [loadingText='Loading activity'] Loading status text.
|
|
@@ -48,6 +57,7 @@ export class ActivityList extends Component {
|
|
|
48
57
|
groupBy: null,
|
|
49
58
|
groupLabel: null,
|
|
50
59
|
label: 'Activity',
|
|
60
|
+
variant: 'default',
|
|
51
61
|
loading: false,
|
|
52
62
|
loadingCount: 3,
|
|
53
63
|
loadingText: 'Loading activity',
|
|
@@ -78,6 +88,8 @@ export class ActivityList extends Component {
|
|
|
78
88
|
this._itemComponents = [];
|
|
79
89
|
this._groupSequence = 0;
|
|
80
90
|
root.setAttribute('aria-label', String(this.options.label || 'Activity'));
|
|
91
|
+
this._variant = normalizeActivityListVariant(this.options.variant);
|
|
92
|
+
if (this._variant === 'compact') root.dataset.variant = 'compact';
|
|
81
93
|
try {
|
|
82
94
|
this._renderList();
|
|
83
95
|
return root;
|
|
@@ -229,6 +241,7 @@ export class ActivityList extends Component {
|
|
|
229
241
|
for (const item of group.items) {
|
|
230
242
|
const component = new ActivityItem(null, {
|
|
231
243
|
...item,
|
|
244
|
+
avatar: this._variant === 'compact' ? compactAvatar(item.avatar) : item.avatar,
|
|
232
245
|
headingLevel: item.headingLevel ?? this.options.itemHeadingLevel
|
|
233
246
|
});
|
|
234
247
|
this._itemComponents.push(component);
|
|
@@ -362,6 +375,27 @@ function normalizeHeadingLevel(value, fallback) {
|
|
|
362
375
|
return /** @type {1|2|3|4|5|6} */ (level >= 1 && level <= 6 ? level : fallback);
|
|
363
376
|
}
|
|
364
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Resolves the entry layout; anything but `'compact'` is the default layout.
|
|
380
|
+
* @param {unknown} value Requested variant.
|
|
381
|
+
* @returns {ActivityListVariant}
|
|
382
|
+
*/
|
|
383
|
+
export function normalizeActivityListVariant(value) {
|
|
384
|
+
return value === 'compact' ? 'compact' : 'default';
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* A compact entry is one line tall, so an avatar descriptor that leaves its size open gets the
|
|
389
|
+
* small one. An explicit size, an Element, or no avatar passes through untouched.
|
|
390
|
+
* @param {ActivityItemOptions['avatar']} avatar Avatar descriptor, Element, or null.
|
|
391
|
+
* @returns {ActivityItemOptions['avatar']}
|
|
392
|
+
*/
|
|
393
|
+
function compactAvatar(avatar) {
|
|
394
|
+
if (!isPlainObject(avatar)) return avatar;
|
|
395
|
+
const descriptor = /** @type {import('../avatar/avatar.js').AvatarOptions} */ (avatar);
|
|
396
|
+
return descriptor.size == null ? { ...descriptor, size: 'sm' } : descriptor;
|
|
397
|
+
}
|
|
398
|
+
|
|
365
399
|
/** @param {unknown} value @returns {number} */
|
|
366
400
|
function normalizeLoadingCount(value) {
|
|
367
401
|
const count = Math.trunc(Number(value));
|
|
@@ -48,13 +48,19 @@
|
|
|
48
48
|
text-decoration: none;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/* Hover, an open flyout, and a branch over the active route are one light step; the current page
|
|
52
|
+
is the neutral current fill. Neither is the accent: where you are is not something you picked. */
|
|
51
53
|
.zx-app-rail__item:hover,
|
|
52
54
|
.zx-app-rail__item:focus-visible,
|
|
53
55
|
.zx-app-rail__item[aria-expanded="true"],
|
|
54
|
-
.zx-app-rail__item[data-active-descendant="true"]
|
|
55
|
-
.zx-app-rail__item[aria-current="page"] {
|
|
56
|
+
.zx-app-rail__item[data-active-descendant="true"] {
|
|
56
57
|
background: var(--zx-color-bg-hover);
|
|
57
|
-
color: var(--zx-color-
|
|
58
|
+
color: var(--zx-color-text);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.zx-app-rail__item[aria-current="page"] {
|
|
62
|
+
background: var(--zx-color-bg-current);
|
|
63
|
+
color: var(--zx-color-text);
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
.zx-app-rail__item[aria-disabled="true"] {
|
|
@@ -86,44 +92,44 @@
|
|
|
86
92
|
* A minimized sidebar is the same navigation as an expanded one, so the panel a rail item opens
|
|
87
93
|
* has to read as a slice of the tree rather than as a menu that happens to hang off it: the same
|
|
88
94
|
* icon gutter, the same item height, labels that wrap instead of truncating, and the same active
|
|
89
|
-
* recipe —
|
|
95
|
+
* recipe — the neutral current fill and a heavier label.
|
|
90
96
|
*
|
|
91
97
|
* The panel is portaled to the overlay host, so `.zx-app-sidebar` descendant selectors and the
|
|
92
98
|
* `--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.
|
|
99
|
+
* against the same tier-2 control and spacing tokens the sidebar derives it from, so the two
|
|
100
|
+
* surfaces move together when a token moves rather than agreeing on a number frozen twice.
|
|
95
101
|
*/
|
|
96
102
|
.zx-app-rail__popover {
|
|
97
103
|
--zx-app-rail-gutter: var(--zx-space-2);
|
|
98
104
|
--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)
|
|
105
|
+
--zx-app-rail-item-gap: var(--zx-space-2);
|
|
106
|
+
--zx-app-rail-item-height: var(--zx-control-height);
|
|
107
|
+
--zx-app-rail-pad-x: var(--zx-space-2);
|
|
108
|
+
--zx-app-rail-row-gap: 2px;
|
|
109
|
+
--zx-app-rail-radius: var(--zx-radius-md);
|
|
110
|
+
--zx-app-rail-hover: var(--zx-color-bg-hover);
|
|
111
|
+
--zx-app-rail-current: var(--zx-color-bg-current);
|
|
112
|
+
--zx-app-rail-press: color-mix(in srgb, var(--zx-color-text) 12%, transparent);
|
|
107
113
|
min-inline-size: 240px;
|
|
108
114
|
}
|
|
109
115
|
|
|
110
116
|
/* `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
|
-
|
|
117
|
+
to take back from the generic overlay surface — the padding, which is the tree's own gutter,
|
|
118
|
+
and the width the panel may grow to, which is the tree's own 280px — have to
|
|
113
119
|
out-specify it instead of merely following it. The viewport clamp the dropdown guarantees is
|
|
114
120
|
carried over rather than dropped. */
|
|
115
121
|
.zx-dropdown.zx-app-rail__popover {
|
|
116
122
|
max-inline-size: min(280px, calc(100vw - (2 * var(--zx-space-3))));
|
|
117
|
-
padding: var(--zx-space-
|
|
123
|
+
padding: var(--zx-space-1) var(--zx-app-rail-gutter);
|
|
118
124
|
}
|
|
119
125
|
|
|
120
126
|
/* The branch's own label, naming what the panel is a slice of. It aligns with the row padding
|
|
121
127
|
rather than with the labels under it: a caption for the list, not a row in it. */
|
|
122
128
|
.zx-app-rail__flyout-title {
|
|
123
|
-
padding: var(--zx-space-1) var(--zx-app-rail-pad-x)
|
|
129
|
+
padding: var(--zx-space-1) var(--zx-app-rail-pad-x);
|
|
124
130
|
color: var(--zx-color-text-muted);
|
|
125
131
|
font-size: var(--zx-text-xs);
|
|
126
|
-
font-weight:
|
|
132
|
+
font-weight: var(--zx-weight-semibold);
|
|
127
133
|
line-height: 1.3;
|
|
128
134
|
}
|
|
129
135
|
|
|
@@ -152,7 +158,9 @@
|
|
|
152
158
|
border: 0;
|
|
153
159
|
border-radius: var(--zx-app-rail-radius);
|
|
154
160
|
background: transparent;
|
|
155
|
-
|
|
161
|
+
/* The icon column fills the row exactly; a row without one keeps a space-1 margin round its
|
|
162
|
+
label. Either way a single-line row is one control tall, and a wrapped one grows. */
|
|
163
|
+
padding-block: min(var(--zx-space-1), (var(--zx-app-rail-item-height) - var(--zx-app-rail-icon)) / 2);
|
|
156
164
|
padding-inline: var(--zx-app-rail-pad-x);
|
|
157
165
|
color: var(--zx-color-text);
|
|
158
166
|
font-size: var(--zx-text-md);
|
|
@@ -166,39 +174,6 @@
|
|
|
166
174
|
.zx-app-rail__flyout-item { min-block-size: 40px; }
|
|
167
175
|
}
|
|
168
176
|
|
|
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
177
|
.zx-app-rail__flyout-item-icon {
|
|
203
178
|
grid-column: 1;
|
|
204
179
|
display: flex;
|
|
@@ -247,34 +222,28 @@
|
|
|
247
222
|
}
|
|
248
223
|
|
|
249
224
|
.zx-app-rail__flyout-item[data-active-descendant="true"][aria-expanded="false"] {
|
|
250
|
-
|
|
251
|
-
font-weight: 600;
|
|
225
|
+
font-weight: var(--zx-weight-semibold);
|
|
252
226
|
}
|
|
253
227
|
|
|
254
|
-
.zx-app-rail__flyout-item[aria-
|
|
255
|
-
|
|
256
|
-
color: var(--zx-color-on-accent);
|
|
257
|
-
font-weight: 600;
|
|
228
|
+
.zx-app-rail__flyout-item[data-active-descendant="true"][aria-expanded="false"] .zx-app-rail__flyout-item-icon {
|
|
229
|
+
color: var(--zx-color-text);
|
|
258
230
|
}
|
|
259
231
|
|
|
260
|
-
|
|
261
|
-
|
|
232
|
+
/* The current page, as in the tree: the neutral current fill, the full text colour and a heavier
|
|
233
|
+
label, with the shared focus ring drawn on top when a keyboard user arrives on it. */
|
|
234
|
+
.zx-app-rail__flyout-item[aria-current="page"] {
|
|
235
|
+
background: var(--zx-app-rail-current);
|
|
236
|
+
color: var(--zx-color-text);
|
|
237
|
+
font-weight: var(--zx-weight-semibold);
|
|
262
238
|
}
|
|
263
239
|
|
|
264
240
|
.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);
|
|
241
|
+
background: var(--zx-app-rail-press);
|
|
272
242
|
}
|
|
273
243
|
|
|
274
244
|
.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
245
|
.zx-app-rail__flyout-item[aria-current="page"] .zx-app-rail__flyout-item-disclosure {
|
|
277
|
-
color:
|
|
246
|
+
color: var(--zx-color-text);
|
|
278
247
|
}
|
|
279
248
|
|
|
280
249
|
/* Unavailable, and still readable: the icon carries the dimming so the label can keep the muted
|
|
@@ -296,23 +265,15 @@
|
|
|
296
265
|
box-shadow var(--zx-dur-fast) var(--zx-ease);
|
|
297
266
|
}
|
|
298
267
|
|
|
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
268
|
.zx-app-rail__flyout-item:active { transform: scale(.985); }
|
|
305
269
|
}
|
|
306
270
|
|
|
307
|
-
/* Forced colours drop the fill
|
|
308
|
-
state there instead. */
|
|
271
|
+
/* Forced colours drop the fill; the system's selected pair carries the state there instead. */
|
|
309
272
|
@media (forced-colors: active) {
|
|
310
273
|
.zx-app-rail__flyout-item[aria-current="page"] {
|
|
311
274
|
background: Highlight;
|
|
312
275
|
color: HighlightText;
|
|
313
276
|
}
|
|
314
|
-
|
|
315
|
-
.zx-app-rail__flyout-item::before { background: Highlight; }
|
|
316
277
|
}
|
|
317
278
|
|
|
318
279
|
@media (prefers-reduced-transparency: reduce) {
|