@zeyos/zx 4.1.1 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +81 -0
- package/DESIGN-SYSTEM.md +33 -3
- package/README.md +22 -5
- package/dist/types/components/app-icon/app-icon.d.ts +8 -1
- package/dist/types/components/aurora/aurora.d.ts +72 -0
- package/dist/types/components/calendar/calendar-model.d.ts +202 -0
- package/dist/types/components/calendar/calendar.d.ts +517 -0
- package/dist/types/components/card-view/card-view.d.ts +303 -0
- package/dist/types/components/card-view/record-card.d.ts +242 -0
- package/dist/types/components/dialog/dialog.d.ts +6 -1
- package/dist/types/components/dropdown/dropdown.d.ts +5 -0
- package/dist/types/components/kanban-view/kanban-view.d.ts +572 -0
- package/dist/types/components/launcher/launcher.d.ts +5 -0
- package/dist/types/components/message/message.d.ts +9 -1
- package/dist/types/components/modal/modal.d.ts +44 -7
- package/dist/types/components/sheet/sheet.d.ts +22 -12
- package/dist/types/components/table/table.d.ts +120 -31
- package/dist/types/components/table-view/table-view.d.ts +192 -0
- package/dist/types/components/view/record-view.d.ts +464 -0
- package/dist/types/core/overlay-host.d.ts +12 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/zeyos/calendar.d.ts +91 -0
- package/dist/types/zeyos/icons.d.ts +18 -2
- package/dist/types/zeyos/index.d.ts +4 -0
- package/dist/types/zeyos/legacy-saved-views.d.ts +66 -0
- package/dist/types/zeyos/saved-views.d.ts +331 -0
- package/dist/types/zeyos/view.d.ts +305 -0
- package/dist/zx-zeyos.esm.js +4742 -1470
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +2536 -471
- package/dist/zx.esm.js +5193 -223
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +5193 -223
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +1729 -182
- package/docs/llms.md +236 -32
- package/docs/llms.txt +16 -6
- package/package.json +1 -1
- package/src/components/account-menu/account-menu.css +1 -1
- package/src/components/app-icon/app-icon.css +77 -51
- package/src/components/app-icon/app-icon.js +28 -4
- package/src/components/app-rail/app-rail.css +12 -3
- package/src/components/app-sidebar/app-sidebar.css +11 -2
- package/src/components/aurora/aurora.css +143 -0
- package/src/components/aurora/aurora.js +146 -0
- package/src/components/avatar/avatar.css +1 -1
- package/src/components/button/button.css +2 -2
- package/src/components/calendar/calendar-model.js +402 -0
- package/src/components/calendar/calendar.css +609 -0
- package/src/components/calendar/calendar.js +1619 -0
- package/src/components/card/card.css +2 -2
- package/src/components/card-view/card-view.css +389 -0
- package/src/components/card-view/card-view.js +527 -0
- package/src/components/card-view/record-card.js +392 -0
- package/src/components/context-menu/context-menu.css +4 -4
- package/src/components/context-menu/context-menu.js +5 -1
- package/src/components/date-picker/date-picker.css +31 -12
- package/src/components/datebox/date-range-box.css +17 -2
- package/src/components/datebox/datebox.css +18 -3
- package/src/components/dialog/dialog.css +4 -3
- package/src/components/dialog/dialog.js +2 -1
- package/src/components/dropdown/dropdown.css +17 -3
- package/src/components/dropdown/dropdown.js +7 -2
- package/src/components/kanban-view/kanban-view.css +216 -0
- package/src/components/kanban-view/kanban-view.js +1228 -0
- package/src/components/launcher/launcher.css +24 -9
- package/src/components/launcher/launcher.js +9 -2
- package/src/components/master-panel/master-panel.css +14 -3
- package/src/components/menu-button/menu-button.css +4 -4
- package/src/components/message/message.css +13 -21
- package/src/components/message/message.js +17 -11
- package/src/components/modal/modal.css +19 -5
- package/src/components/modal/modal.js +99 -25
- package/src/components/navigation-bar/navigation-bar.css +17 -5
- package/src/components/panel/panel.css +10 -2
- package/src/components/select/select.css +26 -8
- package/src/components/sheet/sheet.css +28 -7
- package/src/components/sheet/sheet.js +43 -23
- package/src/components/table/table.css +83 -5
- package/src/components/table/table.js +322 -37
- package/src/components/table-view/table-view.css +18 -0
- package/src/components/table-view/table-view.js +347 -0
- package/src/components/tag-picker/tag-picker.css +18 -4
- package/src/components/tooltip/tooltip.css +17 -3
- package/src/components/tooltip/tooltip.js +4 -1
- package/src/components/view/record-view.css +114 -0
- package/src/components/view/record-view.js +770 -0
- package/src/core/overlay-host.js +23 -0
- package/src/index.js +15 -0
- package/src/zeyos/calendar.js +115 -0
- package/src/zeyos/icons.js +11 -4
- package/src/zeyos/index.js +13 -0
- package/src/zeyos/launcher.js +2 -2
- package/src/zeyos/legacy-saved-views.js +155 -0
- package/src/zeyos/saved-views.js +815 -0
- package/src/zeyos/table.js +3 -1
- package/src/zeyos/view.js +543 -0
- package/styles/icons.css +3 -10
- package/styles/tokens/dark.css +4 -0
- package/styles/tokens/semantic.css +142 -3
- package/styles/zx.css +6 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { h, restoreTarget, snapshotTarget } from '../../core/dom.js';
|
|
3
|
+
import { formatTableCell, Table } from '../table/table.js';
|
|
4
|
+
import { RecordView, readViewField } from '../view/record-view.js';
|
|
5
|
+
|
|
6
|
+
/** @typedef {Record<string, any>} TableViewRecord */
|
|
7
|
+
/** @typedef {import('../view/record-view.js').ViewField} ViewField */
|
|
8
|
+
/** @typedef {import('../view/record-view.js').ViewSort} ViewSort */
|
|
9
|
+
/** @typedef {import('../table/table.js').TableColumn} TableColumn */
|
|
10
|
+
/** @typedef {import('../table/table.js').TableOptions} TableOptions */
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {Object} TableViewOptions
|
|
14
|
+
* @property {ViewField[]} [fields=[]] Shared ordered field descriptors. These win over `table.columns`.
|
|
15
|
+
* @property {TableViewRecord[]} [data=[]] Initial records. These win over `table.data`.
|
|
16
|
+
* @property {string|((record:TableViewRecord)=>unknown)} [recordId='ID'] Stable record id accessor.
|
|
17
|
+
* @property {ViewSort|null} [sort=null] Initial shared sort.
|
|
18
|
+
* @property {'local'|'server'} [sortMode='local'] Local or server-controlled sorting.
|
|
19
|
+
* @property {false|'single'|'multi'} [selectable=false] Shared selection behavior.
|
|
20
|
+
* @property {unknown[]} [selection=[]] Initially selected record ids.
|
|
21
|
+
* @property {string[]} [fieldOrder=[]] Preferred stable field order.
|
|
22
|
+
* @property {string[]} [hiddenFields=[]] Initially hidden field ids.
|
|
23
|
+
* @property {boolean} [fieldControls=true] Show the accessible visibility and reorder disclosure.
|
|
24
|
+
* @property {string|Node|(()=>string|Node)|null} [emptyText=null] Empty-result content.
|
|
25
|
+
* @property {TableOptions} [table={}] Advanced low-level Table options. Shared options win where
|
|
26
|
+
* their contracts overlap; all other options and callbacks are forwarded unchanged.
|
|
27
|
+
* @property {(event:CustomEvent<Record<string,unknown>>)=>void} [onrecordclick]
|
|
28
|
+
* @property {(event:CustomEvent<Record<string,unknown>>)=>void} [onrecorddblclick]
|
|
29
|
+
* @property {(event:CustomEvent<{records:TableViewRecord[]}> )=>void} [ondatachange]
|
|
30
|
+
* @property {(event:CustomEvent<{records:TableViewRecord[],ids:unknown[]}> )=>void} [onselectionchange]
|
|
31
|
+
* @property {(event:CustomEvent<{id:string|null,dir:'asc'|'desc'|null}>)=>void} [onsortchange]
|
|
32
|
+
* @property {(event:CustomEvent<{visible:string[],hidden:string[]}> )=>void} [onfieldvisibilitychange]
|
|
33
|
+
* @property {(event:CustomEvent<{order:string[]}> )=>void} [onfieldorderchange]
|
|
34
|
+
* @property {(event:CustomEvent<Record<string,unknown>>)=>void} [onstatechange]
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Record-oriented table view. Shared record state stays in RecordView while the composed `table`
|
|
39
|
+
* instance retains Table's editing, hierarchy, growth, movement, responsive, and loading behavior.
|
|
40
|
+
* @fires TableView#recordclick
|
|
41
|
+
* @fires TableView#recorddblclick
|
|
42
|
+
* @fires TableView#datachange
|
|
43
|
+
* @fires TableView#selectionchange
|
|
44
|
+
* @fires TableView#sortchange
|
|
45
|
+
* @fires TableView#fieldvisibilitychange
|
|
46
|
+
* @fires TableView#fieldorderchange
|
|
47
|
+
* @fires TableView#statechange
|
|
48
|
+
* @extends {RecordView}
|
|
49
|
+
*/
|
|
50
|
+
export class TableView extends RecordView {
|
|
51
|
+
static cssName = 'table-view';
|
|
52
|
+
|
|
53
|
+
/** @type {Readonly<TableViewOptions>} */
|
|
54
|
+
static defaults = { table: {} };
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Creates or enhances a record table view.
|
|
58
|
+
* @param {Element|string|null} [target=null] Existing root, selector, or null.
|
|
59
|
+
* @param {TableViewOptions} [options={}] Shared and table-specific options.
|
|
60
|
+
*/
|
|
61
|
+
constructor(target = null, options = {}) {
|
|
62
|
+
super(target, options);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** @returns {HTMLElement} Table view root. */
|
|
66
|
+
render() {
|
|
67
|
+
const created = this.el === null;
|
|
68
|
+
const root = /** @type {HTMLElement} */ (this.el ?? h('div'));
|
|
69
|
+
this.el = root;
|
|
70
|
+
this._createdTableViewRoot = created;
|
|
71
|
+
this._tableViewOriginal = created ? null : snapshotTarget(root);
|
|
72
|
+
this._tableViewRestored = false;
|
|
73
|
+
this._tableSyncDepth = 0;
|
|
74
|
+
this._tableViewDestroyed = false;
|
|
75
|
+
this._tableHost = h('div', { class: 'zx-table-view__table' });
|
|
76
|
+
this._initRecordView(root);
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
root.replaceChildren(this._tableHost);
|
|
80
|
+
const viewOptions = /** @type {Readonly<TableViewOptions>} */ (this.options);
|
|
81
|
+
const options = tableOptionsForView(viewOptions.table, {
|
|
82
|
+
fields: this.getFields(),
|
|
83
|
+
data: this.getData(),
|
|
84
|
+
recordId: this.options.recordId,
|
|
85
|
+
sort: this.getSort(),
|
|
86
|
+
sortMode: this.options.sortMode,
|
|
87
|
+
selectable: this.options.selectable,
|
|
88
|
+
hiddenFields: this.getHiddenFields(),
|
|
89
|
+
fieldControls: this.options.fieldControls,
|
|
90
|
+
emptyText: this.options.emptyText
|
|
91
|
+
});
|
|
92
|
+
this.table = new Table(this._tableHost, options);
|
|
93
|
+
// Seed selection before bridging events so construction stays silent like RecordView itself.
|
|
94
|
+
this.table.setSelection(this.getSelectionIds());
|
|
95
|
+
this._bridgeTableEvents();
|
|
96
|
+
return root;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
this.table?.destroy();
|
|
99
|
+
if (!created) restoreTarget(root, this._tableViewOriginal);
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Returns the composed Table for advanced methods and Table-specific events. @returns {Table} */
|
|
105
|
+
getTable() {
|
|
106
|
+
return this.table;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** @protected @param {string} reason Shared refresh reason. @returns {void} */
|
|
110
|
+
_refreshView(reason) {
|
|
111
|
+
if (!this.table || this._tableSyncDepth > 0) return;
|
|
112
|
+
this._withTableSync(() => {
|
|
113
|
+
if (reason === 'fields' || reason === 'state') {
|
|
114
|
+
this.table.setColumns(fieldsToTableColumns(this.getFields()), { silent: true });
|
|
115
|
+
this._syncTableFieldState();
|
|
116
|
+
}
|
|
117
|
+
if (reason === 'data' || reason === 'fields' || reason === 'sort' || reason === 'state') {
|
|
118
|
+
this.table.setData(this.getData());
|
|
119
|
+
}
|
|
120
|
+
if (reason === 'sort' || reason === 'fields' || reason === 'state') {
|
|
121
|
+
const sort = this.getSort();
|
|
122
|
+
if (sort) this.table.setSort(sort.id, sort.dir, { silent: true });
|
|
123
|
+
else this.table.clearSort({ silent: true });
|
|
124
|
+
}
|
|
125
|
+
if (reason === 'selection' || reason === 'data' || reason === 'state') {
|
|
126
|
+
this.table.setSelection(this.getSelectionIds());
|
|
127
|
+
}
|
|
128
|
+
if (reason === 'loading') {
|
|
129
|
+
this.table.setLoading(/** @type {HTMLElement} */ (this.el).dataset.loading === 'true');
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Destroys the inner Table and restores an enhanced target exactly once. @returns {void} */
|
|
135
|
+
destroy() {
|
|
136
|
+
if (this._tableViewDestroyed) return;
|
|
137
|
+
this._tableViewDestroyed = true;
|
|
138
|
+
this.table?.destroy();
|
|
139
|
+
if (!this._tableViewRestored && !this._createdTableViewRoot) {
|
|
140
|
+
restoreTarget(this.el, this._tableViewOriginal);
|
|
141
|
+
this._tableViewRestored = true;
|
|
142
|
+
}
|
|
143
|
+
super.destroy();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** @private @param {()=>void} callback Guarded synchronization. @returns {void} */
|
|
147
|
+
_withTableSync(callback) {
|
|
148
|
+
this._tableSyncDepth += 1;
|
|
149
|
+
try {
|
|
150
|
+
callback();
|
|
151
|
+
} finally {
|
|
152
|
+
this._tableSyncDepth -= 1;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** @private @returns {void} */
|
|
157
|
+
_syncTableFieldState() {
|
|
158
|
+
const hidden = new Set(this.getHiddenFields());
|
|
159
|
+
// Show before hiding so Table's one-visible-column invariant never blocks a valid transition.
|
|
160
|
+
for (const id of this.getFieldOrder()) {
|
|
161
|
+
if (!hidden.has(id)) this.table.setColumnVisible(id, true, { silent: true });
|
|
162
|
+
}
|
|
163
|
+
for (const id of this.getFieldOrder()) {
|
|
164
|
+
if (hidden.has(id)) this.table.setColumnVisible(id, false, { silent: true });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** @private @returns {void} */
|
|
169
|
+
_bridgeTableEvents() {
|
|
170
|
+
// Table and RecordView intentionally share these event names but not their detail keys. Keep
|
|
171
|
+
// the inner DOM events available on the Table root without leaking a second, row-shaped event
|
|
172
|
+
// through the TableView root; the bridged record-shaped event is emitted there once below.
|
|
173
|
+
this.listen(this._tableHost, 'zx-datachange', (event) => event.stopPropagation());
|
|
174
|
+
this.listen(this._tableHost, 'zx-selectionchange', (event) => event.stopPropagation());
|
|
175
|
+
this.listen(this.table, 'rowclick', (event) => {
|
|
176
|
+
const detail = /** @type {CustomEvent<Record<string,unknown>>} */ (event).detail;
|
|
177
|
+
this.emit('recordclick', { ...detail, record: detail.row });
|
|
178
|
+
});
|
|
179
|
+
this.listen(this.table, 'rowdblclick', (event) => {
|
|
180
|
+
const detail = /** @type {CustomEvent<Record<string,unknown>>} */ (event).detail;
|
|
181
|
+
this.emit('recorddblclick', { ...detail, record: detail.row });
|
|
182
|
+
});
|
|
183
|
+
this.listen(this.table, 'sort', (event) => this._tableSortChanged(
|
|
184
|
+
/** @type {CustomEvent<{id:string|null,dir:'asc'|'desc'|null}>} */ (event).detail));
|
|
185
|
+
this.listen(this.table, 'selectionchange', (event) => this._tableSelectionChanged(
|
|
186
|
+
/** @type {CustomEvent<{rows:TableViewRecord[],ids:unknown[]}>} */ (event).detail));
|
|
187
|
+
this.listen(this.table, 'datachange', () => this._tableDataChanged());
|
|
188
|
+
this.listen(this.table, 'columnvisibilitychange', (event) => this._tableVisibilityChanged(
|
|
189
|
+
/** @type {CustomEvent<{visible:string[],hidden:string[]}>} */ (event).detail));
|
|
190
|
+
this.listen(this.table, 'columnorderchange', (event) => this._tableOrderChanged(
|
|
191
|
+
/** @type {CustomEvent<{order:string[]}>} */ (event).detail));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** @private @param {{id:string|null,dir:'asc'|'desc'|null}} detail Sort detail. @returns {void} */
|
|
195
|
+
_tableSortChanged(detail) {
|
|
196
|
+
if (this._tableSyncDepth > 0) return;
|
|
197
|
+
const next = /** @type {ViewSort|null} */ (detail.id == null ? null
|
|
198
|
+
: { id: String(detail.id), dir: detail.dir === 'desc' ? 'desc' : 'asc' });
|
|
199
|
+
if (sameSort(next, this._viewSort)) return;
|
|
200
|
+
this._viewSort = next;
|
|
201
|
+
if (this.options.sortMode === 'local') this._viewData = this.table.getData();
|
|
202
|
+
this.emit('sortchange', next ? { ...next } : { id: null, dir: null });
|
|
203
|
+
this._emitTableViewState('sort');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** @private @param {{rows:TableViewRecord[],ids:unknown[]}} detail Selection detail. @returns {void} */
|
|
207
|
+
_tableSelectionChanged(detail) {
|
|
208
|
+
if (this._tableSyncDepth > 0) return;
|
|
209
|
+
const next = new Set(detail.ids);
|
|
210
|
+
if (sameSet(next, this._viewSelected)) return;
|
|
211
|
+
this._viewSelected = next;
|
|
212
|
+
this._viewSelectionAnchor = detail.ids.at(-1) ?? null;
|
|
213
|
+
this._emitTableViewSelection();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** @private @returns {void} */
|
|
217
|
+
_tableDataChanged() {
|
|
218
|
+
if (this._tableSyncDepth > 0) return;
|
|
219
|
+
this._viewData = this.table.getData();
|
|
220
|
+
const valid = new Set(this._viewData.map((record) => this._viewRecordId(record)));
|
|
221
|
+
const nextSelection = new Set([...this._viewSelected].filter((id) => valid.has(id)));
|
|
222
|
+
const selectionChanged = !sameSet(nextSelection, this._viewSelected);
|
|
223
|
+
this._viewSelected = nextSelection;
|
|
224
|
+
if (this._viewSelectionAnchor != null && !valid.has(this._viewSelectionAnchor)) {
|
|
225
|
+
this._viewSelectionAnchor = null;
|
|
226
|
+
}
|
|
227
|
+
if (selectionChanged) this._emitTableViewSelection();
|
|
228
|
+
this.emit('datachange', { records: this.getData() });
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** @private @param {{visible:string[],hidden:string[]}} detail Visibility detail. @returns {void} */
|
|
232
|
+
_tableVisibilityChanged(detail) {
|
|
233
|
+
if (this._tableSyncDepth > 0) return;
|
|
234
|
+
const allowed = new Set(this.getFieldOrder());
|
|
235
|
+
const next = new Set(detail.hidden.filter((id) => allowed.has(id)));
|
|
236
|
+
if (sameSet(next, this._viewHidden)) return;
|
|
237
|
+
this._viewHidden = next;
|
|
238
|
+
this.emit('fieldvisibilitychange', {
|
|
239
|
+
visible: this.getVisibleFields().map((field) => field.id), hidden: this.getHiddenFields()
|
|
240
|
+
});
|
|
241
|
+
this._emitTableViewState('fieldvisibility');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** @private @param {{order:string[]}} detail Order detail. @returns {void} */
|
|
245
|
+
_tableOrderChanged(detail) {
|
|
246
|
+
if (this._tableSyncDepth > 0) return;
|
|
247
|
+
const allowed = new Set(this.getFieldOrder());
|
|
248
|
+
const next = [...new Set(detail.order.filter((id) => allowed.has(id))),
|
|
249
|
+
...this.getFieldOrder().filter((id) => !detail.order.includes(id))];
|
|
250
|
+
if (sameArray(next, this._viewFieldOrder)) return;
|
|
251
|
+
this._viewFieldOrder = next;
|
|
252
|
+
this.emit('fieldorderchange', { order: this.getFieldOrder() });
|
|
253
|
+
this._emitTableViewState('fieldorder');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** @private @returns {void} */
|
|
257
|
+
_emitTableViewSelection() {
|
|
258
|
+
this.emit('selectionchange', { records: this.getSelection(), ids: this.getSelectionIds() });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** @private @param {string} reason State-change reason. @returns {void} */
|
|
262
|
+
_emitTableViewState(reason) {
|
|
263
|
+
this.emit('statechange', { state: this.getViewState(), reason });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Maps common fields to Table columns without mutating descriptors. Shared get/render/sortValue
|
|
269
|
+
* semantics win; `field.view.table` may provide the remaining advanced TableColumn properties.
|
|
270
|
+
* @param {ViewField[]} fields Shared fields in display order.
|
|
271
|
+
* @returns {TableColumn[]} Table columns.
|
|
272
|
+
*/
|
|
273
|
+
export function fieldsToTableColumns(fields) {
|
|
274
|
+
return fields.map((field) => {
|
|
275
|
+
const table = /** @type {Record<string,any>} */ (field.view && typeof field.view === 'object'
|
|
276
|
+
&& field.view.table && typeof field.view.table === 'object'
|
|
277
|
+
? field.view.table : {});
|
|
278
|
+
const column = /** @type {TableColumn & Record<string,unknown>} */ ({ ...field, ...table,
|
|
279
|
+
id: field.id, label: field.label, sortable: Boolean(field.sortable) });
|
|
280
|
+
delete column.get;
|
|
281
|
+
delete column.visible;
|
|
282
|
+
delete column.duplicate;
|
|
283
|
+
delete column.view;
|
|
284
|
+
if (typeof field.render === 'function') {
|
|
285
|
+
column.render = (record, index) => field.render(record, index, readViewField(field, record, index));
|
|
286
|
+
} else if (typeof table.render !== 'function' && typeof field.get === 'function') {
|
|
287
|
+
const formatter = { ...column };
|
|
288
|
+
delete formatter.render;
|
|
289
|
+
column.render = (record, index) => formatTableCell(formatter,
|
|
290
|
+
{ ...record, [field.id]: readViewField(field, record, index) }, index);
|
|
291
|
+
}
|
|
292
|
+
if (typeof field.sortValue === 'function') {
|
|
293
|
+
column.sortValue = (record) => field.sortValue(record, 0);
|
|
294
|
+
} else if (typeof table.sortValue !== 'function' && typeof field.get === 'function') {
|
|
295
|
+
column.sortValue = (record) => readViewField(field, record, 0);
|
|
296
|
+
}
|
|
297
|
+
return column;
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Builds the composed Table configuration. Shared options deliberately overwrite overlapping
|
|
303
|
+
* low-level options; every advanced option remains untouched.
|
|
304
|
+
* @param {TableOptions|undefined} table Advanced Table options.
|
|
305
|
+
* @param {{fields:ViewField[],data:TableViewRecord[],recordId:string|((record:TableViewRecord)=>unknown),sort:ViewSort|null,sortMode:'local'|'server',selectable:false|'single'|'multi',hiddenFields:string[],fieldControls:boolean,emptyText:string|Node|(()=>string|Node)|null}} shared Shared state.
|
|
306
|
+
* @returns {TableOptions} Table configuration.
|
|
307
|
+
*/
|
|
308
|
+
export function tableOptionsForView(table, shared) {
|
|
309
|
+
return {
|
|
310
|
+
...(table && typeof table === 'object' ? table : {}),
|
|
311
|
+
columns: fieldsToTableColumns(shared.fields),
|
|
312
|
+
data: [...shared.data],
|
|
313
|
+
rowId: shared.recordId,
|
|
314
|
+
sort: shared.sort ? { ...shared.sort } : null,
|
|
315
|
+
sortMode: shared.sortMode,
|
|
316
|
+
selectable: shared.selectable,
|
|
317
|
+
hiddenColumns: [...shared.hiddenFields],
|
|
318
|
+
columnVisibility: Boolean(shared.fieldControls),
|
|
319
|
+
columnReorder: Boolean(shared.fieldControls),
|
|
320
|
+
emptyText: shared.emptyText
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** @event TableView#recordclick @type {CustomEvent<Record<string,unknown>>} */
|
|
325
|
+
/** @event TableView#recorddblclick @type {CustomEvent<Record<string,unknown>>} */
|
|
326
|
+
/** @event TableView#datachange @type {CustomEvent<{records:TableViewRecord[]}>} */
|
|
327
|
+
/** @event TableView#selectionchange @type {CustomEvent<{records:TableViewRecord[],ids:unknown[]}>} */
|
|
328
|
+
/** @event TableView#sortchange @type {CustomEvent<{id:string|null,dir:'asc'|'desc'|null}>} */
|
|
329
|
+
/** @event TableView#fieldvisibilitychange @type {CustomEvent<{visible:string[],hidden:string[]}>} */
|
|
330
|
+
/** @event TableView#fieldorderchange @type {CustomEvent<{order:string[]}>} */
|
|
331
|
+
/** @event TableView#statechange @type {CustomEvent<Record<string,unknown>>} */
|
|
332
|
+
|
|
333
|
+
/** @param {ViewSort|null} left @param {ViewSort|null} right @returns {boolean} */
|
|
334
|
+
function sameSort(left, right) {
|
|
335
|
+
return left === null && right === null
|
|
336
|
+
|| Boolean(left && right && left.id === right.id && left.dir === right.dir);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** @param {Set<unknown>} left @param {Set<unknown>} right @returns {boolean} */
|
|
340
|
+
function sameSet(left, right) {
|
|
341
|
+
return left.size === right.size && [...left].every((value) => right.has(value));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** @param {unknown[]} left @param {unknown[]} right @returns {boolean} */
|
|
345
|
+
function sameArray(left, right) {
|
|
346
|
+
return left.length === right.length && left.every((value, index) => Object.is(value, right[index]));
|
|
347
|
+
}
|
|
@@ -122,14 +122,28 @@
|
|
|
122
122
|
inline-size: max-content;
|
|
123
123
|
max-inline-size: calc(100vw - var(--zx-space-4));
|
|
124
124
|
overflow: hidden;
|
|
125
|
-
border: 1px solid var(--zx-color-border
|
|
125
|
+
border: 1px solid var(--zx-color-overlay-border);
|
|
126
126
|
border-radius: var(--zx-radius-md);
|
|
127
|
-
background: var(--zx-color-
|
|
128
|
-
|
|
127
|
+
background-color: var(--zx-color-overlay-surface);
|
|
128
|
+
background-image: var(--zx-overlay-surface-image);
|
|
129
|
+
background-clip: padding-box;
|
|
130
|
+
-webkit-backdrop-filter: var(--zx-glass-filter-overlay);
|
|
131
|
+
backdrop-filter: var(--zx-glass-filter-overlay);
|
|
132
|
+
box-shadow: var(--zx-glass-overlay-shadow);
|
|
129
133
|
color: var(--zx-color-text);
|
|
130
134
|
padding: var(--zx-space-1);
|
|
131
135
|
}
|
|
132
136
|
|
|
137
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
138
|
+
.zx-tag-picker__panel {
|
|
139
|
+
border-color: var(--zx-color-border-strong);
|
|
140
|
+
background: var(--zx-color-bg-raised);
|
|
141
|
+
-webkit-backdrop-filter: none;
|
|
142
|
+
backdrop-filter: none;
|
|
143
|
+
box-shadow: var(--zx-overlay-shadow);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
133
147
|
.zx-tag-picker__list {
|
|
134
148
|
min-inline-size: 0;
|
|
135
149
|
overflow-y: auto;
|
|
@@ -146,7 +160,7 @@
|
|
|
146
160
|
}
|
|
147
161
|
|
|
148
162
|
.zx-tag-picker__option[data-active] {
|
|
149
|
-
background: var(--zx-color-
|
|
163
|
+
background: var(--zx-color-overlay-hover);
|
|
150
164
|
}
|
|
151
165
|
|
|
152
166
|
.zx-tag-picker__option[aria-selected="true"] {
|
|
@@ -6,10 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
.zx-tooltip {
|
|
8
8
|
max-inline-size: var(--zx-tooltip-max-inline-size, 260px);
|
|
9
|
-
border: 1px solid var(--zx-color-border);
|
|
9
|
+
border: 1px solid var(--zx-color-overlay-border);
|
|
10
10
|
border-radius: var(--zx-radius-md);
|
|
11
|
-
background: var(--zx-color-
|
|
12
|
-
|
|
11
|
+
background-color: var(--zx-color-overlay-surface);
|
|
12
|
+
background-image: var(--zx-overlay-surface-image);
|
|
13
|
+
background-clip: padding-box;
|
|
14
|
+
-webkit-backdrop-filter: var(--zx-glass-filter-overlay);
|
|
15
|
+
backdrop-filter: var(--zx-glass-filter-overlay);
|
|
16
|
+
box-shadow: var(--zx-glass-overlay-shadow);
|
|
13
17
|
padding: var(--zx-space-1) var(--zx-space-2);
|
|
14
18
|
color: var(--zx-color-text);
|
|
15
19
|
font-size: var(--zx-text-sm);
|
|
@@ -22,6 +26,16 @@
|
|
|
22
26
|
user-select: none;
|
|
23
27
|
}
|
|
24
28
|
|
|
29
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
30
|
+
.zx-tooltip {
|
|
31
|
+
border-color: var(--zx-color-border);
|
|
32
|
+
background: var(--zx-color-bg-raised);
|
|
33
|
+
-webkit-backdrop-filter: none;
|
|
34
|
+
backdrop-filter: none;
|
|
35
|
+
box-shadow: var(--zx-overlay-shadow);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
25
39
|
@media (prefers-reduced-motion: no-preference) {
|
|
26
40
|
.zx-tooltip:popover-open {
|
|
27
41
|
animation: zx-tooltip-enter var(--zx-dur-fast) var(--zx-ease);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Component } from '../../core/component.js';
|
|
2
2
|
import { h, resolveElement } from '../../core/dom.js';
|
|
3
|
+
import { overlayHost } from '../../core/overlay-host.js';
|
|
3
4
|
import { position } from '../../core/position.js';
|
|
4
5
|
import { uid } from '../../core/util.js';
|
|
5
6
|
|
|
@@ -95,6 +96,7 @@ export class Tooltip extends Component {
|
|
|
95
96
|
super(null, options);
|
|
96
97
|
|
|
97
98
|
this.#anchor = resolved;
|
|
99
|
+
overlayHost(resolved).append(this.el);
|
|
98
100
|
this.#content = this.options.content;
|
|
99
101
|
this.#disabled = Boolean(this.options.disabled);
|
|
100
102
|
this.#describedBy = resolved.getAttribute('aria-describedby');
|
|
@@ -154,7 +156,6 @@ export class Tooltip extends Component {
|
|
|
154
156
|
popover: 'manual'
|
|
155
157
|
});
|
|
156
158
|
panel.dataset.state = 'closed';
|
|
157
|
-
document.body.append(panel);
|
|
158
159
|
return panel;
|
|
159
160
|
}
|
|
160
161
|
|
|
@@ -167,6 +168,8 @@ export class Tooltip extends Component {
|
|
|
167
168
|
show() {
|
|
168
169
|
if (this.#destroyed || this.#disabled || this.isOpen()) return this;
|
|
169
170
|
this.#cancelTimers();
|
|
171
|
+
const host = overlayHost(this.#anchor);
|
|
172
|
+
if (this.el.parentElement !== host) host.append(this.el);
|
|
170
173
|
const node = resolveContent(this.#content);
|
|
171
174
|
if (node === null) return this;
|
|
172
175
|
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
.zx-record-view {
|
|
2
|
+
min-inline-size: 0;
|
|
3
|
+
color: var(--zx-color-text);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.zx-record-view__toolbar {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: flex-end;
|
|
10
|
+
gap: var(--zx-space-2);
|
|
11
|
+
margin-block-end: var(--zx-space-2);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.zx-record-view__field-controls {
|
|
15
|
+
position: relative;
|
|
16
|
+
inline-size: max-content;
|
|
17
|
+
margin-inline-start: auto;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.zx-record-view__field-controls > summary {
|
|
21
|
+
display: inline-flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
gap: var(--zx-space-1);
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
list-style: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.zx-record-view__field-controls > summary::-webkit-details-marker {
|
|
29
|
+
display: none;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.zx-record-view__field-list {
|
|
33
|
+
position: absolute;
|
|
34
|
+
z-index: 4;
|
|
35
|
+
inset-block-start: calc(100% + var(--zx-space-1));
|
|
36
|
+
inset-inline-end: 0;
|
|
37
|
+
display: grid;
|
|
38
|
+
min-inline-size: 230px;
|
|
39
|
+
max-block-size: min(360px, 70vh);
|
|
40
|
+
overflow: auto;
|
|
41
|
+
border: 1px solid var(--zx-color-border-strong);
|
|
42
|
+
border-radius: var(--zx-radius-md);
|
|
43
|
+
background: var(--zx-color-bg-raised);
|
|
44
|
+
box-shadow: var(--zx-overlay-shadow);
|
|
45
|
+
padding: var(--zx-space-2);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.zx-record-view__field-option {
|
|
49
|
+
display: grid;
|
|
50
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
51
|
+
align-items: center;
|
|
52
|
+
gap: var(--zx-space-2);
|
|
53
|
+
min-block-size: var(--zx-control-height);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.zx-record-view__field-option > label {
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: var(--zx-space-2);
|
|
60
|
+
min-inline-size: 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.zx-record-view__field-option > label > span {
|
|
64
|
+
overflow: hidden;
|
|
65
|
+
text-overflow: ellipsis;
|
|
66
|
+
white-space: nowrap;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.zx-record-view__field-order {
|
|
70
|
+
display: inline-flex;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.zx-record-view__field-move {
|
|
74
|
+
display: inline-grid;
|
|
75
|
+
place-items: center;
|
|
76
|
+
inline-size: 26px;
|
|
77
|
+
block-size: 26px;
|
|
78
|
+
border: 0;
|
|
79
|
+
border-radius: var(--zx-radius-sm);
|
|
80
|
+
background: transparent;
|
|
81
|
+
color: var(--zx-color-text-muted);
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.zx-record-view__field-move:hover:not(:disabled) {
|
|
86
|
+
background: var(--zx-color-bg-hover);
|
|
87
|
+
color: var(--zx-color-text);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.zx-record-view__field-move:focus-visible {
|
|
91
|
+
outline: none;
|
|
92
|
+
box-shadow: var(--zx-focus-ring);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.zx-record-view__field-move:disabled {
|
|
96
|
+
opacity: .35;
|
|
97
|
+
cursor: not-allowed;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.zx-record-view[data-loading="true"] {
|
|
101
|
+
cursor: progress;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.zx-record-view[data-loading="true"] > :not(.zx-record-view__toolbar) {
|
|
105
|
+
opacity: .62;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
109
|
+
.zx-record-view__field-move,
|
|
110
|
+
.zx-record-view[data-loading="true"] > :not(.zx-record-view__toolbar) {
|
|
111
|
+
transition: color var(--zx-dur-fast) var(--zx-ease),
|
|
112
|
+
background var(--zx-dur-fast) var(--zx-ease), opacity var(--zx-dur-fast) var(--zx-ease);
|
|
113
|
+
}
|
|
114
|
+
}
|