@zeyos/zx 4.4.0 → 5.2.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 +447 -0
- package/README.md +44 -11
- package/dist/types/components/account-menu/account-menu.d.ts +8 -0
- package/dist/types/components/activity-list/activity-list.d.ts +31 -1
- package/dist/types/components/app-icon/app-icon.d.ts +14 -2
- package/dist/types/components/app-sidebar/app-sidebar.d.ts +293 -17
- package/dist/types/components/avatar/avatar.d.ts +46 -7
- package/dist/types/components/badge/badge.d.ts +21 -1
- package/dist/types/components/button/button.d.ts +15 -0
- package/dist/types/components/calendar/calendar.d.ts +67 -5
- package/dist/types/components/card-view/card-view.d.ts +7 -4
- package/dist/types/components/card-view/record-card.d.ts +11 -0
- package/dist/types/components/chart/chart.d.ts +10 -0
- package/dist/types/components/checklist/checklist.d.ts +9 -0
- package/dist/types/components/code-editor/code-editor.d.ts +6 -17
- package/dist/types/components/context-menu/context-menu.d.ts +6 -2
- package/dist/types/components/data-filter/data-filter.d.ts +4 -4
- package/dist/types/components/datebox/date-range-box.d.ts +5 -1
- package/dist/types/components/datebox/datebox.d.ts +7 -2
- package/dist/types/components/dialog/dialog.d.ts +46 -6
- package/dist/types/components/dropdown/dropdown.d.ts +24 -9
- package/dist/types/components/entity-ref/entity-ref.d.ts +2 -2
- package/dist/types/components/field/field.d.ts +54 -2
- package/dist/types/components/fieldset/external-adapter.d.ts +83 -0
- package/dist/types/components/fieldset/fieldset.d.ts +26 -1
- package/dist/types/components/file-item/file-item.d.ts +34 -6
- package/dist/types/components/file-list/file-list.d.ts +35 -3
- package/dist/types/components/filter/filter.d.ts +127 -3
- package/dist/types/components/filter-panel/filter-panel.d.ts +36 -7
- package/dist/types/components/form/form.d.ts +12 -1
- package/dist/types/components/kanban-view/kanban-card.d.ts +8 -0
- package/dist/types/components/launcher/launcher.d.ts +268 -15
- package/dist/types/components/list-toolbar/list-toolbar.d.ts +16 -7
- package/dist/types/components/menu-button/menu-button.d.ts +39 -5
- package/dist/types/components/message/message.d.ts +132 -17
- package/dist/types/components/modal/modal.d.ts +43 -2
- package/dist/types/components/module-bar/module-bar.d.ts +395 -0
- package/dist/types/components/pagination/pagination.d.ts +17 -3
- package/dist/types/components/select/color.d.ts +12 -0
- package/dist/types/components/select/glyphs.d.ts +124 -0
- package/dist/types/components/select/select.d.ts +17 -3
- package/dist/types/components/sheet/sheet.d.ts +15 -0
- package/dist/types/components/stat-tile/stat-tile.d.ts +16 -5
- package/dist/types/components/tabbox/tabbox.d.ts +9 -0
- package/dist/types/components/table/table.d.ts +311 -18
- package/dist/types/components/tag-picker/tag-picker.d.ts +21 -4
- package/dist/types/components/tree/tree.d.ts +13 -0
- package/dist/types/components/view/record-view.d.ts +59 -2
- package/dist/types/components/view-settings/view-settings.d.ts +879 -0
- package/dist/types/components/workspace-switcher/workspace-switcher.d.ts +327 -0
- package/dist/types/core/breakpoint.d.ts +1 -1
- package/dist/types/core/component.d.ts +12 -2
- package/dist/types/core/i18n.d.ts +27 -3
- package/dist/types/core/icons.d.ts +23 -25
- package/dist/types/core/keyboard.d.ts +98 -1
- package/dist/types/core/messages.d.ts +382 -0
- package/dist/types/core/motion.d.ts +59 -0
- package/dist/types/core/overlay-host.d.ts +42 -1
- package/dist/types/core/overlay-stack.d.ts +260 -0
- package/dist/types/index.d.ts +10 -5
- package/dist/types/internal/app-rail.d.ts +38 -0
- package/dist/types/internal/menu-items.d.ts +71 -3
- package/dist/types/zeyos/connect.d.ts +6 -0
- package/dist/types/zeyos/form.d.ts +50 -9
- package/dist/types/zeyos/index.d.ts +2 -1
- package/dist/types/zeyos/launcher.d.ts +9 -0
- package/dist/types/zeyos/module-bar.d.ts +176 -0
- package/dist/types/zeyos/query.d.ts +110 -5
- package/dist/types/zeyos/schema.d.ts +24 -1
- package/dist/types/zeyos/table.d.ts +345 -22
- package/dist/types/zeyos/view.d.ts +26 -2
- package/dist/zx-zeyos.esm.js +6178 -1008
- package/dist/zx-zeyos.esm.min.js +53 -5
- package/dist/zx.css +2861 -827
- package/dist/zx.esm.js +8909 -1480
- package/dist/zx.esm.min.js +55 -7
- package/dist/zx.global.js +8911 -1482
- package/dist/zx.global.min.js +55 -7
- package/dist/zx.min.css +1 -1
- package/docs/api.json +1079 -117
- package/docs/llms.md +956 -154
- package/docs/llms.txt +9 -5
- package/package.json +1 -1
- package/src/components/account-menu/account-menu.css +35 -10
- package/src/components/account-menu/account-menu.js +12 -0
- package/src/components/activity-item/activity-item.css +1 -1
- package/src/components/activity-item/activity-item.js +3 -0
- package/src/components/activity-list/activity-list.css +38 -3
- package/src/components/activity-list/activity-list.js +61 -1
- package/src/components/app-icon/app-icon.css +14 -3
- package/src/components/app-icon/app-icon.js +11 -1
- package/src/components/app-rail/app-rail.css +59 -79
- package/src/components/app-sidebar/app-sidebar.css +304 -168
- package/src/components/app-sidebar/app-sidebar.js +667 -46
- package/src/components/aurora/aurora.css +5 -3
- package/src/components/avatar/avatar.css +32 -7
- package/src/components/avatar/avatar.js +72 -8
- package/src/components/badge/badge.css +54 -2
- package/src/components/badge/badge.js +29 -2
- package/src/components/breadcrumb/breadcrumb.css +1 -1
- package/src/components/button/button.css +67 -9
- package/src/components/button/button.js +38 -0
- package/src/components/calendar/calendar.css +47 -47
- package/src/components/calendar/calendar.js +26 -52
- package/src/components/card/card.css +4 -4
- package/src/components/card-view/card-view.css +14 -12
- package/src/components/card-view/card-view.js +13 -9
- package/src/components/card-view/record-card.js +14 -3
- package/src/components/chart/chart.css +4 -1
- package/src/components/chart/chart.js +12 -0
- package/src/components/check-button/check-button.css +10 -2
- package/src/components/checklist/checklist.css +9 -2
- package/src/components/checklist/checklist.js +11 -5
- package/src/components/checklist/field-adapter.js +3 -1
- package/src/components/code-editor/code-editor.css +7 -6
- package/src/components/code-editor/code-editor.js +19 -1
- package/src/components/code-editor/field-adapter.js +9 -2
- package/src/components/context-menu/context-menu.css +9 -4
- package/src/components/context-menu/context-menu.js +8 -2
- package/src/components/copy/copy-button.css +6 -0
- package/src/components/copy/copy-input.css +9 -4
- package/src/components/data-filter/data-filter.css +5 -4
- package/src/components/data-filter/data-filter.js +2 -2
- package/src/components/date-picker/date-picker.css +6 -6
- package/src/components/date-picker/date-range-picker.css +3 -3
- package/src/components/date-picker/time-picker.css +3 -3
- package/src/components/datebox/date-range-box.css +79 -2
- package/src/components/datebox/date-range-box.js +13 -2
- package/src/components/datebox/datebox.css +79 -2
- package/src/components/datebox/datebox.js +13 -2
- package/src/components/dialog/dialog.css +26 -0
- package/src/components/dialog/dialog.js +99 -17
- package/src/components/dock/dock.css +5 -4
- package/src/components/dropdown/dropdown.css +1 -1
- package/src/components/dropdown/dropdown.js +159 -72
- package/src/components/empty-state/empty-state.css +2 -2
- package/src/components/entity-ref/entity-ref.css +1 -1
- package/src/components/entity-ref/entity-ref.js +1 -1
- package/src/components/field/field.css +28 -9
- package/src/components/field/field.js +101 -29
- package/src/components/field-upload/field-upload.css +1 -1
- package/src/components/fieldset/external-adapter.js +363 -0
- package/src/components/fieldset/fieldset.css +5 -3
- package/src/components/fieldset/fieldset.js +38 -64
- package/src/components/file-item/file-item.js +144 -10
- package/src/components/file-list/file-list.js +144 -21
- package/src/components/filter/filter.css +86 -2
- package/src/components/filter/filter.js +307 -11
- package/src/components/filter-panel/filter-panel.css +1 -1
- package/src/components/filter-panel/filter-panel.js +149 -32
- package/src/components/finder/finder.css +16 -14
- package/src/components/form/form.css +5 -4
- package/src/components/form/form.js +11 -2
- package/src/components/grid/grid.css +1 -1
- package/src/components/groupbox/groupbox.css +1 -1
- package/src/components/kanban-view/kanban-card.js +11 -3
- package/src/components/kanban-view/kanban-view.css +42 -18
- package/src/components/kanban-view/kanban-view.js +9 -6
- package/src/components/launcher/launcher.css +108 -26
- package/src/components/launcher/launcher.js +530 -42
- package/src/components/list-toolbar/list-toolbar.css +13 -8
- package/src/components/list-toolbar/list-toolbar.js +40 -14
- package/src/components/master-panel/master-panel.css +2 -0
- package/src/components/master-panel/master-panel.js +0 -1
- package/src/components/menu-button/menu-button.css +72 -4
- package/src/components/menu-button/menu-button.js +160 -18
- package/src/components/message/message.css +33 -3
- package/src/components/message/message.js +264 -22
- package/src/components/modal/modal.css +1 -1
- package/src/components/modal/modal.js +83 -15
- package/src/components/module-bar/module-bar.css +486 -0
- package/src/components/module-bar/module-bar.js +1074 -0
- package/src/components/multi-value-editor/multi-value-editor.css +1 -1
- package/src/components/navigation-bar/navigation-bar.css +18 -9
- package/src/components/number-field/field-adapter.js +3 -1
- package/src/components/number-field/number-field.css +70 -0
- package/src/components/number-field/number-field.js +2 -0
- package/src/components/pagination/pagination.css +13 -11
- package/src/components/pagination/pagination.js +20 -4
- package/src/components/panel/panel.css +4 -3
- package/src/components/questionnaire/questionnaire.css +4 -4
- package/src/components/rating/field-adapter.js +2 -1
- package/src/components/rating/rating.css +8 -1
- package/src/components/search/search.css +4 -1
- package/src/components/select/color.js +22 -0
- package/src/components/select/field-adapter.js +2 -0
- package/src/components/select/glyphs.js +165 -0
- package/src/components/select/select.css +138 -45
- package/src/components/select/select.js +92 -47
- package/src/components/sheet/sheet.css +12 -9
- package/src/components/sheet/sheet.js +74 -32
- package/src/components/slider/field-adapter.js +6 -1
- package/src/components/slider/slider.css +18 -4
- package/src/components/slider/slider.js +1 -1
- package/src/components/sort-control/sort-control.css +4 -1
- package/src/components/split-view/split-view.css +1 -1
- package/src/components/stat-tile/stat-tile.css +42 -47
- package/src/components/stat-tile/stat-tile.js +17 -6
- package/src/components/stepper/stepper.css +1 -1
- package/src/components/tabbox/tabbox.css +41 -32
- package/src/components/tabbox/tabbox.js +5 -1
- package/src/components/table/table.css +187 -37
- package/src/components/table/table.js +718 -73
- package/src/components/tag-picker/field-adapter.js +2 -1
- package/src/components/tag-picker/tag-picker.css +83 -5
- package/src/components/tag-picker/tag-picker.js +25 -8
- package/src/components/timebox/timebox.css +22 -2
- package/src/components/toggle/toggle.css +12 -1
- package/src/components/toolbar/toolbar.js +2 -2
- package/src/components/tooltip/tooltip.css +4 -0
- package/src/components/tooltip/tooltip.js +44 -19
- package/src/components/tree/tree.css +26 -17
- package/src/components/tree/tree.js +31 -0
- package/src/components/value-list/value-list.css +3 -3
- package/src/components/view/record-view.css +1 -1
- package/src/components/view/record-view.js +178 -18
- package/src/components/view-settings/view-settings.css +285 -0
- package/src/components/view-settings/view-settings.js +1830 -0
- package/src/components/workspace-switcher/workspace-switcher.css +188 -0
- package/src/components/workspace-switcher/workspace-switcher.js +463 -0
- package/src/core/breakpoint.js +1 -1
- package/src/core/component.js +88 -17
- package/src/core/fontawesome.js +5 -1
- package/src/core/i18n.js +47 -4
- package/src/core/icons.js +204 -61
- package/src/core/keyboard.js +206 -11
- package/src/core/messages.js +427 -0
- package/src/core/motion.js +147 -0
- package/src/core/overlay-host.js +77 -3
- package/src/core/overlay-stack.js +487 -0
- package/src/core/position.js +22 -3
- package/src/index.js +10 -5
- package/src/internal/app-rail.js +135 -55
- package/src/internal/menu-items.js +61 -3
- package/src/zeyos/connect.js +6 -2
- package/src/zeyos/form.js +69 -27
- package/src/zeyos/index.js +4 -1
- package/src/zeyos/launcher.js +8 -3
- package/src/zeyos/module-bar.js +181 -0
- package/src/zeyos/query.js +197 -13
- package/src/zeyos/schema.js +144 -27
- package/src/zeyos/table.js +287 -84
- package/src/zeyos/view.js +49 -93
- package/styles/base.css +72 -1
- package/styles/icons.css +11 -1
- package/styles/print.css +1 -1
- package/styles/tokens/dark.css +117 -37
- package/styles/tokens/global.css +121 -27
- package/styles/tokens/semantic.css +140 -43
- package/styles/tokens/themes.css +7 -6
- package/styles/zx.css +3 -1
- package/styles/tokens/density.css +0 -9
package/src/zeyos/table.js
CHANGED
|
@@ -4,19 +4,60 @@ import { buildListQuery } from './query.js';
|
|
|
4
4
|
import { fieldToZxColumn, resolveFields } from './schema.js';
|
|
5
5
|
import { normalizeZeyosListResult } from './select.js';
|
|
6
6
|
|
|
7
|
+
/** @typedef {import('../components/table/table.js').TableOptions} TableOptions */
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* @typedef {Object} ZeyosTableOptions
|
|
9
11
|
* @property {string[]} [fields] Curated ordered field allow-list.
|
|
10
12
|
* @property {Record<string, string>} [labels] Per-field label overrides.
|
|
13
|
+
* @property {Record<string, string>} [formats] Per-field semantic format overrides, such as
|
|
14
|
+
* `{ duedate: 'date' }` for a Unix-second field that holds a calendar day.
|
|
15
|
+
* @property {string} [timeZone] IANA time zone of the ZeyOS instance. Date-only columns show the
|
|
16
|
+
* calendar day their stored midnight names in this zone; defaults to the runtime's own zone.
|
|
11
17
|
* @property {string|((row: Record<string, unknown>) => unknown)} [rowId='ID'] Row id accessor.
|
|
12
|
-
* @property {number} [pageSize=50] Server page size.
|
|
18
|
+
* @property {number} [pageSize=50] Server page size, a positive integer. `load({pageSize})` changes
|
|
19
|
+
* it later — what a `Pagination` page-size select does.
|
|
13
20
|
* @property {{id: string, dir: 'asc'|'desc'}|null} [sort=null] Initial server sort.
|
|
14
21
|
* @property {Record<string, unknown>} [filters] Initial ZeyOS predicates.
|
|
15
22
|
* @property {string} [search=''] Initial full-text query.
|
|
16
23
|
* @property {false|'single'|'multi'} [selectable=false] Row selection mode.
|
|
17
24
|
* @property {number|string|null} [height=null] Table scroll-region height.
|
|
18
25
|
* @property {(row: Record<string, unknown>, event: CustomEvent<Record<string, unknown>>) => void} [onRowClick] Row activation callback.
|
|
26
|
+
* @property {TableOptions['variant']} [variant='default'] Table frame treatment; `'plain'` for a
|
|
27
|
+
* table inside a pane or card that already draws the box.
|
|
28
|
+
* @property {boolean} [zebra=true] Whether alternate rows use the zebra background.
|
|
29
|
+
* @property {boolean} [stickyHeader=true] Whether column headers stick while scrolling.
|
|
30
|
+
* @property {TableOptions['responsive']} [responsive=false] Width below which rows stack.
|
|
31
|
+
* @property {TableOptions['growing']} [growing=false] Rows rendered at first from a loaded page.
|
|
32
|
+
* @property {TableOptions['emptyText']} [emptyText=null] What the table shows with no rows.
|
|
33
|
+
* @property {TableOptions['rowClass']} [rowClass=null] Additional row class callback.
|
|
34
|
+
* @property {TableOptions['rowKind']} [rowKind=null] Row property or callback naming each row's kind.
|
|
35
|
+
* @property {TableOptions['rowKinds']} [rowKinds=null] Presentation per row kind.
|
|
36
|
+
* @property {boolean} [columnVisibility=false] Whether a column visibility chooser is rendered.
|
|
37
|
+
* @property {boolean} [columnReorder=false] Whether the column chooser exposes reorder controls.
|
|
38
|
+
* @property {string[]} [hiddenColumns=[]] Initially hidden column ids.
|
|
39
|
+
* @property {TableOptions['onrowclick']} [onrowclick] Table `rowclick` listener, beside `onRowClick`.
|
|
40
|
+
* @property {TableOptions['onrowdblclick']} [onrowdblclick] Table `rowdblclick` listener.
|
|
41
|
+
* @property {TableOptions['onsort']} [onsort] Table `sort` listener; the binding reloads either way.
|
|
42
|
+
* @property {TableOptions['onselectionchange']} [onselectionchange] Table `selectionchange` listener.
|
|
43
|
+
* @property {TableOptions['ondatachange']} [ondatachange] Table `datachange` listener.
|
|
44
|
+
* @property {TableOptions['onstackedchange']} [onstackedchange] Table `stackedchange` listener.
|
|
45
|
+
* @property {TableOptions['ongrow']} [ongrow] Table `grow` listener.
|
|
46
|
+
* @property {TableOptions['oncolumnvisibilitychange']} [oncolumnvisibilitychange] Table `columnvisibilitychange` listener.
|
|
47
|
+
* @property {TableOptions['oncolumnorderchange']} [oncolumnorderchange] Table `columnorderchange` listener.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Table options a binding passes through unchanged: presentation and listeners. The binding owns
|
|
52
|
+
* `columns`, `data`, and `sortMode`; editing, row reordering, and `hierarchy` are left out because
|
|
53
|
+
* they rearrange or change rows the server pages and never persists.
|
|
19
54
|
*/
|
|
55
|
+
const TABLE_OPTION_KEYS = [
|
|
56
|
+
'variant', 'zebra', 'stickyHeader', 'responsive', 'growing', 'emptyText', 'rowClass', 'rowKind',
|
|
57
|
+
'rowKinds', 'columnVisibility', 'columnReorder', 'hiddenColumns', 'onrowdblclick', 'onsort',
|
|
58
|
+
'onselectionchange', 'ondatachange', 'onstackedchange', 'ongrow', 'oncolumnvisibilitychange',
|
|
59
|
+
'oncolumnorderchange'
|
|
60
|
+
];
|
|
20
61
|
|
|
21
62
|
/**
|
|
22
63
|
* @typedef {Object} ZeyosTableConfig
|
|
@@ -30,7 +71,12 @@ import { normalizeZeyosListResult } from './select.js';
|
|
|
30
71
|
* @typedef {Object} ZeyosTableLoadOptions
|
|
31
72
|
* @property {string} [search] Replacement full-text query.
|
|
32
73
|
* @property {Record<string, unknown>} [filters] Replacement ZeyOS predicates.
|
|
33
|
-
* @property {number} [page=0] Zero-based page
|
|
74
|
+
* @property {number} [page=0] Zero-based page, a non-negative integer. Pages above zero append
|
|
75
|
+
* rows unless `replace` is set.
|
|
76
|
+
* @property {boolean} [replace] Replace the rows with this page instead of appending it — what a
|
|
77
|
+
* numbered pager needs. Page zero always replaces.
|
|
78
|
+
* @property {number} [pageSize] New page size for this and every later load; the request offset is
|
|
79
|
+
* `page * pageSize`.
|
|
34
80
|
*/
|
|
35
81
|
|
|
36
82
|
/**
|
|
@@ -51,15 +97,65 @@ import { normalizeZeyosListResult } from './select.js';
|
|
|
51
97
|
* @property {() => Promise<ZeyosTableLoadResult>} loadMore Appends the next page when available.
|
|
52
98
|
* @property {number|null} count Current server count.
|
|
53
99
|
* @property {number} page Current zero-based page.
|
|
100
|
+
* @property {number} pageSize Current server page size.
|
|
54
101
|
* @property {boolean} hasMore Whether another page is available.
|
|
55
|
-
* @property {() => void} destroy Destroys the generated table.
|
|
102
|
+
* @property {() => void} destroy Destroys the generated table. A request still in flight is
|
|
103
|
+
* ignored when it resolves, and later loads reject.
|
|
104
|
+
*/
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The surface a server pager renders into: a Zx `Table`, or any RecordView.
|
|
108
|
+
* @typedef {Object} ServerPagerTarget
|
|
109
|
+
* @property {(rows: Record<string, unknown>[]) => unknown} setData Replaces the rows.
|
|
110
|
+
* @property {(rows: Record<string, unknown>[]) => unknown} addData Appends rows.
|
|
111
|
+
* @property {(loading: boolean) => unknown} setLoading Shows or hides the loading state.
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @typedef {Object} ServerPagerOptions
|
|
116
|
+
* @property {Record<string, any>} client Injected ZeyOS client (for result normalization).
|
|
117
|
+
* @property {{list: (resource: string, query: Record<string, unknown>) => Promise<unknown>}} facade
|
|
118
|
+
* Connection facade from `connect(client)`.
|
|
119
|
+
* @property {string} resource Resource name.
|
|
120
|
+
* @property {Record<string, string>} projection API projection.
|
|
121
|
+
* @property {ServerPagerTarget} target Component the rows go to.
|
|
122
|
+
* @property {unknown} [pageSize=50] Initial page size; must be a positive integer.
|
|
123
|
+
* @property {{id: string, dir: 'asc'|'desc'}|null} [sort=null] Initial sort.
|
|
124
|
+
* @property {unknown} [filters={}] Initial predicates; must be a plain object.
|
|
125
|
+
* @property {unknown} [search=''] Initial full-text query.
|
|
126
|
+
* @property {string[]} [searchFields] UI search-field metadata passed to `buildListQuery`.
|
|
127
|
+
* @property {string} [label='ZeyOS binding'] Name used in error messages.
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @typedef {Object} ServerPager
|
|
132
|
+
* @property {(options?: ZeyosTableLoadOptions) => Promise<ZeyosTableLoadResult>} load Loads a page.
|
|
133
|
+
* @property {(options?: ZeyosTableLoadOptions) => {page: number, pageSize: number, filters: Record<string, unknown>, search: string}} check
|
|
134
|
+
* Validates load options without applying them; throws what `load()` would reject with.
|
|
135
|
+
* @property {() => Promise<ZeyosTableLoadResult>} loadMore Appends the next page when available,
|
|
136
|
+
* else resolves without a request.
|
|
137
|
+
* @property {(sort: {id: string, dir: 'asc'|'desc'}|null) => void} setSort Replaces the sort used
|
|
138
|
+
* by the next load.
|
|
139
|
+
* @property {(state: {filters?: unknown, search?: unknown}) => void} setQuery Replaces filters and
|
|
140
|
+
* search without loading.
|
|
141
|
+
* @property {() => void} reset Forgets the loaded page, count, and `hasMore`.
|
|
142
|
+
* @property {() => void} destroy Stops rendering: in-flight responses are ignored, later loads reject.
|
|
143
|
+
* @property {boolean} destroyed Whether `destroy()` ran.
|
|
144
|
+
* @property {number|null} count Current server count.
|
|
145
|
+
* @property {number} page Current zero-based page.
|
|
146
|
+
* @property {number} pageSize Current page size.
|
|
147
|
+
* @property {boolean} hasMore Whether another page is available.
|
|
148
|
+
* @property {{id: string, dir: 'asc'|'desc'}|null} sort Current sort.
|
|
149
|
+
* @property {Record<string, unknown>} filters Copy of the current predicates.
|
|
150
|
+
* @property {string} search Current full-text query.
|
|
56
151
|
*/
|
|
57
152
|
|
|
58
153
|
/**
|
|
59
154
|
* Builds the DOM-free schema-derived columns and Table options used by {@link zeyosTable}. Entity
|
|
60
155
|
* projections include an `<field>_label` alias from a real label field on the referenced resource.
|
|
61
156
|
* Amount fields use each row's real `currency` value for display while retaining the WP14 typed
|
|
62
|
-
* column strategy and right alignment.
|
|
157
|
+
* column strategy and right alignment. Enum columns show labels, date-only columns calendar days in
|
|
158
|
+
* `timeZone`, and Table presentation options and listeners pass through to `tableOptions`.
|
|
63
159
|
* @param {Record<string, any>} client Injected ZeyOS client instance.
|
|
64
160
|
* @param {string} resource Resource name such as `transactions`.
|
|
65
161
|
* @param {ZeyosTableOptions} [opts={}] Schema, table, and initial query options.
|
|
@@ -68,7 +164,9 @@ import { normalizeZeyosListResult } from './select.js';
|
|
|
68
164
|
export function buildZeyosTableConfig(client, resource, opts = {}) {
|
|
69
165
|
const fieldMeta = resolveFields(client, resource, {
|
|
70
166
|
fields: opts.fields,
|
|
71
|
-
labels: opts.labels
|
|
167
|
+
labels: opts.labels,
|
|
168
|
+
formats: opts.formats,
|
|
169
|
+
timeZone: opts.timeZone
|
|
72
170
|
});
|
|
73
171
|
const projection = {};
|
|
74
172
|
const columns = fieldMeta.map((sourceMeta) => {
|
|
@@ -94,11 +192,16 @@ export function buildZeyosTableConfig(client, resource, opts = {}) {
|
|
|
94
192
|
if (typeof rowId === 'string' && !Object.prototype.hasOwnProperty.call(projection, rowId)) {
|
|
95
193
|
projection[rowId] = rowId;
|
|
96
194
|
}
|
|
195
|
+
const passed = {};
|
|
196
|
+
for (const key of TABLE_OPTION_KEYS) {
|
|
197
|
+
if (opts[key] !== undefined) passed[key] = opts[key];
|
|
198
|
+
}
|
|
97
199
|
return {
|
|
98
200
|
fieldMeta,
|
|
99
201
|
columns,
|
|
100
202
|
projection,
|
|
101
203
|
tableOptions: {
|
|
204
|
+
...passed,
|
|
102
205
|
columns,
|
|
103
206
|
data: [],
|
|
104
207
|
rowId,
|
|
@@ -106,17 +209,23 @@ export function buildZeyosTableConfig(client, resource, opts = {}) {
|
|
|
106
209
|
sortMode: 'server',
|
|
107
210
|
selectable: opts.selectable ?? false,
|
|
108
211
|
height: opts.height ?? null,
|
|
109
|
-
onrowclick:
|
|
110
|
-
? (event) => opts.onRowClick(event.detail.row, event)
|
|
111
|
-
: undefined
|
|
212
|
+
onrowclick: rowClickListener(opts)
|
|
112
213
|
}
|
|
113
214
|
};
|
|
114
215
|
}
|
|
115
216
|
|
|
116
217
|
/**
|
|
117
|
-
* Generates a server-backed Zx Table. Sorting automatically reloads page zero.
|
|
118
|
-
* replaces data, higher pages append it
|
|
119
|
-
*
|
|
218
|
+
* Generates a server-backed Zx Table. Sorting automatically reloads page zero. Paging is
|
|
219
|
+
* zero-based: `load({page: 0})` replaces data, higher pages append it — load-more paging, which
|
|
220
|
+
* `loadMore()` drives from `limit`/`offset` plus the returned count — and `replace: true` replaces
|
|
221
|
+
* at any page, which is what a numbered `Pagination` needs. `Pagination` is one-based, so its
|
|
222
|
+
* `change` maps as `load({page: detail.page - 1, pageSize: detail.pageSize, replace: true})`, the
|
|
223
|
+
* request offset being `(page - 1) * pageSize`; feed `result.count` back with `setTotal()`.
|
|
224
|
+
* DataFilter callers can map state with `dataFilterStateToFilters` before `setFilters()`.
|
|
225
|
+
* Paging is shared with `zeyosView()` through {@link createServerPager}: the same validation, and
|
|
226
|
+
* a response that resolves after `destroy()` never touches the destroyed table.
|
|
227
|
+
* Table presentation options (`variant`, `zebra`, `stickyHeader`, `responsive`, `emptyText`, …) and
|
|
228
|
+
* listeners are passed through to the generated Table.
|
|
120
229
|
* @param {Record<string, any>} client Injected ZeyOS client instance.
|
|
121
230
|
* @param {string} resource Resource name such as `transactions`.
|
|
122
231
|
* @param {ZeyosTableOptions} [opts={}] Schema, table, and initial query options.
|
|
@@ -126,82 +235,171 @@ export function zeyosTable(client, resource, opts = {}) {
|
|
|
126
235
|
const config = buildZeyosTableConfig(client, resource, opts);
|
|
127
236
|
const facade = connect(client);
|
|
128
237
|
const table = new Table(null, config.tableOptions);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
238
|
+
let pager;
|
|
239
|
+
try {
|
|
240
|
+
pager = createServerPager({
|
|
241
|
+
client, facade, resource, projection: config.projection, target: table,
|
|
242
|
+
pageSize: opts.pageSize, sort: opts.sort ?? null, filters: opts.filters ?? {}, search: opts.search,
|
|
243
|
+
label: 'ZeyOS table'
|
|
244
|
+
});
|
|
245
|
+
} catch (error) {
|
|
246
|
+
table.destroy();
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const binding = {
|
|
251
|
+
table,
|
|
252
|
+
load: pager.load,
|
|
253
|
+
setSearch: (search) => pager.load({ search, page: 0 }),
|
|
254
|
+
setFilters: (filters) => pager.load({ filters, page: 0 }),
|
|
255
|
+
loadMore: pager.loadMore,
|
|
256
|
+
get count() { return pager.count; },
|
|
257
|
+
get page() { return pager.page; },
|
|
258
|
+
get pageSize() { return pager.pageSize; },
|
|
259
|
+
get hasMore() { return pager.hasMore; },
|
|
260
|
+
destroy: () => {
|
|
261
|
+
if (pager.destroyed) return;
|
|
262
|
+
pager.destroy();
|
|
263
|
+
table.destroy();
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
table.on('sort', (event) => {
|
|
268
|
+
if (pager.destroyed) return;
|
|
269
|
+
const id = event.detail?.id;
|
|
270
|
+
pager.setSort(id == null ? null : { id: String(id), dir: event.detail.dir === 'desc' ? 'desc' : 'asc' });
|
|
271
|
+
void pager.load({ page: 0 }).catch(() => { /* The connection facade already reported the failure. */ });
|
|
272
|
+
});
|
|
273
|
+
return binding;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* The one server-paging loader behind {@link zeyosTable} and `zeyosView`, so the two bindings
|
|
278
|
+
* cannot drift apart. It builds the list request (`limit = pageSize`, `offset = page * pageSize`,
|
|
279
|
+
* always with `count`), renders the response into its target — page zero or `replace` replaces
|
|
280
|
+
* the rows, a higher page appends them — and keeps `count`, `page`, and `hasMore`.
|
|
281
|
+
*
|
|
282
|
+
* Only the newest request may render: an older one that resolves late is returned to its caller
|
|
283
|
+
* but never touches the target. After `destroy()` nothing renders at all — a response that arrives
|
|
284
|
+
* afterwards is ignored, the loading state is left alone — and further loads reject.
|
|
285
|
+
*
|
|
286
|
+
* Validation is strict and the same for both bindings: a page must be a non-negative integer and a
|
|
287
|
+
* page size a positive integer (`RangeError`), filters a plain object (`TypeError`). Every argument
|
|
288
|
+
* of a call is checked before any of it is applied, so a rejected call changes nothing.
|
|
289
|
+
* @param {ServerPagerOptions} options Pager configuration.
|
|
290
|
+
* @returns {ServerPager}
|
|
291
|
+
*/
|
|
292
|
+
export function createServerPager(options) {
|
|
293
|
+
const { client, facade, resource, projection, target, searchFields } = options;
|
|
294
|
+
const label = String(options.label ?? 'ZeyOS binding');
|
|
295
|
+
let pageSize = normalizePageSize(options.pageSize, label);
|
|
296
|
+
let sort = options.sort ?? null;
|
|
297
|
+
let filters = copyFilters(options.filters ?? {}, label);
|
|
298
|
+
let search = String(options.search ?? '');
|
|
299
|
+
let page = 0;
|
|
300
|
+
let count = null;
|
|
135
301
|
let more = false;
|
|
136
|
-
let
|
|
302
|
+
let sequence = 0;
|
|
303
|
+
let destroyed = false;
|
|
137
304
|
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
305
|
+
const query = (forPage) => buildListQuery({
|
|
306
|
+
fields: projection, sort, filters, search, searchFields,
|
|
307
|
+
limit: pageSize, offset: forPage * pageSize, count: true
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Validates a load request without applying any of it.
|
|
312
|
+
* @param {ZeyosTableLoadOptions} [request={}] Load options.
|
|
313
|
+
* @returns {{page: number, pageSize: number, filters: Record<string, unknown>, search: string}}
|
|
314
|
+
*/
|
|
315
|
+
const check = (request = {}) => {
|
|
316
|
+
const source = request && typeof request === 'object' ? request : {};
|
|
317
|
+
const has = (key) => Object.prototype.hasOwnProperty.call(source, key);
|
|
318
|
+
return {
|
|
319
|
+
page: normalizePage(source.page ?? 0, label),
|
|
320
|
+
pageSize: has('pageSize') && source.pageSize !== undefined
|
|
321
|
+
? normalizePageSize(source.pageSize, label) : pageSize,
|
|
322
|
+
filters: has('filters') ? copyFilters(source.filters ?? {}, label) : filters,
|
|
323
|
+
search: has('search') ? String(source.search ?? '') : search
|
|
324
|
+
};
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
/** @param {ZeyosTableLoadOptions} [request={}] @returns {Promise<ZeyosTableLoadResult>} */
|
|
328
|
+
const load = async (request = {}) => {
|
|
329
|
+
if (destroyed) throw new Error(`${label} binding has been destroyed`);
|
|
330
|
+
// Every argument is validated before any state changes, so a call that rejects leaves the
|
|
331
|
+
// binding exactly as it was: no page size or search half-applied ahead of a bad filter.
|
|
332
|
+
const next = check(request);
|
|
333
|
+
const nextPage = next.page;
|
|
334
|
+
pageSize = next.pageSize;
|
|
335
|
+
filters = next.filters;
|
|
336
|
+
search = next.search;
|
|
337
|
+
const replace = nextPage === 0 || request.replace === true;
|
|
338
|
+
const sent = query(nextPage);
|
|
339
|
+
const mine = ++sequence;
|
|
340
|
+
target.setLoading(true);
|
|
153
341
|
try {
|
|
154
|
-
const normalized = normalizeZeyosListResult(client, await facade.list(resource,
|
|
155
|
-
const
|
|
156
|
-
const hasMore =
|
|
342
|
+
const normalized = normalizeZeyosListResult(client, await facade.list(resource, sent));
|
|
343
|
+
const total = normalized.count ?? null;
|
|
344
|
+
const hasMore = total == null
|
|
157
345
|
? normalized.data.length === pageSize
|
|
158
|
-
:
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
|
|
346
|
+
: nextPage * pageSize + normalized.data.length < total;
|
|
347
|
+
if (!destroyed && mine === sequence) {
|
|
348
|
+
page = nextPage;
|
|
349
|
+
count = total;
|
|
162
350
|
more = hasMore;
|
|
163
|
-
if (
|
|
164
|
-
else
|
|
351
|
+
if (replace) target.setData(normalized.data);
|
|
352
|
+
else target.addData(normalized.data);
|
|
165
353
|
}
|
|
166
|
-
return { data: normalized.data, count, page, hasMore, query };
|
|
354
|
+
return { data: normalized.data, count: total, page: nextPage, hasMore, query: sent };
|
|
167
355
|
} finally {
|
|
168
|
-
if (
|
|
356
|
+
if (!destroyed && mine === sequence) target.setLoading(false);
|
|
169
357
|
}
|
|
170
358
|
};
|
|
171
359
|
|
|
172
|
-
|
|
173
|
-
table,
|
|
360
|
+
return {
|
|
174
361
|
load,
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
data: [],
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
get
|
|
193
|
-
get
|
|
362
|
+
check,
|
|
363
|
+
loadMore: () => more ? load({ page: page + 1 }) : (destroyed
|
|
364
|
+
? Promise.reject(new Error(`${label} binding has been destroyed`))
|
|
365
|
+
: Promise.resolve({ data: [], count, page, hasMore: false, query: query(page) })),
|
|
366
|
+
setSort: (next) => { sort = next ?? null; },
|
|
367
|
+
setQuery: (state = {}) => {
|
|
368
|
+
const nextFilters = Object.prototype.hasOwnProperty.call(state, 'filters')
|
|
369
|
+
? copyFilters(state.filters ?? {}, label) : filters;
|
|
370
|
+
if (Object.prototype.hasOwnProperty.call(state, 'search')) search = String(state.search ?? '');
|
|
371
|
+
filters = nextFilters;
|
|
372
|
+
},
|
|
373
|
+
reset: () => { page = 0; count = null; more = false; },
|
|
374
|
+
destroy: () => {
|
|
375
|
+
if (destroyed) return;
|
|
376
|
+
destroyed = true;
|
|
377
|
+
sequence += 1;
|
|
378
|
+
},
|
|
379
|
+
get destroyed() { return destroyed; },
|
|
380
|
+
get count() { return count; },
|
|
381
|
+
get page() { return page; },
|
|
382
|
+
get pageSize() { return pageSize; },
|
|
194
383
|
get hasMore() { return more; },
|
|
195
|
-
|
|
384
|
+
get sort() { return sort; },
|
|
385
|
+
get filters() { return { ...filters }; },
|
|
386
|
+
get search() { return search; }
|
|
196
387
|
};
|
|
388
|
+
}
|
|
197
389
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
390
|
+
/**
|
|
391
|
+
* Combines the Table-style `onrowclick` listener with the binding's row-first `onRowClick`.
|
|
392
|
+
* @param {ZeyosTableOptions} opts
|
|
393
|
+
* @returns {((event: CustomEvent<Record<string, any>>) => void)|undefined}
|
|
394
|
+
*/
|
|
395
|
+
function rowClickListener(opts) {
|
|
396
|
+
const listener = typeof opts.onrowclick === 'function' ? opts.onrowclick : null;
|
|
397
|
+
const callback = typeof opts.onRowClick === 'function' ? opts.onRowClick : null;
|
|
398
|
+
if (!listener && !callback) return undefined;
|
|
399
|
+
return (event) => {
|
|
400
|
+
listener?.(event);
|
|
401
|
+
callback?.(event.detail.row, event);
|
|
402
|
+
};
|
|
205
403
|
}
|
|
206
404
|
|
|
207
405
|
/** @param {Record<string, any>} meta @returns {string|null} */
|
|
@@ -252,23 +450,28 @@ function schemaHasField(client, resource, id) {
|
|
|
252
450
|
return Boolean(fields && typeof fields === 'object' && Object.prototype.hasOwnProperty.call(fields, id));
|
|
253
451
|
}
|
|
254
452
|
|
|
255
|
-
/**
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
453
|
+
/** Default server page size shared by `zeyosTable()` and `zeyosView()`. */
|
|
454
|
+
export const DEFAULT_SERVER_PAGE_SIZE = 50;
|
|
455
|
+
|
|
456
|
+
/** @param {unknown} value @param {string} [label='ZeyOS table'] @returns {number} */
|
|
457
|
+
function normalizePageSize(value, label = 'ZeyOS table') {
|
|
458
|
+
if (value === undefined || value === null) return DEFAULT_SERVER_PAGE_SIZE;
|
|
459
|
+
const number = Number(value);
|
|
460
|
+
if (!Number.isInteger(number) || number < 1) throw new RangeError(`${label} page size must be a positive integer`);
|
|
461
|
+
return number;
|
|
259
462
|
}
|
|
260
463
|
|
|
261
|
-
/** @param {unknown} value @returns {number} */
|
|
262
|
-
function normalizePage(value) {
|
|
464
|
+
/** @param {unknown} value @param {string} [label='ZeyOS table'] @returns {number} */
|
|
465
|
+
function normalizePage(value, label = 'ZeyOS table') {
|
|
263
466
|
const number = Number(value);
|
|
264
|
-
if (!Number.isInteger(number) || number < 0) throw new RangeError(
|
|
467
|
+
if (!Number.isInteger(number) || number < 0) throw new RangeError(`${label} page must be a non-negative integer`);
|
|
265
468
|
return number;
|
|
266
469
|
}
|
|
267
470
|
|
|
268
|
-
/** @param {
|
|
269
|
-
function copyFilters(filters) {
|
|
471
|
+
/** @param {unknown} filters @param {string} [label='ZeyOS table'] @returns {Record<string, unknown>} */
|
|
472
|
+
function copyFilters(filters, label = 'ZeyOS table') {
|
|
270
473
|
if (!filters || typeof filters !== 'object' || Array.isArray(filters)) {
|
|
271
|
-
throw new TypeError(
|
|
474
|
+
throw new TypeError(`${label} filters must be an object`);
|
|
272
475
|
}
|
|
273
|
-
return {
|
|
476
|
+
return { .../** @type {Record<string, unknown>} */ (filters) };
|
|
274
477
|
}
|