@zeyos/zx 4.3.0 → 4.4.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 +266 -1
- package/README.md +7 -3
- package/dist/types/components/activity-item/activity-item.d.ts +351 -0
- package/dist/types/components/activity-list/activity-list.d.ts +195 -0
- package/dist/types/components/app-sidebar/app-sidebar.d.ts +28 -0
- package/dist/types/components/button/button.d.ts +8 -0
- package/dist/types/components/calendar/calendar-model.d.ts +22 -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 +12 -4
- package/dist/types/components/card-view/record-card.d.ts +53 -5
- package/dist/types/components/code-editor/code-editor.d.ts +160 -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/dialog/dialog.d.ts +29 -0
- package/dist/types/components/entity-ref/entity-ref.d.ts +331 -0
- package/dist/types/components/file-item/file-item.d.ts +234 -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/kanban-view/kanban-card.d.ts +138 -0
- package/dist/types/components/kanban-view/kanban-policy.d.ts +268 -0
- package/dist/types/components/kanban-view/kanban-view.d.ts +837 -26
- package/dist/types/components/launcher/launcher.d.ts +19 -12
- package/dist/types/components/list-toolbar/list-toolbar.d.ts +599 -0
- package/dist/types/components/menu-button/menu-button.d.ts +4 -37
- package/dist/types/components/message/message.d.ts +7 -0
- package/dist/types/components/modal/modal.d.ts +48 -0
- 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/select.d.ts +4 -0
- package/dist/types/components/sort-control/sort-control.d.ts +105 -0
- package/dist/types/components/stat-tile/stat-tile.d.ts +277 -0
- package/dist/types/components/tabbox/tabbox.d.ts +7 -0
- package/dist/types/components/table/table.d.ts +144 -0
- package/dist/types/components/table-view/table-view.d.ts +11 -5
- package/dist/types/components/timebox/timebox.d.ts +7 -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/component.d.ts +6 -1
- package/dist/types/core/icons.d.ts +17 -0
- package/dist/types/index.d.ts +13 -1
- package/dist/types/internal/app-rail.d.ts +10 -0
- package/dist/types/internal/menu-items.d.ts +134 -0
- package/dist/zx-zeyos.esm.js +651 -88
- package/dist/zx-zeyos.esm.min.js +8 -1
- package/dist/zx.css +6266 -3900
- package/dist/zx.esm.js +8487 -1805
- package/dist/zx.esm.min.js +10 -3
- package/dist/zx.global.js +8487 -1805
- package/dist/zx.global.min.js +10 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +1811 -247
- package/docs/llms.md +345 -40
- package/docs/llms.txt +11 -7
- package/package.json +1 -2
- package/src/components/activity-item/activity-item.css +137 -0
- package/src/components/activity-item/activity-item.js +599 -0
- package/src/components/activity-list/activity-list.css +64 -0
- package/src/components/activity-list/activity-list.js +375 -0
- package/src/components/app-rail/app-rail.css +227 -19
- package/src/components/app-sidebar/app-sidebar.css +414 -24
- package/src/components/app-sidebar/app-sidebar.js +65 -5
- package/src/components/button/button.css +8 -0
- package/src/components/button/button.js +6 -1
- package/src/components/calendar/calendar-model.js +45 -0
- package/src/components/calendar/calendar.css +30 -10
- package/src/components/calendar/calendar.js +41 -13
- package/src/components/card/card.js +12 -1
- package/src/components/card-view/card-view.css +212 -9
- package/src/components/card-view/card-view.js +32 -16
- package/src/components/card-view/record-card.js +98 -21
- package/src/components/code-editor/code-editor.css +67 -0
- package/src/components/code-editor/code-editor.js +288 -0
- package/src/components/code-editor/field-adapter.js +34 -0
- package/src/components/context-menu/context-menu.css +80 -3
- package/src/components/context-menu/context-menu.js +22 -30
- package/src/components/date-picker/date-picker.js +22 -8
- package/src/components/date-picker/month-picker.js +3 -3
- package/src/components/date-picker/time-picker.js +20 -8
- package/src/components/dialog/dialog.js +88 -66
- package/src/components/dropdown/dropdown.js +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/file-item/file-item.css +88 -0
- package/src/components/file-item/file-item.js +371 -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 +199 -45
- package/src/components/filter-panel/filter-panel.css +104 -0
- package/src/components/filter-panel/filter-panel.js +964 -0
- package/src/components/kanban-view/kanban-card.js +239 -0
- package/src/components/kanban-view/kanban-policy.js +412 -0
- package/src/components/kanban-view/kanban-view.css +674 -36
- package/src/components/kanban-view/kanban-view.js +1762 -240
- package/src/components/launcher/launcher.js +59 -27
- package/src/components/list-toolbar/list-toolbar.css +189 -0
- package/src/components/list-toolbar/list-toolbar.js +851 -0
- package/src/components/menu-button/menu-button.css +80 -3
- package/src/components/menu-button/menu-button.js +21 -37
- package/src/components/message/message.css +14 -12
- package/src/components/message/message.js +13 -2
- package/src/components/modal/modal.css +15 -0
- package/src/components/modal/modal.js +141 -0
- package/src/components/navigation-bar/navigation-bar.css +77 -2
- package/src/components/navigation-bar/navigation-bar.js +171 -4
- package/src/components/panel/panel.js +13 -2
- package/src/components/search/search.js +17 -5
- package/src/components/select/field-adapter.js +4 -1
- package/src/components/select/select.js +3 -0
- package/src/components/sort-control/sort-control.css +17 -0
- package/src/components/sort-control/sort-control.js +140 -0
- package/src/components/stat-tile/stat-tile.css +213 -0
- package/src/components/stat-tile/stat-tile.js +590 -0
- package/src/components/tabbox/tabbox.js +12 -1
- package/src/components/table/table.css +51 -2
- package/src/components/table/table.js +298 -19
- 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 +3 -1
- package/src/components/timebox/timebox.js +18 -5
- 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/component.js +8 -3
- package/src/core/icons.js +74 -5
- package/src/index.js +33 -3
- package/src/internal/app-rail.js +19 -1
- package/src/internal/menu-items.js +142 -0
- package/styles/print.css +192 -0
- package/styles/zx.css +11 -0
- package/DESIGN-SYSTEM.md +0 -182
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { h, safeHref } from '../core/dom.js';
|
|
2
|
+
import { icon } from '../core/icons.js';
|
|
3
|
+
|
|
4
|
+
/** @typedef {'menuitem'|'menuitemcheckbox'|'menuitemradio'} MenuItemRole */
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} MenuController
|
|
7
|
+
* @property {() => unknown} close Close the owning menu.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} MenuItem
|
|
11
|
+
* @property {string} label Item label.
|
|
12
|
+
* @property {string} [icon] Kernel icon name.
|
|
13
|
+
* @property {unknown} [value] Value emitted on selection.
|
|
14
|
+
* @property {boolean} [disabled=false] Whether selection is disabled.
|
|
15
|
+
* @property {boolean} [danger=false] Whether the action is dangerous.
|
|
16
|
+
* @property {string} [href] Native-link destination.
|
|
17
|
+
* @property {string} [target] Native-link target.
|
|
18
|
+
* @property {string} [rel] Native-link relationship.
|
|
19
|
+
* @property {string} [description] Supporting text under the label.
|
|
20
|
+
* @property {string|number} [badge] Compact trailing metadata.
|
|
21
|
+
* @property {string} [shortcut] Keyboard shortcut label.
|
|
22
|
+
* @property {string|number|Node} [adornment] Additional trailing content.
|
|
23
|
+
* @property {MenuItemRole} [role='menuitem'] APG role.
|
|
24
|
+
* @property {boolean} [checked=false] Checked state for checkbox/radio roles.
|
|
25
|
+
* @property {(value: unknown, item: MenuItem, controller: MenuController) => void} [onselect]
|
|
26
|
+
* Selection callback.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {Object} MenuHeading
|
|
30
|
+
* @property {'heading'} type Entry kind.
|
|
31
|
+
* @property {string} label Visible heading.
|
|
32
|
+
*/
|
|
33
|
+
/** @typedef {{type: 'separator'}} MenuSeparator */
|
|
34
|
+
/** @typedef {MenuItem|MenuHeading|MenuSeparator|'-'} MenuEntry */
|
|
35
|
+
|
|
36
|
+
export const MENU_ITEM_SELECTOR = [
|
|
37
|
+
'[role="menuitem"]',
|
|
38
|
+
'[role="menuitemcheckbox"]',
|
|
39
|
+
'[role="menuitemradio"]'
|
|
40
|
+
].join(',');
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Reports whether an entry is an interactive menu action.
|
|
44
|
+
* @param {MenuEntry|unknown} entry Candidate entry.
|
|
45
|
+
* @returns {entry is MenuItem}
|
|
46
|
+
*/
|
|
47
|
+
export function isMenuItem(entry) {
|
|
48
|
+
return Boolean(entry && entry !== '-' && typeof entry === 'object'
|
|
49
|
+
&& !['heading', 'separator'].includes(String(/** @type {{type?: unknown}} */ (entry).type ?? '')));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Resolves the only APG roles accepted by the shared action model.
|
|
54
|
+
* @param {MenuItem} item Item definition.
|
|
55
|
+
* @returns {MenuItemRole}
|
|
56
|
+
*/
|
|
57
|
+
export function menuItemRole(item) {
|
|
58
|
+
return ['menuitemcheckbox', 'menuitemradio'].includes(String(item.role))
|
|
59
|
+
? /** @type {MenuItemRole} */ (item.role)
|
|
60
|
+
: 'menuitem';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Builds one menu's entries with shared structure and link safety.
|
|
65
|
+
* @param {MenuEntry[]} entries Entry definitions.
|
|
66
|
+
* @param {string} block CSS block name, without the `zx-` prefix.
|
|
67
|
+
* @returns {HTMLElement[]}
|
|
68
|
+
*/
|
|
69
|
+
export function renderMenuEntries(entries, block) {
|
|
70
|
+
const nodes = [];
|
|
71
|
+
entries.forEach((entry, index) => {
|
|
72
|
+
if (entry === '-' || (entry && typeof entry === 'object' && entry.type === 'separator')) {
|
|
73
|
+
nodes.push(h('div', { class: `zx-${block}__separator`, role: 'separator' }));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (entry && typeof entry === 'object' && entry.type === 'heading') {
|
|
77
|
+
nodes.push(h('div', {
|
|
78
|
+
class: `zx-${block}__heading`,
|
|
79
|
+
role: 'presentation'
|
|
80
|
+
}, String(entry.label ?? '')));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!isMenuItem(entry)) return;
|
|
84
|
+
|
|
85
|
+
const role = menuItemRole(entry);
|
|
86
|
+
const href = safeHref(entry.href);
|
|
87
|
+
const target = entry.target == null ? null : String(entry.target);
|
|
88
|
+
const rel = entry.rel == null
|
|
89
|
+
? (target === '_blank' ? 'noopener noreferrer' : null)
|
|
90
|
+
: String(entry.rel);
|
|
91
|
+
const content = [];
|
|
92
|
+
const leading = [];
|
|
93
|
+
if (role !== 'menuitem') {
|
|
94
|
+
leading.push(h('span', {
|
|
95
|
+
class: `zx-${block}__check`,
|
|
96
|
+
'data-check-kind': role === 'menuitemradio' ? 'radio' : 'checkbox'
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
if (entry.icon) leading.push(icon(entry.icon));
|
|
100
|
+
content.push(h('span', {
|
|
101
|
+
class: `zx-${block}__leading`,
|
|
102
|
+
ariaHidden: 'true'
|
|
103
|
+
}, leading));
|
|
104
|
+
content.push(h('span', { class: `zx-${block}__body` },
|
|
105
|
+
h('span', { class: `zx-${block}__label` }, String(entry.label ?? '')),
|
|
106
|
+
entry.description == null || entry.description === '' ? null : h('span', {
|
|
107
|
+
class: `zx-${block}__description`
|
|
108
|
+
}, String(entry.description))));
|
|
109
|
+
|
|
110
|
+
const meta = [];
|
|
111
|
+
if (entry.badge != null) {
|
|
112
|
+
meta.push(h('span', { class: `zx-${block}__badge` }, String(entry.badge)));
|
|
113
|
+
}
|
|
114
|
+
if (entry.shortcut) {
|
|
115
|
+
meta.push(h('kbd', { class: `zx-${block}__shortcut` }, String(entry.shortcut)));
|
|
116
|
+
}
|
|
117
|
+
if (entry.adornment != null) {
|
|
118
|
+
const adornment = entry.adornment && typeof entry.adornment === 'object'
|
|
119
|
+
&& 'nodeType' in entry.adornment
|
|
120
|
+
? /** @type {Node} */ (entry.adornment)
|
|
121
|
+
: String(entry.adornment);
|
|
122
|
+
meta.push(h('span', { class: `zx-${block}__adornment` }, adornment));
|
|
123
|
+
}
|
|
124
|
+
if (meta.length) content.push(h('span', { class: `zx-${block}__meta` }, meta));
|
|
125
|
+
|
|
126
|
+
const properties = {
|
|
127
|
+
class: `zx-${block}__item`,
|
|
128
|
+
role,
|
|
129
|
+
tabindex: '-1',
|
|
130
|
+
'data-menu-item': String(index),
|
|
131
|
+
'data-danger': entry.danger ? 'true' : null,
|
|
132
|
+
ariaDisabled: entry.disabled ? 'true' : null,
|
|
133
|
+
ariaChecked: role === 'menuitem' ? null : String(Boolean(entry.checked))
|
|
134
|
+
};
|
|
135
|
+
if (href) {
|
|
136
|
+
nodes.push(h('a', { ...properties, href, target, rel }, content));
|
|
137
|
+
} else {
|
|
138
|
+
nodes.push(h('button', { ...properties, type: 'button' }, content));
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
return nodes;
|
|
142
|
+
}
|
package/styles/print.css
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Paper.
|
|
3
|
+
*
|
|
4
|
+
* Imported last from `styles/zx.css`, and everything in it lives inside the one `@media print`
|
|
5
|
+
* block below — so on screen this file contributes nothing at all, and on paper it is the last
|
|
6
|
+
* word. Consumers print invoices, delivery notes and reports out of these components, and until
|
|
7
|
+
* this file existed a printed table took its sticky header, its zebra stripes and whatever row
|
|
8
|
+
* the pointer happened to be over along with it.
|
|
9
|
+
*
|
|
10
|
+
* Paper differs from a screen in four ways, and every rule here answers one of them:
|
|
11
|
+
*
|
|
12
|
+
* - There is no viewport, so nothing may stick and nothing may scroll. A sticky header pins
|
|
13
|
+
* itself to the top of a sheet of paper, and a scroller prints exactly one screenful.
|
|
14
|
+
* - There is no pointer and no focus, so hover fills, focus rings and drag affordances are
|
|
15
|
+
* noise that costs toner.
|
|
16
|
+
* - There is no second page for the half of a card that did not fit, so blocks that read as one
|
|
17
|
+
* thing say where they may be cut, and a table repeats its header on every page.
|
|
18
|
+
* - Ink is not free and a fill is not a colour. Backgrounds come off; the one exception is a
|
|
19
|
+
* status badge, whose fill carries meaning its text alone does not.
|
|
20
|
+
*
|
|
21
|
+
* Semantic tokens only, as everywhere else. The borders are the interesting case: the light
|
|
22
|
+
* theme's `--zx-color-border` is a very pale grey that a printer renders as nothing, so the
|
|
23
|
+
* border roles are repointed — once, at the top — to a mix of the text colour, which prints.
|
|
24
|
+
*/
|
|
25
|
+
@media print {
|
|
26
|
+
/*
|
|
27
|
+
* Hairlines. `--zx-color-text` rather than a fixed grey so the mix follows the theme it is
|
|
28
|
+
* printed from, and both selectors are single-class so this beats the theme block that sets the
|
|
29
|
+
* same roles on the same element: this file is imported last, and on a tie the last one wins.
|
|
30
|
+
*/
|
|
31
|
+
.zx-scope,
|
|
32
|
+
[data-zx-theme] {
|
|
33
|
+
--zx-color-border: color-mix(in srgb, var(--zx-color-text) 45%, transparent);
|
|
34
|
+
--zx-color-border-strong: color-mix(in srgb, var(--zx-color-text) 65%, transparent);
|
|
35
|
+
--zx-color-border-control: color-mix(in srgb, var(--zx-color-text) 45%, transparent);
|
|
36
|
+
--zx-color-overlay-border: color-mix(in srgb, var(--zx-color-text) 45%, transparent);
|
|
37
|
+
--zx-color-overlay-divider: color-mix(in srgb, var(--zx-color-text) 45%, transparent);
|
|
38
|
+
/* The glass rim is a light tint of the page, which on white paper is the paper. */
|
|
39
|
+
--zx-color-glass-border: color-mix(in srgb, var(--zx-color-text) 45%, transparent);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* A fill is a boundary too, and the filled variants drew theirs with one: their border is
|
|
44
|
+
* `transparent` because the surface underneath said where the card ended. With the fill gone
|
|
45
|
+
* they would arrive on paper as loose text, so they take the hairline instead.
|
|
46
|
+
*/
|
|
47
|
+
.zx-scope .zx-card[data-variant="filled"],
|
|
48
|
+
.zx-scope .zx-record-card[data-variant="filled"] {
|
|
49
|
+
border-color: var(--zx-color-border);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/*
|
|
53
|
+
* Fills off. A badge is excluded by the selector rather than overridden afterwards, because its
|
|
54
|
+
* eleven kind/variant fills would otherwise have to be restated here one by one, and the copy
|
|
55
|
+
* would rot the first time badge.css gained a kind.
|
|
56
|
+
*/
|
|
57
|
+
.zx-scope,
|
|
58
|
+
.zx-scope *:not(.zx-badge, .zx-badge *),
|
|
59
|
+
.zx-scope *:not(.zx-badge, .zx-badge *)::before,
|
|
60
|
+
.zx-scope *:not(.zx-badge, .zx-badge *)::after {
|
|
61
|
+
background-color: transparent !important;
|
|
62
|
+
background-image: none !important;
|
|
63
|
+
box-shadow: none !important;
|
|
64
|
+
text-shadow: none !important;
|
|
65
|
+
-webkit-backdrop-filter: none !important;
|
|
66
|
+
backdrop-filter: none !important;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* The exception: a badge's colour is half of what it says, so it is asked for explicitly. */
|
|
70
|
+
.zx-scope .zx-badge {
|
|
71
|
+
-webkit-print-color-adjust: exact;
|
|
72
|
+
print-color-adjust: exact;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
* A solid button's label is the colour that reads on its fill — white on the accent. With the
|
|
77
|
+
* fill gone that label is white on white, so the two kinds that have one take the page's text
|
|
78
|
+
* colour and a hairline of their own. Buttons are not hidden: a printed form or an approval
|
|
79
|
+
* sheet is read by someone who needs to see what the screen offered.
|
|
80
|
+
*/
|
|
81
|
+
.zx-scope .zx-btn[data-kind="primary"],
|
|
82
|
+
.zx-scope .zx-btn[data-kind="danger"] {
|
|
83
|
+
border-color: var(--zx-color-border);
|
|
84
|
+
color: var(--zx-color-text);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Nothing sticks to a sheet of paper. Everything below stacks where it was written instead. */
|
|
88
|
+
.zx-scope .zx-table th,
|
|
89
|
+
.zx-scope .zx-table__scroll[data-sticky-header="true"] th,
|
|
90
|
+
.zx-scope .zx-select__group,
|
|
91
|
+
.zx-scope .zx-calendar__agenda-heading,
|
|
92
|
+
.zx-scope .zx-toolbar,
|
|
93
|
+
.zx-scope .zx-list-toolbar {
|
|
94
|
+
position: static;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/*
|
|
98
|
+
* Nothing scrolls either: a scroller prints the rows that happen to be inside it and drops the
|
|
99
|
+
* rest, which on an invoice is a silently short invoice.
|
|
100
|
+
*/
|
|
101
|
+
.zx-scope .zx-table__scroll,
|
|
102
|
+
.zx-scope .zx-tree,
|
|
103
|
+
.zx-scope .zx-card-view,
|
|
104
|
+
.zx-scope .zx-record-view {
|
|
105
|
+
overflow: visible;
|
|
106
|
+
max-block-size: none;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* No pointer, no focus, no zebra — three states that only ever existed on a screen. */
|
|
110
|
+
.zx-scope .zx-table__scroll[data-zebra="true"] .zx-table__body tr:nth-child(even),
|
|
111
|
+
.zx-scope .zx-table__body tr[data-row]:hover,
|
|
112
|
+
.zx-scope .zx-table__body tr[aria-selected="true"] {
|
|
113
|
+
background: transparent;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.zx-scope :focus,
|
|
117
|
+
.zx-scope :focus-visible {
|
|
118
|
+
outline: none;
|
|
119
|
+
box-shadow: none;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Chrome and overlays: navigation a reader cannot follow, and panels floating over the page. */
|
|
123
|
+
.zx-scope .zx-navigation-bar,
|
|
124
|
+
.zx-scope .zx-app-rail,
|
|
125
|
+
.zx-scope .zx-app-sidebar,
|
|
126
|
+
.zx-modal,
|
|
127
|
+
.zx-dialog,
|
|
128
|
+
.zx-sheet,
|
|
129
|
+
.zx-dropdown,
|
|
130
|
+
.zx-context-menu,
|
|
131
|
+
.zx-tooltip,
|
|
132
|
+
.zx-launcher,
|
|
133
|
+
.zx-message-region,
|
|
134
|
+
[popover]:popover-open {
|
|
135
|
+
display: none !important;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
::backdrop {
|
|
139
|
+
display: none;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* A table that runs over a page break reintroduces itself on the next one. */
|
|
143
|
+
.zx-scope .zx-table thead {
|
|
144
|
+
display: table-header-group;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.zx-scope .zx-table tfoot {
|
|
148
|
+
display: table-footer-group;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* Things that read as one object are not cut in half if they can be moved whole. */
|
|
152
|
+
.zx-scope .zx-card,
|
|
153
|
+
.zx-scope .zx-record-card,
|
|
154
|
+
.zx-scope .zx-stat-tile,
|
|
155
|
+
.zx-scope .zx-table__body tr {
|
|
156
|
+
break-inside: avoid;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/*
|
|
160
|
+
* A link on paper is a dead end unless it says where it goes — in prose. Inside a table cell or
|
|
161
|
+
* on a button the appended URL is not information, it is a wrecked column, so those take it back
|
|
162
|
+
* off. Each of those selectors outranks the one above on specificity rather than on source
|
|
163
|
+
* order, so reordering the list cannot quietly turn one of them back on.
|
|
164
|
+
*/
|
|
165
|
+
.zx-scope a[href^="http"]::after {
|
|
166
|
+
content: " (" attr(href) ")";
|
|
167
|
+
font-size: var(--zx-text-sm);
|
|
168
|
+
font-weight: 400;
|
|
169
|
+
word-break: break-all;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.zx-scope a[href]:empty::after,
|
|
173
|
+
.zx-scope a[href].zx-icon-btn::after,
|
|
174
|
+
.zx-scope a:has(> .zx-icon:only-child)::after,
|
|
175
|
+
.zx-scope th a[href]::after,
|
|
176
|
+
.zx-scope td a[href]::after,
|
|
177
|
+
.zx-scope button a[href]::after,
|
|
178
|
+
.zx-scope a[href].zx-btn::after,
|
|
179
|
+
.zx-scope .zx-btn a[href]::after,
|
|
180
|
+
.zx-scope .zx-table a[href]::after,
|
|
181
|
+
.zx-scope .zx-badge a[href]::after,
|
|
182
|
+
.zx-scope a[href].zx-badge::after,
|
|
183
|
+
.zx-scope a[href].zx-entity-ref::after,
|
|
184
|
+
.zx-scope .zx-entity-ref a[href]::after,
|
|
185
|
+
.zx-scope .zx-breadcrumb a[href]::after,
|
|
186
|
+
.zx-scope a[href].zx-stat-tile::after,
|
|
187
|
+
.zx-scope .zx-tabbox a[href]::after,
|
|
188
|
+
.zx-scope .zx-tree a[href]::after,
|
|
189
|
+
.zx-scope .zx-file-item a[href]::after {
|
|
190
|
+
content: none;
|
|
191
|
+
}
|
|
192
|
+
}
|
package/styles/zx.css
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
@import url("../src/components/skeleton/skeleton.css");
|
|
19
19
|
@import url("../src/components/copy/copy-button.css");
|
|
20
20
|
@import url("../src/components/copy/copy-input.css");
|
|
21
|
+
@import url("../src/components/code-editor/code-editor.css");
|
|
21
22
|
@import url("../src/components/toolbar/toolbar.css");
|
|
22
23
|
@import url("../src/components/toggle/toggle.css");
|
|
23
24
|
@import url("../src/components/groupbox/groupbox.css");
|
|
@@ -31,6 +32,11 @@
|
|
|
31
32
|
@import url("../src/components/app-icon/app-icon.css");
|
|
32
33
|
@import url("../src/components/launcher/launcher.css");
|
|
33
34
|
@import url("../src/components/avatar/avatar.css");
|
|
35
|
+
@import url("../src/components/entity-ref/entity-ref.css");
|
|
36
|
+
@import url("../src/components/file-item/file-item.css");
|
|
37
|
+
@import url("../src/components/file-list/file-list.css");
|
|
38
|
+
@import url("../src/components/activity-item/activity-item.css");
|
|
39
|
+
@import url("../src/components/activity-list/activity-list.css");
|
|
34
40
|
@import url("../src/components/account-menu/account-menu.css");
|
|
35
41
|
@import url("../src/components/app-rail/app-rail.css");
|
|
36
42
|
@import url("../src/components/app-sidebar/app-sidebar.css");
|
|
@@ -60,6 +66,7 @@
|
|
|
60
66
|
@import url("../src/components/view/record-view.css");
|
|
61
67
|
@import url("../src/components/table/table.css");
|
|
62
68
|
@import url("../src/components/table-view/table-view.css");
|
|
69
|
+
@import url("../src/components/stat-tile/stat-tile.css");
|
|
63
70
|
@import url("../src/components/card-view/card-view.css");
|
|
64
71
|
@import url("../src/components/kanban-view/kanban-view.css");
|
|
65
72
|
@import url("../src/components/grid/grid.css");
|
|
@@ -67,7 +74,10 @@
|
|
|
67
74
|
@import url("../src/components/tree/tree.css");
|
|
68
75
|
@import url("../src/components/finder/finder.css");
|
|
69
76
|
@import url("../src/components/data-filter/data-filter.css");
|
|
77
|
+
@import url("../src/components/filter-panel/filter-panel.css");
|
|
78
|
+
@import url("../src/components/list-toolbar/list-toolbar.css");
|
|
70
79
|
@import url("../src/components/filter/filter.css");
|
|
80
|
+
@import url("../src/components/sort-control/sort-control.css");
|
|
71
81
|
@import url("../src/components/pagination/pagination.css");
|
|
72
82
|
@import url("../src/components/stepper/stepper.css");
|
|
73
83
|
@import url("../src/components/breadcrumb/breadcrumb.css");
|
|
@@ -78,3 +88,4 @@
|
|
|
78
88
|
@import url("../src/components/multi-value-editor/multi-value-editor.css");
|
|
79
89
|
@import url("../src/components/field-upload/field-upload.css");
|
|
80
90
|
@import url("../src/components/questionnaire/questionnaire.css");
|
|
91
|
+
@import url("./print.css");
|
package/DESIGN-SYSTEM.md
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
# Xenon Design System
|
|
2
|
-
|
|
3
|
-
Xenon is a product-agnostic design system for browser applications; Zx is its dependency-free
|
|
4
|
-
JavaScript implementation. It provides shared foundations, accessible components, interaction
|
|
5
|
-
patterns, and application layouts without taking ownership of an application's routing or domain
|
|
6
|
-
logic.
|
|
7
|
-
|
|
8
|
-
## System boundary
|
|
9
|
-
|
|
10
|
-
Xenon has four layers:
|
|
11
|
-
|
|
12
|
-
1. **Foundations** — semantic tokens, typography, spacing, density, themes, icons, motion, i18n,
|
|
13
|
-
accessibility rules, and the safe DOM kernel.
|
|
14
|
-
2. **Components** — lifecycle-safe controls and containers with one options/events/destroy model.
|
|
15
|
-
3. **Patterns** — repeatable business interactions such as global launch, account/preferences,
|
|
16
|
-
record detail, transaction entry, filtering, and empty/loading/error handling.
|
|
17
|
-
4. **Application layouts** — rails, bars, workspaces, master/detail, dashboards, inboxes, wizards,
|
|
18
|
-
and responsive shell geometry.
|
|
19
|
-
|
|
20
|
-
This separation keeps foundations, components, patterns, and layouts independently reusable while
|
|
21
|
-
preserving a coherent API and visual language.
|
|
22
|
-
|
|
23
|
-
Zx has a generated API reference, live examples, bundled local icons, and no runtime dependency.
|
|
24
|
-
Its compact `__()` DOM builder and pure append/property/event helpers let established applications
|
|
25
|
-
adopt the system without introducing a framework runtime. Domain policy, transport, persistence,
|
|
26
|
-
and navigation remain application-owned.
|
|
27
|
-
|
|
28
|
-
## Coverage ledger
|
|
29
|
-
|
|
30
|
-
| Front-end capability | Xenon/Zx status | Design-system boundary |
|
|
31
|
-
| --- | --- | --- |
|
|
32
|
-
| Buttons, inputs, toggles, dates, selects, checklists, tags | Covered by public components | Components preserve native semantics and expose consistent value/event contracts. |
|
|
33
|
-
| Forms and fieldsets | Covered, including component-backed field adapters | Use for record editors; keep validation/business rules application-owned. |
|
|
34
|
-
| Dialogs, sheets, dropdowns, context menus, tooltips, messages | Covered | Consolidate duplicate menu behavior before building account/launcher surfaces. |
|
|
35
|
-
| Tabs, panels, toolbar, breadcrumbs, stepper, split view, dock | Covered | Compose layouts; do not let navigation components own routes. |
|
|
36
|
-
| Sorting, selection, dynamic/local filtering, pagination, trees/finder | Covered | `Filter` authors typed nested expressions; `DataFilter` executes simple local predicates. Backend query compilation stays in adapters. |
|
|
37
|
-
| Operational calendars and scheduling | Covered | `Calendar` provides agenda/day/week/month/year views, local collision/spanning layout, selection, and configurable optimistic pointer/keyboard edits. The host owns loading, persistence, permissions, recurrence rules, and DAV. |
|
|
38
|
-
| Record table, card, and Kanban views | Covered by one shared contract | `TableView`, `CardView`, and `KanbanView` share schema fields, records, sort, selection, field controls, loading, and versioned state. Core remains persistence-neutral; the optional ZeyOS registry coordinates named layouts through an injected server transport. The host still owns permissions, server validation, and rollback. |
|
|
39
|
-
| Editable billing/transaction rows | Covered at generic-grid level | `Grid.BillingItems()` composes single-click typed currency/unit editors, flat hierarchy, same-parent row movement, and column visibility over `Table`. Tax, subtotal, posting, rounding, and persistence stay domain code. |
|
|
40
|
-
| Global launcher/search | Covered | `Launcher` combines an application grid, current/pinned state, recent records, and abortable grouped sources. The optional ZeyOS adapter maps the complete 29-app shell catalogue while the host retains permissions, icons, and routing. |
|
|
41
|
-
| Account/profile/preferences menu | Covered at presentation level | `Avatar` and `AccountMenu` provide identity and action UI. The host supplies permitted actions and owns preferences, authentication, and logout. |
|
|
42
|
-
| Persistent app rail / side navigation | Covered | One route-agnostic `AppSidebar` owns expanded, minimized, and horizontal presentations. Only expanded vertical branches render inline; every rail presentation uses flyouts. |
|
|
43
|
-
| Charts and analytical summaries | Covered through an adapter | `Chart` is engine-neutral and accessible; `ChartJsAdapter` accepts an injected Chart.js 4 engine. Chart.js is documentation/test tooling only. |
|
|
44
|
-
| Local product/module icons | Covered | `AppIcon` is generic; `zeyosAppIcon()` maps every current ZeyOS module/entity identity and accepts an offline glyph override. The optional remote Font Awesome kit is never loaded automatically. |
|
|
45
|
-
| Domain entity selectors/cards/links | Covered at generic presentation level | `Card` provides media, native primary links, secondary actions, and metadata; applications own entity routing and policy. |
|
|
46
|
-
| Persistent notification/activity center | Missing, lower priority | `Message` covers ephemeral feedback, not read/unread activity history. |
|
|
47
|
-
|
|
48
|
-
## Current ZeyOS delta audit (25 August 2026)
|
|
49
|
-
|
|
50
|
-
This is a source audit, not a visual inventory. The comparison uses the current ZeyOS DOM/UI
|
|
51
|
-
implementation as evidence: its generic control catalogue is concentrated in
|
|
52
|
-
[`4-ui.orig.js`](../zeyos/ext/global.orig/4-ui.orig.js), configurable index-column management in
|
|
53
|
-
[`4-pg.orig.js`](../zeyos/ext/global.orig/4-pg.orig.js#L3464), and the multi-view scheduling surface
|
|
54
|
-
in [`calendar.orig.js`](../zeyos/ext/mod/calendar.orig.js#L61). Capabilities already represented by
|
|
55
|
-
a Zx component or a documented composition are not counted as gaps.
|
|
56
|
-
|
|
57
|
-
### Already closed by Zx core
|
|
58
|
-
|
|
59
|
-
| ZeyOS behavior | Source evidence | Xenon/Zx coverage |
|
|
60
|
-
| --- | --- | --- |
|
|
61
|
-
| Dynamic search/filter authoring and saved filter controls | [`UI.newSearch`](../zeyos/ext/global.orig/4-ui.orig.js#L3378) and [`UI.newContainerFilters`](../zeyos/ext/global.orig/4-ui.orig.js#L7723) | `Filter` supplies a typed/versioned AST editor; `DataFilter` remains the lightweight local-row helper. A compiler and persistence service are intentionally not core. |
|
|
62
|
-
| Agenda/day/week/month/year scheduling | [`calendar.orig.js`](../zeyos/ext/mod/calendar.orig.js#L61) | `Calendar` covers timed collisions, spanning lanes, windowed range events, activity year view, selection, now state, and pointer/keyboard move/resize. `zeyosCalendar()` maps expanded appointment rows and Unix seconds without importing recurrence, DAV, routing, or permissions into core. |
|
|
63
|
-
| Record table/card collections and reusable view state | [`_newCrdLstTbl`](../zeyos/ext/global.orig/4-ui.orig.js#L4457), [`newCrd`](../zeyos/ext/global.orig/4-ui.orig.js#L4649), and [`newTbl`](../zeyos/ext/global.orig/4-ui.orig.js#L4782) | `TableView`, `CardView`, and the additive `KanbanView` consume the same field descriptors and JSON-safe layout state. `buildZeyosViewConfig()` derives them from the runtime schema; `SavedViewRegistry` adds exact user/workspace/resource scoping and can bridge the existing `userfields` endpoints. Neither imports the client into core. |
|
|
64
|
-
| Editable transaction rows and row movement | [`_newTblAddColEdit`](../zeyos/ext/global.orig/4-ui.orig.js#L5613) and [`newTblItems`](../zeyos/ext/global.orig/4-ui.orig.js#L6070) | `Table`/`Grid.BillingItems()` cover typed editing, currencies/units, hierarchy, single-click entry, same-parent drag/keyboard reorder, and show/hide columns. |
|
|
65
|
-
| Entity selection, priority, status, and permission choices | [`newSelEntity`](../zeyos/ext/global.orig/4-ui.orig.js#L3296), [`newSelPriority`](../zeyos/ext/global.orig/4-ui.orig.js#L3101), and the surrounding Select family | `Select.entity()`, `.priority()`, `.status()`, and `.permission()` cover the interaction contract; the ZeyOS adapter owns schemas and localized catalogues. |
|
|
66
|
-
| Application launcher and module/entity identity | [`newExpLst`](../zeyos/ext/global.orig/4-ui.orig.js#L4355) and entity/module item builders through line 4457 | `Launcher`, `AppIcon`, and the optional ZeyOS adapter cover the full app catalogue, current/pinned state, recent records, grouped abortable search, forks/weblets, and injected routing. |
|
|
67
|
-
| Expanded/minimized/horizontal app navigation | Current shell navigation plus the same module identity catalogue | One public `AppSidebar` owns all presentations. The rail renderer is an internal presenter, not a second component; only expanded vertical branches render inline and rail branches use anchored flyouts. |
|
|
68
|
-
| Toasts, loading, cards, dialogs, forms, grids, charts, and layout primitives | Multiple `newContainer*`, input, table, and progress families in `4-ui.orig.js` | Covered by existing Zx components and documented composition; these are not delta items merely because the legacy factory names differ. |
|
|
69
|
-
|
|
70
|
-
### Recommended additions to Zx core
|
|
71
|
-
|
|
72
|
-
| Priority | Missing reusable contract | Evidence and proposed boundary |
|
|
73
|
-
| --- | --- | --- |
|
|
74
|
-
| P0 | **EntityRef** | ZeyOS has a large family of linked/static entity presentations with subtitle, owner/group/assignee context, fork, and record actions: [`newLinkEntity*`](../zeyos/ext/global.orig/4-ui.orig.js#L225) and [`newEntityStatic`/`newEntity*`](../zeyos/ext/global.orig/4-ui.orig.js#L8262). A generic `EntityRef` should own icon/title/subtitle/metadata/link/action semantics; ZeyOS routing, permissions, and context menus remain injected. `Card` is too large for dense table/feed rows. |
|
|
75
|
-
| P0 | **FileItem / FileList** | Existing-file, temporary-file, waiting/progress, MIME, size, and download rows recur in [`newFile`, `newFileTemp`, and `newFileWait`](../zeyos/ext/global.orig/4-ui.orig.js#L8158). `FieldUpload` owns selection/upload but not durable file presentation. Core should render file identity/state/actions; transport, authorization, preview, and download URLs stay with the host. |
|
|
76
|
-
| P0 | **ActivityList / ActivityItem** | [`newFeed`](../zeyos/ext/global.orig/4-ui.orig.js#L8704) combines chronological records, comments, attachments, likes, and incremental updates. Core should own an accessible chronological list, grouping, loading/empty state, and action slots. Posting, reactions, channels, permissions, and persistence remain application code. |
|
|
77
|
-
| P1 | **FacetList** | Counted activity/status/tag/entity side filters appear in [`newNavActivityStatus`](../zeyos/ext/global.orig/4-ui.orig.js#L3967), `newNavTag`, and `newNavEntity`. `Filter` is an expression author, not a compact persistent facet navigator. Core should own selection/count/collapse/loading behavior; query mapping stays injected. |
|
|
78
|
-
| P1 | **EditorHost** | ZeyOS uses plain/Markdown/contenteditable/iframe editors through [`newEditor`](../zeyos/ext/global.orig/4-ui.orig.js#L1607) and `newFrameEditor`. Like `Chart`, core should provide labels, toolbar/action slots, value/lifecycle, dirty/read-only/error states, and an injected engine adapter. Sanitization and document/media policy must not be implicit core behavior. |
|
|
79
|
-
| P1 | **ColorPicker** | The reusable swatch picker is [`newCl`](../zeyos/ext/global.orig/4-ui.orig.js#L3753). A dependency-free accessible swatch/custom-color picker belongs in core and would also serve the Theme Studio. |
|
|
80
|
-
| P2 | **Metric / Summary** | [`newSummary`](../zeyos/ext/global.orig/4-ui.orig.js#L7312) repeatedly formats counts, money, timing, size, stock, and relative values. These can currently be composed from Card and format helpers, but a small semantic Metric/DescriptionList pair would remove repeated markup without importing business formulas. |
|
|
81
|
-
| P2 | **RecordPreview** | Typed preview builders for appointments, contacts, links, messages, tasks, and tickets start at [`newPreviewForm`](../zeyos/ext/global.orig/4-ui.orig.js#L9589). Core should offer a generic preview anatomy or engine-neutral preview host, not one component per ZeyOS entity. |
|
|
82
|
-
| P2 | **Shortcut / Kbd** | Launcher and menus expose shortcuts but Zx has no reusable semantic `<kbd>` treatment. A tiny factory is justified once shortcuts appear outside those components. |
|
|
83
|
-
|
|
84
|
-
### Adapter work, not new core components
|
|
85
|
-
|
|
86
|
-
- A **ZeyOS Filter compiler** mapping allowlisted AST field/operator IDs to the server's plural
|
|
87
|
-
`filters` query shape, plus a parser for stored legacy filter state. Compilation must use bound/
|
|
88
|
-
structured values and never accept raw SQL or URL fragments.
|
|
89
|
-
- A shared **ZeyOS field/status/priority catalogue** built from runtime schemas and localization,
|
|
90
|
-
feeding `Field`, `Filter`, `Select.status()`, and `Select.priority()` without hard-coding
|
|
91
|
-
entity-specific policy in core.
|
|
92
|
-
- **EntityRef/FileItem/ActivityList adapters** that resolve routes, icons, permissions, server
|
|
93
|
-
payloads, and action menus while leaving their visual/interaction contracts reusable.
|
|
94
|
-
- Optional persistence connectors for column visibility/order, sidebar state, saved filters, recent
|
|
95
|
-
items, and theme preferences. Core should expose serializable state and events only.
|
|
96
|
-
|
|
97
|
-
### Deliberately application-owned
|
|
98
|
-
|
|
99
|
-
Routing/history, authentication/session/logout, authorization, server transport/caching, saved-view
|
|
100
|
-
storage, audit policy, transaction tax/discount/subtotal/posting formulas, server reconciliation and undo history,
|
|
101
|
-
calendar recurrence/DAV synchronization, rich-text sanitization, and activity actions are not
|
|
102
|
-
design-system responsibilities. Moving them into Zx would reproduce ZeyOS application logic inside
|
|
103
|
-
the library and make Xenon less reusable.
|
|
104
|
-
|
|
105
|
-
### Launcher contract
|
|
106
|
-
|
|
107
|
-
The launcher searches applications, recent records, and grouped remote record types. The generic
|
|
108
|
-
component supplies the tile/list layouts, pinned-order ranking, current-route marker, request
|
|
109
|
-
cancellation, spatial keyboard movement, visible keyboard hints, native link metadata, and a
|
|
110
|
-
cancelable action. The optional ZeyOS adapter accepts all 29 prototype modules plus forks and
|
|
111
|
-
weblets, cached or async recent items, grouped legacy search payloads, permission/current state,
|
|
112
|
-
and host icon renderers. All state is injected; the adapter reads no shell globals and deliberately
|
|
113
|
-
does not become a second router.
|
|
114
|
-
|
|
115
|
-
### Account contract
|
|
116
|
-
|
|
117
|
-
A typical account surface includes identity, role-dependent links, language, authentication and
|
|
118
|
-
logout, preferences, and About/version content. `AccountMenu` composes `Avatar` with the existing
|
|
119
|
-
menu behavior and emits actions without owning routes or session state. The host decides which
|
|
120
|
-
actions exist and what each one does.
|
|
121
|
-
|
|
122
|
-
## Table and transaction follow-ups
|
|
123
|
-
|
|
124
|
-
The enhanced `Table` now keeps values numeric while formatting per-row locales, currencies, units,
|
|
125
|
-
and decimals; numeric types infer `NumberField` editors. Flat `parentId` data is projected into a
|
|
126
|
-
cycle-safe ARIA treegrid with local sibling sorting and expand/collapse APIs.
|
|
127
|
-
|
|
128
|
-
`Grid.BillingItems()` now packages the conventional description, quantity, unit, unit-price,
|
|
129
|
-
currency, line-total, and hierarchy schema while retaining Table's editing contract. Useful
|
|
130
|
-
follow-ups, deliberately separate from this generic preset:
|
|
131
|
-
|
|
132
|
-
- summary/footer rows and grouped subtotals;
|
|
133
|
-
- persistence connectors for versioned view state (column order and visibility are now component capabilities);
|
|
134
|
-
- product-specific invoice/transaction presets with tax, discount, rounding, and formula golden tests;
|
|
135
|
-
- server persistence, optimistic rejection, and undo owned by the application/controller layer.
|
|
136
|
-
|
|
137
|
-
## Record-view contract
|
|
138
|
-
|
|
139
|
-
`RecordView` is the presentation-neutral base for `TableView`, `CardView`, and `KanbanView`.
|
|
140
|
-
Stable field IDs allow a runtime ZeyOS schema to drive every presentation, while per-view properties
|
|
141
|
-
add table widths, card previews, or board grouping without changing how values, sorting, selection,
|
|
142
|
-
and field visibility work. Each view returns a versioned JSON-safe configuration through
|
|
143
|
-
`getViewState()`; record data and rendered Nodes are deliberately excluded.
|
|
144
|
-
|
|
145
|
-
The optional ZeyOS adapter builds the shared descriptors and server projection, including hidden
|
|
146
|
-
title, media, column, and swim-lane fields. It can page any concrete record view through an injected
|
|
147
|
-
client. Preference storage remains outside Zx, and Kanban moves are cancelable: local mode updates a
|
|
148
|
-
record clone, while external mode waits for the application to supply accepted server state.
|
|
149
|
-
|
|
150
|
-
## Calendar contract
|
|
151
|
-
|
|
152
|
-
`Calendar` normalizes arbitrary event records into stable IDs, copied local Dates, optional colour
|
|
153
|
-
and location, and per-event edit policies. Agenda, day, week, month, and year are presentations of
|
|
154
|
-
the same event set. The interval helpers operate on calendar days rather than fixed 24-hour spans,
|
|
155
|
-
so DST does not move a date; timed overlaps get deterministic columns and all-day/multi-day items
|
|
156
|
-
get non-overlapping clipped lanes.
|
|
157
|
-
|
|
158
|
-
Move and resize proposals are cancelable and keyboard-equivalent. Optimistic mode is the default
|
|
159
|
-
and provides a version-guarded `revert()` that cannot overwrite a newer local edit. Controlled mode
|
|
160
|
-
emits the same proposal without local mutation. The optional ZeyOS adapter maps expanded
|
|
161
|
-
appointment occurrences and Unix seconds while the application retains transport, recurrence/DAV,
|
|
162
|
-
permissions, routing, validation, and conflict handling.
|
|
163
|
-
|
|
164
|
-
## Chart-engine candidates
|
|
165
|
-
|
|
166
|
-
Chart.js was selected for the first adapter. It is pinned at 4.5.1 as a development dependency for
|
|
167
|
-
tests and the documentation asset, but is never imported by Zx runtime source. Applications inject
|
|
168
|
-
their chosen engine into the adapter. Published browser artifact sizes below are approximate and
|
|
169
|
-
remain useful when choosing a different future adapter.
|
|
170
|
-
|
|
171
|
-
| Engine | Current state at audit | Approx. browser cost | Best fit | Main trade-off |
|
|
172
|
-
| --- | --- | ---: | --- | --- |
|
|
173
|
-
| [Chart.js](https://github.com/chartjs/Chart.js/releases) 4.5.1 | Mature, active, MIT | 209 kB / 71 kB gzip (UMD) | Broad default: bar, line, doughnut, mixed business charts | Larger than focused engines; canvas requires a separate accessible data summary. |
|
|
174
|
-
| [Frappe Charts](https://github.com/frappe/charts/releases) 1.6.x | Zero-dependency SVG, ERP-oriented, MIT | 69 kB / 18 kB gzip (UMD) | Lean dashboards with common chart types | GitHub release and npm publication were out of step; ecosystem and extension surface are smaller. |
|
|
175
|
-
| [uPlot](https://github.com/leeoniya/uPlot) 1.6.32 | Active, MIT, highly optimized | 51 kB JS + 2 kB CSS / about 23 kB gzip | Dense time-series and operational telemetry | Deliberately narrow; not a general categorical/stacked business-chart toolkit. |
|
|
176
|
-
| [Apache ECharts](https://echarts.apache.org/en/changelog.html) 6.1.0 | Very capable, active, Apache-2.0 | 1.12 MB / 369 kB gzip (full build) | Advanced analytical visualization | Too large as Xenon's default; only sensible as an opt-in advanced adapter or custom build. |
|
|
177
|
-
|
|
178
|
-
Implemented architecture: `Chart` accepts an injected engine adapter, maps Xenon semantic tokens,
|
|
179
|
-
owns loading/empty/error states, and mirrors its data in an accessible table. Chart.js is the broad
|
|
180
|
-
default adapter; Frappe Charts remains the lean ERP-oriented alternative, uPlot a specialized
|
|
181
|
-
time-series candidate, and ECharts an opt-in advanced adapter. Any additional package still
|
|
182
|
-
requires explicit dependency approval.
|