@zeyos/zx 4.3.4 → 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 +438 -0
- package/README.md +49 -10
- package/dist/types/components/activity-item/activity-item.d.ts +351 -0
- package/dist/types/components/activity-list/activity-list.d.ts +217 -0
- package/dist/types/components/app-sidebar/app-sidebar.d.ts +33 -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 +23 -0
- package/dist/types/components/calendar/calendar.d.ts +19 -0
- package/dist/types/components/card/card.d.ts +7 -0
- package/dist/types/components/card-view/card-view.d.ts +11 -3
- package/dist/types/components/card-view/record-card.d.ts +7 -1
- 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 +149 -0
- package/dist/types/components/code-editor/field-adapter.d.ts +5 -0
- package/dist/types/components/context-menu/context-menu.d.ts +18 -2
- package/dist/types/components/date-picker/date-picker.d.ts +8 -0
- package/dist/types/components/date-picker/time-picker.d.ts +7 -0
- 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 +75 -6
- package/dist/types/components/dropdown/dropdown.d.ts +2 -2
- package/dist/types/components/entity-ref/entity-ref.d.ts +331 -0
- 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 +262 -0
- package/dist/types/components/file-list/file-list.d.ts +171 -0
- package/dist/types/components/filter/filter.d.ts +91 -36
- package/dist/types/components/filter-panel/filter-panel.d.ts +449 -0
- package/dist/types/components/form/form.d.ts +12 -1
- package/dist/types/components/kanban-view/kanban-card.d.ts +146 -0
- package/dist/types/components/kanban-view/kanban-view.d.ts +170 -4
- package/dist/types/components/launcher/launcher.d.ts +278 -26
- package/dist/types/components/list-toolbar/list-toolbar.d.ts +599 -0
- package/dist/types/components/menu-button/menu-button.d.ts +37 -42
- package/dist/types/components/message/message.d.ts +126 -17
- package/dist/types/components/modal/modal.d.ts +61 -2
- package/dist/types/components/navigation-bar/navigation-bar.d.ts +105 -21
- package/dist/types/components/panel/panel.d.ts +7 -0
- package/dist/types/components/search/search.d.ts +7 -0
- 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 +15 -3
- package/dist/types/components/sort-control/sort-control.d.ts +105 -0
- package/dist/types/components/stat-tile/stat-tile.d.ts +288 -0
- package/dist/types/components/tabbox/tabbox.d.ts +16 -0
- package/dist/types/components/table/table.d.ts +190 -1
- package/dist/types/components/table-view/table-view.d.ts +11 -5
- package/dist/types/components/tag-picker/tag-picker.d.ts +21 -4
- package/dist/types/components/timebox/timebox.d.ts +7 -0
- package/dist/types/components/tree/tree.d.ts +13 -0
- package/dist/types/components/value-list/value-list.d.ts +8 -0
- package/dist/types/components/view/record-view.d.ts +96 -5
- package/dist/types/core/breakpoint.d.ts +1 -1
- package/dist/types/core/icons.d.ts +37 -22
- 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 +16 -3
- package/dist/types/internal/app-rail.d.ts +10 -0
- package/dist/types/internal/menu-items.d.ts +134 -0
- 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 +2206 -489
- package/dist/zx-zeyos.esm.min.js +56 -1
- package/dist/zx.css +3322 -724
- package/dist/zx.esm.js +12244 -5809
- package/dist/zx.esm.min.js +58 -3
- package/dist/zx.global.js +12246 -5811
- package/dist/zx.global.min.js +58 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +1683 -146
- package/docs/llms.md +777 -110
- package/docs/llms.txt +14 -8
- package/package.json +1 -1
- package/src/components/account-menu/account-menu.css +35 -10
- package/src/components/activity-item/activity-item.css +137 -0
- package/src/components/activity-item/activity-item.js +602 -0
- package/src/components/activity-list/activity-list.css +99 -0
- package/src/components/activity-list/activity-list.js +409 -0
- package/src/components/app-icon/app-icon.css +1 -1
- package/src/components/app-rail/app-rail.css +191 -22
- package/src/components/app-sidebar/app-sidebar.css +349 -24
- package/src/components/app-sidebar/app-sidebar.js +86 -11
- 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 +74 -8
- package/src/components/button/button.js +44 -1
- package/src/components/calendar/calendar.css +46 -46
- package/src/components/calendar/calendar.js +33 -3
- package/src/components/card/card.css +2 -2
- package/src/components/card/card.js +12 -1
- package/src/components/card-view/card-view.css +10 -7
- package/src/components/card-view/card-view.js +25 -9
- package/src/components/card-view/record-card.js +7 -3
- 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 +68 -0
- package/src/components/code-editor/code-editor.js +306 -0
- package/src/components/code-editor/field-adapter.js +34 -0
- package/src/components/context-menu/context-menu.css +87 -5
- package/src/components/context-menu/context-menu.js +22 -30
- 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-picker.js +22 -8
- package/src/components/date-picker/date-range-picker.css +3 -3
- package/src/components/date-picker/month-picker.js +3 -3
- package/src/components/date-picker/time-picker.css +1 -1
- package/src/components/date-picker/time-picker.js +20 -8
- 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 +174 -70
- package/src/components/dropdown/dropdown.css +1 -1
- package/src/components/dropdown/dropdown.js +7 -5
- package/src/components/empty-state/empty-state.css +2 -2
- package/src/components/entity-ref/entity-ref.css +182 -0
- package/src/components/entity-ref/entity-ref.js +496 -0
- 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.css +88 -0
- package/src/components/file-item/file-item.js +505 -0
- package/src/components/file-list/file-list.css +51 -0
- package/src/components/file-list/file-list.js +320 -0
- package/src/components/filter/filter.css +27 -1
- package/src/components/filter/filter.js +205 -47
- package/src/components/filter-panel/filter-panel.css +104 -0
- package/src/components/filter-panel/filter-panel.js +964 -0
- 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 +247 -0
- package/src/components/kanban-view/kanban-view.css +226 -8
- package/src/components/kanban-view/kanban-view.js +255 -12
- package/src/components/launcher/launcher.css +107 -25
- package/src/components/launcher/launcher.js +557 -63
- package/src/components/list-toolbar/list-toolbar.css +194 -0
- package/src/components/list-toolbar/list-toolbar.js +851 -0
- package/src/components/master-panel/master-panel.css +2 -0
- package/src/components/menu-button/menu-button.css +87 -5
- package/src/components/menu-button/menu-button.js +86 -46
- package/src/components/message/message.css +38 -5
- package/src/components/message/message.js +230 -17
- package/src/components/modal/modal.css +15 -0
- package/src/components/modal/modal.js +159 -3
- package/src/components/navigation-bar/navigation-bar.css +95 -11
- package/src/components/navigation-bar/navigation-bar.js +171 -4
- 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/panel/panel.js +13 -2
- package/src/components/rating/rating.css +7 -0
- package/src/components/search/search.js +17 -5
- package/src/components/select/color.js +22 -0
- package/src/components/select/field-adapter.js +6 -1
- package/src/components/select/glyphs.js +165 -0
- package/src/components/select/select.css +138 -45
- package/src/components/select/select.js +86 -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 +20 -0
- package/src/components/sort-control/sort-control.js +140 -0
- package/src/components/stat-tile/stat-tile.css +208 -0
- package/src/components/stat-tile/stat-tile.js +601 -0
- package/src/components/tabbox/tabbox.css +32 -27
- package/src/components/tabbox/tabbox.js +17 -2
- package/src/components/table/table.css +155 -31
- package/src/components/table/table.js +386 -33
- package/src/components/table-view/table-view.css +8 -2
- package/src/components/table-view/table-view.js +21 -6
- package/src/components/tag-picker/tag-picker.css +86 -6
- package/src/components/tag-picker/tag-picker.js +25 -8
- package/src/components/timebox/timebox.css +22 -2
- package/src/components/timebox/timebox.js +18 -5
- 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/value-list/value-list.js +22 -6
- package/src/components/view/record-view.css +5 -0
- package/src/components/view/record-view.js +100 -7
- package/src/core/breakpoint.js +1 -1
- package/src/core/icons.js +259 -57
- package/src/core/motion.js +147 -0
- package/src/core/overlay-host.js +21 -2
- package/src/index.js +30 -3
- package/src/internal/app-rail.js +18 -1
- package/src/internal/menu-items.js +142 -0
- 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/print.css +192 -0
- 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 +11 -1
- package/styles/tokens/density.css +0 -9
package/docs/llms.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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 controls, semantic `--zx-*` design
|
|
5
|
-
> tokens (light/dark
|
|
5
|
+
> tokens (light/dark, one token-driven geometry), and lifecycle-safe components. Zero runtime dependencies.
|
|
6
6
|
> This file is the single, complete reference a coding agent needs to build UI with Zx; each
|
|
7
7
|
> component below is wrapped in `<!-- doc:<name> -->` markers so tools can extract a single
|
|
8
8
|
> component's section (`website/docs.html` renders them as each component's Reference tab).
|
|
@@ -49,13 +49,23 @@ const t = new Table(target, options);
|
|
|
49
49
|
|
|
50
50
|
## Theming
|
|
51
51
|
|
|
52
|
-
Load `zx.css`, then set theme
|
|
52
|
+
Load `zx.css`, then set the theme on any ancestor (usually `<html>`):
|
|
53
53
|
|
|
54
54
|
```html
|
|
55
|
-
<html data-zx-theme="dark"
|
|
55
|
+
<html data-zx-theme="dark">
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
Themes: `light` | `dark` | `auto` (follows OS).
|
|
58
|
+
Themes: `light` | `dark` | `auto` (follows OS).
|
|
59
|
+
|
|
60
|
+
**One geometry.** Spacing steps are multiples of one unit
|
|
61
|
+
(`--zx-space-unit`, 4px: `--zx-space-1…8` = 4/8/12/16/20/24/32/40px), radius steps are fractions of
|
|
62
|
+
one base (`--zx-radius`, 4px: `-sm/-md/-lg/-xl` = 2/3/4/6px; `-full` only for shapes whose roundness
|
|
63
|
+
is the meaning — badges, avatars, toggles), and controls are `--zx-control-height` (30px) tall.
|
|
64
|
+
To tighten or loosen an application, override those tokens on `:root` or on any region:
|
|
65
|
+
|
|
66
|
+
```css
|
|
67
|
+
:root { --zx-space-unit: 3.5px; --zx-control-height: 28px; }
|
|
68
|
+
```
|
|
59
69
|
|
|
60
70
|
**Accent presets.** A second, orthogonal attribute picks the accent ramp:
|
|
61
71
|
`data-zx-preset="zx" | "zeyos" | "ocean" | "violet" | "rose" | "slate"` (`zx` is the default and
|
|
@@ -76,7 +86,7 @@ Keep the 600 stop at 4.5:1 or better against white and the 400 stop at 4.5:1 aga
|
|
|
76
86
|
— that is what makes `--zx-color-on-accent` legible without being restated per theme.
|
|
77
87
|
|
|
78
88
|
The theme studio treats those stable IDs as complete recipes: selecting a preset also changes its
|
|
79
|
-
|
|
89
|
+
spacing unit, neutral tint, radius, control height, base text size, font stack, and material strength.
|
|
80
90
|
Its **Material** control is CSS-first (`Flat`, `Glass`, or `Deep glass`). Aurora is the independent
|
|
81
91
|
ambient canvas layer; material controls how surfaces respond to that light. The shared recipe
|
|
82
92
|
applies to AppIcons, ordinary controls, persistent chrome, raised cards, and every transient overlay: Dropdown, ContextMenu,
|
|
@@ -92,14 +102,45 @@ Top-layer components remain DOM children of their nearest Zx theme scope, so a t
|
|
|
92
102
|
set on an application region continues to inherit through its menus, dialogs, tooltips, and toasts.
|
|
93
103
|
|
|
94
104
|
Define a product theme by overriding **semantic** tokens under a `[data-zx-theme="name"]` selector:
|
|
95
|
-
`--zx-color-bg-page/surface/raised/control/hover/selected`, `--zx-color-
|
|
105
|
+
`--zx-color-bg-page/surface/raised/control/muted/hover/selected`, `--zx-color-bg-current` (the
|
|
106
|
+
neutral "you are here" fill of the current nav item, row, tab, or pressed toggle — never the
|
|
107
|
+
accent), `--zx-color-bg-chrome` (navigation and inspector columns, one tone off the canvas),
|
|
108
|
+
`--zx-color-glass-chrome/surface`,
|
|
96
109
|
`--zx-color-overlay-surface/panel/backdrop/border/hover/selected/divider/scrim`,
|
|
97
|
-
`--zx-overlay-surface-image/toast-image/panel-image`,
|
|
98
|
-
`--zx-color-
|
|
99
|
-
`--zx-color-
|
|
100
|
-
`--zx-
|
|
101
|
-
`--zx-
|
|
102
|
-
|
|
110
|
+
`--zx-overlay-surface-image/toast-image/panel-image`,
|
|
111
|
+
`--zx-color-border(-subtle/-strong/-control)` (subtle for row and grid dividers),
|
|
112
|
+
`--zx-color-text(-muted/-subtle/-placeholder)` (subtle for icons and non-essential meta),
|
|
113
|
+
`--zx-color-accent(-hover/-subtle)/on-accent`, `--zx-color-accent-solid(-hover/-active)` with
|
|
114
|
+
`--zx-color-on-accent-solid` (the one filled primary button per screen),
|
|
115
|
+
`--zx-color-danger/warning/success/info(+ -bg)`, the categorical tone palette
|
|
116
|
+
`--zx-color-tone-{blue,teal,green,amber,orange,red,pink,violet,slate}-bg/-fg` (tags, chips, icon
|
|
117
|
+
tiles, avatars — neither the accent nor a status), the chart series `--zx-color-chart-1…8`,
|
|
118
|
+
`--zx-focus-ring`, `--zx-control-height/-radius`, `--zx-space-*`, `--zx-radius-*`, `--zx-text-*`
|
|
119
|
+
(`-3xl` for document titles), `--zx-weight-medium/-semibold`, `--zx-tracking-tight/-title`. Never
|
|
120
|
+
use tier-1 palette tokens (`--zx-gray-*`, `--zx-green-*`, `--zx-emerald-*`, `--zx-tone-*`) or raw
|
|
121
|
+
color literals in application/component CSS. Prefer the least-specific override that works (global
|
|
122
|
+
token → then, if needed, a component style).
|
|
123
|
+
|
|
124
|
+
**Print.** `zx.css` imports `styles/print.css` last. It releases sticky headers and scroll
|
|
125
|
+
containers, drops fills, shadows, zebra striping and focus rings, hides overlays and navigation
|
|
126
|
+
chrome, repeats table headers across pages, keeps cards and rows from breaking, and prints link
|
|
127
|
+
URLs in prose but never inside a table cell or on a button. Status badges keep their colour,
|
|
128
|
+
because there it carries meaning beside the word. An application that pins `data-zx-theme="dark"`
|
|
129
|
+
still prints the dark token set; `auto` resolves to light under `@media print` and is unaffected.
|
|
130
|
+
|
|
131
|
+
**Motion.** Motion is a setting, beside the theme: put `data-zx-motion="reduced"` on `<html>` (or
|
|
132
|
+
on any region) to stop movement inside it. There, every `--zx-dur-*` token is `0ms` and a base rule
|
|
133
|
+
collapses CSS animations and transitions to their end state — menus still open, toasts still leave,
|
|
134
|
+
a spinner stops turning. The operating system's reduced-motion preference does the same for the
|
|
135
|
+
whole page. There is no value that turns motion back on: someone who asked their system for less
|
|
136
|
+
motion gets less, whatever an application or region says. Apply the attribute before first paint
|
|
137
|
+
(the documentation site stores it with the theme). Code that animates in script asks
|
|
138
|
+
`prefersReducedMotion(element?)` — true under the system preference or inside a reduced region,
|
|
139
|
+
across shadow roots — and subscribes with `watchMotion(element, (reduced) => …, {signal, immediate})`,
|
|
140
|
+
which returns a disposer; Chart already does. Name motion with the curves rather than inventing
|
|
141
|
+
them: `--zx-ease` for state changes, `--zx-ease-spring` (a small overshoot) for things that pop in,
|
|
142
|
+
`--zx-ease-drawer` (a decisive ease-out) for panels that slide, as Sheet does; and lift an item
|
|
143
|
+
being dragged with `--zx-drag-shadow`, as KanbanView's drag preview and keyboard-grabbed card do.
|
|
103
144
|
|
|
104
145
|
## Talking to ZeyOS — use `@zeyos/client`
|
|
105
146
|
|
|
@@ -175,13 +216,20 @@ await records.saveView({ name: 'My open invoices', setDefault: true });
|
|
|
175
216
|
operation receives a query built by `buildListQuery`.
|
|
176
217
|
- `zeyosForm(client, resource, opts)` returns
|
|
177
218
|
`{form, ready, load(id), save(), getForm(), destroy()}`. `fields` is an ordered allow-list;
|
|
178
|
-
`exclude`, `labels`, `title`, `columns`, `value|id`, and `onSaved` curate
|
|
179
|
-
stored timestamps to `Date`; `save` validates and creates/updates, maps back
|
|
180
|
-
calls `onSaved`, and shows a success toast.
|
|
219
|
+
`exclude`, `labels`, `formats`, `timeZone`, `title`, `columns`, `value|id`, and `onSaved` curate
|
|
220
|
+
behavior. `load` maps stored timestamps to `Date`; `save` validates and creates/updates, maps back
|
|
221
|
+
to Unix seconds, calls `onSaved`, and shows a success toast. Its toasts (that confirmation and a
|
|
222
|
+
failed request's report) appear in the theme scope of `scope` — default: the form's own element,
|
|
223
|
+
so a form in a scoped dialog toasts inside that scope; `notify: false` raises none, leaving the
|
|
224
|
+
promise's result or rejection to the caller.
|
|
181
225
|
- `zeyosTable(client, resource, opts)` returns
|
|
182
226
|
`{table, load(), setSearch(), setFilters(), loadMore(), count, page, hasMore, destroy()}`. Columns
|
|
183
227
|
come from schema metadata, sorting uses `sortMode:'server'`, and page zero replaces rows while
|
|
184
|
-
later pages append them using server `limit`/`offset` and `count`.
|
|
228
|
+
later pages append them using server `limit`/`offset` and `count`. Table presentation options and
|
|
229
|
+
listeners pass through — `variant`, `zebra`, `stickyHeader`, `responsive`, `growing`, `emptyText`,
|
|
230
|
+
`rowClass`, `rowKind(s)`, `columnVisibility`, `columnReorder`, `hiddenColumns`, `on<event>`;
|
|
231
|
+
`onrowclick` runs beside `onRowClick`. The binding owns `columns`, `data`, and `sortMode`, and
|
|
232
|
+
leaves out editing, `rowReorder`, and `hierarchy`, which change rows it never persists.
|
|
185
233
|
- `buildZeyosViewConfig(client, resource, opts)` resolves one schema to shared `ViewField[]`, a
|
|
186
234
|
complete projection, and view options. `zeyosView(client, resource, CardView|TableView|KanbanView,
|
|
187
235
|
opts)` returns `{view, load(), setSearch(), setFilters(), loadMore(), count, page, hasMore,
|
|
@@ -199,7 +247,11 @@ await records.saveView({ name: 'My open invoices', setDefault: true });
|
|
|
199
247
|
scope's `userId` is intentionally not sent to the endpoint; the server session remains authority.
|
|
200
248
|
|
|
201
249
|
Field mapping is schema-driven: entity/FK → async `zxselect`; enum/list → `optionlist` or native
|
|
202
|
-
`select
|
|
250
|
+
`select` with readable labels (a schema `{value, label}` as given, a stored constant in sentence
|
|
251
|
+
case — `INVOICED` → "Invoiced", `PARTLYPAID` → "Partly paid"); date-only bigints (indexed `date`,
|
|
252
|
+
`duedate`, `birthdate`, `datefrom`/`dateto` outside appointments and events, `datecancel`,
|
|
253
|
+
`lastbilling`) → `date`; other date/time bigints and `lastmodified` → `datetime`;
|
|
254
|
+
`formats: { field: 'date' }` overrides the guess; money/price/numeric
|
|
203
255
|
→ right-aligned `float`/number columns (amount columns use the row currency); boolean/checked →
|
|
204
256
|
`toggle` and a check column; percent/progress → numeric percent; email/tel/url → typed text; arrays
|
|
205
257
|
→ `valuelist`; long text/JSON → `textarea`; short scalar text → `text`.
|
|
@@ -207,8 +259,13 @@ Field mapping is schema-driven: entity/FK → async `zxselect`; enum/list → `o
|
|
|
207
259
|
ZeyOS queries always use `filters` (plural), not `filter`. Full-text input becomes `query`; sort is
|
|
208
260
|
sent as signed server fields (`+field`/`-field`); projection supports aliases and dot joins; and
|
|
209
261
|
search/filter/sort/pagination remain server-side. ZeyOS timestamps are integer Unix seconds, so
|
|
210
|
-
forms convert them to/from `Date`.
|
|
211
|
-
|
|
262
|
+
forms convert them to/from `Date`. A date-only field stores midnight of a calendar day in the
|
|
263
|
+
instance's zone — a day, not an instant: tables show it with no time, and forms edit it as that day,
|
|
264
|
+
so neither the viewer's zone nor daylight saving moves it. Pass the instance's IANA zone as
|
|
265
|
+
`timeZone` (table, form, view; default: the runtime's own); `unixSecondsToCalendarDay(seconds, tz)`
|
|
266
|
+
and `calendarDayToUnixSeconds(date, tz)` do the same conversion for application code. Use
|
|
267
|
+
`dataFilterStateToFilters(state, defs)` before `tableBinding.setFilters(...)` when wiring a
|
|
268
|
+
`DataFilter`.
|
|
212
269
|
|
|
213
270
|
### Module icons and colours
|
|
214
271
|
|
|
@@ -271,20 +328,40 @@ accept.
|
|
|
271
328
|
|
|
272
329
|
- **`button(options)`** → `HTMLButtonElement`. Options: `label`, `icon` (an icon name), `kind:
|
|
273
330
|
'default'|'primary'|'danger'|'ghost'`, `size: 'md'|'sm'`, `disabled`, `title`, `onclick`.
|
|
331
|
+
- **Shortcut hints** — `shortcut: {label:'N', keys:'N'}` adds a visible key hint, hover text and
|
|
332
|
+
`aria-keyshortcuts`. The host binds the action and owns editing/modality/permission guards;
|
|
333
|
+
a hint never installs a global handler. Dialog and Sheet footer descriptors accept it too.
|
|
274
334
|
- **`buttonGroup([button(…), …])`** → a container that joins the buttons into one control.
|
|
335
|
+
- **Appearance** — `primary` is the screen's one solid accent and reads the
|
|
336
|
+
`--zx-color-accent-solid*` roles, so in dark it keeps the deep stop with white ink rather than a
|
|
337
|
+
bright slab. A `ghost` button's icon is drawn in `--zx-color-text-subtle` until hovered. A button
|
|
338
|
+
with `aria-pressed="true"` (a Toolbar item with `active`, a view toggle) is a mode, not a pick:
|
|
339
|
+
it takes the neutral `--zx-color-bg-current` fill and the text colour, never the accent.
|
|
275
340
|
<!-- /doc -->
|
|
276
341
|
|
|
277
342
|
<!-- doc:badge -->
|
|
278
343
|
### Badge — `badge(options)` / `badgeGroup(badges)`
|
|
279
344
|
|
|
280
345
|
Factories (not classes) returning a status pill: a small, non-interactive `<span>` carrying a
|
|
281
|
-
semantic colour, used for stages, counts, tags, and
|
|
282
|
-
but a `title` is named for assistive technology
|
|
283
|
-
and needs no ARIA.
|
|
346
|
+
semantic colour — or, with `tone`, a categorical one — used for stages, counts, tags, and
|
|
347
|
+
record-header metadata. A badge with no label but a `title` is named for assistive technology
|
|
348
|
+
through `role="img"`; everything else is plain text and needs no ARIA.
|
|
284
349
|
|
|
285
350
|
- **Options** — `label`, `icon` (an icon name, rendered before the label), `kind:
|
|
286
|
-
'neutral'|'accent'|'success'|'warning'|'danger'|'info'`, `
|
|
287
|
-
|
|
351
|
+
'neutral'|'accent'|'success'|'warning'|'danger'|'info'`, `tone:
|
|
352
|
+
'blue'|'teal'|'green'|'amber'|'orange'|'red'|'pink'|'violet'|'slate'|null`, `variant:
|
|
353
|
+
'soft'|'solid'|'outline'`, `size: 'md'|'sm'`, `dot` (a leading status dot instead of an icon),
|
|
354
|
+
`title`.
|
|
355
|
+
- **`kind` vs `tone`** — `kind` says how something is doing (status); `tone` says what it is (a
|
|
356
|
+
project, a label, a module) and draws from the `--zx-color-tone-*` palette, which is neither the
|
|
357
|
+
accent nor the status set, so a project tagged red never reads as an error. When `tone` is set it
|
|
358
|
+
decides the colour and `kind` is ignored for it; every `variant` still applies (soft: tinted,
|
|
359
|
+
borderless; solid: filled with the tone's ink; outline). The badge carries `data-tone`. Badges
|
|
360
|
+
are medium weight and sentence case.
|
|
361
|
+
- **Neutral and small** — the neutral soft fill is a wash of the text colour, not an opaque surface
|
|
362
|
+
step, so it stays visible on raised surfaces in the dark theme. `size: 'sm'` fits table cells
|
|
363
|
+
and board cards; a small badge with `dot` keeps the 12px text and a full spacing step between
|
|
364
|
+
dot and label.
|
|
288
365
|
- **Methods** — none. `badge(options)` → `HTMLSpanElement`, `badgeGroup([badge(…), …])` → a wrapping
|
|
289
366
|
inline row with its own spacing; you own both elements.
|
|
290
367
|
- **Events** — none; a badge is not interactive.
|
|
@@ -309,6 +386,8 @@ a `MenuButton`, where choosing one activates the original control.
|
|
|
309
386
|
`onclick`, from the row and from the overflow menu alike.
|
|
310
387
|
- **Keyboard** — Tab reaches the toolbar once; ArrowLeft/ArrowRight move between controls and wrap;
|
|
311
388
|
Home/End jump to the first and last control.
|
|
389
|
+
- **Pressed items** — an item with `active` renders `aria-pressed="true"` and takes the neutral
|
|
390
|
+
`--zx-color-bg-current` fill, never the accent.
|
|
312
391
|
<!-- /doc -->
|
|
313
392
|
|
|
314
393
|
<!-- doc:empty-state -->
|
|
@@ -403,7 +482,8 @@ shape is genuinely known. All three are factories returning elements.
|
|
|
403
482
|
|
|
404
483
|
Two-state pressed button on a native `<button aria-pressed>`. The check indicator has a glyph in
|
|
405
484
|
both states — an empty box when off, a check when on — so an unpressed or disabled button still
|
|
406
|
-
reads as a two-state control.
|
|
485
|
+
reads as a two-state control. Pressed, the button goes neutral (the `--zx-color-bg-current` fill,
|
|
486
|
+
a stronger border, the text colour); only the check glyph, the mark, keeps the accent.
|
|
407
487
|
|
|
408
488
|
- **Options** — `label: '' | [onLabel, offLabel]`, `checked: false`, `icon: true`,
|
|
409
489
|
`disabled: false`.
|
|
@@ -418,6 +498,8 @@ reads as a two-state control.
|
|
|
418
498
|
Switch (`role="switch"`, `aria-checked`).
|
|
419
499
|
|
|
420
500
|
- **Options** — `checked: false`, `value: true`, `label: null`, `disabled: false`.
|
|
501
|
+
- **Label** — start-aligned and wrapping like text within the available width, the track level
|
|
502
|
+
with its first line.
|
|
421
503
|
- **Methods** — `get()`, `set(checked, {silent})`, `toggle()`, `getValue()` (returns
|
|
422
504
|
`options.value` when on, `false` when off), `enable()`/`disable()`.
|
|
423
505
|
- **Events** — `change {checked, value}`.
|
|
@@ -446,17 +528,34 @@ permissions, recent-history/cache policy, and the route or command ultimately in
|
|
|
446
528
|
`new Launcher(existingDialog, options)` enhances and later restores an existing `<dialog>`.
|
|
447
529
|
- **Options** — `items`, abortable `sources: [{id, label?, minQuery?, when?, order?, load(query, {signal})}]`,
|
|
448
530
|
`query`, `debounce: 250`, `minQuery: 0`, `maxResults: 100`, `placeholder`, `label`, `emptyText`,
|
|
449
|
-
`loadingText`, `shortcut: 'mod+k'|false`, visible `hints` labels
|
|
450
|
-
explicitly select an element whose nearest Zx theme scope owns the launcher.
|
|
451
|
-
- **Items** — `{id, label, description?, keywords?, group?, icon?, badge?,
|
|
452
|
-
invoke?, pinned?, disabled?, kind?, current?, when?, groupOrder?,
|
|
453
|
-
bundled icon name, Node, or
|
|
531
|
+
`loadingText`, `shortcut: 'mod+k'|false`, visible `hints` labels `{move, open, back, close}` (or
|
|
532
|
+
`false`), and `scope` to explicitly select an element whose nearest Zx theme scope owns the launcher.
|
|
533
|
+
- **Items** — `{id, label, description?, keywords?, group?, icon?, badge?, hint?, shortcut?, keys?,
|
|
534
|
+
value?, href?, target?, invoke?, pinned?, disabled?, kind?, current?, when?, groupOrder?,
|
|
535
|
+
itemOrder?, items?, placeholder?, order?}`. `icon` accepts a bundled icon name, Node, or
|
|
536
|
+
factory. Links stay native so modified and middle clicks work;
|
|
454
537
|
non-link commands may provide an application-owned `invoke` callback. Malformed and executable
|
|
455
|
-
URL schemes are stripped; a descriptor with no safe link or callback is disabled.
|
|
456
|
-
|
|
457
|
-
`
|
|
458
|
-
- **
|
|
459
|
-
|
|
538
|
+
URL schemes are stripped; a descriptor with no safe link or callback is disabled. `hint` is muted
|
|
539
|
+
text at the end of the row (a current value, a project); `shortcut: ['⌘', 'K']` (or Button's
|
|
540
|
+
`{label, keys}`) draws key caps for display only, and `keys: 'Meta+K'` adds `aria-keyshortcuts`.
|
|
541
|
+
- **Pages** — an item with `items` opens a page instead of being selected: an array, or a loader
|
|
542
|
+
`(query, {signal}) => items | Promise<items>` that runs when the page opens and, debounced, as
|
|
543
|
+
its query changes (the loading and empty states show meanwhile; `order: 'source'` keeps the
|
|
544
|
+
loader's order instead of ranking it). Opening a page clears the query, shows a scope chip with
|
|
545
|
+
the item's label before the input — part of the combobox's name, `Search <label>` — and adds
|
|
546
|
+
"⌫ Back" to the footer; `placeholder` sets the page's placeholder. Page rows show a chevron.
|
|
547
|
+
Backspace on an empty query, ArrowLeft on an empty query, or the chip's × goes back one page,
|
|
548
|
+
restoring the query and highlighted row beneath; Escape closes from any page, and the next
|
|
549
|
+
opening starts on the root. Pages nest. `open({page})` opens straight into a page — a local page
|
|
550
|
+
id (at any depth of array pages, the path becoming the back stack) or a page item. A polite
|
|
551
|
+
status line announces each change ("Agents, 7 results").
|
|
552
|
+
- **Methods** — `open({page}?)`, `close()`, `toggle()`, `isOpen()`, `focus()`, `setQuery()`,
|
|
553
|
+
`getQuery()`, `back()`, `getPage()`, `setItems()` (the root's items), `setCurrent()`,
|
|
554
|
+
`setSources()`, `destroy()`.
|
|
555
|
+
- **Events** — cancelable `select {item, value, source, query, page}`, `query {query}`,
|
|
556
|
+
`page {page, path}` (after every page change; `page` is null on the root), `open`, `close`, and
|
|
557
|
+
`error {source, error, page}` (a failing page loader reports as source `page:<id>`). Preventing
|
|
558
|
+
`select` suppresses the supplied link or callback.
|
|
460
559
|
- **Ranking** — local results are case/diacritic-insensitive and deterministic: exact label,
|
|
461
560
|
label prefix, word prefix, label substring, identifier substring, then acronym prefix. Pinned
|
|
462
561
|
items retain an explicit order ahead of otherwise equal results. A new async query aborts and
|
|
@@ -464,13 +563,18 @@ permissions, recent-history/cache policy, and the route or command ultimately in
|
|
|
464
563
|
- **Keyboard** — Cmd+K on macOS or Ctrl+K elsewhere opens by default; the shortcut is ignored in
|
|
465
564
|
editable controls, composition, and modal contexts. Application tiles use spatial arrow-key
|
|
466
565
|
movement; Up/Down may continue into record groups. Home/End, Page Up/Down, and Enter operate the
|
|
467
|
-
single listbox selection model; Escape closes and restores focus.
|
|
468
|
-
|
|
566
|
+
single listbox selection model; Escape closes and restores focus. Enter, or ArrowRight with the
|
|
567
|
+
caret at the end of the query, opens a page row; Backspace or ArrowLeft on an empty query goes
|
|
568
|
+
back. `aria-current` marks the active destination independently from the keyboard's
|
|
569
|
+
`aria-selected` option.
|
|
469
570
|
- **Optional ZeyOS adapter** — import `zeyosLauncher()` or `buildZeyosLauncherConfig()` from
|
|
470
571
|
`@zeyos/zx/zeyos`. The adapter receives the permitted module/menu catalogue, pins, current
|
|
471
572
|
identifier, recent loader or cache, abortable search, destination resolvers, and application/
|
|
472
573
|
record icon renderers explicitly. It maps the complete shell payload without reading globals or
|
|
473
574
|
taking over application routing.
|
|
575
|
+
- **Appearance** — group labels are sentence-case captions. The application you are in
|
|
576
|
+
(`aria-current="page"`) takes the neutral `--zx-color-bg-current` fill and a semibold label; the
|
|
577
|
+
highlighted row is the lighter hover step. Neither uses the accent.
|
|
474
578
|
<!-- /doc -->
|
|
475
579
|
|
|
476
580
|
<!-- doc:app-icon -->
|
|
@@ -509,18 +613,135 @@ options rather than application globals.
|
|
|
509
613
|
<!-- doc:avatar -->
|
|
510
614
|
### Avatar
|
|
511
615
|
|
|
512
|
-
A fixed-geometry user image with deterministic initials
|
|
513
|
-
|
|
514
|
-
announced twice.
|
|
515
|
-
|
|
516
|
-
- **Options** — `src`, `name`, `initials`, `label`, `size: 'sm'|'md'|'lg'|<pixels
|
|
517
|
-
`shape: 'circle'|'rounded'|'square'`, `
|
|
518
|
-
`
|
|
616
|
+
A fixed-geometry user image with a deterministic fallback: initials on a tint from the categorical
|
|
617
|
+
tone palette, picked by a stable hash of the name. Image loading or failure never changes layout,
|
|
618
|
+
and the component is decorative by default so adjacent identity text is not announced twice.
|
|
619
|
+
|
|
620
|
+
- **Options** — `src`, `name`, `initials`, `label`, `size: 'sm'|'md'|'lg'|<pixels>` (24 / 32 /
|
|
621
|
+
40px at the default spacing unit), `shape: 'circle'|'rounded'|'square'`, `tone:
|
|
622
|
+
'blue'|'teal'|'green'|'amber'|'orange'|'red'|'pink'|'violet'|'slate'|null`, `status:
|
|
623
|
+
'online'|'away'|'busy'|'offline'|null`, and `statusLabel`.
|
|
624
|
+
- **Fallback** — no ring; the `--zx-color-tone-*-bg` fill with its `-fg` ink, initials at 0.4× the
|
|
625
|
+
size (never under 10px), weight 600, one initial below 24px. `tone: null` derives the tone with
|
|
626
|
+
`avatarTone(name)` (exported from the module): FNV-1a over the trimmed, NFC-normalised,
|
|
627
|
+
lower-cased name, mapped onto the eight chromatic tones, so a person keeps their colour on every
|
|
628
|
+
screen. `slate`, the neutral, is reserved for an avatar with no name and can be set explicitly.
|
|
629
|
+
The root carries `data-tone`. `avatarInitials(name, limit = 2)` returns the initials.
|
|
519
630
|
- **Methods** — `set(values)`, `setStatus(status, label?)`, `hasImage()`, `destroy()`.
|
|
520
631
|
- **Accessibility** — set `label` only when the avatar stands alone. Presence is supplementary
|
|
521
632
|
status, not the account's accessible name; supply `statusLabel` when it conveys information.
|
|
522
633
|
<!-- /doc -->
|
|
523
634
|
|
|
635
|
+
<!-- doc:entity-ref -->
|
|
636
|
+
### EntityRef
|
|
637
|
+
|
|
638
|
+
Dense record identity for table cells, feeds, selectors, and detail metadata. It owns presentation
|
|
639
|
+
and safe native interaction semantics; the application injects routes, permissions, context menus,
|
|
640
|
+
and record mutations.
|
|
641
|
+
|
|
642
|
+
- **Options** — `id`, `title`, `subtitle` (text, Node, or Component), `icon` (name, Node,
|
|
643
|
+
Component, or lazy renderer), `metadata: [{label,value,icon?,showLabel?}]`, `link` (a URL or
|
|
644
|
+
`{href,target?,rel?,download?,onclick?}`), `actions`, `size: 'sm'|'md'|'lg'`, `wrap: false`, and
|
|
645
|
+
`actionsLabel`.
|
|
646
|
+
- **Actions** — an action is a caller-owned Element or
|
|
647
|
+
`{id,label?,icon?,title?,href?,target?,rel?,kind?,disabled?,onselect?}`. Descriptors with `href`
|
|
648
|
+
remain native anchors; others are buttons. Executable destinations are discarded, `_blank`
|
|
649
|
+
gains `noopener`, and an icon-only action requires `title`.
|
|
650
|
+
- **Methods** — `set(values)`, `setMetadata()`, `setActions()`, `setLink()`, `getEntity()`,
|
|
651
|
+
`focus()`, and `destroy()`.
|
|
652
|
+
- **Events** — cancelable `activate {id,entity,href,event}` before a primary-link callback/native
|
|
653
|
+
navigation and `action {id,action,entity,event}` before an action callback/navigation. The
|
|
654
|
+
bubbling `zx-*` mirrors may cancel the same boundary.
|
|
655
|
+
- **Accessibility** — metadata is a definition list; `showLabel:false` clips its label visually
|
|
656
|
+
while keeping it available to assistive technology. Identity links and secondary controls keep
|
|
657
|
+
native keyboard and modifier-click behavior.
|
|
658
|
+
<!-- /doc -->
|
|
659
|
+
|
|
660
|
+
<!-- doc:file-item -->
|
|
661
|
+
### FileItem
|
|
662
|
+
|
|
663
|
+
A durable or transient file row composed from `EntityRef`, MIME/byte-size metadata, lifecycle
|
|
664
|
+
status, and accessible progress. It never starts a transfer, decides authorization, resolves a
|
|
665
|
+
preview, or persists a file.
|
|
666
|
+
|
|
667
|
+
- **Options** — `id`, `name`, `size` (bytes), `mime`, `typeLabel: null`, `subtitle`,
|
|
668
|
+
`icon: 'file'`, additional
|
|
669
|
+
`metadata`, safe native `link`, `actions`, status (`ready` | `temporary` | `waiting` |
|
|
670
|
+
`uploading` | `processing` | `success` | `error`), `statusLabel`, `progress` (0–100), `indeterminate`,
|
|
671
|
+
`identitySize: 'sm'|'md'|'lg'`, `wrap`, `actionsLabel`, and `locale`.
|
|
672
|
+
- **Methods** — `set(values)`, `setStatus(status,label?)`,
|
|
673
|
+
`setProgress(percent|null,{indeterminate?})`, `getFile()`, `focus()`, and `destroy()`.
|
|
674
|
+
- **Events** — cancelable `activate {id,file,href,event}` and
|
|
675
|
+
`action {id,fileId,action,file,event}`. Cancellation propagates to the composed EntityRef, so it
|
|
676
|
+
suppresses callbacks and navigation rather than merely observing them.
|
|
677
|
+
- **Progress** — determinate work exposes min/max/now and percentage text. Indeterminate work
|
|
678
|
+
omits `aria-valuenow`; status changes are announced separately.
|
|
679
|
+
- **Type** — the MIME type is shown as a readable type ("PDF", "CSV", "Image", "Spreadsheet"),
|
|
680
|
+
falling back to the name's extension ("EXE") when it is unfamiliar and never to the raw string;
|
|
681
|
+
`typeLabel` overrides it and `''` hides it. The default `icon: 'file'` gives way to a glyph for
|
|
682
|
+
the MIME type (`file-pdf`, `file-image`, …) when the active icon set has one — Font Awesome, or
|
|
683
|
+
a glyph added with `registerIcons()`; any other `icon` is used as given.
|
|
684
|
+
<!-- /doc -->
|
|
685
|
+
|
|
686
|
+
<!-- doc:file-list -->
|
|
687
|
+
### FileList
|
|
688
|
+
|
|
689
|
+
An accessible ordered collection of `FileItem` rows with loading and empty presentations.
|
|
690
|
+
Explicit duplicate file IDs are rejected; source order is preserved.
|
|
691
|
+
|
|
692
|
+
- **Options** — `items`, `label: 'Files'`, `loading`, `loadingCount: 3`,
|
|
693
|
+
`loadingText: 'Loading files'`, and `emptyText: 'No files'` (text, Node, or Component).
|
|
694
|
+
- **Methods** — `setItems(items,{silent?})`, `getItems()`, `getItem(id)`,
|
|
695
|
+
`addItem(item,index?,{silent?})`, `updateItem(id,values,{silent?})`,
|
|
696
|
+
`removeItem(id,{silent?})`, `setLoading()`, and `destroy()`.
|
|
697
|
+
- **Events** — cancelable `activate {id,index,file,href,event}` and
|
|
698
|
+
`action {id,fileId,index,action,file,event}`, plus `datachange {items}` after a collection
|
|
699
|
+
mutation. Returned items are defensive snapshots.
|
|
700
|
+
<!-- /doc -->
|
|
701
|
+
|
|
702
|
+
<!-- doc:activity-item -->
|
|
703
|
+
### ActivityItem
|
|
704
|
+
|
|
705
|
+
One chronological activity entry. It owns actor/avatar anatomy, semantic time, safe content slots,
|
|
706
|
+
attachments, and actions; the application owns posting, reactions, permissions, routing,
|
|
707
|
+
persistence, and sanitization before any rich content becomes a Node.
|
|
708
|
+
|
|
709
|
+
- **Options** — `id`, `actor`, `avatar` (Avatar options or Element), `title`, `content`,
|
|
710
|
+
`timestamp`, `timeLabel`, `metadata`, `attachments`, `actions`, `kind`, and
|
|
711
|
+
`headingLevel: 1..6`. Content slots accept text, Nodes, or Components and never parse HTML.
|
|
712
|
+
- **Actions** — Elements pass through; descriptors accept
|
|
713
|
+
`{id,label?,icon?,href?,target?,rel?,title?,disabled?,kind?,onselect?}`. Unsafe links and unnamed
|
|
714
|
+
icon-only controls are omitted.
|
|
715
|
+
- **Methods** — `setActivity(values)`, `getActivity()`, and `destroy()`.
|
|
716
|
+
- **Events** — cancelable `action {id,action,event}` before a callback or native navigation; its
|
|
717
|
+
bubbling `zx-action` mirror can cancel the same boundary.
|
|
718
|
+
- **Accessibility** — an entry is a native list item containing an article and semantic heading;
|
|
719
|
+
valid timestamps use `<time datetime>`, while display-only labels remain ordinary text.
|
|
720
|
+
<!-- /doc -->
|
|
721
|
+
|
|
722
|
+
<!-- doc:activity-list -->
|
|
723
|
+
### ActivityList
|
|
724
|
+
|
|
725
|
+
An accessible chronological ordered collection with stable grouping, loading/empty states, and
|
|
726
|
+
incremental host-controlled mutation. It deliberately preserves source order instead of guessing
|
|
727
|
+
whether a feed is newest- or oldest-first.
|
|
728
|
+
|
|
729
|
+
- **Options** — `items`, `groupBy` (property, resolver, or null), `groupLabel`,
|
|
730
|
+
`label: 'Activity'`, `variant: 'default'|'compact'`, `loading`, `loadingCount: 3`,
|
|
731
|
+
`loadingText`, `emptyText`, `groupHeadingLevel`, and `itemHeadingLevel`.
|
|
732
|
+
- **Compact** — `variant: 'compact'` (reflected as `data-variant`) keeps every entry on one line at
|
|
733
|
+
any width, for a summary card narrower than 30rem: actor and title read as one sentence that
|
|
734
|
+
wraps as text, the time keeps the trailing end instead of stacking under the title, an avatar
|
|
735
|
+
descriptor without a `size` is small, and metadata follows as a muted second line.
|
|
736
|
+
- **Methods** — `setItems(items,{silent?})`, `getItems()`, `getItem(id)`,
|
|
737
|
+
`appendItems(items,{silent?})`, `prependItems(items,{silent?})`,
|
|
738
|
+
`updateItem(id,values,{silent?})`, `removeItem(id,{silent?})`, `setLoading()`, and `destroy()`.
|
|
739
|
+
- **Events** — `datachange {items}` after mutation. Each child's cancelable `zx-action` bubbles
|
|
740
|
+
naturally through the list for delegated application handling.
|
|
741
|
+
- **Grouping** — groups keep first-seen group order and item source order. The exported pure
|
|
742
|
+
`groupActivityItems()` and `normalizeActivityItems()` helpers do not mutate caller arrays.
|
|
743
|
+
<!-- /doc -->
|
|
744
|
+
|
|
524
745
|
<!-- doc:account-menu -->
|
|
525
746
|
### AccountMenu
|
|
526
747
|
|
|
@@ -537,6 +758,9 @@ out, or mutates the session.
|
|
|
537
758
|
action without rebuilding the account surface.
|
|
538
759
|
- **Accessibility** — compact mode is visually avatar-only but keeps the full account-menu name on
|
|
539
760
|
its single native trigger; MenuButton retains focus return, typeahead, and arrow-key behavior.
|
|
761
|
+
- **Appearance** — the trigger is a flat row (avatar, name, secondary line, chevron) with the hover
|
|
762
|
+
fill, not a button: it takes back the border, glass fill, blur and shadow MenuButton's `.zx-btn`
|
|
763
|
+
would give it, whatever order the stylesheets load in.
|
|
540
764
|
<!-- /doc -->
|
|
541
765
|
|
|
542
766
|
<!-- doc:select -->
|
|
@@ -547,6 +771,7 @@ Unifies single-select, local filtering, and async loading.
|
|
|
547
771
|
- **Options** — `items: []`, `fixedItems: []`, optional `fixedLabel`, `valueKey: 'ID'` (string key or `(item)=>id`),
|
|
548
772
|
`labelKey: 'name'` (string or `(item)=>string`), `renderItem`, `renderValue`,
|
|
549
773
|
`renderValueAdornment`, `renderNone`, `noneLabel`, `actions`, `value`, `disabled`, `placeholder`,
|
|
774
|
+
`label` (accessible combobox name; defaults to the placeholder or "Select"),
|
|
550
775
|
`clearable: false`, `filter: false | 'local' | async (query)=>items`, `searchKeys`,
|
|
551
776
|
`minQuery: 0`, `debounce: 200`, `listHeight: 280`, `groupKey`.
|
|
552
777
|
- **Getters** — `.value`, `.selected`.
|
|
@@ -560,13 +785,33 @@ Unifies single-select, local filtering, and async loading.
|
|
|
560
785
|
entries are narrowed locally against the same query and stay resolvable by `set()`. They survive
|
|
561
786
|
`setItems()` and are shown below `minQuery`. Use them for choices with no row in the source —
|
|
562
787
|
"Unassigned", "Everyone", "Private".
|
|
563
|
-
- **Presets** — `Select.priority(target, opts)` renders the five-level
|
|
564
|
-
|
|
565
|
-
|
|
788
|
+
- **Presets** — `Select.priority(target, opts)` renders the five-level ZeyOS priority scale (values
|
|
789
|
+
0–4) as grey signal bars filled to the level; only the highest is coloured, a danger square with
|
|
790
|
+
"!". A "No priority" row (value null, message `priority.none`) heads the list and is the empty
|
|
791
|
+
control's placeholder; `clearable: false` removes it, as does a `required` `zxselect` field with
|
|
792
|
+
`preset: 'priority'`. `Select.status(target, opts)` supplies shape- and semantic-color-distinct workflow states
|
|
793
|
+
and accepts replacement `items` (each `{ID, name, indicator, tone, description?, shortcut?}`);
|
|
794
|
+
`Select.permission(target, {groups, value})` pins Private and
|
|
566
795
|
Public above record-access groups. Its value is the tri-state ZeyOS stores (`false` private,
|
|
567
796
|
`true` public, or a group ID), and `groups` takes an array or loader `(query)=>items`.
|
|
797
|
+
- **Glyphs** — `statusIcon(status, {tone?, indicator?, label?})` and `priorityIcon(level, {label?})`
|
|
798
|
+
return the exact glyphs the two presets draw (`.zx-status-icon`, `.zx-priority-icon`; inside a
|
|
799
|
+
Select they also carry `.zx-select__status-indicator` / `.zx-select__priority-icon`), for list
|
|
800
|
+
rows, board columns, filter chips, and headings. Status ids: `backlog` (dotted ring), `todo`
|
|
801
|
+
(empty ring), `in-progress` (ring with a centre dot), `in-review` or `review` (half filled),
|
|
802
|
+
`ready` (a third filled), `waiting` (two thirds filled), `done` (filled, check), `canceled` or
|
|
803
|
+
`cancelled` (filled, cross), `duplicate` (ring with a slash); a `Select.status` item object is
|
|
804
|
+
accepted too, and an unknown id draws a muted empty ring. Indicators: `dotted`, `ring`,
|
|
805
|
+
`progress`, `review`, `ready`, `waiting`, `done`, `canceled`, `duplicate`; tones: `muted`,
|
|
806
|
+
`neutral`, `warning`, `info`, `accent`, `success`, `danger`. Priority levels: `null` (no
|
|
807
|
+
priority, dashes), `0` lowest … `3` high (signal bars filled to the level), `4` highest (danger
|
|
808
|
+
square with "!"). Both are decorative (`aria-hidden`) — always pair them with the status or
|
|
809
|
+
priority as text, or a tooltip on the control holding them; `label` instead makes a standalone
|
|
810
|
+
glyph `role="img"` with that name.
|
|
568
811
|
- **Entity preset** — `Select.entity(target, options)` renders icon/color, label, and secondary
|
|
569
|
-
text using `iconKey`, `colorKey`, and `subtitleKey` (or `renderIcon`). `
|
|
812
|
+
text using `iconKey`, `colorKey`, and `subtitleKey` (or `renderIcon`). `colorKey` takes a CSS
|
|
813
|
+
colour or a token reference (`var(--zx-color-tone-blue-fg)`); without one the glyph is a neutral
|
|
814
|
+
tile in muted ink. `recent` pins a deduplicated
|
|
570
815
|
**Recent** section above ordinary/local/async results; `create: true|actionDescriptor` appends an
|
|
571
816
|
application-owned command at the bottom without changing the selected ID. `recentLabel` and the
|
|
572
817
|
ordinary Select options remain overridable. For schema-driven ZeyOS loading,
|
|
@@ -575,6 +820,32 @@ Unifies single-select, local filtering, and async loading.
|
|
|
575
820
|
- **Keyboard** (APG combobox) — ArrowDown/Alt+ArrowDown open; arrows navigate and wrap; Home/End;
|
|
576
821
|
Enter selects; Esc closes; Tab closes; printable characters filter (editable) or run a typeahead
|
|
577
822
|
(readonly). `aria-activedescendant` tracks the active option.
|
|
823
|
+
- **Appearance** — the highlighted option is a neutral step; a chosen option is a pick and keeps
|
|
824
|
+
the accent tint and its check mark. Group labels are sentence-case captions. The focus ring
|
|
825
|
+
follows keyboard focus: a click on a read-only input (no `filter`) does not draw it
|
|
826
|
+
(`data-focus-origin="pointer"` on the root until the next key). Disabled, the whole control fades
|
|
827
|
+
as a disabled Field does, and not a second time inside one.
|
|
828
|
+
- **Quiet fields** — inside `[data-zx-fields="quiet"]` the value reads as text: no border, fill,
|
|
829
|
+
chevron or clear button until hover; full field chrome while focused or open.
|
|
830
|
+
<!-- /doc -->
|
|
831
|
+
|
|
832
|
+
<!-- doc:sort-control -->
|
|
833
|
+
### SortControl
|
|
834
|
+
|
|
835
|
+
A searchable field selector with an adjacent icon button to reverse the direction. Each field
|
|
836
|
+
appears once. Changing fields preserves direction; toggling direction preserves the field. Built
|
|
837
|
+
from `Select`, with its themed popover and keyboard behavior.
|
|
838
|
+
|
|
839
|
+
- **Options** — `fields: [{id,label}]`, `value: {id,dir:'asc'|'desc'}` (null selects the first field
|
|
840
|
+
ascending), `label: 'Sort by'`, `labels: {asc,desc}` for localized direction text, `disabled`.
|
|
841
|
+
- **Methods** — defensive `getValue()`, `setValue(value,{silent})`, `enable()`, `disable()`,
|
|
842
|
+
`focus()`, `destroy()`. Unknown fields or directions are rejected. An empty catalogue disables
|
|
843
|
+
selection and returns null.
|
|
844
|
+
- **Keyboard** — Tab reaches the direction button and field selector; Space/Enter toggle the
|
|
845
|
+
focused direction button. The field retains Select’s combobox keyboard map. The button’s
|
|
846
|
+
accessible name includes the current direction; its hover title shows current → next direction.
|
|
847
|
+
- **Events** — one `change {value:{id,dir}}` event per changed field or direction. Typed search text does
|
|
848
|
+
not change the sort. The application applies the resulting state to its table, data source or route.
|
|
578
849
|
<!-- /doc -->
|
|
579
850
|
|
|
580
851
|
<!-- doc:checklist -->
|
|
@@ -583,7 +854,10 @@ Unifies single-select, local filtering, and async loading.
|
|
|
583
854
|
Searchable multi-check list with optional async loading.
|
|
584
855
|
|
|
585
856
|
- **Options** — `items`, `valueKey: 'ID'`, `labelKey: 'name'`, `checkedKey: 'on'`, `search: true`,
|
|
586
|
-
`height: 280`, `defaultChecked: false`, `
|
|
857
|
+
`height: 280`, `defaultChecked: false`, `hideLabel: false` (leaves out the heading; the group
|
|
858
|
+
keeps its text as `aria-label`), `load: async ()=>items`.
|
|
859
|
+
- **In a Field** — field type `checklist` draws no heading of its own: the field's label names the
|
|
860
|
+
group (`aria-labelledby`).
|
|
587
861
|
- **Methods** — `setItems()`, `getValues()`, `setValues(ids)`, `checkAll()`/`uncheckAll()`,
|
|
588
862
|
`search(query)`, `reload()`.
|
|
589
863
|
- **Events** — `change {values}`, `loaded`.
|
|
@@ -609,6 +883,11 @@ Field type: `number`.
|
|
|
609
883
|
set (otherwise they keep their caret meaning), Enter commits, wheel steps while focused.
|
|
610
884
|
- **Also exported** — `parseNumber(raw)` (accepts `.` and `,` as the decimal separator, ignores
|
|
611
885
|
grouping, returns null for junk) and `snapNumber(value, {min, max, step, precision})`.
|
|
886
|
+
- **In a Field** — field type `number` fills the control column like a text input and takes the
|
|
887
|
+
field key as its `name`.
|
|
888
|
+
- **Quiet fields** — inside `[data-zx-fields="quiet"]` the number reads as text: no border, no
|
|
889
|
+
fill, the value start-aligned with the quiet values around it, both steppers at the trailing end
|
|
890
|
+
and hidden (kept in the layout so nothing moves) until hover or focus; full chrome on focus.
|
|
612
891
|
<!-- /doc -->
|
|
613
892
|
|
|
614
893
|
<!-- doc:rating -->
|
|
@@ -640,7 +919,9 @@ from; everything Zx adds is chrome around it. Field type: `slider`.
|
|
|
640
919
|
`showValue: true`, `showBounds: false` (draws `min` and `max` either side of the track),
|
|
641
920
|
`showInput: false` (adds a number box for entering a value precisely instead of aiming at it),
|
|
642
921
|
`unit: ''`, `marks: null` (numbers, or `{value, label}` objects), `disabled: false`,
|
|
643
|
-
`readonly: false`, `size: 'md'|'sm'`, `formatValue: (value) => string`.
|
|
922
|
+
`readonly: false`, `size: 'md'|'sm'`, `formatValue: (value) => string`. Each mark sits under
|
|
923
|
+
the thumb's centre at its value; the outermost labels end flush with the track instead of
|
|
924
|
+
spilling out of the component.
|
|
644
925
|
- **Methods** — `get()`, `set(value, {silent})`, `setRange(min, max, step?)`, `setLabel()`,
|
|
645
926
|
`setReadonly()`, `focus()`, `enable()`/`disable()`. `value` is also a getter/setter.
|
|
646
927
|
- **Events** — `input {value}` continuously while dragging, `change {value}` once the value is
|
|
@@ -675,7 +956,8 @@ to a hidden textarea when the async Clipboard API is unavailable or denied.
|
|
|
675
956
|
that did not happen.
|
|
676
957
|
- **Accessibility** — on success the glyph becomes a tick and a visually hidden `role="status"`
|
|
677
958
|
speaks the confirmation, then both revert. The icon-only form carries the `title` as its
|
|
678
|
-
accessible name.
|
|
959
|
+
accessible name. The status line is positioned inside the button, so it never adds scrollable
|
|
960
|
+
overflow to the page or to a scrolling pane.
|
|
679
961
|
|
|
680
962
|
#### CopyInput
|
|
681
963
|
|
|
@@ -726,10 +1008,16 @@ Text date input with a calendar popover. `DateTimeBox(target, opts)` is a Datebo
|
|
|
726
1008
|
tokens are the date-utility ones: `%d %m %Y %H %M`.
|
|
727
1009
|
|
|
728
1010
|
- **Options** — `value: Date | number(unix) | string`, `format: '%d.%m.%Y'`, `time: false`, `min`,
|
|
729
|
-
`max`, `placeholder`, `clearable: true
|
|
1011
|
+
`max`, `placeholder`, `clearable: true` (the clear button shows while there is a value and the
|
|
1012
|
+
box is enabled; without `clearable` the input keeps no room for it), `disabled`.
|
|
1013
|
+
- **In a Field** — field types `date`/`datetime` (and `daterange`) fill the control column like a
|
|
1014
|
+
text input and give way in a narrow two-column row; standalone, the box keeps its own width.
|
|
730
1015
|
- **Methods** — `get(unit='date')` (`'seconds'` → unix), `set(value, {silent})` (accepts a Date;
|
|
731
1016
|
pass Date objects, not display strings), `open()`/`close()`, `enable()`/`disable()`, `focus()`.
|
|
732
1017
|
- **Events** — `change {date}`, `invalid {text}`, `open`, `close`.
|
|
1018
|
+
- **Quiet fields** — inside `[data-zx-fields="quiet"]` the date reads as text: no border, no fill,
|
|
1019
|
+
the calendar button hidden until hover; full chrome on focus. An invalid value keeps its danger
|
|
1020
|
+
border. DateRangeBox behaves the same way.
|
|
733
1021
|
<!-- /doc -->
|
|
734
1022
|
|
|
735
1023
|
<!-- doc:date-range -->
|
|
@@ -747,7 +1035,7 @@ An inline two-month calendar for picking a start and an end day, plus the form-u
|
|
|
747
1035
|
|
|
748
1036
|
#### DateRangeBox
|
|
749
1037
|
|
|
750
|
-
- **Options** — everything DateRangePicker takes, plus `format: '%d.%m.%Y'`, `separator: ' – '`, `placeholder: null` (derived from the format when null), `clearable: true
|
|
1038
|
+
- **Options** — everything DateRangePicker takes, plus `format: '%d.%m.%Y'`, `separator: ' – '`, `placeholder: null` (derived from the format when null), `clearable: true` (no clear button while disabled, and no room kept for one without it). Typed text is parsed as `"<start><separator><end>"`; a lone date reads as a one-day range and a trailing separator keeps the range half open.
|
|
751
1039
|
- **Methods** — `get(unit = 'date')` (`'seconds'` returns `{start, end}` as Unix seconds), `set(range, {silent})` accepting `{start, end}` of Dates, Unix-seconds numbers, or formatted strings, `clear({silent})`, `open()`, `close()`, `enable()`, `disable()`, `focus()`, `destroy()`.
|
|
752
1040
|
- **Events** — `change {start, end}`, `invalid {text}` when the typed value cannot be parsed (the text stays editable and the field takes `data-state="invalid"` plus `aria-invalid`), `open`, `close`.
|
|
753
1041
|
<!-- /doc -->
|
|
@@ -763,6 +1051,8 @@ use `TimePicker` or `DateTimeBox`.
|
|
|
763
1051
|
- **Methods** — `get(unit)`, `set(value, unit?, {silent})`, `enable()`/`disable()`.
|
|
764
1052
|
- **Events** — `change {value}`, in `options.unit`.
|
|
765
1053
|
- **Also exported** — the pure helpers `splitTime` and `joinTime`.
|
|
1054
|
+
- **Quiet fields** — inside `[data-zx-fields="quiet"]` the segments (native inputs) and the sign
|
|
1055
|
+
button drop their border and fill until hover or focus; the sign keeps its colour.
|
|
766
1056
|
<!-- /doc -->
|
|
767
1057
|
|
|
768
1058
|
<!-- doc:message -->
|
|
@@ -778,8 +1068,24 @@ messages rather than the page, and uses the translucent glass surface tokens wit
|
|
|
778
1068
|
`Message.progress(text, {scope?})` → `{ update(pct, text?), done(), fail(text?) }`. `scope` is
|
|
779
1069
|
an element or selector whose nearest Zx theme scope owns that floating region; without it the
|
|
780
1070
|
focused opener supplies the scope.
|
|
1071
|
+
- **Region lifecycle** — a floating region is created on the first toast for its scope and removed
|
|
1072
|
+
when its last toast closes. One whose host was emptied or left the document is replaced on the
|
|
1073
|
+
next toast rather than reused, and a scope that is itself detached falls back to the focused
|
|
1074
|
+
element's scope. `Message.disposeRegion(scope)` → boolean removes a scope's region at once,
|
|
1075
|
+
toasts and all, for a host torn down while toasts are still showing.
|
|
781
1076
|
- **Per-message options** — `kind: 'info'`, `timeout`, `closable: true`, `maxVisible: 5` (extras
|
|
782
1077
|
queue, and hovering pauses dismissal).
|
|
1078
|
+
- **Action** — `action: { label, onclick?, kind? }` renders one small button (`.zx-btn`,
|
|
1079
|
+
`data-size="sm"`, secondary unless `kind` is `primary`/`danger`/`ghost`) in a toast or an inline
|
|
1080
|
+
message; activating it runs `onclick(event)` and closes the message. A message with an action
|
|
1081
|
+
stays at least 8000ms (`timeout: 0` still persists). Every message holds its timer while the
|
|
1082
|
+
pointer is over it or focus is inside it (WCAG 2.2.1) and resumes once neither is; the region is
|
|
1083
|
+
not modal, so Tab moves between the action and the close button, and closing a message that holds
|
|
1084
|
+
focus returns focus to where it came from. The live region still announces the text.
|
|
1085
|
+
- **Leading** — `leading: Node | component` (an Avatar, say) replaces the kind icon. It is
|
|
1086
|
+
decorative; the text must name what it shows.
|
|
1087
|
+
- **Handle** — every `show()` (static or inline) and `info/success/warning/error()` return
|
|
1088
|
+
`{ close() }`; closing twice, or a queued message, is safe.
|
|
783
1089
|
<!-- /doc -->
|
|
784
1090
|
|
|
785
1091
|
<!-- doc:modal -->
|
|
@@ -789,7 +1095,9 @@ Thin overlay on the native `<dialog>` element. The surface uses the shared, read
|
|
|
789
1095
|
panel material; the backdrop uses `--zx-color-overlay-backdrop`, and focus returns to the opener on close.
|
|
790
1096
|
|
|
791
1097
|
- **Options** — `content`, `width: 'auto'`, `closable: true`, `lightDismiss: false`,
|
|
792
|
-
`destroyOnClose: false`, `scope: null` (the opener's nearest Zx theme scope by default).
|
|
1098
|
+
`destroyOnClose: false`, `scope: null` (the opener's nearest Zx theme scope by default). The
|
|
1099
|
+
scope is resolved again on every `open()`; a `scope` element not yet in the document leaves the
|
|
1100
|
+
dialog detached until then, rather than parking it on `<body>` outside the theme.
|
|
793
1101
|
- **Methods** — `open()`, `close(result?)`, `setContent()`, `isOpen()`.
|
|
794
1102
|
- **Events** — `open`, `close {result}`, `cancel` (Esc; preventable with `event.preventDefault()`).
|
|
795
1103
|
<!-- /doc -->
|
|
@@ -800,12 +1108,20 @@ panel material; the backdrop uses `--zx-color-overlay-backdrop`, and focus retur
|
|
|
800
1108
|
Structured modal: a header with title and close button, a body, and footer buttons.
|
|
801
1109
|
|
|
802
1110
|
- **Options** — `title`, `size: 'sm'|'md'|'lg'|number`,
|
|
803
|
-
`buttons: [{ label, kind, action: 'close'|'cancel'|fn(dialog), autofocus }]`, `closable: true`.
|
|
1111
|
+
`buttons: [{ label, kind, action: 'close'|'cancel'|fn(dialog), autofocus, shortcut }]`, `closable: true`.
|
|
804
1112
|
- **Methods** — `setTitle()`, `setContent()`, `setButtons()`,
|
|
805
1113
|
`addView(key, { content, buttons? })`, `showView(key)`.
|
|
806
|
-
- **Statics returning Promises** — `Dialog.alert({ title, message })`,
|
|
807
|
-
`Dialog.confirm({ title, message, danger })` → boolean,
|
|
808
|
-
`Dialog.prompt({ title, message, value })` → string|null.
|
|
1114
|
+
- **Statics returning Promises** — `Dialog.alert({ title, message, okLabel })`,
|
|
1115
|
+
`Dialog.confirm({ title, message, okLabel, cancelLabel, danger })` → boolean,
|
|
1116
|
+
`Dialog.prompt({ title, message, value, placeholder, okLabel, cancelLabel })` → string|null.
|
|
1117
|
+
The labels default to the translated "OK" and "Cancel"; name the action instead where you can
|
|
1118
|
+
("Delete", "Rename").
|
|
1119
|
+
- **Type to confirm** — `Dialog.confirm({ ..., confirmText: 'Website Redesign' })` adds an input
|
|
1120
|
+
labelled "Type **Website Redesign** to confirm" (a real `<label>`; message
|
|
1121
|
+
`dialog.typeToConfirm`, `'Type %1 to confirm'`). Focus starts in the input; the confirm button
|
|
1122
|
+
stays disabled, and Enter does nothing, until the value matches exactly (case-sensitive, ignoring
|
|
1123
|
+
surrounding spaces). Escape, the close button, and Cancel resolve `false` as before. Use it for
|
|
1124
|
+
deletes that cannot be undone, with `danger: true` and an `okLabel` that names the action.
|
|
809
1125
|
<!-- /doc -->
|
|
810
1126
|
|
|
811
1127
|
<!-- doc:sheet -->
|
|
@@ -857,12 +1173,14 @@ When a Dock adopts it, the Sheet becomes an opaque layout surface instead.
|
|
|
857
1173
|
`aria-valuenow`/`aria-valuemin`/`aria-valuemax`: arrow keys along the axis step it by 16px, Shift
|
|
858
1174
|
by 64px. A docked sheet hides its handle — the dock's own divider resizes it there.
|
|
859
1175
|
- **Custom properties** — `--zx-sheet-size` (written by `size`; the per-side default otherwise)
|
|
860
|
-
and `--zx-sheet-radius` (inner corners of a top/bottom sheet, default `--zx-radius-
|
|
1176
|
+
and `--zx-sheet-radius` (inner corners of a top/bottom sheet, default `--zx-radius-lg`) are
|
|
861
1177
|
internal geometry, not published styling hooks — prefer the options. `--zx-overlay-blur` is a
|
|
862
1178
|
tier-2 semantic token and is public: it sets the `'blur'` backdrop's radius, default `8px`.
|
|
863
1179
|
- **Motion** — entry and exit both animate, via `@starting-style` and
|
|
864
1180
|
`transition-behavior: allow-discrete` on `display`/`overlay`; without them the sheet appears and
|
|
865
|
-
disappears instantly.
|
|
1181
|
+
disappears instantly. The slide (and a docked track's collapse, and a stack's shift) runs on
|
|
1182
|
+
`--zx-ease-drawer`; the fade keeps `--zx-ease`. All of it sits inside
|
|
1183
|
+
`prefers-reduced-motion: no-preference`, and stops inside a `data-zx-motion="reduced"` region.
|
|
866
1184
|
- **Keyboard and screen readers** — identical to Dialog: the native `<dialog>` supplies the role
|
|
867
1185
|
and `aria-labelledby` points at the title. Escape closes when `closable`, focus moves to the
|
|
868
1186
|
`[autofocus]` control or the first focusable descendant on open, and returns to the opener on
|
|
@@ -919,6 +1237,10 @@ DOM child of the anchor's nearest Zx theme scope while the browser lifts it into
|
|
|
919
1237
|
`left*` and `right*` placements, `offset: 4`,
|
|
920
1238
|
`matchWidth: false`, `openOn: 'click'|'manual'`, `closeOnSelect: false`, `scope: null` (the
|
|
921
1239
|
anchor's nearest Zx theme scope by default).
|
|
1240
|
+
- **Hosting** — the host is resolved again on every `open()`. A dropdown created before its anchor
|
|
1241
|
+
is in the document keeps its panel detached until the first open instead of parking it on
|
|
1242
|
+
`<body>`, so an anchor inserted into a `[data-zx-theme]` region or a `.zx-scope` afterwards still
|
|
1243
|
+
gets its theme. Tooltip, MenuButton, and ContextMenu panels follow the same rule.
|
|
922
1244
|
- **Methods** — `open()`, `close()`, `toggle()`, `isOpen()`, `setContent()`, `getPanel()`.
|
|
923
1245
|
- **Events** — `open`, `close`.
|
|
924
1246
|
<!-- /doc -->
|
|
@@ -928,15 +1250,32 @@ DOM child of the anchor's nearest Zx theme scope while the browser lifts it into
|
|
|
928
1250
|
|
|
929
1251
|
A button that opens a `role="menu"`.
|
|
930
1252
|
|
|
931
|
-
- **Options** — `label`, `icon`, `kind`,
|
|
932
|
-
`
|
|
933
|
-
-
|
|
934
|
-
`
|
|
1253
|
+
- **Options** — `label`, `icon`, `kind`, `placement`, and a shared `items` entry model:
|
|
1254
|
+
- `'-'` or `{type: 'separator'}` draws a separator; `{type: 'heading', label}` draws a visible,
|
|
1255
|
+
non-interactive heading.
|
|
1256
|
+
- Actions keep `label`, `icon`, `value`, `disabled`, `danger`, and `onselect`, and may add
|
|
1257
|
+
`description`, `badge`, `shortcut`, or a text/Node `adornment`.
|
|
1258
|
+
- `href`, `target`, and `rel` render a genuine safe native link, preserving modifier clicks and
|
|
1259
|
+
targets. Executable URL schemes are rejected; `_blank` receives `noopener noreferrer` by
|
|
1260
|
+
default.
|
|
1261
|
+
- `role: 'menuitemcheckbox'|'menuitemradio'` with `checked` exposes an APG checked action. The
|
|
1262
|
+
application owns state changes and calls `setItems()` with the new model.
|
|
1263
|
+
- **Methods** — `setItems()`, `open({returnFocus})`, `close()`, `setLabel()`, `getTrigger()`,
|
|
1264
|
+
`getPanel()`, `focusFirst({returnFocus})`, `focusLast({returnFocus})`.
|
|
935
1265
|
- **Events** — `select {value, item}`, `open`, `close`.
|
|
1266
|
+
- **Focus return** — choosing an item closes the menu and returns focus to the trigger before the
|
|
1267
|
+
`select` event and the item's `onselect` run, so a dialog they open returns to the trigger when
|
|
1268
|
+
it closes, and focus they move themselves stays where they put it. Escape returns focus the same
|
|
1269
|
+
way. `returnFocus` sets the destination for one opening: `true` (default) is the trigger, an
|
|
1270
|
+
element is that element — the list row a keyboard shortcut opened the menu from — and `false`
|
|
1271
|
+
leaves focus alone.
|
|
936
1272
|
- **Cancellation** — `select` is cancelable. Preventing it skips the item's `onselect` callback and
|
|
937
|
-
leaves the menu open, which is the composition seam used by
|
|
1273
|
+
leaves the menu open with focus back on the item, which is the composition seam used by
|
|
1274
|
+
`AccountMenu` and rail flyouts.
|
|
938
1275
|
- **Keyboard** — ArrowDown/Enter/Space open and focus the first item; ArrowUp opens and focuses the
|
|
939
1276
|
last; arrows cycle; Home/End; typeahead; Enter/Space select; Esc/Tab close.
|
|
1277
|
+
- **Appearance** — the highlighted item is a neutral step, a checked item keeps its accent check
|
|
1278
|
+
mark, and section headings are sentence-case captions. ContextMenu shares the same rules.
|
|
940
1279
|
<!-- /doc -->
|
|
941
1280
|
|
|
942
1281
|
<!-- doc:context-menu -->
|
|
@@ -949,8 +1288,9 @@ parked at the pointer, so it flips near a viewport edge like every other floatin
|
|
|
949
1288
|
- **Constructor** — `new ContextMenu(target, options)` where `target` is an element or selector.
|
|
950
1289
|
Unlike most components the target is not enhanced or replaced; `destroy()` removes only the
|
|
951
1290
|
anchor the menu created.
|
|
952
|
-
- **Options** — `items: []`
|
|
953
|
-
|
|
1291
|
+
- **Options** — `items: []` uses the same headings, separators, links, descriptions, trailing
|
|
1292
|
+
metadata, checked roles, and ordinary action shape MenuButton takes, **or a function**
|
|
1293
|
+
`(context) => items` called on
|
|
954
1294
|
every opening so a row menu can disable the actions that row does not allow; returning an empty
|
|
955
1295
|
array cancels the opening and leaves the platform menu alone. `selector: null` restricts the
|
|
956
1296
|
menu to matching descendants and reports the matched element as the context — a table passes
|
|
@@ -965,6 +1305,8 @@ parked at the pointer, so it flips near a viewport edge like every other floatin
|
|
|
965
1305
|
half most pointer-only context menus miss. Arrows move between items, typing jumps to one by its
|
|
966
1306
|
first letters, Enter and Space activate, Escape and Tab close — and closing returns focus to
|
|
967
1307
|
wherever it came from.
|
|
1308
|
+
- **Cancellation** — `select` is cancelable just like MenuButton. Preventing it suppresses the
|
|
1309
|
+
item callback and native navigation and leaves the context menu open.
|
|
968
1310
|
<!-- /doc -->
|
|
969
1311
|
|
|
970
1312
|
<!-- doc:tooltip -->
|
|
@@ -996,17 +1338,41 @@ Two panes with a divider the user owns. The layout is a three-track CSS grid dri
|
|
|
996
1338
|
<!-- doc:table -->
|
|
997
1339
|
### Table
|
|
998
1340
|
|
|
1341
|
+
**Group and section rows.** `rowKind` names the field (or a callback) that classifies a row, and
|
|
1342
|
+
`rowKinds` maps a kind to `{span, render, class}`. A `span: true` row renders one
|
|
1343
|
+
`<th scope="colgroup">` across every rendered cell — including the selection and reorder columns —
|
|
1344
|
+
and carries no others, which is what a heading row in a billing document actually is. Both
|
|
1345
|
+
default to null, so a table that sets neither renders exactly as before. While any spanning row is
|
|
1346
|
+
present the header sort controls are withheld and `setSort()` is a no-op: sorting a grouped table
|
|
1347
|
+
scatters its positions out of their sections, and silently doing so is worse than not offering it.
|
|
1348
|
+
The stacked presentation renders the heading alone rather than a row of labelled blanks.
|
|
1349
|
+
|
|
999
1350
|
Sortable, selectable, sticky-header data table. `fr` widths fill the container while px/auto
|
|
1000
1351
|
widths scroll horizontally, and multi-select adds a tri-state header checkbox plus Shift+click
|
|
1001
1352
|
range select.
|
|
1002
1353
|
|
|
1003
|
-
- **Options** — `columns: [{ id, label, sortable?, width ('120px'|'2fr'|'auto'), align,
|
|
1354
|
+
- **Options** — `columns: [{ id, label, ariaLabel?, sortable?, width ('120px'|'2fr'|'auto'), align,
|
|
1004
1355
|
type?: 'text'|'number'|'currency'|'percent'|'unit', locale?, decimals?, currency?, unit?,
|
|
1005
1356
|
render?: (row,i)=>Node|string, sortValue?, headerTitle? }]`, `data`, `rowId: 'ID'`,
|
|
1006
1357
|
`sort: { id, dir }`, `sortMode: 'local'|'server'`, `selectable: false|'single'|'multi'`,
|
|
1007
|
-
`stickyHeader: true`, `height`, `emptyText`, `rowClass`, `zebra: true`,
|
|
1008
|
-
`hierarchy: false|{parentId, column?, expanded?}`. Locale, currency,
|
|
1009
|
-
row callbacks; values remain numbers in row data while display
|
|
1358
|
+
`stickyHeader: true`, `height`, `emptyText`, `rowClass`, `zebra: true`, `variant:
|
|
1359
|
+
'default'|'plain'`, and `hierarchy: false|{parentId, column?, expanded?}`. Locale, currency,
|
|
1360
|
+
unit, and decimals may be row callbacks; values remain numbers in row data while display
|
|
1361
|
+
formatting is locale-aware.
|
|
1362
|
+
- **Appearance** — the header sits on the table's surface (no band) in the muted text colour at a
|
|
1363
|
+
medium weight, sentence case; the sorted column's label takes the text colour beside its arrow.
|
|
1364
|
+
Rows are divided by `--zx-color-border-subtle`, the header by the ordinary border, and every cell
|
|
1365
|
+
uses tabular figures. Checked rows of a multi-selection keep the accent tint (`--zx-color-bg-selected`);
|
|
1366
|
+
the selected row of a `selectable: 'single'` table is the current record of a master–detail list
|
|
1367
|
+
and takes the neutral `--zx-color-bg-current` fill. Sortable and plain headers set the same type,
|
|
1368
|
+
and the scroll region reserves no scrollbar gutter until the table actually scrolls.
|
|
1369
|
+
- **Columns in the DOM** — every header and body cell carries `data-column="<id>"` in every mode,
|
|
1370
|
+
read-only included, so a table can be styled per column. A column with `label: ''` (icons,
|
|
1371
|
+
actions) keeps a blank header named by `ariaLabel`, or by its id; `ariaLabel` also names the
|
|
1372
|
+
column in the chooser and its cell editors.
|
|
1373
|
+
- **`variant: 'plain'`** — for a table inside a card or panel that already draws the box: no outer
|
|
1374
|
+
frame, no header band, no zebra, hairline row dividers. Reflected as `data-variant="plain"` on the
|
|
1375
|
+
root; TableView passes it through `table: { variant: 'plain' }`.
|
|
1010
1376
|
- **Methods** — `setData()`, `addData()`, `updateRow(id,row)`, `removeRow(id)`, `getRow(id)`,
|
|
1011
1377
|
`getData()`, `empty()`, `setSort(id,dir)`, `getSort()`, `clearSort()`, `getSelection()`, `setSelection(ids)`,
|
|
1012
1378
|
`clearSelection()`, `setLoading(bool)` (busy/skeleton state — dims rows, shows an indeterminate
|
|
@@ -1024,6 +1390,8 @@ range select.
|
|
|
1024
1390
|
`'cell'` edits one cell, `'row'` opens every editable cell of the row and commits them as a unit.
|
|
1025
1391
|
Editors are sized into the cell without changing row height, and an editing cell carries
|
|
1026
1392
|
`data-editing="true"` (`data-invalid="true"` plus an `aria-live` message when a validator rejects).
|
|
1393
|
+
Text, number, and date editors select their content on entry, so typing replaces the value
|
|
1394
|
+
("6" over "1.00" gives 6); a textarea keeps its caret.
|
|
1027
1395
|
- **Editable columns** — extra column keys: `editable: false|true|'text'|'number'|'select'|'date'|
|
|
1028
1396
|
'checkbox'|'textarea'|((row)=>boolean)` (`true` infers `'number'` for number/currency/percent/unit
|
|
1029
1397
|
columns and `'text'` otherwise; a function is evaluated per row, so individual rows stay
|
|
@@ -1098,6 +1466,12 @@ range select.
|
|
|
1098
1466
|
<!-- doc:table-view -->
|
|
1099
1467
|
### TableView and the shared RecordView contract
|
|
1100
1468
|
|
|
1469
|
+
**`fieldControls` takes an object.** `true` and `false` behave as they always have; a
|
|
1470
|
+
`{label, target}` object names the chooser's trigger and mounts the disclosure into a host
|
|
1471
|
+
element — a list toolbar, say — instead of the view's own. `label` omitted resolves
|
|
1472
|
+
`recordView.fields`. This is what lets a translated application use the chooser at all: the
|
|
1473
|
+
trigger used to be the literal `Fields`, with no way to change it or move it.
|
|
1474
|
+
|
|
1101
1475
|
`TableView` is the record-oriented entry point above the lower-level `Table`. It extends
|
|
1102
1476
|
`RecordView` and composes one complete `Table`, so editing, hierarchy, growing, row movement,
|
|
1103
1477
|
responsive stacking, loading, and advanced table events remain available through `getTable()`.
|
|
@@ -1130,6 +1504,10 @@ portable state as TableView and KanbanView.
|
|
|
1130
1504
|
- **Options** — the shared RecordView options plus `titleField`, `subtitleField`, `preview`,
|
|
1131
1505
|
`previewAlt`, `link`, `actions`, `groupBy`, `groupOrder`, `minCardWidth`, `maxColumns`,
|
|
1132
1506
|
`variant: 'outlined'|'raised'|'filled'`, `loadingCount`, `headingLevel`, and `label`.
|
|
1507
|
+
- **Selection trigger** — `selectionTrigger:'checkbox'` preserves existing selection behavior.
|
|
1508
|
+
With `'card'`, selectable backgrounds toggle on click, Enter or Space, Shift+click selects a range,
|
|
1509
|
+
and the checkbox is omitted. Links, actions and text selection keep their native behavior. Selection
|
|
1510
|
+
remains announced through the card's ARIA description. This option also works in KanbanView.
|
|
1133
1511
|
- **Cards** — each record is a focusable `<li>` with a native title link, separate secondary
|
|
1134
1512
|
actions, optional native multi-select checkbox, labelled metadata, and a global ARIA selection
|
|
1135
1513
|
description that retains valid listitem semantics.
|
|
@@ -1145,9 +1523,12 @@ portable state as TableView and KanbanView.
|
|
|
1145
1523
|
a stable fallback. Title/action links are normalized; `_blank` links receive `noopener`.
|
|
1146
1524
|
- **Grouping and layout** — `groupBy` is a field ID or callback; `groupOrder` may deliberately show
|
|
1147
1525
|
empty groups. Cards reflow through container queries and may cap columns without viewport logic.
|
|
1148
|
-
- **Interaction** — Enter activates a focused card; Space toggles selection.
|
|
1526
|
+
- **Interaction** — By default Enter activates a focused card; Space toggles selection. With
|
|
1527
|
+
`selectionTrigger:'card'`, Enter also toggles selection. Links, actions,
|
|
1149
1528
|
selection controls, and selected text do not also activate the card. `recordaction` adds the
|
|
1150
1529
|
resolved action to the shared event set.
|
|
1530
|
+
- **Appearance** — a selected card is a pick and keeps the accent: the `--zx-color-bg-selected`
|
|
1531
|
+
tint inside an accent outline. Metadata pairs are borderless tinted chips, not boxes in a box.
|
|
1151
1532
|
<!-- /doc -->
|
|
1152
1533
|
|
|
1153
1534
|
<!-- doc:kanban-view -->
|
|
@@ -1161,6 +1542,8 @@ cards, advisory work-in-progress limits, and equivalent pointer/programmatic/key
|
|
|
1161
1542
|
`columnOrder`, `swimlaneOrder`, collapsed IDs, `showCounts`, `showEmptyColumns`, `variant`, and
|
|
1162
1543
|
`label`. Column/lane descriptors use stable `id`, `label`, optional stored `value`, and `accept`;
|
|
1163
1544
|
columns may also declare an advisory `limit`.
|
|
1545
|
+
- **Selection trigger** — `selectionTrigger:'card'` provides the same checkbox-free click and
|
|
1546
|
+
keyboard selection as CardView. The default remains `'checkbox'`.
|
|
1164
1547
|
- **Methods** — configure/get/move columns and swim lanes, collapse either axis, and
|
|
1165
1548
|
`moveRecord(id,{column?,lane?,index?})`. Board-specific order/collapse joins the common view
|
|
1166
1549
|
state; record order, movement history, and selection do not. Saved preferences for data-derived
|
|
@@ -1171,6 +1554,9 @@ cards, advisory work-in-progress limits, and equivalent pointer/programmatic/key
|
|
|
1171
1554
|
`eyebrow-start`, `eyebrow-end`, and `title-prefix`. Unslotted metadata stays as quiet inline
|
|
1172
1555
|
facts. Titles clamp visually to two lines while retaining their complete accessible text. Move
|
|
1173
1556
|
and action controls reveal on hover/focus/selection/grab, and remain visible on touch devices.
|
|
1557
|
+
- **Assignees** — `assignees` faces (a name, names, or `{name, src?, initials?, tone?}`) are drawn
|
|
1558
|
+
like a small Avatar: initials on the tint `avatarTone(name)` picks, so a person has the same
|
|
1559
|
+
colour on the board as in every Avatar; a descriptor's own `tone` wins.
|
|
1174
1560
|
- **Movement boundary** — `recordmove {record,id,from,to,column,swimlane,limitExceeded}` is
|
|
1175
1561
|
cancelable and fires before a commit/request; either the component event or bubbling
|
|
1176
1562
|
`zx-recordmove` mirror may veto it. `accept` is a hard eligibility gate; WIP is visual advice.
|
|
@@ -1181,7 +1567,8 @@ cards, advisory work-in-progress limits, and equivalent pointer/programmatic/key
|
|
|
1181
1567
|
chooses a column, Up/Down chooses a bucket-relative position, and Alt+Up/Down chooses a swim
|
|
1182
1568
|
lane. A polite live region announces every target, result, and reached/exceeded WIP limit.
|
|
1183
1569
|
Collapse keeps focus on its control, including when a programmatic collapse hides the focused
|
|
1184
|
-
card. Native drag is an enhancement.
|
|
1570
|
+
card. Native drag is an enhancement. The pointer-drag preview and a keyboard-grabbed card lift
|
|
1571
|
+
on `--zx-drag-shadow`.
|
|
1185
1572
|
<!-- /doc -->
|
|
1186
1573
|
|
|
1187
1574
|
<!-- doc:grid -->
|
|
@@ -1221,13 +1608,16 @@ code, so future adapters do not change the public `Chart` API.
|
|
|
1221
1608
|
`setError()`, `clearError()`, `resize()`, `refreshTheme()`, `getInstance()`, `destroy()`.
|
|
1222
1609
|
- **Events** — `ready {instance}`, `update {data, type}`, `select {datasetIndex, index, dataset,
|
|
1223
1610
|
label, value, nativeEvent}`, and `error {error}`.
|
|
1224
|
-
- **Adapter contract** — `adapter.create(canvas, {type, data, options, onSelect})`
|
|
1225
|
-
`{update(spec), resize(), destroy(), instance?}`. Implementations must destroy engine
|
|
1226
|
-
and must not retain the component after teardown.
|
|
1611
|
+
- **Adapter contract** — `adapter.create(canvas, {type, data, options, reducedMotion, onSelect})`
|
|
1612
|
+
returns `{update(spec), resize(), destroy(), instance?}`. Implementations must destroy engine
|
|
1613
|
+
resources and must not retain the component after teardown. `reducedMotion` is true under the
|
|
1614
|
+
system preference or inside a `data-zx-motion="reduced"` region, and an adapter must then turn its
|
|
1615
|
+
engine's animation off; Chart watches for the answer changing and updates the mounted engine.
|
|
1227
1616
|
- **`ChartJsAdapter`** — construct it with a Chart.js constructor or namespace:
|
|
1228
1617
|
`new ChartJsAdapter(window.Chart)`. The documentation pins Chart.js 4.5.1 as a development-only
|
|
1229
1618
|
asset; `package.json` has no runtime dependencies and application builds explicitly choose how
|
|
1230
|
-
the engine is loaded.
|
|
1619
|
+
the engine is loaded. Under reduced motion it sets `animation: false`, over any animation the
|
|
1620
|
+
application configured.
|
|
1231
1621
|
- **Accessibility** — the canvas always has a name and optional description, while synchronized
|
|
1232
1622
|
labels and dataset values live in a semantic table. `summary: 'hidden'` keeps it visually hidden
|
|
1233
1623
|
for assistive technology; `'visible'` makes exact values part of the interface.
|
|
@@ -1268,6 +1658,9 @@ windowed loader.
|
|
|
1268
1658
|
- **View layout** — agenda days keep every event in one vertically stacked content column. Month
|
|
1269
1659
|
uses a continuous seven-day grid with full-height day boundaries and event lanes overlaid below
|
|
1270
1660
|
the date labels; today is indicated with accent text instead of a decorative edge highlight.
|
|
1661
|
+
- **Appearance** — the view switch is one framed control with the current view on the neutral
|
|
1662
|
+
`--zx-color-bg-current` fill; weekday headings are sentence case; inner grid lines use
|
|
1663
|
+
`--zx-color-border-subtle`; the New button is the solid primary (`--zx-color-accent-solid`).
|
|
1271
1664
|
- **Pointer editing** — a timed move preserves the point where the event was grabbed and snaps the
|
|
1272
1665
|
resulting start to `slotDuration`. The lower grip stretches the event in its original day column,
|
|
1273
1666
|
while pointer capture keeps either interaction active if the pointer leaves the event surface.
|
|
@@ -1295,6 +1688,46 @@ Declarative client-side filter bar producing a filtered array, commonly wired to
|
|
|
1295
1688
|
- **Events** — `filter {rows, state}`.
|
|
1296
1689
|
<!-- /doc -->
|
|
1297
1690
|
|
|
1691
|
+
<!-- doc:filter-panel -->
|
|
1692
|
+
### FilterPanel — typed filters from server metadata
|
|
1693
|
+
|
|
1694
|
+
The third filter component, and the one a data-driven application reaches for. `DataFilter`
|
|
1695
|
+
filters a client-side row set; `Filter` builds a backend-neutral AST with groups and operators.
|
|
1696
|
+
`FilterPanel` takes the `{key: {type, label, options, min, max}}` map a list endpoint already
|
|
1697
|
+
publishes and gives back a query object — so the shape the server describes is the shape the
|
|
1698
|
+
form renders, with no per-application translation layer in between.
|
|
1699
|
+
|
|
1700
|
+
- **Options** — `fields: Record<string, {type, label?, options?, min?, max?, step?, search?}>`,
|
|
1701
|
+
`order` (display order; omitted uses object order), `value` (the applied filters, drafted
|
|
1702
|
+
from), `mode: 'draft'|'live'`, `serialize: 'seconds'|'ms'|'iso'|(date, edge) => unknown`,
|
|
1703
|
+
`emptyText`, `msg`.
|
|
1704
|
+
- **Types** — `text`, `select`, `date`, `date:range`, `int`, `float`, `number`, and
|
|
1705
|
+
`int:range` / `float:range` / `number:range`. An unrecognised type renders a visible,
|
|
1706
|
+
labelled placeholder naming it and is excluded from the value — never dropped silently.
|
|
1707
|
+
- **Methods** — `getValue()`, `setValue()`, `apply()`, `reset()`, `clear()`.
|
|
1708
|
+
- **Events** — `apply {value}`, `change {value, field}`.
|
|
1709
|
+
|
|
1710
|
+
Five behaviours are deliberate rather than incidental:
|
|
1711
|
+
|
|
1712
|
+
- **A select is multi-valued.** "Open or waiting" is the common request, and single choice turns
|
|
1713
|
+
it into two visits. It composes `Checklist`, which brings its own search past eight options,
|
|
1714
|
+
and the group is named after the filter rather than after the component.
|
|
1715
|
+
- **Each end of a range carries its own accessible name.** One `<label for>` cannot name two
|
|
1716
|
+
inputs, and a group label that names neither is worse than no group label.
|
|
1717
|
+
- **`clear()` commits; `reset()` does not.** Clear is "no filters" and applies immediately —
|
|
1718
|
+
emptying the controls while the list stays filtered is a panel that lies about what is on
|
|
1719
|
+
screen. Reset restores the constructed value without committing, which is a host sheet's
|
|
1720
|
+
Cancel.
|
|
1721
|
+
- **An empty `fields` map is normal.** An instance that configures no filters for a settings
|
|
1722
|
+
section is the ordinary case; the panel renders `emptyText`, never an error.
|
|
1723
|
+
- **`0` is a value.** `''`, `null`, `[]` and `{from: null, to: null}` are not, and only active
|
|
1724
|
+
constraints reach the emitted value.
|
|
1725
|
+
|
|
1726
|
+
`serialize` decides how a date leaves the component, because the answer is a wire-format
|
|
1727
|
+
decision rather than an application one. A range's upper bound takes the **end** of its day, or
|
|
1728
|
+
a one-day range matches nothing.
|
|
1729
|
+
<!-- /doc -->
|
|
1730
|
+
|
|
1298
1731
|
<!-- doc:filter -->
|
|
1299
1732
|
### Filter — dynamic backend-neutral expressions
|
|
1300
1733
|
|
|
@@ -1310,7 +1743,17 @@ adapter to compile into its own backend query language.
|
|
|
1310
1743
|
priority/country/currency/unit/entity, and tags. Compatible operators and their arity come from
|
|
1311
1744
|
the exported `filterOperators` matrix; applications may explicitly override/add operator IDs.
|
|
1312
1745
|
- **Options** — `fields`, `value`, `operators`, `allowGroups: true`, `maxDepth: 3`,
|
|
1313
|
-
`maxConditions: 50`, `autoApply: false`, `readonly`, `disabled`, `applyLabel`, `clearLabel
|
|
1746
|
+
`maxConditions: 50`, `autoApply: false`, `readonly`, `disabled`, `applyLabel`, `clearLabel`,
|
|
1747
|
+
`searchable: false`, `layout: 'standard'|'compact'`, `rootLogic: null|'and'|'or'`,
|
|
1748
|
+
`showApply: true`, `showRootActions: true`.
|
|
1749
|
+
- **Searchable conditions** — opt in to local-search `Select` controls for fields, operators and
|
|
1750
|
+
static single-choice/boolean values. Uncommitted search text never becomes a condition value.
|
|
1751
|
+
Multi-choice, async and custom editors retain their existing contracts. Compact rows keep field,
|
|
1752
|
+
operator, value and removal together, wrapping the value below on narrow containers.
|
|
1753
|
+
- **Embedding** — hide internal Apply and duplicate root Add controls when the parent drawer owns
|
|
1754
|
+
submission. `rootLogic` fixes the authored root logic; incompatible saved expressions remain
|
|
1755
|
+
visible and invalid until corrected, never silently rewritten. Removing the last condition
|
|
1756
|
+
returns keyboard focus to Add filter.
|
|
1314
1757
|
- **Methods** — `getValue()`, atomic `setValue()`, `addCondition()`, `addGroup()`, `update()`,
|
|
1315
1758
|
`remove()`, `move()`, `validate()`, `apply()`, `clear()`, `focus()`, `setReadonly()`,
|
|
1316
1759
|
`enable()`/`disable()`.
|
|
@@ -1346,7 +1789,12 @@ mutate the caller's data.
|
|
|
1346
1789
|
double-click), `check {ids, node}`.
|
|
1347
1790
|
- **Keyboard** — ↑/↓ move, → expands then descends, ← collapses then ascends, Home/End, Enter
|
|
1348
1791
|
activates, Space selects or toggles the checkbox, `*` expands every sibling, typing jumps to the
|
|
1349
|
-
next match.
|
|
1792
|
+
next match. A typed character's default action is prevented, so find-as-you-type and page
|
|
1793
|
+
shortcuts do not act on it too; typing into a control inside a row is left to that control.
|
|
1794
|
+
- **Appearance** — the selected node of a single-selection tree is where you are: the neutral
|
|
1795
|
+
`--zx-color-bg-current` fill, the text colour, a semibold label. A `selection: 'multi'` tree is a
|
|
1796
|
+
set of picks and keeps the accent tint. Icons, folders included, are drawn in
|
|
1797
|
+
`--zx-color-text-subtle`; badges are plain muted counts.
|
|
1350
1798
|
<!-- /doc -->
|
|
1351
1799
|
|
|
1352
1800
|
<!-- doc:finder -->
|
|
@@ -1367,6 +1815,9 @@ breadcrumb.
|
|
|
1367
1815
|
- **Keyboard** — ↑/↓ move within the active column, → steps into the selected branch and lands on
|
|
1368
1816
|
its first row, ← returns to the parent column and truncates the path, Home/End, Enter opens a
|
|
1369
1817
|
leaf, typing jumps to the next match.
|
|
1818
|
+
- **Appearance** — the selected row of each column is the path you are on: the neutral
|
|
1819
|
+
`--zx-color-bg-current` fill, the text colour and a semibold label, with subtle icons and
|
|
1820
|
+
column dividers.
|
|
1370
1821
|
<!-- /doc -->
|
|
1371
1822
|
|
|
1372
1823
|
<!-- doc:form -->
|
|
@@ -1374,7 +1825,8 @@ breadcrumb.
|
|
|
1374
1825
|
|
|
1375
1826
|
#### Form
|
|
1376
1827
|
|
|
1377
|
-
- **Options** — `fieldsets: []`, `actions: [button descriptors]`, `novalidate: true
|
|
1828
|
+
- **Options** — `fieldsets: []`, `actions: [button descriptors]`, `novalidate: true`,
|
|
1829
|
+
`idPrefix: null` (handed to the fieldsets it builds; a fieldset's own wins).
|
|
1378
1830
|
- **Methods** (proxied across the fieldsets) — `getValues()`, `setValues()`, `getField()`,
|
|
1379
1831
|
`setValue()`, `getValue()`, `reset()`, `setHighlights()`, `clearHighlights()`, `addFieldset()`,
|
|
1380
1832
|
`setActions()`, `submit()`.
|
|
@@ -1383,21 +1835,47 @@ breadcrumb.
|
|
|
1383
1835
|
|
|
1384
1836
|
#### Fieldset
|
|
1385
1837
|
|
|
1386
|
-
- **Options** — `title
|
|
1838
|
+
- **Options** — `title` (the legend: a quiet sentence-case caption, small, medium weight, muted —
|
|
1839
|
+
the fields are the content), `columns: 1|2|3`, `fields: { id: Field-options }`,
|
|
1840
|
+
`idPrefix: null`.
|
|
1841
|
+
- **Keys and ids** — a field key names the value, not an element: it is the field's `name` (and
|
|
1842
|
+
a native control's `name`) and its `data-field-id`, never a DOM id by itself. Control ids are
|
|
1843
|
+
`<idPrefix>-<key>`, with a prefix generated per instance unless `idPrefix` is given, so a form
|
|
1844
|
+
mounted twice repeats no id and a field `stage` never collides with a `#stage` elsewhere; the
|
|
1845
|
+
label, description, and message ids derive from it (`<id>-label`). Radio groups are named after
|
|
1846
|
+
the control id. An `id` in the field options that differs from the key is used as given.
|
|
1387
1847
|
- **Methods** — `addField(id, opts)`, `getField`/`hasField`/`getFields`,
|
|
1388
1848
|
`getValues`/`setValues`/`getValue`/`setValue`, `reset`, `clear`, `focus(id)`,
|
|
1389
1849
|
`setHighlights`/`clearHighlights`.
|
|
1390
1850
|
|
|
1391
1851
|
#### Field
|
|
1392
1852
|
|
|
1393
|
-
- **Options** — `id
|
|
1394
|
-
`
|
|
1853
|
+
- **Options** — `id` (the control's DOM id; generated when omitted), `name` (the field name and
|
|
1854
|
+
native control `name`; defaults to `id` on a standalone field), `type: 'text'`, `label`,
|
|
1855
|
+
`description`, `value`, `placeholder`, `required`, `disabled`, `options`,
|
|
1856
|
+
`layout: 'stack'|'inline'`, `props: {}`.
|
|
1857
|
+
- **Properties** — `controlId` (the control's DOM id), `name`.
|
|
1395
1858
|
- **Built-in types** — `text`, `password`, `textarea`, `checkbox`, `int`, `float`, `select`,
|
|
1396
|
-
`optionlist`, `hidden`, `html
|
|
1859
|
+
`optionlist`, `hidden`, `html` (display content as text: no input frame, a control's height,
|
|
1860
|
+
inset like the values around it in a quiet form).
|
|
1397
1861
|
- **Methods** — `getValue`/`setValue`, `focus`, `reset`, `setDisabled`,
|
|
1398
1862
|
`setHighlight(msg, kind)`, `clearHighlight`, `getInput`, `own(child)`, plus the statics
|
|
1399
1863
|
`Field.register(type, adapter)` and `Field.has(type)`.
|
|
1400
1864
|
- **Events** — `change {value}`, `invalid {message}`.
|
|
1865
|
+
- **Labels** — small, medium weight, in the muted text colour: quieter than the values they name.
|
|
1866
|
+
With `layout: 'inline'` the label lines up with the control's first line.
|
|
1867
|
+
|
|
1868
|
+
#### Quiet fields
|
|
1869
|
+
|
|
1870
|
+
Inside any element with `data-zx-fields="quiet"` — an inspector, a property list, an inline record
|
|
1871
|
+
— fields read as values rather than a wall of boxes. Native inputs, selects and textareas, and the
|
|
1872
|
+
composite controls (`Select`, `TagPicker`, `NumberField`, `Datebox`, `DateRangeBox`, `Timebox`)
|
|
1873
|
+
show no border, fill, chevron, stepper or calendar button until hovered (the hover fill and the
|
|
1874
|
+
affordances come back) and their full field chrome and focus ring while focused or open. A
|
|
1875
|
+
NumberField's value starts where a quiet text input's text starts, and `html` display content is
|
|
1876
|
+
inset to match. Disabled
|
|
1877
|
+
and read-only controls never light up; an invalid Datebox keeps its danger border. It is a context
|
|
1878
|
+
attribute, not an option: set it on the container.
|
|
1401
1879
|
<!-- /doc -->
|
|
1402
1880
|
|
|
1403
1881
|
<!-- doc:form-widgets -->
|
|
@@ -1414,6 +1892,28 @@ Beyond the built-in field types, these widget types wrap whole components, used
|
|
|
1414
1892
|
`toggle` (Toggle).
|
|
1415
1893
|
<!-- /doc -->
|
|
1416
1894
|
|
|
1895
|
+
<!-- doc:code-editor -->
|
|
1896
|
+
### CodeEditor — structured text without an editor dependency
|
|
1897
|
+
|
|
1898
|
+
A textarea that behaves like a source editor, and deliberately not more than that: native undo,
|
|
1899
|
+
native IME, and no syntax engine. It never executes or injects the value, so it is the safe
|
|
1900
|
+
surface for structured text — templates, queries, configuration — while rich HTML authoring
|
|
1901
|
+
stays a separate policy decision.
|
|
1902
|
+
|
|
1903
|
+
- **Options** — `value`, `name`, `language` (shown to the reader and exposed as data),
|
|
1904
|
+
`label`, `placeholder`, `rows` (clamped 2–100), `indent` (inserted by Tab, removed by
|
|
1905
|
+
Shift+Tab), `wrap: 'off'|'soft'|'hard'`, `spellcheck`, `readOnly`, `disabled`,
|
|
1906
|
+
`status` (the line/column readout).
|
|
1907
|
+
- **Methods** — `setValue()`.
|
|
1908
|
+
- **Events** — `input {value}`, `change {value}`.
|
|
1909
|
+
|
|
1910
|
+
Tab indents rather than moving focus, which is what a code surface has to do and what makes it a
|
|
1911
|
+
keyboard trap if nothing else is arranged — so Escape releases the capture until the next
|
|
1912
|
+
keystroke in the editor: Escape then Tab (or Shift+Tab) leaves the field, and any other key,
|
|
1913
|
+
or leaving the editor, restores indentation. A `code` Form field adapter is registered by
|
|
1914
|
+
`registerFieldAdapters()`.
|
|
1915
|
+
<!-- /doc -->
|
|
1916
|
+
|
|
1417
1917
|
<!-- doc:questionnaire -->
|
|
1418
1918
|
### Questionnaire
|
|
1419
1919
|
|
|
@@ -1535,12 +2035,18 @@ shows a fixed set, and `ValueList` takes free text with no catalogue. Unknown ID
|
|
|
1535
2035
|
yet. Field type: `tagpicker`.
|
|
1536
2036
|
|
|
1537
2037
|
- **Options** — `items`, `values: []`, `valueKey: 'ID'`, `labelKey: 'name'`, `iconKey: 'icon'`,
|
|
1538
|
-
`colorKey: 'color'`, `searchKeys`,
|
|
2038
|
+
`colorKey: 'color'`, `toneKey: 'tone'`, `searchKeys`,
|
|
1539
2039
|
`filter: 'local'|(query)=>Promise<items>`, `minQuery: 0`, `debounce: 200`, `allowCreate: false`,
|
|
1540
2040
|
`max` (maximum tags; further options become `aria-disabled`), `closeOnSelect: false`,
|
|
1541
2041
|
`placeholder`, `listHeight: 260`, `disabled`, `readonly`, `renderItem`, `renderTag`.
|
|
1542
|
-
- **Tag identity** — icon values accept a bundled icon name or Node; colors
|
|
1543
|
-
|
|
2042
|
+
- **Tag identity** — icon values accept a bundled icon name or Node; colors (a CSS colour or a token
|
|
2043
|
+
reference such as `var(--zx-color-tone-blue-fg)`) are scoped to the tag and its option through
|
|
2044
|
+
`--zx-tag-color`. `toneKey` names a categorical tone from the Badge palette (`blue`, `teal`,
|
|
2045
|
+
`green`, `amber`, `orange`, `red`, `pink`, `violet`, `slate`): the tag takes its fill and ink as a
|
|
2046
|
+
toned Badge does (`data-tone`), and a tone wins over a colour. Custom `renderItem`/`renderTag`
|
|
2047
|
+
remain authoritative.
|
|
2048
|
+
- **Read-only** — `readonly` (or `setReadonly(true)`) shows only the tags: no input, chevron, or
|
|
2049
|
+
remove buttons, so a full row of tags never wraps onto an empty line.
|
|
1544
2050
|
- **Methods** — `getValues()`, `getItems()`, `setValues(values, {silent})`, `addValue()`,
|
|
1545
2051
|
`removeValue()`, `clear()`, `reset()`, `setItems()`, `isFull()`, `open()`/`close()`, `focus()`,
|
|
1546
2052
|
`setReadonly()`, `enable()`/`disable()`.
|
|
@@ -1549,6 +2055,8 @@ yet. Field type: `tagpicker`.
|
|
|
1549
2055
|
- **Keyboard** — ↓ opens and moves, ↑ moves, Enter toggles the active option (or creates one from
|
|
1550
2056
|
the query), Escape closes, Backspace on an empty input removes the last tag, Home/End jump
|
|
1551
2057
|
within the list.
|
|
2058
|
+
- **Quiet fields** — inside `[data-zx-fields="quiet"]` the tags read as the value: no border, fill
|
|
2059
|
+
or chevron until hover; full field chrome while focused or open.
|
|
1552
2060
|
<!-- /doc -->
|
|
1553
2061
|
|
|
1554
2062
|
<!-- doc:groupbox -->
|
|
@@ -1585,6 +2093,41 @@ so buttons and links in its action, content, and footer regions remain valid sib
|
|
|
1585
2093
|
container query when the card itself becomes narrow.
|
|
1586
2094
|
<!-- /doc -->
|
|
1587
2095
|
|
|
2096
|
+
<!-- doc:stat-tile -->
|
|
2097
|
+
### StatTile — `statTile(options)` / `StatTile`
|
|
2098
|
+
|
|
2099
|
+
A labelled number with optional delta, trend and icon: the most-copied snippet in any
|
|
2100
|
+
application UI, and the reason competing template products advertise a card count rather than a
|
|
2101
|
+
component count. `statTile()` is the convenience form of `new StatTile(null, options)` and
|
|
2102
|
+
returns the component, the way `tooltip()` does; call `.toElement()` for the node to place.
|
|
2103
|
+
|
|
2104
|
+
- **Options** — `label`, `value`, `format: 'number'|'currency'|'percent'|'fileSize'|null|fn`,
|
|
2105
|
+
`currency`, `locale`, `caption`, `delta`, `deltaKind: 'positive'|'negative'|'neutral'`,
|
|
2106
|
+
`deltaFormat`, `deltaLabel`, `trend: number[]`, `icon`, `kind`, `href`, `clickable`, `loading`,
|
|
2107
|
+
`msg`.
|
|
2108
|
+
- **Appearance** — the label is a sentence-case muted line (13px, medium); the value is the loud
|
|
2109
|
+
part (24px, weight 650, tabular figures); `caption` is one muted context line under it ("of
|
|
2110
|
+
13,184"). `icon` is a muted glyph beside the label, never a coloured chip; `kind` tints only the
|
|
2111
|
+
sparkline. The tile is a column with the sparkline at its foot, so tiles in a row are the same
|
|
2112
|
+
height and line up whether or not each has a caption, a delta or a trend.
|
|
2113
|
+
- **Methods** — `update(options)`, `getAccessibleName()`.
|
|
2114
|
+
- **Events** — `click {event}`.
|
|
2115
|
+
|
|
2116
|
+
- **A delta is never colour alone.** It carries its sign and a direction glyph, and `deltaKind`
|
|
2117
|
+
decides the colour rather than the sign — rising ticket counts are not a success, and a
|
|
2118
|
+
component that assumes "up is good" is wrong for half the metrics in any business.
|
|
2119
|
+
- **The accessible name is label + value (with its caption) + spoken delta** ("Active items,
|
|
2120
|
+
2,184 of 13,184, up 2 vs. last week"). The visible
|
|
2121
|
+
`+2` row and the sparkline are `aria-hidden`, because a sparkline nobody can read is
|
|
2122
|
+
decoration and saying the delta twice is worse than saying it once.
|
|
2123
|
+
- **The element follows the behaviour** — `href` renders an `<a>`, `onclick` without `href` a
|
|
2124
|
+
`<button>`, neither a `<div>` with `role="group"`. Never a div with a click handler.
|
|
2125
|
+
- **`loading: true` renders a skeleton shaped like the tile**, not a spinner.
|
|
2126
|
+
|
|
2127
|
+
Nothing in it assumes a fixed set of buckets or a fixed label: the content of a dashboard tile
|
|
2128
|
+
is the instance's, not the library's.
|
|
2129
|
+
<!-- /doc -->
|
|
2130
|
+
|
|
1588
2131
|
<!-- doc:aurora -->
|
|
1589
2132
|
### Aurora
|
|
1590
2133
|
|
|
@@ -1729,10 +2272,12 @@ detail side of a master–detail screen. Give the root a size of its own (a grid
|
|
|
1729
2272
|
|
|
1730
2273
|
Four appearances, all square-cornered. `divided` (the default) sets flat blocks on a muted track,
|
|
1731
2274
|
split by hairline dividers, above a bordered panel, with the active block lifted onto the panel
|
|
1732
|
-
surface
|
|
1733
|
-
|
|
1734
|
-
and section-level navigation. `segmented` renders a compact group
|
|
1735
|
-
|
|
2275
|
+
surface. `bracket` outlines folder tabs that fuse into that same panel, the active tab bridging
|
|
2276
|
+
the seam. `line` underlines the active tab in the text colour across a full-width rule, for page-
|
|
2277
|
+
and section-level navigation. `segmented` renders a compact framed group with the active tab on
|
|
2278
|
+
the neutral `--zx-color-bg-current` fill, so the row reads as one control inside a toolbar or card
|
|
2279
|
+
header. The active tab is where you are, not a pick: every variant gives it the text colour and a
|
|
2280
|
+
semibold label, and none uses the accent. Tab badges are neutral counts, not accent bubbles.
|
|
1736
2281
|
|
|
1737
2282
|
`divided` and `bracket` draw the panel box themselves; `line` and `segmented` leave the panel bare
|
|
1738
2283
|
for the surrounding layout to frame. The boxed variants read `--zx-tabbox-radius` (default `0px`)
|
|
@@ -1740,7 +2285,8 @@ from the component root, so `el.style.setProperty('--zx-tabbox-radius', 'var(--z
|
|
|
1740
2285
|
restores rounded corners without forking the stylesheet.
|
|
1741
2286
|
|
|
1742
2287
|
- **Options** — `tabs: [{ name, title, content, icon?, closable?, disabled? }]`, `active`,
|
|
1743
|
-
`variant: 'divided'|'bracket'|'line'|'segmented'`, `keepAlive: true
|
|
2288
|
+
`variant: 'divided'|'bracket'|'line'|'segmented'`, `keepAlive: true`, `label` (the tab list's
|
|
2289
|
+
accessible name — give one whenever a page has more than one tab list).
|
|
1744
2290
|
- **Methods** — `addTab()`, `removeTab()`, `openTab(name)`, `getActive()`, `setTitle()`,
|
|
1745
2291
|
`setBadge(name, text|null)`, `enableTab()`/`disableTab()`.
|
|
1746
2292
|
- **Events** — `change {name, previous}` (preventable), `close {name}`.
|
|
@@ -1795,6 +2341,40 @@ first and last items.
|
|
|
1795
2341
|
`aria-current="page"` and is deliberately not interactive; separators are `aria-hidden`.
|
|
1796
2342
|
<!-- /doc -->
|
|
1797
2343
|
|
|
2344
|
+
<!-- doc:list-toolbar -->
|
|
2345
|
+
### ListToolbar — the bar above a record view
|
|
2346
|
+
|
|
2347
|
+
Every list carries the same four questions — what am I searching for, which records, which
|
|
2348
|
+
columns, in what shape. The parts have always existed (`Search`, `SortControl`, `badge()`,
|
|
2349
|
+
`button()`); this is the composition, so that every consumer stops assembling it differently and
|
|
2350
|
+
forgetting the same thing.
|
|
2351
|
+
|
|
2352
|
+
- **Options** — `search: {placeholder?, value?, debounce?, clearable?}|false`, `count`,
|
|
2353
|
+
`countText`, `views: Array<string|{id, label?, icon?}>` (string presets `auto|cards|table|list`),
|
|
2354
|
+
`view`, `tools: [{id, label?, icon?, badge?, badgeKind?, pressed?, disabled?, onclick?}]`,
|
|
2355
|
+
`label`, `msg`.
|
|
2356
|
+
- **Methods** — `setCount()`, `setBadge()`, `setPressed()`, `setToolDisabled()`, `setTools()`,
|
|
2357
|
+
`getTool()`, `getToolsElement()`, `addTool()`, `setView()`, `getView()`, `getSearch()`,
|
|
2358
|
+
`setSearch()`, `focusSearch()`, `bind()`, `unbind()`.
|
|
2359
|
+
- **Events** — `search {value}`, `viewchange {view, previous}`, `action {id, pressed, tool}`.
|
|
2360
|
+
|
|
2361
|
+
Three properties are the reason to use this rather than assemble your own:
|
|
2362
|
+
|
|
2363
|
+
- **A tool's badge is part of its accessible name**, not a second thing beside it — a tool
|
|
2364
|
+
showing `2` announces as "Filters (2)". Once filters live behind a panel, a list can show four
|
|
2365
|
+
rows out of twenty with nothing on screen saying why, and the badge is what prevents that.
|
|
2366
|
+
- **The count line is a live region**, and `setCount(null)` clears it. The previous number
|
|
2367
|
+
describes the previous filters; a stale count during a load is worse than none.
|
|
2368
|
+
- **Tool buttons meet the 44 px target** at any spacing.
|
|
2369
|
+
|
|
2370
|
+
The current view and a pressed tool take the neutral `--zx-color-bg-current` fill — a mode the
|
|
2371
|
+
list is in, not a pick — never the accent tint.
|
|
2372
|
+
|
|
2373
|
+
`bind(view)` is optional sugar for a `TableView` or `CardView`: it feature-detects rather than
|
|
2374
|
+
importing them, adopts the view's shape, and mounts a field chooser built with
|
|
2375
|
+
`fieldControls: {target}` into the toolbar. The toolbar is fully functional without it.
|
|
2376
|
+
<!-- /doc -->
|
|
2377
|
+
|
|
1798
2378
|
<!-- doc:pagination -->
|
|
1799
2379
|
### Pagination
|
|
1800
2380
|
|
|
@@ -1816,6 +2396,8 @@ treated as one empty page rather than zero pages.
|
|
|
1816
2396
|
- **Semantics** — `<nav aria-label="Pagination">`; the current page carries `aria-current="page"`;
|
|
1817
2397
|
previous/next are labelled icon buttons that go `disabled` at the ends; the ellipses are
|
|
1818
2398
|
`aria-hidden` spans, not buttons.
|
|
2399
|
+
- **Appearance** — the current page is the neutral `--zx-color-bg-current` fill with a semibold
|
|
2400
|
+
figure: where you are, not a call to action.
|
|
1819
2401
|
<!-- /doc -->
|
|
1820
2402
|
|
|
1821
2403
|
<!-- doc:navigation-bar -->
|
|
@@ -1824,9 +2406,19 @@ treated as one empty page rather than zero pages.
|
|
|
1824
2406
|
Application navigation bar: brand, items, and right-aligned actions. Items overflow into a "More"
|
|
1825
2407
|
menu on narrow widths.
|
|
1826
2408
|
|
|
1827
|
-
- **Options** — `title`, `items: [{ name, title, badge? }]`, `active`, `actions: []
|
|
2409
|
+
- **Options** — `title`, `items: [{ name, title, badge? }]`, `active`, `actions: []`,
|
|
2410
|
+
`overflow: true`, `overflowBelow: '44rem'`, `minVisible: 0`.
|
|
1828
2411
|
- **Methods** — `setTitle()`, `setItems()`, `setActive()`, `setBadge()`, `setActions()`.
|
|
1829
2412
|
- **Events** — `change {name}`.
|
|
2413
|
+
|
|
2414
|
+
**Overflow is a default, not a law.** Collapsing every item behind "More" is right for a top app
|
|
2415
|
+
bar and wrong for a phone bottom bar, where the navigation is the point. `overflow: false` keeps
|
|
2416
|
+
every item at every width; `overflowBelow` moves the threshold; `minVisible: n` pins the first `n`
|
|
2417
|
+
items and overflows the rest. At the default the collapse is a container query with no observer
|
|
2418
|
+
installed; any other threshold installs a `ResizeObserver`, which is the cost of asking for one.
|
|
2419
|
+
|
|
2420
|
+
The current item is the neutral `--zx-color-bg-current` fill with the text colour and a semibold
|
|
2421
|
+
label; item badges are neutral counts. Only the brand mark keeps the accent.
|
|
1830
2422
|
<!-- /doc -->
|
|
1831
2423
|
|
|
1832
2424
|
<!-- doc:app-sidebar -->
|
|
@@ -1837,14 +2429,16 @@ vertical rail, and horizontal rail layouts. It mounts exactly one presentation a
|
|
|
1837
2429
|
preserving active and expanded branch state. Only the expanded vertical state reveals descendants
|
|
1838
2430
|
inline; every rail state opens descendants toward the workspace in anchored flyouts.
|
|
1839
2431
|
|
|
1840
|
-
- **Options** — `items`, `active`, `expanded`, `collapsed`, `collapsible
|
|
1841
|
-
`orientation: 'vertical'|'horizontal'`,
|
|
2432
|
+
- **Options** — `items`, `active`, `expanded`, `collapsed`, `collapsible` (`false` removes the
|
|
2433
|
+
minimise and expand buttons), `orientation: 'vertical'|'horizontal'`,
|
|
2434
|
+
`side: 'left'|'right'|'top'|'bottom'`, `label`,
|
|
1842
2435
|
`header`, `footer`, `railHeader`, `railFooter`, `openDelay: 80`, `closeDelay: 160`, and
|
|
1843
2436
|
`renderIcon(item, context)`. Icon values may also be names, nodes, components, or factories.
|
|
1844
2437
|
- **Methods** — `setItems()`, `getItems()`, `setActive()`, `setExpanded()`, `getExpanded()`,
|
|
1845
2438
|
`toggleBranch()`, `expand()`, `collapse()`, `setCollapsed()`, `isCollapsed()`,
|
|
1846
2439
|
`setLayout({orientation, side})`, `openFlyout(id, {focus})`, `closeFlyout()`,
|
|
1847
|
-
`closeAllFlyouts()`, `isFlyoutOpen()`, `focus()`, `destroy()`.
|
|
2440
|
+
`closeAllFlyouts()`, `isFlyoutOpen()`, `focus()`, `destroy()`. `getItems()` returns copies of
|
|
2441
|
+
the normalized tree; a leaf carries no `children` property.
|
|
1848
2442
|
- **Events** — cancelable `select {item, id, value, href, event}`,
|
|
1849
2443
|
`branchchange {item, id, expanded, ids}`, `collapsechange {collapsed}`, and
|
|
1850
2444
|
`flyoutchange {item, id, open}`.
|
|
@@ -1852,11 +2446,17 @@ inline; every rail state opens descendants toward the workspace in anchored flyo
|
|
|
1852
2446
|
an expand control. Switching layouts does not erase the saved vertical collapsed preference or
|
|
1853
2447
|
expanded branch IDs. `AppSidebar` is the single public application-navigation component; use
|
|
1854
2448
|
`collapsed: true` or `orientation: 'horizontal'` for its rail presentations.
|
|
2449
|
+
- **Current state** — the current page is neutral: the `--zx-color-bg-current` fill, the full
|
|
2450
|
+
text colour and a semibold label, in the tree, the rail and every flyout; hover is a lighter
|
|
2451
|
+
step of the same overlay. A closed branch (or a rail item) over the active route takes the
|
|
2452
|
+
semibold label or the text colour, never the accent. Nothing is docked to an edge.
|
|
1855
2453
|
- **Pointer and focus** — hover or focus reveals rail descendants without stealing focus. Click,
|
|
1856
2454
|
Enter, Space, and the arrow toward the workspace also open a flyout; Escape, outside activation,
|
|
1857
2455
|
or selection closes it. Expanded navigation supports arrows plus Home/End across visible rows.
|
|
1858
2456
|
- **Semantics** — persistent navigation uses `<nav>` and nested lists with native links and
|
|
1859
|
-
disclosure buttons, not menu or menubar roles.
|
|
2457
|
+
disclosure buttons, not menu or menubar roles. The `<nav>` is the only landmark: a root the
|
|
2458
|
+
sidebar creates is a plain `<div>`, and a caller's own root keeps its element and attributes.
|
|
2459
|
+
Icon-only destinations retain an accessible name
|
|
1860
2460
|
and Tooltip. A parent with children is disclosure-only; add an Overview child when it also needs
|
|
1861
2461
|
a destination. Malformed and executable URL schemes are stripped; an item with no remaining
|
|
1862
2462
|
link or callback is disabled in every presentation.
|
|
@@ -1888,6 +2488,9 @@ inline; every rail state opens descendants toward the workspace in anchored flyo
|
|
|
1888
2488
|
`%d %m %Y %y %H %M %S %a %A %b %B %s`), `clampDate`, `isSameDay`, `addDays`, `addMonths`,
|
|
1889
2489
|
`getWeekStart`.
|
|
1890
2490
|
- **Keyboard** — `focusTrap`, `rovingTabindex`, `typeahead`.
|
|
2491
|
+
- **Motion** — `prefersReducedMotion(element?)` → boolean, and
|
|
2492
|
+
`watchMotion(element, fn, {signal?, immediate?})` → disposer, for code that animates in script;
|
|
2493
|
+
see Theming.
|
|
1891
2494
|
- **Utils** — `debounce`, `uid`, `deepMerge`, `isElement`, `clamp`, `toArray`.
|
|
1892
2495
|
<!-- /doc -->
|
|
1893
2496
|
|
|
@@ -1904,7 +2507,8 @@ import { loadFontAwesome, useFontAwesome, registerIcons } from '/assets/zx.esm.j
|
|
|
1904
2507
|
|
|
1905
2508
|
await loadFontAwesome({ kit: 'ae8320b210', style: 'duotone' }); // injects the kit, switches over
|
|
1906
2509
|
useFontAwesome(); // page already carries Font Awesome; no network
|
|
1907
|
-
registerIcons({ rocket: ['0 0 512 512', 'M…'] });
|
|
2510
|
+
registerIcons({ rocket: ['0 0 512 512', 'M…'] }); // a filled glyph (Font Awesome paths)
|
|
2511
|
+
registerIcons({ anchor: ['0 0 24 24', 'M12 6v16…', 'stroke'] }); // an outline glyph (Lucide paths)
|
|
1908
2512
|
```
|
|
1909
2513
|
|
|
1910
2514
|
- `loadFontAwesome(kit | {kit, style, family, fixedWidth, activate})` — injects the kit script once
|
|
@@ -1913,18 +2517,38 @@ registerIcons({ rocket: ['0 0 512 512', 'M…'] }); // extend the bundled inli
|
|
|
1913
2517
|
domains configured in the Font Awesome account.
|
|
1914
2518
|
- `useFontAwesome(opts)` / `useBuiltinIcons()` / `configureIcons(opts)` / `getIconConfig()` —
|
|
1915
2519
|
switch or read the renderer without touching the network.
|
|
1916
|
-
- `iconNames()` lists every inline glyph; `
|
|
2520
|
+
- `iconNames()` lists every inline glyph, bundled and registered; `icons` is the bundled table.
|
|
2521
|
+
- `registerIcons(map)` adds or replaces inline glyphs. `[viewBox, path]` is a filled shape — the
|
|
2522
|
+
form Font Awesome path data comes in. `[viewBox, path, 'stroke']` is an outline drawn with the
|
|
2523
|
+
bundled pen; its stroke scales with the view box, so a 24-unit outline set such as Lucide renders
|
|
2524
|
+
exactly like the bundled glyphs. Anything else throws `TypeError`.
|
|
2525
|
+
|
|
2526
|
+
**The bundled set is outline.** The inline glyphs are Lucide icons (https://lucide.dev, ISC licence;
|
|
2527
|
+
the notice is in `src/core/icons.js`) on a 24-unit grid: `fill="none"`, `stroke="currentColor"`,
|
|
2528
|
+
round caps and joins, a stroke of 1.75 units — about 1.2px at the default 16px, the weight of
|
|
2529
|
+
13–14px interface text — and never thinner than one pixel, so 11–13px glyphs stay legible. The
|
|
2530
|
+
names are Zx's own (`gear`, `dots`, `warning`, `success`, `drag`, …), not Lucide's. Colour follows
|
|
2531
|
+
`currentColor`; muting an icon is the caller's choice. The paint sits on the `<svg>` as attributes,
|
|
2532
|
+
so CSS on `.zx-icon` overrides it — `fill: currentColor` turns an outline into its solid form, which
|
|
2533
|
+
is how a state such as a set star or a liked heart is shown without a second glyph.
|
|
1917
2534
|
|
|
1918
2535
|
**Names decide their own renderer.** A bare name (`'check'`) follows the active provider, and the
|
|
1919
2536
|
built-in names are translated to their Font Awesome counterparts (`x` → `fa-xmark`, `search` →
|
|
1920
2537
|
`fa-magnifying-glass`, `warning` → `fa-triangle-exclamation`, …), so component code never changes.
|
|
1921
2538
|
Prefixes override: `'fa:user'`, `'fas:user'`/`'regular:user'`/`'duotone:user'`/`'thin:user'`,
|
|
1922
2539
|
`'kit:zeyos-notes'` (custom kit icons), `'builtin:check'` (force the inline SVG). A literal class
|
|
1923
|
-
list — `'fa-sharp fa-solid fa-user'` — is used verbatim. Unknown
|
|
1924
|
-
|
|
2540
|
+
list — `'fa-sharp fa-solid fa-user'` — is used verbatim. Unknown names under Font Awesome are
|
|
2541
|
+
passed through to the kit, which resolves them in the browser.
|
|
2542
|
+
|
|
2543
|
+
**An unknown *built-in* name does not throw.** Icon names routinely arrive from a server, and one
|
|
2544
|
+
unconfigured name must not take down the screen it appears on, so it renders an empty `<svg>` of
|
|
2545
|
+
the requested size — layout does not shift — carrying `data-zx-icon-missing="<name>"`, and reports
|
|
2546
|
+
itself once per distinct name through `console.warn`. Pass `icon(name, { strict: true })` for the
|
|
2547
|
+
old `RangeError`, at a call site that can afford to fail: a test, or a build-time check.
|
|
1925
2548
|
|
|
1926
2549
|
Font Awesome elements are sized with `font-size` and occupy 1em; bundled SVGs use width/height
|
|
1927
|
-
attributes. Both honour `label` (`null` ⇒ `aria-hidden`, otherwise `role="img"` +
|
|
2550
|
+
attributes (default 16). Both honour `label` (`null` ⇒ `aria-hidden`, otherwise `role="img"` +
|
|
2551
|
+
`aria-label`).
|
|
1928
2552
|
<!-- /doc -->
|
|
1929
2553
|
|
|
1930
2554
|
<!-- doc:helpers -->
|
|
@@ -2028,15 +2652,49 @@ Registered elements: `<zx-toggle>`, `<zx-check-button>`, `<zx-select items='[…
|
|
|
2028
2652
|
<!-- doc:tokens -->
|
|
2029
2653
|
### Design tokens
|
|
2030
2654
|
|
|
2031
|
-
Two tiers of CSS custom properties. Dark mode
|
|
2032
|
-
(`[data-zx-theme="dark"]
|
|
2033
|
-
colour literals and tier-1 references
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
`--zx-
|
|
2655
|
+
Two tiers of CSS custom properties. Dark mode is a pure token/attribute swap
|
|
2656
|
+
(`[data-zx-theme="dark"]`), there is one geometry derived from `--zx-space-unit` and
|
|
2657
|
+
`--zx-radius`, and `tests/lint-tokens.js` forbids raw colour literals and tier-1 colour references
|
|
2658
|
+
in component CSS.
|
|
2659
|
+
|
|
2660
|
+
- **Tier 1** — the global palette and primitives in `styles/tokens/global.css`: `--zx-gray-0…950`,
|
|
2661
|
+
`--zx-emerald-*` (the brand ramp the default accent reads), the status ramps
|
|
2662
|
+
`--zx-green/red/amber/blue-*` (green is a yellow-green, deliberately apart from the brand), the
|
|
2663
|
+
tone ramps `--zx-tone-<hue>-300/500/700` — none of them referenced by components — plus the
|
|
2664
|
+
spacing scale (`--zx-space-unit`, `--zx-space-1…8`), the radius scale (`--zx-radius`,
|
|
2665
|
+
`--zx-radius-sm/md/lg/xl/full`), the type scale (`--zx-text-xs…3xl`, 12–30px), weights
|
|
2666
|
+
(`--zx-weight-medium` 500, `--zx-weight-semibold` 600), tracking (`--zx-tracking-tight`
|
|
2667
|
+
-0.01em, set on `.zx-scope`; `--zx-tracking-title` -0.015em), shadows, and motion
|
|
2668
|
+
(`--zx-dur-fast` 120ms, `--zx-dur-base` 180ms — both 0ms in a reduced-motion region — and the
|
|
2669
|
+
curves `--zx-ease`, `--zx-ease-spring`, `--zx-ease-drawer`).
|
|
2038
2670
|
- **Tier 2** — the semantic set, the only tokens components may use: `--zx-color-*`,
|
|
2039
|
-
`--zx-control-*`, `--zx-focus-ring`, `--zx-overlay-shadow
|
|
2671
|
+
`--zx-control-*`, `--zx-focus-ring`, `--zx-overlay-shadow`, `--zx-drag-shadow` (an item being
|
|
2672
|
+
dragged; its own value in dark, with a faint light rim). The roles that carry the visual
|
|
2673
|
+
language:
|
|
2674
|
+
- **Columns.** `--zx-color-bg-surface` is the working canvas; `--zx-color-bg-chrome` is the
|
|
2675
|
+
navigation and inspector tone beside it (one step below the canvas in light, between page and
|
|
2676
|
+
canvas in dark), so tone rather than a line separates them. Dividers inside a column use
|
|
2677
|
+
`--zx-color-border-subtle`; `--zx-color-border` is for frames.
|
|
2678
|
+
- **Current vs selected.** Where you *are* — the active navigation item, the current row, the
|
|
2679
|
+
open node, the active view — is neutral: `--zx-color-bg-current` (translucent text, about 8%,
|
|
2680
|
+
so it works on any column), text colour, weight 600. Hover is `--zx-color-bg-hover`, about half
|
|
2681
|
+
as strong. What you have *picked* keeps the accent tint `--zx-color-bg-selected`.
|
|
2682
|
+
- **Text.** `--zx-color-text`, `--zx-color-text-muted` (secondary text, 4.5:1 on every column and
|
|
2683
|
+
on a current row), `--zx-color-text-subtle` (icons and non-essential meta, 3:1),
|
|
2684
|
+
`--zx-color-text-placeholder`.
|
|
2685
|
+
- **Solid accent.** `--zx-color-accent-solid` (`-hover`, `-active`) with
|
|
2686
|
+
`--zx-color-on-accent-solid` is the one primary action per screen: the 600 stop with white ink in
|
|
2687
|
+
both themes. `--zx-color-accent` is the accent as text, marks, and focus (600 in light, 400 in
|
|
2688
|
+
dark) and pairs with `--zx-color-on-accent` when something is filled with it.
|
|
2689
|
+
- **Status.** `--zx-color-success/warning/danger/info` and their `-bg` fills mean state and
|
|
2690
|
+
nothing else; they ship with an icon or a word.
|
|
2691
|
+
- **Tones.** `--zx-color-tone-{blue,teal,green,amber,orange,red,pink,violet,slate}-bg` and `-fg`:
|
|
2692
|
+
categorical colour for tags, chips, icon tiles, and avatars — neither the accent nor a status. A
|
|
2693
|
+
soft fill (13% of the tone over the surface in light, 20% in dark) with ink at 4.5:1 or better.
|
|
2694
|
+
- **Chart series.** `--zx-color-chart-1…8`, assigned in order, never cycled: blue, orange, teal,
|
|
2695
|
+
amber, pink, green, violet, red. Neighbours stay apart under red-green colour-vision deficiency
|
|
2696
|
+
and every series clears 3:1 as a mark on the light and the dark surface; the list does not open
|
|
2697
|
+
on the accent.
|
|
2040
2698
|
- **Component styling hooks** — a short, deliberately small set of component-level custom
|
|
2041
2699
|
properties for restyling one component without touching its internals. Each falls back to the
|
|
2042
2700
|
semantic token it replaces, so setting none of them changes nothing:
|
|
@@ -2045,4 +2703,13 @@ colour literals and tier-1 references in component CSS.
|
|
|
2045
2703
|
Reach for the least specific override that works: a global semantic token first, one of these
|
|
2046
2704
|
second, an internal selector never. Component CSS declares many other `--zx-<component>-*`
|
|
2047
2705
|
properties — those are internal geometry, not API, and will change without notice.
|
|
2706
|
+
|
|
2707
|
+
**Base typography.** `.zx-scope` sets Inter with `--zx-tracking-tight` and the `cv11` feature (the
|
|
2708
|
+
single-storey a, chosen because it reads rounder and calmer at 13–14px; fonts without it ignore it).
|
|
2709
|
+
|
|
2710
|
+
**Quiet fields.** Put `data-zx-fields="quiet"` on a property panel, an inspector, or an inline
|
|
2711
|
+
record: native inputs, selects, and textareas inside it drop their border and fill until hover
|
|
2712
|
+
(`--zx-color-bg-hover`) or focus (the normal border, the control fill, and the focus ring), so values
|
|
2713
|
+
read as text rather than as a wall of boxes. Read-only and disabled fields stay quiet. The rules have
|
|
2714
|
+
zero specificity, so a component's own input styling still wins.
|
|
2048
2715
|
<!-- /doc -->
|