@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
|
@@ -2,39 +2,49 @@
|
|
|
2
2
|
* AppSidebar — the surface an application is judged by before it is read.
|
|
3
3
|
*
|
|
4
4
|
* Everything here hangs off one spacing scale declared on the root. Item height, icon gutter,
|
|
5
|
-
* row gap, group gap and indentation are derived from
|
|
6
|
-
*
|
|
5
|
+
* row gap, group gap and indentation are derived from the tier-2 control and spacing tokens
|
|
6
|
+
* rather than picked per rule, so a theme that changes `--zx-space-unit` or
|
|
7
|
+
* `--zx-control-height` changes the rhythm of the whole nav instead of one row of it.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
+
* Three deliberate choices worth stating:
|
|
9
10
|
*
|
|
10
11
|
* - **Labels wrap, they never truncate.** German runs 20-35% longer than English and a nav is
|
|
11
12
|
* where that shows first: "Belege & Rechnungen" is two lines in a 16.5rem sidebar. An ellipsis
|
|
12
13
|
* there hides the word that distinguishes one destination from another, so the row grows
|
|
13
14
|
* instead and the fixed icon column keeps the second line aligned under the first.
|
|
14
|
-
* - **The active state never rests on colour alone.** It is a
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* - **The active state never rests on colour alone.** It is a neutral fill and a heavier label —
|
|
16
|
+
* where you are, not something you picked, so it takes the `--zx-color-bg-current` role rather
|
|
17
|
+
* than the accent. The weight survives greyscale and a reduced-transparency page, and forced
|
|
18
|
+
* colours swap the fill for the system's own selected pair. Nothing is docked to the edge.
|
|
19
|
+
* - **A child never starts further in than its parent's label.** A text-only child lines up with
|
|
20
|
+
* the words above it; a child with its own icon steps in only far enough for that icon to clear
|
|
21
|
+
* the guide line. A parent list without icons indents its children by a short text step.
|
|
17
22
|
*/
|
|
18
23
|
|
|
19
24
|
.zx-app-sidebar {
|
|
20
|
-
/* The scale.
|
|
21
|
-
|
|
22
|
-
column and the indent steady — a narrower rhythm, not a smaller one. */
|
|
25
|
+
/* The scale. A row is exactly one control tall, the icon column is as wide as the 24px module
|
|
26
|
+
chips at the default unit, and every gap and inset is a step of the spacing scale. */
|
|
23
27
|
--zx-app-sidebar-gutter: var(--zx-space-2);
|
|
24
28
|
--zx-app-sidebar-icon: var(--zx-space-6);
|
|
25
|
-
--zx-app-sidebar-item-gap: var(--zx-space-
|
|
26
|
-
--zx-app-sidebar-item-height:
|
|
27
|
-
--zx-app-sidebar-pad-x: var(--zx-space-
|
|
28
|
-
--zx-app-sidebar-row-gap:
|
|
29
|
-
--zx-app-sidebar-group-gap: var(--zx-space-
|
|
30
|
-
|
|
31
|
-
--zx-app-sidebar-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
--zx-app-sidebar-item-gap: var(--zx-space-2);
|
|
30
|
+
--zx-app-sidebar-item-height: var(--zx-control-height);
|
|
31
|
+
--zx-app-sidebar-pad-x: var(--zx-space-2);
|
|
32
|
+
--zx-app-sidebar-row-gap: 2px;
|
|
33
|
+
--zx-app-sidebar-group-gap: var(--zx-space-3);
|
|
34
|
+
/* The indent under a parent list that has no icon column to hand down. */
|
|
35
|
+
--zx-app-sidebar-indent: var(--zx-space-3);
|
|
36
|
+
/* The indent of a child list that brings icons of its own: enough for them to clear the guide,
|
|
37
|
+
not a whole icon column, so a deep branch never walks across the sidebar. */
|
|
38
|
+
--zx-app-sidebar-icon-indent: var(--zx-space-4);
|
|
39
|
+
--zx-app-sidebar-radius: var(--zx-radius-md);
|
|
40
|
+
/* Three steps of one overlay: hover, the current row, and press. The hover and current roles
|
|
41
|
+
mix the *text* colour into whatever sits behind, which moves the fill away from the page in
|
|
42
|
+
whichever direction the theme runs, so a sidebar on the grey chrome tone and one on a white
|
|
43
|
+
surface both read correctly. Hover stays below the current fill, so pointing at a row never
|
|
44
|
+
looks like being on it; press goes one step past it. */
|
|
45
|
+
--zx-app-sidebar-hover: var(--zx-color-bg-hover);
|
|
46
|
+
--zx-app-sidebar-current: var(--zx-color-bg-current);
|
|
47
|
+
--zx-app-sidebar-press: color-mix(in srgb, var(--zx-color-text) 12%, transparent);
|
|
38
48
|
display: flex;
|
|
39
49
|
min-block-size: 0;
|
|
40
50
|
border: 1px solid var(--zx-color-glass-border);
|
|
@@ -43,11 +53,6 @@
|
|
|
43
53
|
color: var(--zx-color-text);
|
|
44
54
|
}
|
|
45
55
|
|
|
46
|
-
[data-zx-density="compact"] .zx-app-sidebar,
|
|
47
|
-
.zx-app-sidebar[data-zx-density="compact"] {
|
|
48
|
-
--zx-app-sidebar-group-gap: var(--zx-space-3);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
56
|
.zx-app-sidebar[data-orientation="horizontal"] {
|
|
52
57
|
inline-size: 100%;
|
|
53
58
|
}
|
|
@@ -84,6 +89,9 @@
|
|
|
84
89
|
|
|
85
90
|
.zx-app-sidebar__header-content { min-inline-size: 0; }
|
|
86
91
|
|
|
92
|
+
/* `collapsible: false` hides the minimise button; the icon-button display rule must not show it. */
|
|
93
|
+
.zx-app-sidebar .zx-app-sidebar__collapse[hidden] { display: none; }
|
|
94
|
+
|
|
87
95
|
/* The block padding is not decoration: the focus ring is a 3px outer glow, and a nav that padded
|
|
88
96
|
its scroll box by less would clip the ring off the first and last row. */
|
|
89
97
|
.zx-app-sidebar__nav {
|
|
@@ -101,13 +109,42 @@
|
|
|
101
109
|
list-style: none;
|
|
102
110
|
}
|
|
103
111
|
|
|
104
|
-
/* A
|
|
105
|
-
single icon keeps the column on every row so the labels stay in one line down the list.
|
|
106
|
-
|
|
112
|
+
/* A list whose items carry no icons should not pay for an empty gutter; one that has even a
|
|
113
|
+
single icon keeps the column on every row so the labels stay in one line down the list. Each
|
|
114
|
+
list answers for its own rows only — a branch of plain labels under iconned parents drops the
|
|
115
|
+
column without taking it from the parents. */
|
|
116
|
+
.zx-app-sidebar__list:not(:has(> .zx-app-sidebar__row > .zx-app-sidebar__item .zx-app-sidebar__item-icon)),
|
|
117
|
+
.zx-app-sidebar__sublist:not(:has(> .zx-app-sidebar__row > .zx-app-sidebar__item .zx-app-sidebar__item-icon)) {
|
|
107
118
|
--zx-app-sidebar-icon: 0px;
|
|
108
119
|
--zx-app-sidebar-item-gap: 0px;
|
|
109
120
|
}
|
|
110
121
|
|
|
122
|
+
/* Where a branch's children start, measured from the row padding, and where the guide that scopes
|
|
123
|
+
them runs. Both are resolved on the parent row — where the parent's own icon column is still
|
|
124
|
+
the one in force — and inherited from there, so a child list that drops its column cannot pull
|
|
125
|
+
the indent or the guide back with it. A row's step is its icon column, or the text indent when
|
|
126
|
+
it has none; level 3 adds its own step to the one level 2 already sits at. */
|
|
127
|
+
.zx-app-sidebar__row[data-level="1"] {
|
|
128
|
+
--zx-app-sidebar-step-1: max(var(--zx-app-sidebar-indent), var(--zx-app-sidebar-icon) + var(--zx-app-sidebar-item-gap));
|
|
129
|
+
--zx-app-sidebar-guide: calc(var(--zx-app-sidebar-pad-x) + max(var(--zx-app-sidebar-indent), var(--zx-app-sidebar-icon)) / 2);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.zx-app-sidebar__row[data-level="2"] {
|
|
133
|
+
--zx-app-sidebar-step-2: calc(var(--zx-app-sidebar-step-1) + max(var(--zx-app-sidebar-indent), var(--zx-app-sidebar-icon) + var(--zx-app-sidebar-item-gap)));
|
|
134
|
+
--zx-app-sidebar-guide: calc(var(--zx-app-sidebar-pad-x) + var(--zx-app-sidebar-step-1) + max(var(--zx-app-sidebar-indent), var(--zx-app-sidebar-icon)) / 2);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* Children with icons of their own step in by the shorter icon indent instead: their icons sit
|
|
138
|
+
just past the guide rather than under the parent's label. Text-only children keep lining up
|
|
139
|
+
with the words above them. */
|
|
140
|
+
.zx-app-sidebar__row[data-level="1"]:has(> .zx-app-sidebar__sublist > .zx-app-sidebar__row > .zx-app-sidebar__item .zx-app-sidebar__item-icon) {
|
|
141
|
+
--zx-app-sidebar-step-1: max(var(--zx-app-sidebar-indent), var(--zx-app-sidebar-icon-indent));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.zx-app-sidebar__row[data-level="2"]:has(> .zx-app-sidebar__sublist > .zx-app-sidebar__row > .zx-app-sidebar__item .zx-app-sidebar__item-icon) {
|
|
145
|
+
--zx-app-sidebar-step-2: calc(var(--zx-app-sidebar-step-1) + max(var(--zx-app-sidebar-indent), var(--zx-app-sidebar-icon-indent)));
|
|
146
|
+
}
|
|
147
|
+
|
|
111
148
|
.zx-app-sidebar__sublist { position: relative; margin-block-start: var(--zx-app-sidebar-row-gap); }
|
|
112
149
|
.zx-app-sidebar__sublist[hidden] { display: none; }
|
|
113
150
|
|
|
@@ -116,8 +153,8 @@
|
|
|
116
153
|
.zx-app-sidebar__sublist::before {
|
|
117
154
|
content: "";
|
|
118
155
|
position: absolute;
|
|
119
|
-
inset-block:
|
|
120
|
-
inset-inline-start:
|
|
156
|
+
inset-block: 2px;
|
|
157
|
+
inset-inline-start: var(--zx-app-sidebar-guide);
|
|
121
158
|
inline-size: 1px;
|
|
122
159
|
border-radius: var(--zx-radius-full);
|
|
123
160
|
background: var(--zx-color-border);
|
|
@@ -146,7 +183,9 @@
|
|
|
146
183
|
border: 0;
|
|
147
184
|
border-radius: var(--zx-app-sidebar-radius);
|
|
148
185
|
background: transparent;
|
|
149
|
-
|
|
186
|
+
/* The icon column fills the row exactly; a row without one keeps a space-1 margin round its
|
|
187
|
+
label. Either way a single-line row is one control tall, and a wrapped one grows. */
|
|
188
|
+
padding-block: min(var(--zx-space-1), (var(--zx-app-sidebar-item-height) - var(--zx-app-sidebar-icon)) / 2);
|
|
150
189
|
padding-inline: var(--zx-app-sidebar-pad-x);
|
|
151
190
|
color: var(--zx-color-text);
|
|
152
191
|
font-size: var(--zx-text-md);
|
|
@@ -156,49 +195,14 @@
|
|
|
156
195
|
text-decoration: none;
|
|
157
196
|
}
|
|
158
197
|
|
|
159
|
-
/* A nav is a touch target before it is a dense list.
|
|
160
|
-
coarse pointer puts the hit area back to 40px whatever the
|
|
198
|
+
/* A nav is a touch target before it is a dense list. The drawn row is one control tall, and a
|
|
199
|
+
coarse pointer puts the hit area back to 40px whatever the theme's control height says. */
|
|
161
200
|
@media (pointer: coarse) {
|
|
162
201
|
.zx-app-sidebar__item { min-block-size: 40px; }
|
|
163
202
|
}
|
|
164
203
|
|
|
165
|
-
.zx-app-sidebar__row[data-level="2"] > .zx-app-sidebar__item { padding-inline-start: calc(var(--zx-app-sidebar-pad-x) + var(--zx-app-sidebar-
|
|
166
|
-
.zx-app-sidebar__row[data-level="3"] > .zx-app-sidebar__item { padding-inline-start: calc(var(--zx-app-sidebar-pad-x) +
|
|
167
|
-
|
|
168
|
-
/* The active marker. It is anchored to the control and not to the row, because a row is the
|
|
169
|
-
branch *and everything open under it* — a marker measured against that would grow into a
|
|
170
|
-
bracket down the whole group. It sits in the nav's own gutter, docked to the sidebar's outer
|
|
171
|
-
edge, so every marker lines up whatever the nesting depth, and it is the signal that survives
|
|
172
|
-
when colour does not. A collapsed branch that merely *contains* the active route gets a short
|
|
173
|
-
stub of the same marker: the same vocabulary, a quieter sentence. */
|
|
174
|
-
.zx-app-sidebar__item::before {
|
|
175
|
-
content: "";
|
|
176
|
-
position: absolute;
|
|
177
|
-
inset-block: 50%;
|
|
178
|
-
inset-inline-start: calc(-1 * var(--zx-app-sidebar-gutter));
|
|
179
|
-
inline-size: var(--zx-app-sidebar-marker);
|
|
180
|
-
border-start-end-radius: var(--zx-radius-full);
|
|
181
|
-
border-end-end-radius: var(--zx-radius-full);
|
|
182
|
-
background: var(--zx-color-accent);
|
|
183
|
-
opacity: 0;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
.zx-app-sidebar[data-side="right"] .zx-app-sidebar__item::before {
|
|
187
|
-
inset-inline: auto calc(-1 * var(--zx-app-sidebar-gutter));
|
|
188
|
-
border-radius: 0;
|
|
189
|
-
border-start-start-radius: var(--zx-radius-full);
|
|
190
|
-
border-end-start-radius: var(--zx-radius-full);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.zx-app-sidebar__item[aria-current="page"]::before {
|
|
194
|
-
inset-block: 25%;
|
|
195
|
-
opacity: 1;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
.zx-app-sidebar__item[data-active-descendant="true"][aria-expanded="false"]::before {
|
|
199
|
-
inset-block: 38%;
|
|
200
|
-
opacity: .55;
|
|
201
|
-
}
|
|
204
|
+
.zx-app-sidebar__row[data-level="2"] > .zx-app-sidebar__item { padding-inline-start: calc(var(--zx-app-sidebar-pad-x) + var(--zx-app-sidebar-step-1)); }
|
|
205
|
+
.zx-app-sidebar__row[data-level="3"] > .zx-app-sidebar__item { padding-inline-start: calc(var(--zx-app-sidebar-pad-x) + var(--zx-app-sidebar-step-2)); }
|
|
202
206
|
|
|
203
207
|
.zx-app-sidebar__item-icon {
|
|
204
208
|
grid-column: 1;
|
|
@@ -241,42 +245,34 @@
|
|
|
241
245
|
.zx-app-sidebar__item:hover .zx-app-sidebar__item-disclosure { color: var(--zx-color-text); }
|
|
242
246
|
.zx-app-sidebar__item:active { background: var(--zx-app-sidebar-press); }
|
|
243
247
|
|
|
244
|
-
/* A branch that is closed over the active route
|
|
245
|
-
|
|
248
|
+
/* A branch that is closed over the active route carries the heavier label, so the way back to
|
|
249
|
+
the current page is visible without opening it; a branch that is open says so with its chevron
|
|
250
|
+
and shows the active row itself. */
|
|
246
251
|
.zx-app-sidebar__item[data-active-descendant="true"][aria-expanded="false"] {
|
|
247
|
-
|
|
248
|
-
font-weight: 600;
|
|
252
|
+
font-weight: var(--zx-weight-semibold);
|
|
249
253
|
}
|
|
250
254
|
|
|
251
|
-
.zx-app-sidebar__item[aria-
|
|
252
|
-
|
|
253
|
-
color: var(--zx-color-on-accent);
|
|
254
|
-
font-weight: 600;
|
|
255
|
+
.zx-app-sidebar__item[data-active-descendant="true"][aria-expanded="false"] .zx-app-sidebar__item-icon {
|
|
256
|
+
color: var(--zx-color-text);
|
|
255
257
|
}
|
|
256
258
|
|
|
257
|
-
/*
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
259
|
+
/* The current page: a neutral fill, the full text colour, a heavier label. Focus is a ring on top
|
|
260
|
+
of it, never a replacement for it, so a keyboard user arrowing past the active route can still
|
|
261
|
+
tell which row is current — the shared `--zx-focus-ring` reads on a neutral fill as it does on
|
|
262
|
+
none. */
|
|
263
|
+
.zx-app-sidebar__item[aria-current="page"] {
|
|
264
|
+
background: var(--zx-app-sidebar-current);
|
|
265
|
+
color: var(--zx-color-text);
|
|
266
|
+
font-weight: var(--zx-weight-semibold);
|
|
261
267
|
}
|
|
262
268
|
|
|
263
269
|
.zx-app-sidebar__item[aria-current="page"]:active {
|
|
264
|
-
background:
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/* Focus is a ring, never a fill: a keyboard user arrowing past the active route must still be
|
|
268
|
-
able to tell which row is current. Ordinary rows take the shared `--zx-focus-ring` from
|
|
269
|
-
`base.css` untouched; only the filled pill needs help, because an accent glow around an accent
|
|
270
|
-
fill is a ring nobody can see. The inner line is drawn in the pill's own text colour, which the
|
|
271
|
-
preset contract already guarantees against that fill. */
|
|
272
|
-
.zx-app-sidebar__item[aria-current="page"]:focus-visible {
|
|
273
|
-
box-shadow: inset 0 0 0 2px var(--zx-color-on-accent), var(--zx-focus-ring);
|
|
270
|
+
background: var(--zx-app-sidebar-press);
|
|
274
271
|
}
|
|
275
272
|
|
|
276
273
|
.zx-app-sidebar__item[aria-current="page"] .zx-app-sidebar__item-icon,
|
|
277
|
-
.zx-app-sidebar__item[aria-current="page"] .zx-app-sidebar__item-badge,
|
|
278
274
|
.zx-app-sidebar__item[aria-current="page"] .zx-app-sidebar__item-disclosure {
|
|
279
|
-
color:
|
|
275
|
+
color: var(--zx-color-text);
|
|
280
276
|
}
|
|
281
277
|
|
|
282
278
|
/* Unavailable, and still readable. A blanket `opacity` on the row took the label under 3:1, which
|
|
@@ -301,16 +297,12 @@
|
|
|
301
297
|
.zx-app-sidebar__rail-header { display: grid; gap: var(--zx-space-1); }
|
|
302
298
|
|
|
303
299
|
/*
|
|
304
|
-
* The minimized rail. Only the presentation changes; the same vocabulary applies —
|
|
305
|
-
*
|
|
300
|
+
* The minimized rail. Only the presentation changes; the same vocabulary applies — the neutral
|
|
301
|
+
* current fill, the full text colour for a branch over the active route, a badge that survives
|
|
302
|
+
* the loss of the label.
|
|
306
303
|
*/
|
|
307
|
-
.zx-app-sidebar__rail .zx-app-rail__item
|
|
308
|
-
|
|
309
|
-
color: var(--zx-color-on-accent);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
.zx-app-sidebar__rail .zx-app-rail__item[aria-current="page"]:hover {
|
|
313
|
-
background: color-mix(in srgb, var(--zx-color-text) 8%, var(--zx-color-accent));
|
|
304
|
+
.zx-app-sidebar__rail .zx-app-rail__item {
|
|
305
|
+
color: var(--zx-color-text-muted);
|
|
314
306
|
}
|
|
315
307
|
|
|
316
308
|
.zx-app-sidebar__rail .zx-app-rail__item:not([aria-current="page"]):hover,
|
|
@@ -319,65 +311,25 @@
|
|
|
319
311
|
color: var(--zx-color-text);
|
|
320
312
|
}
|
|
321
313
|
|
|
322
|
-
.zx-app-sidebar__rail .zx-app-rail__item:
|
|
314
|
+
.zx-app-sidebar__rail .zx-app-rail__item:active {
|
|
323
315
|
background: var(--zx-app-sidebar-press);
|
|
324
316
|
}
|
|
325
317
|
|
|
326
|
-
.zx-app-sidebar__rail .zx-app-rail__item[aria-current="page"]
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
.zx-app-sidebar__rail .zx-app-rail__item::after {
|
|
331
|
-
content: "";
|
|
332
|
-
position: absolute;
|
|
333
|
-
inset-block: 22%;
|
|
334
|
-
inset-inline-start: calc(-1 * var(--zx-space-1));
|
|
335
|
-
inline-size: var(--zx-app-sidebar-marker);
|
|
336
|
-
border-start-end-radius: var(--zx-radius-full);
|
|
337
|
-
border-end-end-radius: var(--zx-radius-full);
|
|
338
|
-
background: var(--zx-color-accent);
|
|
339
|
-
opacity: 0;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
.zx-app-sidebar[data-side="right"] .zx-app-rail__item::after {
|
|
343
|
-
inset-inline: auto calc(-1 * var(--zx-space-1));
|
|
344
|
-
border-radius: 0;
|
|
345
|
-
border-start-start-radius: var(--zx-radius-full);
|
|
346
|
-
border-end-start-radius: var(--zx-radius-full);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
.zx-app-sidebar[data-orientation="horizontal"] .zx-app-rail__item::after {
|
|
350
|
-
inset-block: auto calc(-1 * var(--zx-space-1));
|
|
351
|
-
inset-inline: 22%;
|
|
352
|
-
inline-size: auto;
|
|
353
|
-
block-size: var(--zx-app-sidebar-marker);
|
|
354
|
-
border-radius: 0;
|
|
355
|
-
border-start-start-radius: var(--zx-radius-full);
|
|
356
|
-
border-start-end-radius: var(--zx-radius-full);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
.zx-app-sidebar[data-side="bottom"] .zx-app-rail__item::after {
|
|
360
|
-
inset-block: calc(-1 * var(--zx-space-1)) auto;
|
|
361
|
-
border-radius: 0;
|
|
362
|
-
border-end-start-radius: var(--zx-radius-full);
|
|
363
|
-
border-end-end-radius: var(--zx-radius-full);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
.zx-app-sidebar__rail .zx-app-rail__item[aria-current="page"]::after { opacity: 1; }
|
|
367
|
-
.zx-app-sidebar__rail .zx-app-rail__item[data-active-descendant="true"]:not([aria-current="page"])::after {
|
|
368
|
-
inset-block: 34%;
|
|
369
|
-
opacity: .55;
|
|
318
|
+
.zx-app-sidebar__rail .zx-app-rail__item[aria-current="page"] {
|
|
319
|
+
background: var(--zx-app-sidebar-current);
|
|
320
|
+
color: var(--zx-color-text);
|
|
370
321
|
}
|
|
371
322
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
323
|
+
/* A rail item whose flyout holds the active route says so the way a closed branch does in the
|
|
324
|
+
tree: in the full text colour, without a fill of its own. */
|
|
325
|
+
.zx-app-sidebar__rail .zx-app-rail__item[data-active-descendant="true"]:not([aria-current="page"]) {
|
|
326
|
+
color: var(--zx-color-text);
|
|
375
327
|
}
|
|
376
328
|
|
|
377
329
|
/*
|
|
378
330
|
* The count a minimized rail would otherwise lose. `data-zx-badge` is set by the component from
|
|
379
331
|
* the item's own badge: a number that fits stays a number, and one that does not becomes a dot,
|
|
380
|
-
* because a three-digit pill on a
|
|
332
|
+
* because a three-digit pill on a 30px target stops being a badge and starts being a label. The
|
|
381
333
|
* figure itself moves into the control's accessible name either way, so nothing is dropped —
|
|
382
334
|
* only redrawn.
|
|
383
335
|
*/
|
|
@@ -393,7 +345,7 @@
|
|
|
393
345
|
color: var(--zx-color-on-accent);
|
|
394
346
|
font-size: var(--zx-text-xs);
|
|
395
347
|
font-variant-numeric: tabular-nums;
|
|
396
|
-
font-weight:
|
|
348
|
+
font-weight: var(--zx-weight-semibold);
|
|
397
349
|
line-height: var(--zx-space-4);
|
|
398
350
|
text-align: center;
|
|
399
351
|
}
|
|
@@ -407,14 +359,6 @@
|
|
|
407
359
|
line-height: 0;
|
|
408
360
|
}
|
|
409
361
|
|
|
410
|
-
/* On the filled pill the badge would be accent-on-accent. Invert it so the count keeps a fill
|
|
411
|
-
of its own without introducing a third colour. */
|
|
412
|
-
.zx-app-sidebar__rail .zx-app-rail__item[aria-current="page"] .zx-app-rail__item-badge {
|
|
413
|
-
background: var(--zx-color-on-accent);
|
|
414
|
-
box-shadow: none;
|
|
415
|
-
color: var(--zx-color-accent);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
362
|
/*
|
|
419
363
|
* Motion. Everything here is decoration on a state change that is already expressed statically,
|
|
420
364
|
* so all of it is opt-in.
|
|
@@ -431,12 +375,6 @@
|
|
|
431
375
|
box-shadow var(--zx-dur-fast) var(--zx-ease);
|
|
432
376
|
}
|
|
433
377
|
|
|
434
|
-
.zx-app-sidebar__item::before,
|
|
435
|
-
.zx-app-sidebar__rail .zx-app-rail__item::after {
|
|
436
|
-
transition: opacity var(--zx-dur-base) var(--zx-ease),
|
|
437
|
-
inset-block var(--zx-dur-base) var(--zx-ease);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
378
|
.zx-app-sidebar__item:active,
|
|
441
379
|
.zx-app-sidebar__rail .zx-app-rail__item:active { transform: scale(.985); }
|
|
442
380
|
|
|
@@ -456,8 +394,8 @@
|
|
|
456
394
|
to { opacity: 1; transform: translateY(0); }
|
|
457
395
|
}
|
|
458
396
|
|
|
459
|
-
/* Forced colours drop backgrounds
|
|
460
|
-
|
|
397
|
+
/* Forced colours drop author backgrounds, so the current fill would disappear. The system's own
|
|
398
|
+
selected-item pair carries the state there instead. */
|
|
461
399
|
@media (forced-colors: active) {
|
|
462
400
|
.zx-app-sidebar__item[aria-current="page"],
|
|
463
401
|
.zx-app-sidebar__rail .zx-app-rail__item[aria-current="page"],
|
|
@@ -465,9 +403,6 @@
|
|
|
465
403
|
background: Highlight;
|
|
466
404
|
color: HighlightText;
|
|
467
405
|
}
|
|
468
|
-
|
|
469
|
-
.zx-app-sidebar__item::before,
|
|
470
|
-
.zx-app-sidebar__rail .zx-app-rail__item::after { background: Highlight; }
|
|
471
406
|
}
|
|
472
407
|
|
|
473
408
|
@media (prefers-reduced-transparency: reduce) {
|
|
@@ -78,7 +78,9 @@ export class AppSidebar extends Component {
|
|
|
78
78
|
/** @returns {HTMLElement} */
|
|
79
79
|
render() {
|
|
80
80
|
this._createdRoot = this.el === null;
|
|
81
|
-
|
|
81
|
+
// A plain box: the <nav> inside is the landmark. An <aside> here would add an unnamed
|
|
82
|
+
// complementary landmark around every navigation column.
|
|
83
|
+
const root = /** @type {HTMLElement} */ (this.el ?? h('div'));
|
|
82
84
|
this.el = root;
|
|
83
85
|
this._original = this._createdRoot ? null : {
|
|
84
86
|
attributes: Array.from(root.attributes, (attribute) => [attribute.name, attribute.value]),
|
|
@@ -107,12 +109,13 @@ export class AppSidebar extends Component {
|
|
|
107
109
|
return this;
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* Returns a copy of the normalized items. A leaf has no `children` property, as it was written,
|
|
114
|
+
* rather than the empty list normalization gives it.
|
|
115
|
+
* @returns {AppNavItem[]}
|
|
116
|
+
*/
|
|
111
117
|
getItems() {
|
|
112
|
-
return this._items
|
|
113
|
-
...item,
|
|
114
|
-
children: item.children?.map((child) => ({ ...child }))
|
|
115
|
-
}));
|
|
118
|
+
return copyItems(this._items);
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
/** Sets the active id without routing. @param {string|number|null} id @returns {this} */
|
|
@@ -644,5 +647,17 @@ function normalizeSide(orientation, side) {
|
|
|
644
647
|
|
|
645
648
|
/** Fired when a destination/action is activated. @event AppSidebar#select @type {CustomEvent<AppNavSelectDetail>} */
|
|
646
649
|
/** Fired when an inline branch changes. @event AppSidebar#branchchange @type {CustomEvent<{item: AppNavItem|null, id: string|number|null, expanded: boolean|null, ids: Array<string|number>}>} */
|
|
650
|
+
/**
|
|
651
|
+
* Copies a navigation tree, leaving out the empty `children` lists normalization adds to leaves.
|
|
652
|
+
* @param {AppNavItem[]} items Normalized items.
|
|
653
|
+
* @returns {AppNavItem[]}
|
|
654
|
+
*/
|
|
655
|
+
function copyItems(items) {
|
|
656
|
+
return items.map((item) => {
|
|
657
|
+
const { children, ...rest } = item;
|
|
658
|
+
return children?.length ? { ...rest, children: copyItems(children) } : rest;
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
|
|
647
662
|
/** Fired when expanded/minimized presentation changes. @event AppSidebar#collapsechange @type {CustomEvent<{collapsed: boolean}>} */
|
|
648
663
|
/** Fired when a minimized flyout changes. @event AppSidebar#flyoutchange @type {CustomEvent<{item: AppNavItem, id: string|number, open: boolean}>} */
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
.zx-aurora {
|
|
2
|
+
/* The brand accent, then three chart series: decoration takes categorical colour, never the
|
|
3
|
+
status set, so the light behind a page never reads as a success or a warning. */
|
|
2
4
|
--zx-aurora-color-1: var(--zx-color-accent);
|
|
3
|
-
--zx-aurora-color-2: var(--zx-color-
|
|
4
|
-
--zx-aurora-color-3: var(--zx-color-
|
|
5
|
-
--zx-aurora-color-4: var(--zx-color-
|
|
5
|
+
--zx-aurora-color-2: var(--zx-color-chart-1);
|
|
6
|
+
--zx-aurora-color-3: var(--zx-color-chart-3);
|
|
7
|
+
--zx-aurora-color-4: var(--zx-color-chart-4);
|
|
6
8
|
--zx-aurora-core: 7%;
|
|
7
9
|
--zx-aurora-wash: 4%;
|
|
8
10
|
--zx-aurora-faint: 2%;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The fallback is the avatar most people see, so it carries an identity of its own: initials on a
|
|
3
|
+
* tint from the categorical tone palette, picked by a stable hash of the name and written as
|
|
4
|
+
* `data-tone` by the component. No ring — the tint is the edge. The three presets are steps of the
|
|
5
|
+
* spacing scale: 24 / 32 / 40px at the default unit.
|
|
6
|
+
*/
|
|
1
7
|
.zx-avatar {
|
|
2
|
-
--zx-avatar-size: var(--zx-
|
|
8
|
+
--zx-avatar-size: var(--zx-space-7);
|
|
3
9
|
position: relative;
|
|
4
10
|
display: inline-grid;
|
|
5
11
|
flex: none;
|
|
@@ -11,12 +17,22 @@
|
|
|
11
17
|
vertical-align: middle;
|
|
12
18
|
}
|
|
13
19
|
|
|
14
|
-
.zx-avatar[data-size="sm"] { --zx-avatar-size:
|
|
15
|
-
.zx-avatar[data-size="lg"] { --zx-avatar-size:
|
|
20
|
+
.zx-avatar[data-size="sm"] { --zx-avatar-size: var(--zx-space-6); }
|
|
21
|
+
.zx-avatar[data-size="lg"] { --zx-avatar-size: var(--zx-space-8); }
|
|
16
22
|
.zx-avatar[data-shape="circle"] { border-radius: 50%; }
|
|
17
23
|
.zx-avatar[data-shape="rounded"] { border-radius: var(--zx-radius-md); }
|
|
18
24
|
.zx-avatar[data-shape="square"] { border-radius: 0; }
|
|
19
25
|
|
|
26
|
+
.zx-avatar[data-tone="blue"] { --zx-avatar-bg: var(--zx-color-tone-blue-bg); --zx-avatar-fg: var(--zx-color-tone-blue-fg); }
|
|
27
|
+
.zx-avatar[data-tone="teal"] { --zx-avatar-bg: var(--zx-color-tone-teal-bg); --zx-avatar-fg: var(--zx-color-tone-teal-fg); }
|
|
28
|
+
.zx-avatar[data-tone="green"] { --zx-avatar-bg: var(--zx-color-tone-green-bg); --zx-avatar-fg: var(--zx-color-tone-green-fg); }
|
|
29
|
+
.zx-avatar[data-tone="amber"] { --zx-avatar-bg: var(--zx-color-tone-amber-bg); --zx-avatar-fg: var(--zx-color-tone-amber-fg); }
|
|
30
|
+
.zx-avatar[data-tone="orange"] { --zx-avatar-bg: var(--zx-color-tone-orange-bg); --zx-avatar-fg: var(--zx-color-tone-orange-fg); }
|
|
31
|
+
.zx-avatar[data-tone="red"] { --zx-avatar-bg: var(--zx-color-tone-red-bg); --zx-avatar-fg: var(--zx-color-tone-red-fg); }
|
|
32
|
+
.zx-avatar[data-tone="pink"] { --zx-avatar-bg: var(--zx-color-tone-pink-bg); --zx-avatar-fg: var(--zx-color-tone-pink-fg); }
|
|
33
|
+
.zx-avatar[data-tone="violet"] { --zx-avatar-bg: var(--zx-color-tone-violet-bg); --zx-avatar-fg: var(--zx-color-tone-violet-fg); }
|
|
34
|
+
.zx-avatar[data-tone="slate"] { --zx-avatar-bg: var(--zx-color-tone-slate-bg); --zx-avatar-fg: var(--zx-color-tone-slate-fg); }
|
|
35
|
+
|
|
20
36
|
.zx-avatar__image,
|
|
21
37
|
.zx-avatar__fallback {
|
|
22
38
|
inline-size: 100%;
|
|
@@ -35,14 +51,18 @@
|
|
|
35
51
|
display: none;
|
|
36
52
|
}
|
|
37
53
|
|
|
54
|
+
/* Initials at 0.4 of the size, never under 10px; one of them below 24px (the component decides). */
|
|
38
55
|
.zx-avatar__fallback {
|
|
39
56
|
display: grid;
|
|
40
57
|
place-items: center;
|
|
41
|
-
|
|
42
|
-
background: var(--zx-color-bg-muted);
|
|
43
|
-
|
|
44
|
-
font-
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
background: var(--zx-avatar-bg, var(--zx-color-bg-muted));
|
|
60
|
+
color: var(--zx-avatar-fg, var(--zx-color-text-muted));
|
|
61
|
+
font-size: max(10px, calc(var(--zx-avatar-size) * .4));
|
|
62
|
+
font-weight: var(--zx-weight-semibold);
|
|
63
|
+
letter-spacing: 0;
|
|
45
64
|
line-height: 1;
|
|
65
|
+
white-space: nowrap;
|
|
46
66
|
}
|
|
47
67
|
|
|
48
68
|
.zx-avatar__status {
|
|
@@ -59,3 +79,8 @@
|
|
|
59
79
|
.zx-avatar__status[data-status="online"] { background: var(--zx-color-success); }
|
|
60
80
|
.zx-avatar__status[data-status="away"] { background: var(--zx-color-warning); }
|
|
61
81
|
.zx-avatar__status[data-status="busy"] { background: var(--zx-color-danger); }
|
|
82
|
+
|
|
83
|
+
/* Forced colours drop the tint; a system-coloured edge keeps the fallback a shape. */
|
|
84
|
+
@media (forced-colors: active) {
|
|
85
|
+
.zx-avatar__fallback { border: 1px solid CanvasText; }
|
|
86
|
+
}
|