@zeyos/zx 2.2.1 → 2.3.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 +110 -0
- package/README.md +28 -2
- package/dist/types/components/multi-value-editor/multi-value-editor.d.ts +67 -8
- package/dist/types/components/permission/permission.d.ts +3 -0
- package/dist/types/components/select/select.d.ts +36 -1
- package/dist/types/components/tabbox/tabbox.d.ts +12 -3
- package/dist/zx-compat.esm.js +302 -34
- package/dist/zx-compat.esm.min.js +1 -1
- package/dist/zx-compat.global.js +302 -34
- package/dist/zx-compat.global.min.js +3 -3
- package/dist/zx-zeyos.esm.js +280 -27
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +159 -33
- package/dist/zx.esm.js +302 -34
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +302 -34
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +420 -17
- package/docs/llms.md +54 -12
- package/package.json +1 -1
- package/src/components/date-picker/date-range-picker.css +7 -0
- package/src/components/fieldset/fieldset.css +7 -0
- package/src/components/form/form.css +7 -0
- package/src/components/multi-value-editor/multi-value-editor.css +99 -15
- package/src/components/multi-value-editor/multi-value-editor.js +235 -18
- package/src/components/navigation-bar/navigation-bar.css +7 -0
- package/src/components/permission/permission.js +3 -0
- package/src/components/search/search.css +10 -2
- package/src/components/select/select.css +6 -0
- package/src/components/select/select.js +99 -11
- package/src/components/stepper/stepper.css +12 -2
- package/src/components/tabbox/tabbox.css +13 -3
- package/src/components/tabbox/tabbox.js +34 -10
- package/styles/tokens/dark.css +13 -10
- package/styles/tokens/global.css +12 -0
- package/styles/tokens/semantic.css +9 -5
- package/styles/tokens/themes.css +72 -0
- package/styles/zx.css +1 -0
package/docs/llms.md
CHANGED
|
@@ -53,8 +53,27 @@ Load `zx.css`, then set theme/density on any ancestor (usually `<html>`):
|
|
|
53
53
|
<html data-zx-theme="dark" data-zx-density="compact">
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
Themes: `light` | `dark` | `auto` (follows OS). Density: `cozy` | `compact`.
|
|
57
|
-
|
|
56
|
+
Themes: `light` | `dark` | `auto` (follows OS). Density: `cozy` | `compact`.
|
|
57
|
+
|
|
58
|
+
**Accent presets.** A second, orthogonal attribute picks the accent ramp:
|
|
59
|
+
`data-zx-preset="zx" | "zeyos" | "ocean" | "violet" | "rose" | "slate"` (`zx` is the default and
|
|
60
|
+
need not be set). A preset repoints five tier-1 tokens — `--zx-accent-300` … `--zx-accent-700` —
|
|
61
|
+
and the semantic tier takes its own stop per polarity (600/700 on light surfaces, 300/400/500 on
|
|
62
|
+
dark), so one five-line block rethemes light, dark, and auto together. Status colours are not part
|
|
63
|
+
of a preset. Define your own the same way, or build one at `/theme.html` (the theme studio, which
|
|
64
|
+
renders every component live and exports the CSS):
|
|
65
|
+
|
|
66
|
+
```css
|
|
67
|
+
:root {
|
|
68
|
+
--zx-accent-300: #c4b4ff; --zx-accent-400: #a684ff; --zx-accent-500: #8e51ff;
|
|
69
|
+
--zx-accent-600: #7f22fe; --zx-accent-700: #7008e7;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Keep the 600 stop at 4.5:1 or better against white and the 400 stop at 4.5:1 against the dark page
|
|
74
|
+
— that is what makes `--zx-color-on-accent` legible without being restated per theme.
|
|
75
|
+
|
|
76
|
+
Define a product theme by overriding **semantic** tokens under a `[data-zx-theme="name"]` selector:
|
|
58
77
|
`--zx-color-bg-page/surface/raised/control/hover/selected`, `--zx-color-border(-strong/-control)`,
|
|
59
78
|
`--zx-color-text(-muted/-placeholder)`, `--zx-color-accent(-hover)/on-accent`,
|
|
60
79
|
`--zx-color-danger/warning/success/info(+ -bg)`, `--zx-focus-ring`, `--zx-control-height/-radius`,
|
|
@@ -404,16 +423,26 @@ Search input with embedded search and clear buttons (`role="search"`).
|
|
|
404
423
|
|
|
405
424
|
Unifies single-select, local filtering, and async loading.
|
|
406
425
|
|
|
407
|
-
- **Options** — `items: []`, `valueKey: 'ID'` (string key or `(item)=>id`),
|
|
408
|
-
(string or `(item)=>string`), `renderItem`, `renderValue`, `value`,
|
|
409
|
-
`clearable: false`, `filter: false | 'local' | async (query)=>items`,
|
|
410
|
-
`minQuery: 0`, `debounce: 200`, `listHeight: 280`, `groupKey`.
|
|
426
|
+
- **Options** — `items: []`, `fixedItems: []`, `valueKey: 'ID'` (string key or `(item)=>id`),
|
|
427
|
+
`labelKey: 'name'` (string or `(item)=>string`), `renderItem`, `renderValue`, `value`,
|
|
428
|
+
`disabled`, `placeholder`, `clearable: false`, `filter: false | 'local' | async (query)=>items`,
|
|
429
|
+
`searchKeys`, `minQuery: 0`, `debounce: 200`, `listHeight: 280`, `groupKey`.
|
|
411
430
|
- **Getters** — `.value`, `.selected`.
|
|
412
|
-
- **Methods** — `set(id, {silent})`, `setItems()`, `reset()`, `open()`/`close()`,
|
|
431
|
+
- **Methods** — `set(id, {silent})`, `setItems()`, `setFixedItems()`, `reset()`, `open()`/`close()`,
|
|
413
432
|
`enable()`/`disable()`, `focus()`.
|
|
414
433
|
- **Events** — `change {value, item}` (`item` is null on clear), `open`, `close`, `query {query}`,
|
|
415
434
|
`loaded {items}`.
|
|
416
|
-
- **
|
|
435
|
+
- **Fixed choices** — `fixedItems` pins entries to the top of the list, separated by a rule (the
|
|
436
|
+
rule is dropped when `groupKey` is on, since a heading already divides them). They belong to the
|
|
437
|
+
control, not to its data: a `filter` function replaces `items` on every query, while the pinned
|
|
438
|
+
entries are narrowed locally against the same query and stay resolvable by `set()`. They survive
|
|
439
|
+
`setItems()` and are shown below `minQuery`. Use them for choices with no row in the source —
|
|
440
|
+
"Unassigned", "Everyone", "Private".
|
|
441
|
+
- **Presets** — `Select.priority(target, opts)`, a 5-level priority picker;
|
|
442
|
+
`Select.permission(target, {groups, value})`, the record-access picker with Private and Public
|
|
443
|
+
pinned above the groups. Its value is the tri-state ZeyOS stores (`false` private, `true` public,
|
|
444
|
+
or a group ID), `groups` takes an array or a loader `(query)=>items`, and the labels come from
|
|
445
|
+
`msg` (`permission.private`, `permission.public`). Replaces the deprecated `Permission`.
|
|
417
446
|
- **Keyboard** (APG combobox) — ArrowDown/Alt+ArrowDown open; arrows navigate and wrap; Home/End;
|
|
418
447
|
Enter selects; Esc closes; Tab closes; printable characters filter (editable) or run a typeahead
|
|
419
448
|
(readonly). `aria-activedescendant` tracks the active option.
|
|
@@ -917,9 +946,13 @@ or Ctrl+Arrow reorders.
|
|
|
917
946
|
Ordered value editor with explicit rows (add / remove / move).
|
|
918
947
|
|
|
919
948
|
- **Options** — `values`, `options` (allowed values, which turns the rows into selects),
|
|
920
|
-
`addLabel
|
|
949
|
+
`addLabel`, `reorder: 'both'` (`'both'` | `'buttons'` | `'drag'` | `'none'` — a drag handle, the
|
|
950
|
+
up/down buttons, both, or neither).
|
|
921
951
|
- **Methods** — `getValues()`, `setValues()`.
|
|
922
952
|
- **Events** — `change {values}`.
|
|
953
|
+
- Dragging goes through a dedicated handle, so text selection inside a row's input still works. The
|
|
954
|
+
handle is also a keyboard control (Arrow keys, Home, End), which is what keeps `reorder: 'drag'`
|
|
955
|
+
operable without a pointer.
|
|
923
956
|
<!-- /doc -->
|
|
924
957
|
|
|
925
958
|
<!-- doc:field-upload -->
|
|
@@ -961,7 +994,9 @@ yet. Field type: `tagpicker`.
|
|
|
961
994
|
<!-- doc:permission -->
|
|
962
995
|
### Permission
|
|
963
996
|
|
|
964
|
-
Private / public / group record-permission selector.
|
|
997
|
+
Private / public / group record-permission selector. **Deprecated** — use
|
|
998
|
+
`Select.permission(target, {groups})`, which carries the same tri-state value in one control and
|
|
999
|
+
can query the groups remotely. This class stays for `gx.zeyos.Permission` and is removed in 3.0.
|
|
965
1000
|
|
|
966
1001
|
- **Options** — `value: true|false|groupId`, `groups: []`, `groupsValueKey: 'ID'`,
|
|
967
1002
|
`groupsLabelKey: 'name'`.
|
|
@@ -1058,8 +1093,13 @@ restores rounded corners without forking the stylesheet.
|
|
|
1058
1093
|
- **Methods** — `addTab()`, `removeTab()`, `openTab(name)`, `getActive()`, `setTitle()`,
|
|
1059
1094
|
`setBadge(name, text|null)`, `enableTab()`/`disableTab()`.
|
|
1060
1095
|
- **Events** — `change {name, previous}` (preventable), `close {name}`.
|
|
1096
|
+
- **Behaviour** — a `closable` tab renders a × that closes it on click, and `close` fires for that
|
|
1097
|
+
gesture just as it does for Delete. `removeTab()` is the silent programmatic path. Closing the
|
|
1098
|
+
active tab activates its neighbour through the usual `change` event, so a `preventDefault()` on
|
|
1099
|
+
that change also vetoes the close.
|
|
1061
1100
|
- **Keyboard** — ArrowLeft/Right and Home/End move focus, Enter/Space activate, Delete closes a
|
|
1062
|
-
closable tab.
|
|
1101
|
+
closable tab. The × is a plain glyph rather than a nested button (buttons cannot nest), so
|
|
1102
|
+
keyboard and screen-reader users close through Delete; focus lands on the neighbouring tab.
|
|
1063
1103
|
<!-- /doc -->
|
|
1064
1104
|
|
|
1065
1105
|
<!-- doc:stepper -->
|
|
@@ -1068,7 +1108,9 @@ restores rounded corners without forking the stylesheet.
|
|
|
1068
1108
|
Linear progress through a multi-step flow: a numbered `<ol>` where each step is upcoming, active,
|
|
1069
1109
|
complete, or errored. It promotes the markup the checkout and record wizards used to hand-roll.
|
|
1070
1110
|
Advancing past a step marks it complete automatically, and only steps the `clickable` policy allows
|
|
1071
|
-
are rendered as real buttons — the rest are not focusable at all.
|
|
1111
|
+
are rendered as real buttons — the rest are not focusable at all. A horizontal rail fills the width
|
|
1112
|
+
it is given: the labels keep their own width and the connectors stretch to take the slack, wrapping
|
|
1113
|
+
onto more lines only when the container is too narrow to hold them.
|
|
1072
1114
|
|
|
1073
1115
|
- **Options** — `steps: [{name, title, description?, optional?, disabled?}]`, `active` (defaults to
|
|
1074
1116
|
the first enabled step), `orientation: 'horizontal'|'vertical'`, `clickable:
|
package/package.json
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
.zx-date-range-picker {
|
|
4
4
|
display: block;
|
|
5
5
|
container-type: inline-size;
|
|
6
|
+
/* A size container takes no width from its own contents, so in a shrink-to-fit host — a flex
|
|
7
|
+
row, an inline-block, a grid `auto` track — it would collapse to nothing and spill. Only the
|
|
8
|
+
host can size it, so it always fills the host. That covers every host with a width of its
|
|
9
|
+
own; a host that shrink-wraps *its* contents still collapses, because 100% then resolves
|
|
10
|
+
against a containing block this element's own containment zeroed. Dropping containment is the
|
|
11
|
+
only fix for that — see multi-value-editor.css. */
|
|
12
|
+
inline-size: 100%;
|
|
6
13
|
color: var(--zx-color-text);
|
|
7
14
|
font-size: var(--zx-control-font-size);
|
|
8
15
|
}
|
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
border-radius: var(--zx-radius-lg);
|
|
7
7
|
background: var(--zx-color-bg-surface);
|
|
8
8
|
container-type: inline-size;
|
|
9
|
+
/* A size container takes no width from its own contents, so in a shrink-to-fit host — a flex
|
|
10
|
+
row, an inline-block, a grid `auto` track — it would collapse to nothing and spill. Only the
|
|
11
|
+
host can size it, so it always fills the host. That covers every host with a width of its
|
|
12
|
+
own; a host that shrink-wraps *its* contents still collapses, because 100% then resolves
|
|
13
|
+
against a containing block this element's own containment zeroed. Dropping containment is the
|
|
14
|
+
only fix for that — see multi-value-editor.css. */
|
|
15
|
+
inline-size: 100%;
|
|
9
16
|
}
|
|
10
17
|
|
|
11
18
|
.zx-fieldset__legend {
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
min-inline-size: 0;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
/* Fieldsets are size containers, and a contained descendant contributes nothing to its ancestors'
|
|
9
|
+
intrinsic width either — so a form in a shrink-to-fit host would shrink-wrap to its action
|
|
10
|
+
buttons and leave the fields spilling out of a sliver. The form fills its host instead. */
|
|
11
|
+
.zx-form {
|
|
12
|
+
inline-size: 100%;
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
.zx-form__actions {
|
|
9
16
|
display: flex;
|
|
10
17
|
flex-wrap: wrap;
|
|
@@ -1,12 +1,22 @@
|
|
|
1
|
+
/* Deliberately not a size container. Inline-size containment removes an element's own intrinsic
|
|
2
|
+
width, so every host that sizes to its contents — a flex row, an inline-block, a grid `auto`
|
|
3
|
+
track, a table cell — measured this editor as nothing and the rows spilled out of a 26px sliver.
|
|
4
|
+
`inline-size: 100%` does not rescue it either: the percentage resolves against a containing
|
|
5
|
+
block that was itself measured from the contribution containment had already zeroed. The row
|
|
6
|
+
below reflows on its own instead, which reads the width that actually matters — the row's.
|
|
7
|
+
|
|
8
|
+
Without containment `inline-size: 100%` does work: a percentage that has no definite containing
|
|
9
|
+
block to resolve against counts as `auto` while the host measures its contents, so the editor
|
|
10
|
+
contributes its real width and then fills whatever the host settled on. */
|
|
1
11
|
.zx-multi-value-editor {
|
|
2
12
|
display: grid;
|
|
3
13
|
gap: var(--zx-space-3);
|
|
4
14
|
min-inline-size: 0;
|
|
15
|
+
inline-size: 100%;
|
|
5
16
|
padding: var(--zx-space-3);
|
|
6
17
|
border: 1px solid var(--zx-color-border);
|
|
7
18
|
border-radius: var(--zx-radius-lg);
|
|
8
19
|
background: var(--zx-color-bg-surface);
|
|
9
|
-
container-type: inline-size;
|
|
10
20
|
}
|
|
11
21
|
|
|
12
22
|
.zx-multi-value-editor__rows {
|
|
@@ -15,19 +25,32 @@
|
|
|
15
25
|
}
|
|
16
26
|
|
|
17
27
|
.zx-multi-value-editor__row {
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
position: relative;
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-wrap: wrap;
|
|
31
|
+
/* A grid item refuses to go under its min-content width unless told to, which in a pane narrower
|
|
32
|
+
than the control cluster pushed the whole row back out through the editor's side. */
|
|
33
|
+
min-inline-size: 0;
|
|
20
34
|
gap: var(--zx-space-2);
|
|
21
35
|
align-items: center;
|
|
22
36
|
}
|
|
23
37
|
|
|
38
|
+
/* Below roughly this basis the controls wrap onto a line of their own, which is what the container
|
|
39
|
+
query used to do — except a row measures the space the row actually has. */
|
|
24
40
|
.zx-multi-value-editor__value {
|
|
25
|
-
|
|
41
|
+
flex: 1 1 11rem;
|
|
42
|
+
min-inline-size: 0;
|
|
26
43
|
}
|
|
27
44
|
|
|
45
|
+
/* Wide enough and this keeps its natural size at the end of the row; narrower than the buttons it
|
|
46
|
+
holds, and they wrap among themselves rather than out through the side. */
|
|
28
47
|
.zx-multi-value-editor__controls {
|
|
29
48
|
display: inline-flex;
|
|
49
|
+
flex: 0 1 auto;
|
|
50
|
+
flex-wrap: wrap;
|
|
51
|
+
min-inline-size: 0;
|
|
30
52
|
gap: var(--zx-space-1);
|
|
53
|
+
margin-inline-start: auto;
|
|
31
54
|
}
|
|
32
55
|
|
|
33
56
|
.zx-multi-value-editor__controls .zx-btn {
|
|
@@ -35,22 +58,83 @@
|
|
|
35
58
|
padding-inline: var(--zx-space-2);
|
|
36
59
|
}
|
|
37
60
|
|
|
38
|
-
.zx-multi-value-editor__controls .zx-btn
|
|
61
|
+
.zx-multi-value-editor__controls .zx-btn[data-action="remove"] {
|
|
39
62
|
color: var(--zx-color-danger);
|
|
40
63
|
}
|
|
41
64
|
|
|
65
|
+
/* A row at either end keeps its move button focusable and merely announces that it cannot go
|
|
66
|
+
further, so pressing it repeatedly never drops focus to the document. */
|
|
67
|
+
.zx-multi-value-editor__controls .zx-btn[aria-disabled="true"] {
|
|
68
|
+
opacity: 0.55;
|
|
69
|
+
cursor: not-allowed;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.zx-multi-value-editor__controls .zx-btn[aria-disabled="true"]:hover {
|
|
73
|
+
background: var(--zx-color-bg-control);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.zx-multi-value-editor__handle {
|
|
77
|
+
display: inline-flex;
|
|
78
|
+
flex: none;
|
|
79
|
+
align-items: center;
|
|
80
|
+
justify-content: center;
|
|
81
|
+
min-block-size: var(--zx-control-height);
|
|
82
|
+
min-inline-size: var(--zx-space-6);
|
|
83
|
+
padding: 0;
|
|
84
|
+
border: 0;
|
|
85
|
+
border-radius: var(--zx-control-radius);
|
|
86
|
+
background: none;
|
|
87
|
+
color: var(--zx-color-text-muted);
|
|
88
|
+
cursor: grab;
|
|
89
|
+
touch-action: none;
|
|
90
|
+
transition: color var(--zx-dur-fast) var(--zx-ease),
|
|
91
|
+
background-color var(--zx-dur-fast) var(--zx-ease);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.zx-multi-value-editor__handle:hover:not(:disabled) {
|
|
95
|
+
background: var(--zx-color-bg-hover);
|
|
96
|
+
color: var(--zx-color-text);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.zx-multi-value-editor__handle:focus-visible {
|
|
100
|
+
outline: none;
|
|
101
|
+
box-shadow: var(--zx-focus-ring);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.zx-multi-value-editor__handle:disabled {
|
|
105
|
+
opacity: 0.55;
|
|
106
|
+
cursor: default;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.zx-multi-value-editor[data-state="dragging"] .zx-multi-value-editor__handle {
|
|
110
|
+
cursor: grabbing;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.zx-multi-value-editor__row[data-dragging="true"] {
|
|
114
|
+
opacity: 0.45;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* The line sits in the gap between two rows, so it reads as "it lands here", not "it replaces
|
|
118
|
+
this row". */
|
|
119
|
+
.zx-multi-value-editor__row[data-drop]::after {
|
|
120
|
+
content: '';
|
|
121
|
+
position: absolute;
|
|
122
|
+
inset-inline: 0;
|
|
123
|
+
block-size: 2px;
|
|
124
|
+
border-radius: var(--zx-radius-full);
|
|
125
|
+
background: var(--zx-color-accent);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.zx-multi-value-editor__row[data-drop="before"]::after {
|
|
129
|
+
inset-block-start: calc(var(--zx-space-2) / -2 - 1px);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.zx-multi-value-editor__row[data-drop="after"]::after {
|
|
133
|
+
inset-block-end: calc(var(--zx-space-2) / -2 - 1px);
|
|
134
|
+
}
|
|
135
|
+
|
|
42
136
|
.zx-multi-value-editor__add {
|
|
43
137
|
justify-self: start;
|
|
44
138
|
border-color: var(--zx-color-accent);
|
|
45
139
|
color: var(--zx-color-accent);
|
|
46
140
|
}
|
|
47
|
-
|
|
48
|
-
@container (max-width: 24rem) {
|
|
49
|
-
.zx-multi-value-editor__row {
|
|
50
|
-
grid-template-columns: minmax(0, 1fr);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.zx-multi-value-editor__controls {
|
|
54
|
-
justify-self: end;
|
|
55
|
-
}
|
|
56
|
-
}
|