@zeyos/zx 2.1.0 → 2.2.1
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 +51 -0
- package/README.md +15 -0
- package/dist/types/compat/base.d.ts +156 -0
- package/dist/types/compat/globals.d.ts +20 -0
- package/dist/types/compat/index.d.ts +128 -0
- package/dist/types/compat/map/basic.d.ts +175 -0
- package/dist/types/compat/map/data.d.ts +102 -0
- package/dist/types/compat/map/date-time.d.ts +80 -0
- package/dist/types/compat/map/factory.d.ts +9 -0
- package/dist/types/compat/map/form.d.ts +98 -0
- package/dist/types/compat/map/helpers.d.ts +12 -0
- package/dist/types/compat/map/http.d.ts +36 -0
- package/dist/types/compat/map/layout.d.ts +106 -0
- package/dist/types/compat/map/options.d.ts +80 -0
- package/dist/types/compat/map/select.d.ts +95 -0
- package/dist/types/compat/map/stubs.d.ts +32 -0
- package/dist/types/compat/map/utilities.d.ts +37 -0
- package/dist/types/compat-entry.d.ts +5 -0
- package/dist/types/components/badge/badge.d.ts +46 -0
- package/dist/types/components/breadcrumb/breadcrumb.d.ts +200 -0
- package/dist/types/components/button/button.d.ts +42 -0
- package/dist/types/components/check-button/check-button.d.ts +98 -0
- package/dist/types/components/checklist/checklist.d.ts +143 -0
- package/dist/types/components/checklist/field-adapter.d.ts +5 -0
- package/dist/types/components/context-menu/context-menu.d.ts +130 -0
- package/dist/types/components/copy/copy-button.d.ts +48 -0
- package/dist/types/components/copy/copy-input.d.ts +91 -0
- package/dist/types/components/data-filter/data-filter.d.ts +250 -0
- package/dist/types/components/data-filter/filter-core.d.ts +52 -0
- package/dist/types/components/date-picker/date-picker.d.ts +137 -0
- package/dist/types/components/date-picker/date-range-picker.d.ts +350 -0
- package/dist/types/components/date-picker/field-adapter.d.ts +6 -0
- package/dist/types/components/date-picker/month-picker.d.ts +75 -0
- package/dist/types/components/date-picker/range-field-adapter.d.ts +7 -0
- package/dist/types/components/date-picker/time-picker.d.ts +127 -0
- package/dist/types/components/datebox/date-range-box.d.ts +218 -0
- package/dist/types/components/datebox/datebox.d.ts +160 -0
- package/dist/types/components/datebox/field-adapter.d.ts +5 -0
- package/dist/types/components/dialog/dialog.d.ts +284 -0
- package/dist/types/components/dropdown/dropdown.d.ts +118 -0
- package/dist/types/components/empty-state/empty-state.d.ts +38 -0
- package/dist/types/components/field/field.d.ts +231 -0
- package/dist/types/components/field-upload/field-adapter.d.ts +5 -0
- package/dist/types/components/field-upload/field-upload.d.ts +213 -0
- package/dist/types/components/fieldset/fieldset.d.ts +125 -0
- package/dist/types/components/finder/finder.d.ts +252 -0
- package/dist/types/components/form/form.d.ts +194 -0
- package/dist/types/components/groupbox/groupbox.d.ts +89 -0
- package/dist/types/components/layout/layout.d.ts +132 -0
- package/dist/types/components/loading/inline-loading.d.ts +86 -0
- package/dist/types/components/loading/progress-bar.d.ts +168 -0
- package/dist/types/components/loading/spinner.d.ts +34 -0
- package/dist/types/components/master-panel/master-panel.d.ts +112 -0
- package/dist/types/components/menu-button/menu-button.d.ts +142 -0
- package/dist/types/components/message/message.d.ts +212 -0
- package/dist/types/components/modal/modal.d.ts +105 -0
- package/dist/types/components/multi-value-editor/field-adapter.d.ts +5 -0
- package/dist/types/components/multi-value-editor/multi-value-editor.d.ts +66 -0
- package/dist/types/components/navigation-bar/navigation-bar.d.ts +229 -0
- package/dist/types/components/number-field/field-adapter.d.ts +5 -0
- package/dist/types/components/number-field/number-field.d.ts +292 -0
- package/dist/types/components/pagination/pagination.d.ts +267 -0
- package/dist/types/components/panel/panel.d.ts +194 -0
- package/dist/types/components/permission/permission.d.ts +93 -0
- package/dist/types/components/rating/field-adapter.d.ts +5 -0
- package/dist/types/components/rating/rating.d.ts +213 -0
- package/dist/types/components/search/search.d.ts +98 -0
- package/dist/types/components/select/field-adapter.d.ts +5 -0
- package/dist/types/components/select/filter.d.ts +11 -0
- package/dist/types/components/select/select.d.ts +235 -0
- package/dist/types/components/skeleton/skeleton.d.ts +97 -0
- package/dist/types/components/slider/field-adapter.d.ts +5 -0
- package/dist/types/components/slider/slider.d.ts +216 -0
- package/dist/types/components/split-view/split-view.d.ts +335 -0
- package/dist/types/components/stepper/stepper.d.ts +314 -0
- package/dist/types/components/stepper/steps.d.ts +131 -0
- package/dist/types/components/tabbox/tabbox.d.ts +230 -0
- package/dist/types/components/table/sort.d.ts +49 -0
- package/dist/types/components/table/table.d.ts +872 -0
- package/dist/types/components/tag-picker/field-adapter.d.ts +5 -0
- package/dist/types/components/tag-picker/tag-picker.d.ts +375 -0
- package/dist/types/components/timebox/field-adapter.d.ts +5 -0
- package/dist/types/components/timebox/timebox.d.ts +121 -0
- package/dist/types/components/toggle/field-adapter.d.ts +5 -0
- package/dist/types/components/toggle/toggle.d.ts +94 -0
- package/dist/types/components/toolbar/toolbar.d.ts +230 -0
- package/dist/types/components/tooltip/tooltip.d.ts +183 -0
- package/dist/types/components/tree/hierarchy.d.ts +113 -0
- package/dist/types/components/tree/tree.d.ts +383 -0
- package/dist/types/components/truncate/truncate.d.ts +61 -0
- package/dist/types/components/value-list/field-adapter.d.ts +5 -0
- package/dist/types/components/value-list/value-list.d.ts +143 -0
- package/dist/types/core/breakpoint.d.ts +68 -0
- package/dist/types/core/component.d.ts +115 -0
- package/dist/types/core/date.d.ts +50 -0
- package/dist/types/core/dom.d.ts +86 -0
- package/dist/types/core/export.d.ts +70 -0
- package/dist/types/core/fontawesome.d.ts +107 -0
- package/dist/types/core/format.d.ts +107 -0
- package/dist/types/core/http.d.ts +122 -0
- package/dist/types/core/i18n.d.ts +35 -0
- package/dist/types/core/icons.d.ts +137 -0
- package/dist/types/core/keyboard.d.ts +72 -0
- package/dist/types/core/position.d.ts +45 -0
- package/dist/types/core/storage.d.ts +46 -0
- package/dist/types/core/util.d.ts +117 -0
- package/dist/types/elements/define.d.ts +73 -0
- package/dist/types/elements/reflect.d.ts +27 -0
- package/dist/types/index.d.ts +79 -0
- package/dist/types/zeyos/connect.d.ts +65 -0
- package/dist/types/zeyos/form.d.ts +140 -0
- package/dist/types/zeyos/icons.d.ts +104 -0
- package/dist/types/zeyos/index.d.ts +12 -0
- package/dist/types/zeyos/modules.d.ts +110 -0
- package/dist/types/zeyos/query.d.ts +157 -0
- package/dist/types/zeyos/schema.d.ts +79 -0
- package/dist/types/zeyos/select.d.ts +167 -0
- package/dist/types/zeyos/table.d.ts +205 -0
- package/dist/zx-compat.esm.js +375 -39
- package/dist/zx-compat.esm.min.js +1 -1
- package/dist/zx-compat.global.js +372 -73
- package/dist/zx-compat.global.min.js +3 -3
- package/dist/zx-zeyos.esm.js +355 -49
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +68 -6
- package/dist/zx.esm.js +359 -73
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +359 -73
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +48 -2
- package/docs/llms.md +31 -0
- package/package.json +37 -9
- package/src/compat/base.js +291 -0
- package/src/compat/globals.js +165 -0
- package/src/compat/index.js +84 -0
- package/src/compat/map/basic.js +464 -0
- package/src/compat/map/data.js +232 -0
- package/src/compat/map/date-time.js +177 -0
- package/src/compat/map/factory.js +45 -0
- package/src/compat/map/form.js +330 -0
- package/src/compat/map/helpers.js +46 -0
- package/src/compat/map/http.js +117 -0
- package/src/compat/map/layout.js +256 -0
- package/src/compat/map/options.js +267 -0
- package/src/compat/map/select.js +198 -0
- package/src/compat/map/stubs.js +26 -0
- package/src/compat/map/utilities.js +113 -0
- package/src/compat-entry.js +11 -0
- package/src/components/badge/badge.css +147 -0
- package/src/components/badge/badge.js +92 -0
- package/src/components/breadcrumb/breadcrumb.css +105 -0
- package/src/components/breadcrumb/breadcrumb.js +331 -0
- package/src/components/button/button.css +104 -0
- package/src/components/button/button.js +76 -0
- package/src/components/check-button/check-button.css +66 -0
- package/src/components/check-button/check-button.js +167 -0
- package/src/components/checklist/checklist.css +65 -0
- package/src/components/checklist/checklist.js +282 -0
- package/src/components/checklist/field-adapter.js +46 -0
- package/src/components/context-menu/context-menu.css +54 -0
- package/src/components/context-menu/context-menu.js +334 -0
- package/src/components/copy/copy-button.css +27 -0
- package/src/components/copy/copy-button.js +78 -0
- package/src/components/copy/copy-input.css +59 -0
- package/src/components/copy/copy-input.js +123 -0
- package/src/components/data-filter/data-filter.css +53 -0
- package/src/components/data-filter/data-filter.js +400 -0
- package/src/components/data-filter/filter-core.js +128 -0
- package/src/components/date-picker/date-picker.css +181 -0
- package/src/components/date-picker/date-picker.js +500 -0
- package/src/components/date-picker/date-range-picker.css +264 -0
- package/src/components/date-picker/date-range-picker.js +990 -0
- package/src/components/date-picker/field-adapter.js +36 -0
- package/src/components/date-picker/month-picker.js +223 -0
- package/src/components/date-picker/range-field-adapter.js +31 -0
- package/src/components/date-picker/time-picker.css +191 -0
- package/src/components/date-picker/time-picker.js +406 -0
- package/src/components/datebox/date-range-box.css +128 -0
- package/src/components/datebox/date-range-box.js +492 -0
- package/src/components/datebox/datebox.css +108 -0
- package/src/components/datebox/datebox.js +358 -0
- package/src/components/datebox/field-adapter.js +34 -0
- package/src/components/dialog/dialog.css +74 -0
- package/src/components/dialog/dialog.js +450 -0
- package/src/components/dropdown/dropdown.css +24 -0
- package/src/components/dropdown/dropdown.js +288 -0
- package/src/components/empty-state/empty-state.css +80 -0
- package/src/components/empty-state/empty-state.js +92 -0
- package/src/components/field/field.css +127 -0
- package/src/components/field/field.js +605 -0
- package/src/components/field-upload/field-adapter.js +21 -0
- package/src/components/field-upload/field-upload.css +108 -0
- package/src/components/field-upload/field-upload.js +475 -0
- package/src/components/fieldset/fieldset.css +37 -0
- package/src/components/fieldset/fieldset.js +307 -0
- package/src/components/finder/finder.css +98 -0
- package/src/components/finder/finder.js +509 -0
- package/src/components/form/form.css +32 -0
- package/src/components/form/form.js +302 -0
- package/src/components/groupbox/groupbox.css +51 -0
- package/src/components/groupbox/groupbox.js +199 -0
- package/src/components/layout/layout.css +117 -0
- package/src/components/layout/layout.js +136 -0
- package/src/components/loading/inline-loading.css +34 -0
- package/src/components/loading/inline-loading.js +140 -0
- package/src/components/loading/progress-bar.css +115 -0
- package/src/components/loading/progress-bar.js +259 -0
- package/src/components/loading/spinner.css +58 -0
- package/src/components/loading/spinner.js +57 -0
- package/src/components/master-panel/master-panel.css +51 -0
- package/src/components/master-panel/master-panel.js +203 -0
- package/src/components/menu-button/menu-button.css +49 -0
- package/src/components/menu-button/menu-button.js +294 -0
- package/src/components/message/message.css +122 -0
- package/src/components/message/message.js +499 -0
- package/src/components/modal/modal.css +47 -0
- package/src/components/modal/modal.js +203 -0
- package/src/components/multi-value-editor/field-adapter.js +44 -0
- package/src/components/multi-value-editor/multi-value-editor.css +56 -0
- package/src/components/multi-value-editor/multi-value-editor.js +170 -0
- package/src/components/navigation-bar/navigation-bar.css +130 -0
- package/src/components/navigation-bar/navigation-bar.js +450 -0
- package/src/components/number-field/field-adapter.js +40 -0
- package/src/components/number-field/number-field.css +77 -0
- package/src/components/number-field/number-field.js +545 -0
- package/src/components/pagination/pagination.css +95 -0
- package/src/components/pagination/pagination.js +481 -0
- package/src/components/panel/panel.css +123 -0
- package/src/components/panel/panel.js +336 -0
- package/src/components/permission/permission.css +47 -0
- package/src/components/permission/permission.js +198 -0
- package/src/components/rating/field-adapter.js +34 -0
- package/src/components/rating/rating.css +97 -0
- package/src/components/rating/rating.js +395 -0
- package/src/components/search/search.css +70 -0
- package/src/components/search/search.js +193 -0
- package/src/components/select/field-adapter.js +24 -0
- package/src/components/select/filter.js +39 -0
- package/src/components/select/select.css +180 -0
- package/src/components/select/select.js +663 -0
- package/src/components/skeleton/skeleton.css +76 -0
- package/src/components/skeleton/skeleton.js +139 -0
- package/src/components/slider/field-adapter.js +36 -0
- package/src/components/slider/slider.css +181 -0
- package/src/components/slider/slider.js +399 -0
- package/src/components/split-view/split-view.css +139 -0
- package/src/components/split-view/split-view.js +806 -0
- package/src/components/stepper/stepper.css +234 -0
- package/src/components/stepper/stepper.js +459 -0
- package/src/components/stepper/steps.js +131 -0
- package/src/components/tabbox/tabbox.css +281 -0
- package/src/components/tabbox/tabbox.js +484 -0
- package/src/components/table/sort.js +85 -0
- package/src/components/table/table.css +364 -0
- package/src/components/table/table.js +1711 -0
- package/src/components/tag-picker/field-adapter.js +35 -0
- package/src/components/tag-picker/tag-picker.css +169 -0
- package/src/components/tag-picker/tag-picker.js +837 -0
- package/src/components/timebox/field-adapter.js +24 -0
- package/src/components/timebox/timebox.css +51 -0
- package/src/components/timebox/timebox.js +276 -0
- package/src/components/toggle/field-adapter.js +24 -0
- package/src/components/toggle/toggle.css +66 -0
- package/src/components/toggle/toggle.js +150 -0
- package/src/components/toolbar/toolbar.css +81 -0
- package/src/components/toolbar/toolbar.js +511 -0
- package/src/components/tooltip/tooltip.css +36 -0
- package/src/components/tooltip/tooltip.js +420 -0
- package/src/components/tree/hierarchy.js +151 -0
- package/src/components/tree/tree.css +99 -0
- package/src/components/tree/tree.js +784 -0
- package/src/components/truncate/truncate.css +20 -0
- package/src/components/truncate/truncate.js +92 -0
- package/src/components/value-list/field-adapter.js +24 -0
- package/src/components/value-list/value-list.css +108 -0
- package/src/components/value-list/value-list.js +336 -0
- package/src/core/breakpoint.js +150 -0
- package/src/core/component.js +292 -0
- package/src/core/date.js +216 -0
- package/src/core/dom.js +260 -0
- package/src/core/export.js +204 -0
- package/src/core/fontawesome.js +177 -0
- package/src/core/format.js +283 -0
- package/src/core/http.js +246 -0
- package/src/core/i18n.js +83 -0
- package/src/core/icons.js +283 -0
- package/src/core/keyboard.js +240 -0
- package/src/core/position.js +177 -0
- package/src/core/storage.js +175 -0
- package/src/core/util.js +336 -0
- package/src/elements/define.js +715 -0
- package/src/elements/reflect.js +79 -0
- package/src/index.js +131 -0
- package/src/zeyos/connect.js +142 -0
- package/src/zeyos/form.js +254 -0
- package/src/zeyos/icons.js +92 -0
- package/src/zeyos/index.js +21 -0
- package/src/zeyos/modules.js +269 -0
- package/src/zeyos/query.js +199 -0
- package/src/zeyos/schema.js +474 -0
- package/src/zeyos/select.js +175 -0
- package/src/zeyos/table.js +272 -0
- package/styles/base.css +124 -0
- package/styles/icons.css +34 -0
- package/styles/tokens/dark.css +84 -0
- package/styles/tokens/density.css +9 -0
- package/styles/tokens/global.css +80 -0
- package/styles/tokens/modules.css +109 -0
- package/styles/tokens/semantic.css +49 -0
- package/styles/zx.css +61 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZeyOS module icons: the Font Awesome kit that carries them, and the two renderers built on the
|
|
3
|
+
* configuration in `./modules.js`.
|
|
4
|
+
* @module zx/zeyos/icons
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { h, icon, loadFontAwesome } from '../index.js';
|
|
8
|
+
import { moduleGlyphColor, moduleInfo, normalizeModuleName } from './modules.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The ZeyOS Font Awesome kit. It carries the licensed Font Awesome styles plus the custom
|
|
12
|
+
* `zeyos-*` module glyphs, which render as `<i class="fa-kit fa-zeyos-notes">`.
|
|
13
|
+
* @type {string}
|
|
14
|
+
*/
|
|
15
|
+
export const ZEYOS_ICON_KIT = 'https://kit.fontawesome.com/ae8320b210.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Loads the ZeyOS kit and makes Font Awesome the renderer for every Zx icon. Call it once at
|
|
19
|
+
* startup; repeat calls reuse the first load.
|
|
20
|
+
* @param {{kit?: string, style?: string, family?: string, fixedWidth?: boolean}} [options={}]
|
|
21
|
+
* Overrides — a different kit, or a different default style such as `'duotone'`.
|
|
22
|
+
* @returns {Promise<object>} Resolves with the icon configuration once the kit has loaded.
|
|
23
|
+
*/
|
|
24
|
+
export function useZeyosIcons(options = {}) {
|
|
25
|
+
return loadFontAwesome({ kit: ZEYOS_ICON_KIT, ...options });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {Object} ModuleIconOptions
|
|
30
|
+
* @property {number|string} [size=16] Rendered size.
|
|
31
|
+
* @property {string|null} [label=null] Accessible label; null marks the icon decorative.
|
|
32
|
+
* @property {boolean} [standard=false] Render the stock Font Awesome fallback instead of the
|
|
33
|
+
* kit's custom glyph — for pages running on a kit without the ZeyOS icon uploads.
|
|
34
|
+
* @property {string|string[]} [class] Extra class names.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates a module's icon. Module, entity, and API resource names are all accepted
|
|
39
|
+
* (`'notes'`, `'transactions.billing'`, `'invoices'`); unknown names fall back to the ZeyOS
|
|
40
|
+
* default glyph.
|
|
41
|
+
* @param {string} name Module, entity, or resource name.
|
|
42
|
+
* @param {ModuleIconOptions} [options={}] Display and accessibility options.
|
|
43
|
+
* @returns {HTMLElement} An `<i>` element carrying the Font Awesome classes.
|
|
44
|
+
*/
|
|
45
|
+
export function moduleIcon(name, options = {}) {
|
|
46
|
+
const { standard = false, ...rest } = options;
|
|
47
|
+
const info = moduleInfo(name);
|
|
48
|
+
return icon(standard ? `fa:${info.fa}` : `kit:${info.icon}`, rest);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @typedef {Object} ModuleChipOptions
|
|
53
|
+
* @property {number|string} [size=24] Chip edge length.
|
|
54
|
+
* @property {number|string} [iconSize='1em'] Glyph size; `1em` is 60% of the chip edge.
|
|
55
|
+
* @property {string|null} [label=null] Accessible label; null marks the chip decorative.
|
|
56
|
+
* @property {boolean} [title=false] Adds the module's display name as a `title` tooltip.
|
|
57
|
+
* @property {boolean} [standard=false] Use the stock Font Awesome fallback glyph.
|
|
58
|
+
* @property {string|string[]} [class] Extra class names.
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Creates a module's icon chip: the glyph on the module's identity colour, the way ZeyOS draws
|
|
63
|
+
* module icons in navigation and record headers. The glyph colour is whichever of ZeyOS's two
|
|
64
|
+
* foregrounds contrasts better with the background.
|
|
65
|
+
* @param {string} name Module, entity, or resource name.
|
|
66
|
+
* @param {ModuleChipOptions} [options={}] Display and accessibility options.
|
|
67
|
+
* @returns {HTMLElement} A `<span class="zx-module-icon">` wrapping the glyph.
|
|
68
|
+
*/
|
|
69
|
+
export function moduleChip(name, options = {}) {
|
|
70
|
+
const { size = 24, iconSize = '1em', label = null, title = false, standard = false } = options;
|
|
71
|
+
const key = normalizeModuleName(name);
|
|
72
|
+
const info = moduleInfo(name);
|
|
73
|
+
|
|
74
|
+
const chip = h('span', {
|
|
75
|
+
class: ['zx-module-icon', options.class].flat().filter(Boolean),
|
|
76
|
+
dataset: { module: key },
|
|
77
|
+
style: {
|
|
78
|
+
'--zx-module-color': info.color,
|
|
79
|
+
'--zx-module-glyph': moduleGlyphColor(info.color),
|
|
80
|
+
'--zx-module-icon-size': typeof size === 'number' ? `${size}px` : String(size)
|
|
81
|
+
},
|
|
82
|
+
title: title ? info.label : null
|
|
83
|
+
}, moduleIcon(name, { size: iconSize, standard }));
|
|
84
|
+
|
|
85
|
+
if (label === null) {
|
|
86
|
+
chip.setAttribute('aria-hidden', 'true');
|
|
87
|
+
} else {
|
|
88
|
+
chip.setAttribute('role', 'img');
|
|
89
|
+
chip.setAttribute('aria-label', label);
|
|
90
|
+
}
|
|
91
|
+
return chip;
|
|
92
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional injection-based Zx binding for `@zeyos/client` applications.
|
|
3
|
+
* @module zx/zeyos
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
fieldToZxField, fieldToZxColumn, resolveFields, registerFieldType
|
|
8
|
+
} from './schema.js';
|
|
9
|
+
export {
|
|
10
|
+
buildListQuery, tableSortToQuery, dataFilterStateToFilters,
|
|
11
|
+
dateToUnixSeconds, unixSecondsToDate
|
|
12
|
+
} from './query.js';
|
|
13
|
+
export { connect } from './connect.js';
|
|
14
|
+
export {
|
|
15
|
+
zeyosModules, moduleAliases, moduleInfo, moduleColor, moduleIconName, moduleKeys,
|
|
16
|
+
moduleGlyphColor, normalizeModuleName, registerModules
|
|
17
|
+
} from './modules.js';
|
|
18
|
+
export { ZEYOS_ICON_KIT, useZeyosIcons, moduleIcon, moduleChip } from './icons.js';
|
|
19
|
+
export { zeyosSelect } from './select.js';
|
|
20
|
+
export { zeyosForm } from './form.js';
|
|
21
|
+
export { zeyosTable } from './table.js';
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZeyOS module and entity identity — the icon and the colour each one is drawn with.
|
|
3
|
+
*
|
|
4
|
+
* This is the configuration file the rest of the ZeyOS layer reads. One entry per module:
|
|
5
|
+
*
|
|
6
|
+
* ```js
|
|
7
|
+
* notes: { label: 'Notes', icon: 'zeyos-notes', color: '#008853', fa: 'note-sticky' }
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* - `icon` is the icon's name in the ZeyOS Font Awesome kit; it renders as
|
|
11
|
+
* `<i class="fa-kit fa-zeyos-notes">` and needs the kit loaded (see `./icons.js`).
|
|
12
|
+
* - `fa` is a stock Font Awesome name used when the custom icons are unavailable — every one of
|
|
13
|
+
* them exists in Font Awesome Free, so the fallback also works on a Free kit.
|
|
14
|
+
* - `color` is the module's identity colour, used for icon chips and header accents. Colours
|
|
15
|
+
* marked "ZeyOS runtime" below are the values ZeyOS itself ships (`ICO.Colors`); the rest are
|
|
16
|
+
* Zx defaults for modules ZeyOS has no colour for. Both are overridable: a ZeyOS menu payload
|
|
17
|
+
* is authoritative, so pass its colours to `registerModules()` at startup.
|
|
18
|
+
*
|
|
19
|
+
* The `--zx-module-*` CSS custom properties in `styles/tokens/modules.css` are generated from
|
|
20
|
+
* this file by `tools/build-module-tokens.js`; `tests/unit/zeyos-modules.test.js` fails if the
|
|
21
|
+
* two drift apart.
|
|
22
|
+
*
|
|
23
|
+
* @module zx/zeyos/modules
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {Object} ZeyosModule
|
|
28
|
+
* @property {string} label English display name.
|
|
29
|
+
* @property {string} icon Icon name in the ZeyOS Font Awesome kit.
|
|
30
|
+
* @property {string} color Identity colour as a hex string.
|
|
31
|
+
* @property {string} fa Stock Font Awesome name used when the kit's custom icons are missing.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Every module in the ZeyOS Font Awesome kit, keyed by module identifier.
|
|
36
|
+
* @type {Readonly<Record<string, ZeyosModule>>}
|
|
37
|
+
*/
|
|
38
|
+
export const zeyosModules = Object.freeze({
|
|
39
|
+
/* ---------------------------------------------- colours from the ZeyOS runtime (ICO.Colors) */
|
|
40
|
+
accounts: { label: 'Accounts', icon: 'zeyos-accounts', color: '#bc3885', fa: 'building' },
|
|
41
|
+
actionsteps: { label: 'Action Steps', icon: 'zeyos-actionsteps', color: '#f67f00', fa: 'clock' },
|
|
42
|
+
appointments: { label: 'Appointments', icon: 'zeyos-appointments', color: '#bd1e32', fa: 'calendar-check' },
|
|
43
|
+
billing: { label: 'Billing', icon: 'zeyos-billing', color: '#535494', fa: 'file-invoice-dollar' },
|
|
44
|
+
calendar: { label: 'Calendar', icon: 'zeyos-calendar', color: '#bd1e32', fa: 'calendar-days' },
|
|
45
|
+
campaigns: { label: 'Campaigns', icon: 'zeyos-campaigns', color: '#f16528', fa: 'bullhorn' },
|
|
46
|
+
collection: { label: 'Collection', icon: 'zeyos-collection', color: '#0d60ae', fa: 'hand-holding-dollar' },
|
|
47
|
+
contacts: { label: 'Contacts', icon: 'zeyos-contacts', color: '#8c6641', fa: 'address-book' },
|
|
48
|
+
contracts: { label: 'Contracts', icon: 'zeyos-contracts', color: '#823e89', fa: 'file-contract' },
|
|
49
|
+
coupons: { label: 'Coupons', icon: 'zeyos-coupons', color: '#10775c', fa: 'ticket-simple' },
|
|
50
|
+
devices: { label: 'Devices', icon: 'zeyos-devices', color: '#064e4f', fa: 'desktop' },
|
|
51
|
+
documents: { label: 'Documents', icon: 'zeyos-documents', color: '#808000', fa: 'file' },
|
|
52
|
+
dunning: { label: 'Dunning', icon: 'zeyos-dunning', color: '#0d60ae', fa: 'file-invoice-dollar' },
|
|
53
|
+
events: { label: 'Events', icon: 'zeyos-events', color: '#bd1e32', fa: 'calendar-day' },
|
|
54
|
+
inventory: { label: 'Inventory', icon: 'zeyos-inventory', color: '#064e4f', fa: 'warehouse' },
|
|
55
|
+
items: { label: 'Items', icon: 'zeyos-items', color: '#064e4f', fa: 'box-open' },
|
|
56
|
+
ledgers: { label: 'Ledgers', icon: 'zeyos-ledgers', color: '#4679c8', fa: 'book-open' },
|
|
57
|
+
line: { label: 'Line Items', icon: 'zeyos-line', color: '#064e4f', fa: 'list-ul' },
|
|
58
|
+
links: { label: 'Links', icon: 'zeyos-links', color: '#405e76', fa: 'bookmark' },
|
|
59
|
+
mailinglists: { label: 'Mailing Lists', icon: 'zeyos-mailinglists', color: '#0299ac', fa: 'envelope-open-text' },
|
|
60
|
+
messages: { label: 'Messages', icon: 'zeyos-messages', color: '#31a8e0', fa: 'message' },
|
|
61
|
+
'messages-unread': { label: 'Unread Messages', icon: 'zeyos-messages-unread', color: '#31a8e0', fa: 'envelope' },
|
|
62
|
+
notes: { label: 'Notes', icon: 'zeyos-notes', color: '#008853', fa: 'note-sticky' },
|
|
63
|
+
opportunities: { label: 'Opportunities', icon: 'zeyos-opportunities', color: '#7b67ae', fa: 'handshake' },
|
|
64
|
+
payments: { label: 'Payments', icon: 'zeyos-payments', color: '#4679c8', fa: 'sack-dollar' },
|
|
65
|
+
pricelists: { label: 'Price Lists', icon: 'zeyos-pricelists', color: '#10775c', fa: 'clipboard-list' },
|
|
66
|
+
procurement: { label: 'Procurement', icon: 'zeyos-procurement', color: '#006e9f', fa: 'truck' },
|
|
67
|
+
production: { label: 'Production', icon: 'zeyos-production', color: '#226375', fa: 'industry' },
|
|
68
|
+
projects: { label: 'Projects', icon: 'zeyos-projects', color: '#db532d', fa: 'diagram-project' },
|
|
69
|
+
stocktransactions: { label: 'Stock Transactions', icon: 'zeyos-stocktransactions', color: '#064e4f', fa: 'arrow-right-arrow-left' },
|
|
70
|
+
storages: { label: 'Storages', icon: 'zeyos-storages', color: '#064e4f', fa: 'boxes-stacked' },
|
|
71
|
+
tasks: { label: 'Tasks', icon: 'zeyos-tasks', color: '#e2b301', fa: 'list-check' },
|
|
72
|
+
text: { label: 'Text Blocks', icon: 'zeyos-text', color: '#064e4f', fa: 'file-lines' },
|
|
73
|
+
tickets: { label: 'Tickets', icon: 'zeyos-tickets', color: '#f04639', fa: 'ticket' },
|
|
74
|
+
'transactions-billing': { label: 'Billing Transactions', icon: 'zeyos-transactions-billing', color: '#535494', fa: 'file-invoice' },
|
|
75
|
+
'transactions-collection': { label: 'Collection Transactions', icon: 'zeyos-transactions-collection', color: '#0d60ae', fa: 'money-bill-transfer' },
|
|
76
|
+
'transactions-procurement': { label: 'Procurement Transactions', icon: 'zeyos-transactions-procurement', color: '#006e9f', fa: 'cart-shopping' },
|
|
77
|
+
'transactions-production': { label: 'Production Transactions', icon: 'zeyos-transactions-production', color: '#226375', fa: 'gears' },
|
|
78
|
+
|
|
79
|
+
/* ------------------------------------- Zx defaults: modules ZeyOS ships no identity colour for */
|
|
80
|
+
zeyos: { label: 'ZeyOS', icon: 'zeyos', color: '#007a55', fa: 'cloud' },
|
|
81
|
+
default: { label: 'Default', icon: 'zeyos-default', color: '#21cc75', fa: 'cube' },
|
|
82
|
+
admin: { label: 'Administration', icon: 'zeyos-admin', color: '#4b5563', fa: 'screwdriver-wrench' },
|
|
83
|
+
applications: { label: 'Applications', icon: 'zeyos-applications', color: '#2563eb', fa: 'window-maximize' },
|
|
84
|
+
auth: { label: 'Credentials', icon: 'zeyos-auth', color: '#a16207', fa: 'shield-halved' },
|
|
85
|
+
categories: { label: 'Categories', icon: 'zeyos-categories', color: '#9a3412', fa: 'folder-tree' },
|
|
86
|
+
channels: { label: 'Channels', icon: 'zeyos-channels', color: '#0891b2', fa: 'share-nodes' },
|
|
87
|
+
control: { label: 'Control Center', icon: 'zeyos-control', color: '#0f766e', fa: 'sliders' },
|
|
88
|
+
davservers: { label: 'DAV Servers', icon: 'zeyos-davservers', color: '#475569', fa: 'server' },
|
|
89
|
+
dev: { label: 'Development', icon: 'zeyos-dev', color: '#1f2937', fa: 'code' },
|
|
90
|
+
error: { label: 'Errors', icon: 'zeyos-error', color: '#b91c1c', fa: 'triangle-exclamation' },
|
|
91
|
+
global: { label: 'Global', icon: 'zeyos-global', color: '#0369a1', fa: 'globe' },
|
|
92
|
+
groups: { label: 'Groups', icon: 'zeyos-groups', color: '#007a55', fa: 'users' },
|
|
93
|
+
logout: { label: 'Logout', icon: 'zeyos-logout', color: '#9f1239', fa: 'right-from-bracket' },
|
|
94
|
+
objects: { label: 'Custom Objects', icon: 'zeyos-objects', color: '#7e22ce', fa: 'cubes' },
|
|
95
|
+
participants: { label: 'Participants', icon: 'zeyos-participants', color: '#4338ca', fa: 'user-group' },
|
|
96
|
+
permissions: { label: 'Permissions', icon: 'zeyos-permissions', color: '#5b21b6', fa: 'user-lock' },
|
|
97
|
+
records: { label: 'Records', icon: 'zeyos-records', color: '#57534e', fa: 'database' },
|
|
98
|
+
resources: { label: 'Resources', icon: 'zeyos-resources', color: '#15803d', fa: 'layer-group' },
|
|
99
|
+
services: { label: 'Services', icon: 'zeyos-services', color: '#b45309', fa: 'bell-concierge' },
|
|
100
|
+
system: { label: 'System', icon: 'zeyos-system', color: '#374151', fa: 'gear' },
|
|
101
|
+
users: { label: 'Users', icon: 'zeyos-users', color: '#007a55', fa: 'user' },
|
|
102
|
+
weblets: { label: 'Weblets', icon: 'zeyos-weblets', color: '#4f46e5', fa: 'window-restore' }
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Alternative identifiers resolving to a module key: API resource names, ZeyOS entity names, and
|
|
107
|
+
* the legacy gx module names. Dotted ZeyOS identifiers (`transactions.billing`) need no entry —
|
|
108
|
+
* `normalizeModuleName()` turns dots into hyphens first.
|
|
109
|
+
* @type {Readonly<Record<string, string>>}
|
|
110
|
+
*/
|
|
111
|
+
export const moduleAliases = Object.freeze({
|
|
112
|
+
addresses: 'contacts',
|
|
113
|
+
clocking: 'actionsteps',
|
|
114
|
+
comments: 'notes',
|
|
115
|
+
couponcodes: 'coupons',
|
|
116
|
+
customers: 'accounts',
|
|
117
|
+
customfields: 'objects',
|
|
118
|
+
davids: 'davservers',
|
|
119
|
+
documentversions: 'documents',
|
|
120
|
+
enhancements: 'applications',
|
|
121
|
+
extdata: 'objects',
|
|
122
|
+
feedservers: 'channels',
|
|
123
|
+
files: 'documents',
|
|
124
|
+
filters: 'records',
|
|
125
|
+
forks: 'applications',
|
|
126
|
+
imports: 'records',
|
|
127
|
+
invoices: 'transactions-billing',
|
|
128
|
+
linecoupon: 'line',
|
|
129
|
+
logins: 'auth',
|
|
130
|
+
mailservers: 'channels',
|
|
131
|
+
notifications: 'messages',
|
|
132
|
+
'participants-campaigns': 'participants',
|
|
133
|
+
'participants-mailinglists': 'participants',
|
|
134
|
+
prices: 'pricelists',
|
|
135
|
+
pwd: 'auth',
|
|
136
|
+
purchaseorders: 'transactions-procurement',
|
|
137
|
+
sessions: 'auth',
|
|
138
|
+
settings: 'system',
|
|
139
|
+
suppliers: 'accounts',
|
|
140
|
+
tags: 'categories',
|
|
141
|
+
tokens: 'auth',
|
|
142
|
+
transactions: 'transactions-billing',
|
|
143
|
+
usergroups: 'groups',
|
|
144
|
+
usermgmt: 'users',
|
|
145
|
+
usersettings: 'system',
|
|
146
|
+
userfields: 'objects',
|
|
147
|
+
userfilters: 'records'
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
/** Glyph colour candidates, matching the two foregrounds ZeyOS measured its palette against. */
|
|
151
|
+
const glyphColors = Object.freeze({ light: '#ffffff', dark: '#141414' });
|
|
152
|
+
|
|
153
|
+
/** @type {Record<string, ZeyosModule>} Modules registered or overridden at runtime. */
|
|
154
|
+
const overrides = Object.create(null);
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Normalizes a module, entity, or resource name to a module key. Case, surrounding whitespace,
|
|
158
|
+
* dots (`transactions.billing`), and underscores are all accepted; aliases are resolved.
|
|
159
|
+
* @param {string|null|undefined} name Module, entity, or resource name.
|
|
160
|
+
* @returns {string} A module key, or `'default'` when nothing matches.
|
|
161
|
+
*/
|
|
162
|
+
export function normalizeModuleName(name) {
|
|
163
|
+
const key = String(name ?? '').trim().toLowerCase().replaceAll(/[._\s]+/g, '-');
|
|
164
|
+
if (!key) return 'default';
|
|
165
|
+
if (Object.hasOwn(overrides, key) || Object.hasOwn(zeyosModules, key)) return key;
|
|
166
|
+
const alias = moduleAliases[key];
|
|
167
|
+
if (alias) return alias;
|
|
168
|
+
return 'default';
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Looks a module up. Unknown names fall back to the `default` entry, so callers always get an
|
|
173
|
+
* icon and a colour.
|
|
174
|
+
* @param {string|null|undefined} name Module, entity, or resource name.
|
|
175
|
+
* @returns {ZeyosModule} The module's configuration.
|
|
176
|
+
*/
|
|
177
|
+
export function moduleInfo(name) {
|
|
178
|
+
const key = normalizeModuleName(name);
|
|
179
|
+
return overrides[key] ?? zeyosModules[key] ?? zeyosModules.default;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Reads a module's identity colour.
|
|
184
|
+
* @param {string|null|undefined} name Module, entity, or resource name.
|
|
185
|
+
* @returns {string} Hex colour.
|
|
186
|
+
*/
|
|
187
|
+
export function moduleColor(name) {
|
|
188
|
+
return moduleInfo(name).color;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Reads the kit icon name for a module (`'zeyos-notes'`), or its stock Font Awesome fallback.
|
|
193
|
+
* @param {string|null|undefined} name Module, entity, or resource name.
|
|
194
|
+
* @param {{standard?: boolean}} [options={}] Set `standard` for the stock Font Awesome name.
|
|
195
|
+
* @returns {string} Icon name without the `fa-` prefix.
|
|
196
|
+
*/
|
|
197
|
+
export function moduleIconName(name, { standard = false } = {}) {
|
|
198
|
+
const info = moduleInfo(name);
|
|
199
|
+
return standard ? info.fa : info.icon;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Lists every module key, including runtime registrations.
|
|
204
|
+
* @returns {string[]} Sorted module keys.
|
|
205
|
+
*/
|
|
206
|
+
export function moduleKeys() {
|
|
207
|
+
return [...new Set([...Object.keys(zeyosModules), ...Object.keys(overrides)])].sort();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Registers modules, or overrides shipped ones — for forks, weblets, and the colours a ZeyOS
|
|
212
|
+
* menu payload carries. A partial entry inherits the rest from the module it replaces.
|
|
213
|
+
* @param {Record<string, string|Partial<ZeyosModule>>} modules Entries keyed by module name;
|
|
214
|
+
* a string value sets the colour only.
|
|
215
|
+
* @returns {void}
|
|
216
|
+
*/
|
|
217
|
+
export function registerModules(modules) {
|
|
218
|
+
for (const [name, value] of Object.entries(modules)) {
|
|
219
|
+
const key = String(name).trim().toLowerCase().replaceAll(/[._\s]+/g, '-');
|
|
220
|
+
const patch = typeof value === 'string' ? { color: value } : value;
|
|
221
|
+
const base = overrides[key] ?? zeyosModules[key] ?? zeyosModules.default;
|
|
222
|
+
overrides[key] = Object.freeze({
|
|
223
|
+
label: patch.label ?? base.label,
|
|
224
|
+
icon: patch.icon ?? base.icon,
|
|
225
|
+
color: patch.color ?? base.color,
|
|
226
|
+
fa: patch.fa ?? base.fa
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Picks the higher-contrast glyph colour for a background, using the same two candidates and the
|
|
233
|
+
* same WCAG relative-luminance maths ZeyOS used to measure its palette.
|
|
234
|
+
* @param {string} background Hex colour, three or six digits.
|
|
235
|
+
* @returns {string} `'#ffffff'` or `'#141414'`.
|
|
236
|
+
*/
|
|
237
|
+
export function moduleGlyphColor(background) {
|
|
238
|
+
const luminance = hexLuminance(background);
|
|
239
|
+
if (luminance === null) return glyphColors.light;
|
|
240
|
+
const light = contrastRatio(luminance, hexLuminance(glyphColors.light) ?? 1);
|
|
241
|
+
const dark = contrastRatio(luminance, hexLuminance(glyphColors.dark) ?? 0);
|
|
242
|
+
return light >= dark ? glyphColors.light : glyphColors.dark;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* WCAG relative luminance of a hex colour.
|
|
247
|
+
* @param {string} color Hex colour, three or six digits.
|
|
248
|
+
* @returns {number|null} Luminance, or null when the colour is not hex.
|
|
249
|
+
*/
|
|
250
|
+
function hexLuminance(color) {
|
|
251
|
+
const match = /^#([\da-f]{3}|[\da-f]{6})$/i.exec(String(color).trim());
|
|
252
|
+
if (!match) return null;
|
|
253
|
+
const digits = match[1].length === 3 ? [...match[1]].map((digit) => digit + digit).join('') : match[1];
|
|
254
|
+
const [red, green, blue] = [0, 2, 4].map((offset) => {
|
|
255
|
+
const channel = Number.parseInt(digits.slice(offset, offset + 2), 16) / 255;
|
|
256
|
+
return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
|
|
257
|
+
});
|
|
258
|
+
return 0.2126 * red + 0.7152 * green + 0.0722 * blue;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Contrast ratio between two relative luminances.
|
|
263
|
+
* @param {number} first First luminance.
|
|
264
|
+
* @param {number} second Second luminance.
|
|
265
|
+
* @returns {number}
|
|
266
|
+
*/
|
|
267
|
+
function contrastRatio(first, second) {
|
|
268
|
+
return (Math.max(first, second) + 0.05) / (Math.min(first, second) + 0.05);
|
|
269
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/** @typedef {{id?: string, field?: string, rawField?: string, dir?: 'asc'|'desc'}} ZxSort */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} ListQueryOptions
|
|
5
|
+
* @property {string[]|Record<string, string>} [fields] Projection, including aliases and dot joins.
|
|
6
|
+
* @property {ZxSort|ZxSort[]} [sort] Zx table sort state.
|
|
7
|
+
* @property {Record<string, unknown>} [filters] ZeyOS field predicates.
|
|
8
|
+
* @property {string} [search] Full-text query.
|
|
9
|
+
* @property {string[]} [searchFields] UI search-field metadata; ZeyOS searches its indexed text fields.
|
|
10
|
+
* @property {number} [limit] Page size.
|
|
11
|
+
* @property {number} [offset] Page offset.
|
|
12
|
+
* @property {number|null} [visibility=0] Visibility predicate; null is preserved deliberately.
|
|
13
|
+
* @property {boolean} [count=false] Request a server-side count instead of counting a returned page.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {Object} ServerFilterDefinition
|
|
18
|
+
* @property {string} id DataFilter state key.
|
|
19
|
+
* @property {string} [field] Raw ZeyOS field.
|
|
20
|
+
* @property {string} [rawField] Raw ZeyOS field when the UI id is an alias.
|
|
21
|
+
* @property {string[]} [fields] Raw fields combined as an OR group.
|
|
22
|
+
* @property {string} [operator] ZeyOS comparison operator such as `>=` or `IN`.
|
|
23
|
+
* @property {string} [format] Semantic value format; date/datetime values become Unix seconds.
|
|
24
|
+
* @property {string} [type] Semantic value type; date/datetime values become Unix seconds.
|
|
25
|
+
* @property {boolean} [includeEmpty=false] Preserve an empty-string value instead of treating it as inactive UI state.
|
|
26
|
+
* @property {boolean} [includeNull=false] Preserve null instead of treating it as inactive UI state.
|
|
27
|
+
* @property {(value: unknown, state: Record<string, unknown>) => unknown} [transform] Value transform.
|
|
28
|
+
* @property {(value: unknown, state: Record<string, unknown>) => Record<string, unknown>|undefined} [toFilter] Complete predicate transform.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Converts a Date to the Unix-second unit used by every ZeyOS timestamp.
|
|
33
|
+
* Milliseconds are truncated because the server stores whole seconds.
|
|
34
|
+
* @param {Date|null|undefined} date Date value.
|
|
35
|
+
* @returns {number|null} Unix seconds, or null for a nullish input.
|
|
36
|
+
*/
|
|
37
|
+
export function dateToUnixSeconds(date) {
|
|
38
|
+
if (date == null) return null;
|
|
39
|
+
if (!(date instanceof Date)) throw new TypeError('Expected a Date');
|
|
40
|
+
const milliseconds = date.getTime();
|
|
41
|
+
if (!Number.isFinite(milliseconds)) throw new RangeError('Invalid Date');
|
|
42
|
+
return Math.floor(milliseconds / 1000);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Converts a ZeyOS Unix-second timestamp to a Date.
|
|
47
|
+
* Numeric strings are accepted because some endpoints serialize bigint columns as strings.
|
|
48
|
+
* @param {number|string|null|undefined} seconds Unix seconds.
|
|
49
|
+
* @returns {Date|null} Date value, or null for a nullish input.
|
|
50
|
+
*/
|
|
51
|
+
export function unixSecondsToDate(seconds) {
|
|
52
|
+
if (seconds == null) return null;
|
|
53
|
+
const value = Number(seconds);
|
|
54
|
+
if (!Number.isFinite(value)) throw new TypeError('Expected finite Unix seconds');
|
|
55
|
+
return new Date(value * 1000);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Converts one or more Zx Table sort records to the signed field notation used by ZeyOS.
|
|
60
|
+
* A missing sort produces an empty array.
|
|
61
|
+
* @param {ZxSort|ZxSort[]|null|undefined} sort Zx sort state.
|
|
62
|
+
* @returns {string[]} Signed raw fields such as `+name` or `-lastmodified`.
|
|
63
|
+
*/
|
|
64
|
+
export function tableSortToQuery(sort) {
|
|
65
|
+
const entries = Array.isArray(sort) ? sort : (sort ? [sort] : []);
|
|
66
|
+
return entries.flatMap((entry) => {
|
|
67
|
+
if (!entry || typeof entry !== 'object') return [];
|
|
68
|
+
const source = entry.rawField ?? entry.field ?? entry.id;
|
|
69
|
+
if (source == null || String(source).trim() === '') return [];
|
|
70
|
+
const field = String(source).trim().replace(/^[+-]/, '');
|
|
71
|
+
return [`${entry.dir === 'desc' ? '-' : '+'}${field}`];
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Converts DataFilter state to ZeyOS field predicates. Definitions may rename UI ids,
|
|
77
|
+
* add comparison operators, combine several fields with OR, or transform values. Blank/null
|
|
78
|
+
* values are DataFilter's inactive sentinels by default; `includeEmpty`/`includeNull` opt in to
|
|
79
|
+
* the distinct ZeyOS meanings. Zero and false are always retained.
|
|
80
|
+
* @param {Record<string, unknown>} state DataFilter state keyed by definition id.
|
|
81
|
+
* @param {ServerFilterDefinition[]|Record<string, string|ServerFilterDefinition>} [defs=[]] Server mappings.
|
|
82
|
+
* @returns {Record<string, unknown>} ZeyOS `filters` object (plural at the call site).
|
|
83
|
+
*/
|
|
84
|
+
export function dataFilterStateToFilters(state, defs = []) {
|
|
85
|
+
if (!state || typeof state !== 'object' || Array.isArray(state)) {
|
|
86
|
+
throw new TypeError('DataFilter state must be an object');
|
|
87
|
+
}
|
|
88
|
+
const definitions = normalizeDefinitions(state, defs);
|
|
89
|
+
const filters = {};
|
|
90
|
+
let groupIndex = 0;
|
|
91
|
+
|
|
92
|
+
for (const definition of definitions) {
|
|
93
|
+
if (!definition.id || !Object.prototype.hasOwnProperty.call(state, definition.id)) continue;
|
|
94
|
+
const original = state[definition.id];
|
|
95
|
+
if (original === undefined) continue;
|
|
96
|
+
if (original === null && definition.includeNull !== true) continue;
|
|
97
|
+
if (typeof original === 'string' && original.trim() === '' && definition.includeEmpty !== true) continue;
|
|
98
|
+
|
|
99
|
+
if (typeof definition.toFilter === 'function') {
|
|
100
|
+
const predicate = definition.toFilter(original, state);
|
|
101
|
+
if (predicate && typeof predicate === 'object' && !Array.isArray(predicate)) {
|
|
102
|
+
Object.assign(filters, predicate);
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let value = typeof definition.transform === 'function'
|
|
108
|
+
? definition.transform(original, state)
|
|
109
|
+
: normalizeFilterValue(original, definition);
|
|
110
|
+
if (value === undefined) continue;
|
|
111
|
+
if (definition.operator) value = { [definition.operator]: value };
|
|
112
|
+
|
|
113
|
+
const fields = Array.isArray(definition.fields)
|
|
114
|
+
? definition.fields.filter((field) => typeof field === 'string' && field)
|
|
115
|
+
: [];
|
|
116
|
+
if (fields.length > 1) {
|
|
117
|
+
while (Object.prototype.hasOwnProperty.call(filters, String(groupIndex))) groupIndex += 1;
|
|
118
|
+
filters[String(groupIndex)] = ['OR', ...fields.map((field) => ({ [field]: value }))];
|
|
119
|
+
groupIndex += 1;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const field = fields[0] ?? definition.rawField ?? definition.field ?? definition.id;
|
|
123
|
+
if (typeof field === 'string' && field) filters[field] = value;
|
|
124
|
+
}
|
|
125
|
+
return filters;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Builds the body passed to a generated `client.api.list<Resource>()` operation. Predicates are
|
|
130
|
+
* always emitted under `filters` (plural), which is required for indexed foreign-key matching.
|
|
131
|
+
* Full-text `query` is server-scoped: `searchFields` describes the UI but is not an API parameter.
|
|
132
|
+
* Set `count` to use ZeyOS's server-side count; there is no equivalent server-side SUM.
|
|
133
|
+
* @param {ListQueryOptions} [options={}] Zx list state.
|
|
134
|
+
* @returns {Record<string, unknown>} ZeyOS list request body.
|
|
135
|
+
*/
|
|
136
|
+
export function buildListQuery(options = {}) {
|
|
137
|
+
const {
|
|
138
|
+
fields, sort, filters = {}, search, limit, offset, visibility = 0, count = false
|
|
139
|
+
} = options;
|
|
140
|
+
if (!filters || typeof filters !== 'object' || Array.isArray(filters)) {
|
|
141
|
+
throw new TypeError('ZeyOS filters must be an object');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const query = {};
|
|
145
|
+
if (Array.isArray(fields)) query.fields = [...fields];
|
|
146
|
+
else if (fields && typeof fields === 'object') query.fields = { ...fields };
|
|
147
|
+
|
|
148
|
+
const fieldPredicates = { ...filters };
|
|
149
|
+
const explicitVisibility = Object.prototype.hasOwnProperty.call(options, 'visibility');
|
|
150
|
+
if (visibility !== undefined && (explicitVisibility || !Object.prototype.hasOwnProperty.call(fieldPredicates, 'visibility'))) {
|
|
151
|
+
fieldPredicates.visibility = visibility;
|
|
152
|
+
}
|
|
153
|
+
if (Object.keys(fieldPredicates).length) query.filters = fieldPredicates;
|
|
154
|
+
|
|
155
|
+
const mappedSort = mapSortAliases(sort, fields);
|
|
156
|
+
const sortQuery = tableSortToQuery(mappedSort);
|
|
157
|
+
if (sortQuery.length) query.sort = sortQuery;
|
|
158
|
+
|
|
159
|
+
if (search != null && String(search).trim()) query.query = String(search).trim();
|
|
160
|
+
if (limit != null) query.limit = limit;
|
|
161
|
+
if (offset != null) query.offset = offset;
|
|
162
|
+
if (count) query.count = true;
|
|
163
|
+
return query;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** @param {Record<string, unknown>} state @param {ServerFilterDefinition[]|Record<string, string|ServerFilterDefinition>} defs @returns {ServerFilterDefinition[]} */
|
|
167
|
+
function normalizeDefinitions(state, defs) {
|
|
168
|
+
if (Array.isArray(defs) && defs.length) return defs.map((definition) => ({ ...definition }));
|
|
169
|
+
if (defs && typeof defs === 'object' && !Array.isArray(defs) && Object.keys(defs).length) {
|
|
170
|
+
return Object.entries(defs).map(([id, definition]) => typeof definition === 'string'
|
|
171
|
+
? { id, field: definition }
|
|
172
|
+
: { ...definition, id });
|
|
173
|
+
}
|
|
174
|
+
return Object.keys(state).map((id) => ({ id, field: id }));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** @param {unknown} value @param {ServerFilterDefinition} definition @returns {unknown} */
|
|
178
|
+
function normalizeFilterValue(value, definition) {
|
|
179
|
+
const semantic = String(definition.format ?? definition.type ?? '').toLowerCase();
|
|
180
|
+
if (value instanceof Date && ['date', 'datetime', 'date-time'].includes(semantic)) {
|
|
181
|
+
return dateToUnixSeconds(value);
|
|
182
|
+
}
|
|
183
|
+
if (Array.isArray(value) && ['date', 'datetime', 'date-time'].includes(semantic)) {
|
|
184
|
+
return value.map((entry) => entry instanceof Date ? dateToUnixSeconds(entry) : entry);
|
|
185
|
+
}
|
|
186
|
+
return value;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** @param {ZxSort|ZxSort[]|undefined} sort @param {string[]|Record<string, string>|undefined} fields @returns {ZxSort|ZxSort[]|undefined} */
|
|
190
|
+
function mapSortAliases(sort, fields) {
|
|
191
|
+
if (!sort || !fields || Array.isArray(fields) || typeof fields !== 'object') return sort;
|
|
192
|
+
const mapOne = (entry) => {
|
|
193
|
+
if (!entry || typeof entry !== 'object') return entry;
|
|
194
|
+
const alias = entry.id;
|
|
195
|
+
if (!alias || !Object.prototype.hasOwnProperty.call(fields, alias)) return entry;
|
|
196
|
+
return { ...entry, rawField: fields[alias] };
|
|
197
|
+
};
|
|
198
|
+
return Array.isArray(sort) ? sort.map(mapOne) : mapOne(sort);
|
|
199
|
+
}
|