@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
|
@@ -161,6 +161,11 @@ export class RecordView extends Component {
|
|
|
161
161
|
this._viewSelectionAnchor = null;
|
|
162
162
|
this._viewFieldControls = null;
|
|
163
163
|
this._viewFieldList = null;
|
|
164
|
+
this._viewFieldStatus = null;
|
|
165
|
+
this._viewFieldHome = null;
|
|
166
|
+
this._viewNativeMounted = null;
|
|
167
|
+
/** @type {{id: string, action: 'toggle'|'up'|'down'}|null} */
|
|
168
|
+
this._viewFieldFocus = null;
|
|
164
169
|
this._viewFieldSettings = normalizeViewFieldControls(this.options.fieldControls);
|
|
165
170
|
// Resolved once, here, so the mount point cannot drift between render and destroy.
|
|
166
171
|
this._viewFieldControlsTarget = resolveElement(this._viewFieldSettings.target);
|
|
@@ -286,6 +291,57 @@ export class RecordView extends Component {
|
|
|
286
291
|
return [...this._viewFieldOrder];
|
|
287
292
|
}
|
|
288
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Returns the element that lets a reader show, hide, and reorder this view's fields: the shared
|
|
296
|
+
* field chooser, or — for a `TableView` with `fieldControls: true` — the column chooser of its
|
|
297
|
+
* composed `Table`. Null when the view renders none (`fieldControls: false`).
|
|
298
|
+
*
|
|
299
|
+
* The element stays owned by the view: move it with {@link RecordView#mountFieldControls}
|
|
300
|
+
* rather than appending it elsewhere yourself, so `destroy()` can still find and remove it.
|
|
301
|
+
* @returns {HTMLElement|null}
|
|
302
|
+
*/
|
|
303
|
+
getFieldControls() {
|
|
304
|
+
if (this._viewFieldControls) return this._viewFieldControls;
|
|
305
|
+
return this._nativeFieldControls();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Moves the view's field chooser into a host element — a list toolbar, a pane header — or, with
|
|
310
|
+
* `null`, back to where the view first rendered it. It keeps driving this view wherever it
|
|
311
|
+
* sits, and `destroy()` removes it from the host. `ListToolbar.bind()` calls this for every
|
|
312
|
+
* record view, which is how its field chooser ends up in the bar.
|
|
313
|
+
* @param {Element|string|null} target Host element or selector; null returns the chooser home.
|
|
314
|
+
* @returns {HTMLElement|null} The chooser, or null when the view renders none or the target
|
|
315
|
+
* does not resolve.
|
|
316
|
+
*/
|
|
317
|
+
mountFieldControls(target) {
|
|
318
|
+
const controls = this.getFieldControls();
|
|
319
|
+
if (!controls) return null;
|
|
320
|
+
if (target === null || target === undefined) {
|
|
321
|
+
const home = this._viewFieldHome;
|
|
322
|
+
if (!home) return controls;
|
|
323
|
+
this._viewFieldHome = null;
|
|
324
|
+
if (controls === this._viewNativeMounted) this._viewNativeMounted = null;
|
|
325
|
+
if (home.parent) home.parent.insertBefore(controls, home.next?.parentNode === home.parent ? home.next : null);
|
|
326
|
+
else controls.remove();
|
|
327
|
+
if (home.mounted === undefined) delete controls.dataset.mounted;
|
|
328
|
+
else controls.dataset.mounted = home.mounted;
|
|
329
|
+
return controls;
|
|
330
|
+
}
|
|
331
|
+
const host = resolveElement(target);
|
|
332
|
+
if (!host) return null;
|
|
333
|
+
if (controls.parentNode === host) return controls;
|
|
334
|
+
if (!this._viewFieldHome) {
|
|
335
|
+
this._viewFieldHome = {
|
|
336
|
+
parent: controls.parentNode, next: controls.nextSibling, mounted: controls.dataset.mounted
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
if (controls !== this._viewFieldControls) this._viewNativeMounted = controls;
|
|
340
|
+
controls.dataset.mounted = 'external';
|
|
341
|
+
host.append(controls);
|
|
342
|
+
return controls;
|
|
343
|
+
}
|
|
344
|
+
|
|
289
345
|
/**
|
|
290
346
|
* Shows or hides one field. The final visible field cannot be hidden.
|
|
291
347
|
* @param {string} id Field id.
|
|
@@ -347,7 +403,7 @@ export class RecordView extends Component {
|
|
|
347
403
|
if (arraysEqual(next, this._viewFieldOrder)) return this;
|
|
348
404
|
this._viewFieldOrder = next;
|
|
349
405
|
this._refreshView('fields');
|
|
350
|
-
this._renderViewFieldControls();
|
|
406
|
+
this._renderViewFieldControls(this._viewFieldFocus ?? null);
|
|
351
407
|
if (!options.silent) {
|
|
352
408
|
this.emit('fieldorderchange', { order: this.getFieldOrder() });
|
|
353
409
|
this._emitViewState('fieldorder');
|
|
@@ -499,12 +555,30 @@ export class RecordView extends Component {
|
|
|
499
555
|
_createViewFieldControls(label = null) {
|
|
500
556
|
const text = label ?? this._viewFieldControlsLabel();
|
|
501
557
|
const list = h('div', { class: 'zx-record-view__field-list' });
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
558
|
+
// Present from the start and only ever re-worded: a live region inserted together with its
|
|
559
|
+
// text is frequently missed. Clipped inline because it has no visual role at all.
|
|
560
|
+
const status = h('div', {
|
|
561
|
+
class: 'zx-record-view__field-status', role: 'status', ariaLive: 'polite',
|
|
562
|
+
style: {
|
|
563
|
+
position: 'absolute', inlineSize: '1px', blockSize: '1px', overflow: 'hidden',
|
|
564
|
+
clipPath: 'inset(50%)', whiteSpace: 'nowrap'
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
const summary = h('summary', { class: 'zx-btn', dataset: { size: 'sm', kind: 'ghost' } }, icon('fields'), text);
|
|
568
|
+
const details = /** @type {HTMLDetailsElement} */ (h('details', { class: 'zx-record-view__field-controls' }, summary, list, status));
|
|
505
569
|
this._viewFieldControls = details;
|
|
506
570
|
this._viewFieldList = list;
|
|
571
|
+
this._viewFieldStatus = status;
|
|
507
572
|
this._renderViewFieldControls();
|
|
573
|
+
this.listen(details, 'keydown', (event) => {
|
|
574
|
+
const key = /** @type {KeyboardEvent} */ (event);
|
|
575
|
+
if (key.key !== 'Escape' || !details.open) return;
|
|
576
|
+
// Handled here so an enclosing sheet or dialog does not close along with the disclosure.
|
|
577
|
+
key.preventDefault();
|
|
578
|
+
key.stopPropagation();
|
|
579
|
+
details.open = false;
|
|
580
|
+
summary.focus();
|
|
581
|
+
});
|
|
508
582
|
this.listen(list, 'change', (event) => {
|
|
509
583
|
const input = /** @type {HTMLInputElement|null} */ (event.target);
|
|
510
584
|
if (input?.classList.contains('zx-record-view__field-toggle')) {
|
|
@@ -516,11 +590,7 @@ export class RecordView extends Component {
|
|
|
516
590
|
if (!button) return;
|
|
517
591
|
const id = /** @type {HTMLElement} */ (button).dataset.fieldId;
|
|
518
592
|
const action = /** @type {HTMLElement} */ (button).dataset.viewFieldAction;
|
|
519
|
-
|
|
520
|
-
if (action === 'up' && index > 0) this.moveField(String(id), this._viewFieldOrder[index - 1], 'before');
|
|
521
|
-
if (action === 'down' && index >= 0 && index < this._viewFieldOrder.length - 1) {
|
|
522
|
-
this.moveField(String(id), this._viewFieldOrder[index + 1], 'after');
|
|
523
|
-
}
|
|
593
|
+
this._moveViewFieldFromChooser(String(id), action === 'up' ? -1 : 1);
|
|
524
594
|
});
|
|
525
595
|
return details;
|
|
526
596
|
}
|
|
@@ -549,11 +619,68 @@ export class RecordView extends Component {
|
|
|
549
619
|
*/
|
|
550
620
|
_removeViewFieldControls() {
|
|
551
621
|
this._viewFieldControls?.remove?.();
|
|
622
|
+
// A native chooser moved out by mountFieldControls() would outlive its owner in the host.
|
|
623
|
+
if (this._viewNativeMounted && !this.el?.contains(this._viewNativeMounted)) {
|
|
624
|
+
this._viewNativeMounted.remove();
|
|
625
|
+
}
|
|
626
|
+
this._viewNativeMounted = null;
|
|
627
|
+
this._viewFieldHome = null;
|
|
552
628
|
this._viewFieldControls = null;
|
|
553
629
|
this._viewFieldList = null;
|
|
630
|
+
this._viewFieldStatus = null;
|
|
554
631
|
this._viewFieldControlsTarget = null;
|
|
555
632
|
}
|
|
556
633
|
|
|
634
|
+
/**
|
|
635
|
+
* A field chooser the concrete view renders natively instead of the shared one. The base class
|
|
636
|
+
* recognises a composed `Table` exposed through `getTable()` — `TableView` with
|
|
637
|
+
* `fieldControls: true` keeps Table's own column chooser — and any other subclass may override
|
|
638
|
+
* this to publish its own.
|
|
639
|
+
* @protected
|
|
640
|
+
* @returns {HTMLElement|null}
|
|
641
|
+
*/
|
|
642
|
+
_nativeFieldControls() {
|
|
643
|
+
if (!this._viewFieldSettings?.enabled) return null;
|
|
644
|
+
const self = /** @type {{getTable?: () => unknown}} */ (/** @type {unknown} */ (this));
|
|
645
|
+
const table = typeof self.getTable === 'function' ? /** @type {{el?: Element|null}|null} */ (self.getTable()) : null;
|
|
646
|
+
const root = table?.el;
|
|
647
|
+
if (!root || typeof root.querySelector !== 'function') return null;
|
|
648
|
+
const own = /** @type {HTMLElement|null} */ (root.querySelector('.zx-table__column-controls'));
|
|
649
|
+
return own ?? this._viewNativeMounted;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Moves a field one place through the chooser's buttons, keeps focus on that field's control
|
|
654
|
+
* across the re-render, and announces where it landed.
|
|
655
|
+
* @private
|
|
656
|
+
* @param {string} id Field id.
|
|
657
|
+
* @param {-1|1} delta Direction.
|
|
658
|
+
* @returns {void}
|
|
659
|
+
*/
|
|
660
|
+
_moveViewFieldFromChooser(id, delta) {
|
|
661
|
+
const order = this._viewFieldOrder;
|
|
662
|
+
const index = order.indexOf(id);
|
|
663
|
+
if (index < 0) return;
|
|
664
|
+
const neighbour = order[index + delta];
|
|
665
|
+
const action = /** @type {'up'|'down'} */ (delta < 0 ? 'up' : 'down');
|
|
666
|
+
if (neighbour === undefined) {
|
|
667
|
+
this._renderViewFieldControls({ id, action });
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
this._viewFieldFocus = { id, action };
|
|
671
|
+
try {
|
|
672
|
+
this.moveField(id, neighbour, delta < 0 ? 'before' : 'after');
|
|
673
|
+
} finally {
|
|
674
|
+
this._viewFieldFocus = null;
|
|
675
|
+
}
|
|
676
|
+
const field = this._viewFields.find((candidate) => candidate.id === id);
|
|
677
|
+
const position = this._viewFieldOrder.indexOf(id) + 1;
|
|
678
|
+
if (this._viewFieldStatus && field) {
|
|
679
|
+
this._viewFieldStatus.textContent = this._message('recordView.fieldMoved',
|
|
680
|
+
'%1 moved to position %2 of %3', field.label, position, this._viewFieldOrder.length);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
557
684
|
/**
|
|
558
685
|
* Resolves the field-chooser trigger label: the configured one, else the translated message.
|
|
559
686
|
* @protected
|
|
@@ -629,8 +756,15 @@ export class RecordView extends Component {
|
|
|
629
756
|
this.emit('statechange', { state: this.getViewState(), reason });
|
|
630
757
|
}
|
|
631
758
|
|
|
632
|
-
/**
|
|
633
|
-
|
|
759
|
+
/**
|
|
760
|
+
* Re-renders the chooser list. A focus request names the field and the control that should
|
|
761
|
+
* hold focus afterwards — a bare id means its checkbox — and falls back to the field's other
|
|
762
|
+
* enabled control when the requested one became disabled (a move button at either end).
|
|
763
|
+
* @private
|
|
764
|
+
* @param {string|{id:string,action:'toggle'|'up'|'down'}|null} [focus=null] Control to focus.
|
|
765
|
+
* @returns {void}
|
|
766
|
+
*/
|
|
767
|
+
_renderViewFieldControls(focus = null) {
|
|
634
768
|
if (!this._viewFieldList) return;
|
|
635
769
|
const fields = this.getFields();
|
|
636
770
|
const visible = fields.filter((field) => !this._viewHidden.has(field.id));
|
|
@@ -641,7 +775,8 @@ export class RecordView extends Component {
|
|
|
641
775
|
type: 'checkbox',
|
|
642
776
|
value: field.id,
|
|
643
777
|
checked: !this._viewHidden.has(field.id),
|
|
644
|
-
disabled: !this._viewHidden.has(field.id) && visible.length === 1
|
|
778
|
+
disabled: !this._viewHidden.has(field.id) && visible.length === 1,
|
|
779
|
+
dataset: { viewFieldAction: 'toggle', fieldId: field.id }
|
|
645
780
|
});
|
|
646
781
|
const up = h('button', {
|
|
647
782
|
class: 'zx-record-view__field-move',
|
|
@@ -662,14 +797,39 @@ export class RecordView extends Component {
|
|
|
662
797
|
h('span', { class: 'zx-record-view__field-order' }, up, down));
|
|
663
798
|
});
|
|
664
799
|
this._viewFieldList.replaceChildren(...controls);
|
|
665
|
-
if (
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
800
|
+
if (focus == null) return;
|
|
801
|
+
const request = typeof focus === 'string' ? { id: focus, action: 'toggle' } : focus;
|
|
802
|
+
const match = chooserFocusTarget(controls, request);
|
|
803
|
+
if (!match) return;
|
|
804
|
+
// Synchronously, so focus never passes through <body> between the removal and the restore.
|
|
805
|
+
match.focus();
|
|
806
|
+
queueMicrotask(() => {
|
|
807
|
+
if (match.isConnected && match.ownerDocument?.activeElement !== match) match.focus();
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Picks the chooser control that should hold focus after a re-render: the requested control of
|
|
814
|
+
* the field, else that field's other enabled move button, else any enabled control of the field.
|
|
815
|
+
* @param {HTMLElement[]} options Rendered `.zx-record-view__field-option` rows.
|
|
816
|
+
* @param {{id:string,action:string}} request Field id and control kind.
|
|
817
|
+
* @returns {HTMLElement|null}
|
|
818
|
+
*/
|
|
819
|
+
function chooserFocusTarget(options, request) {
|
|
820
|
+
/** @type {HTMLElement[]} */
|
|
821
|
+
const controls = [];
|
|
822
|
+
for (const option of options) {
|
|
823
|
+
for (const control of option.querySelectorAll?.('[data-view-field-action]') ?? []) {
|
|
824
|
+
if (/** @type {HTMLElement} */ (control).dataset.fieldId === request.id) controls.push(/** @type {HTMLElement} */ (control));
|
|
671
825
|
}
|
|
672
826
|
}
|
|
827
|
+
const enabled = (control) => !(/** @type {HTMLButtonElement} */ (control).disabled);
|
|
828
|
+
const exact = controls.find((control) => control.dataset.viewFieldAction === request.action);
|
|
829
|
+
if (exact && enabled(exact)) return exact;
|
|
830
|
+
const move = controls.find((control) => control.dataset.viewFieldAction !== 'toggle' && enabled(control));
|
|
831
|
+
if (request.action !== 'toggle' && move) return move;
|
|
832
|
+
return controls.find(enabled) ?? null;
|
|
673
833
|
}
|
|
674
834
|
|
|
675
835
|
/**
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The content of a "View settings" panel: the shown fields in order, the hidden ones to bring
|
|
3
|
+
* back, grouping, and the board's axes. It fills whatever the host gives it — a sheet body, a
|
|
4
|
+
* dropdown, a settings page — and draws no frame of its own.
|
|
5
|
+
*/
|
|
6
|
+
.zx-view-settings {
|
|
7
|
+
/* A size container, so the board settings stack when the panel is narrow rather than the window.
|
|
8
|
+
Size containment ignores the contents' width, so the width is declared: the host's, with a
|
|
9
|
+
floor for a host such as a dropdown that sizes itself to its contents. */
|
|
10
|
+
container-type: inline-size;
|
|
11
|
+
inline-size: 100%;
|
|
12
|
+
min-inline-size: 15rem;
|
|
13
|
+
display: grid;
|
|
14
|
+
gap: var(--zx-space-5);
|
|
15
|
+
color: var(--zx-color-text);
|
|
16
|
+
font-size: var(--zx-control-font-size);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.zx-view-settings__section {
|
|
20
|
+
display: grid;
|
|
21
|
+
gap: var(--zx-space-2);
|
|
22
|
+
min-inline-size: 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.zx-view-settings__heading {
|
|
26
|
+
margin: 0;
|
|
27
|
+
color: var(--zx-color-text);
|
|
28
|
+
font-size: var(--zx-text-md);
|
|
29
|
+
font-weight: var(--zx-weight-semibold);
|
|
30
|
+
line-height: 1.3;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.zx-view-settings__subheading {
|
|
34
|
+
margin: var(--zx-space-1) 0 0;
|
|
35
|
+
color: var(--zx-color-text-muted);
|
|
36
|
+
font-size: var(--zx-text-sm);
|
|
37
|
+
font-weight: var(--zx-weight-medium);
|
|
38
|
+
line-height: 1.3;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.zx-view-settings__search-slot:empty {
|
|
42
|
+
display: none;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.zx-view-settings__search {
|
|
46
|
+
inline-size: 100%;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* The shown list and its insertion line share a positioned frame; the line is not a list item. */
|
|
50
|
+
.zx-view-settings__frame {
|
|
51
|
+
position: relative;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.zx-view-settings__list,
|
|
55
|
+
.zx-view-settings__hidden {
|
|
56
|
+
display: grid;
|
|
57
|
+
gap: calc(var(--zx-space-1) / 2);
|
|
58
|
+
margin: 0;
|
|
59
|
+
padding: 0;
|
|
60
|
+
list-style: none;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* A long hidden list scrolls on its own, so the shown fields above never leave the screen. */
|
|
64
|
+
.zx-view-settings__hidden {
|
|
65
|
+
max-block-size: calc(var(--zx-control-height) * 8);
|
|
66
|
+
overflow-y: auto;
|
|
67
|
+
overscroll-behavior: contain;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.zx-view-settings__hidden[hidden] {
|
|
71
|
+
display: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/*
|
|
75
|
+
* One row: handle, type glyph, label, action. The type glyph follows the label in the source, so
|
|
76
|
+
* a screen reader hears "Owner, Text", and is placed before it here, where the eye expects it.
|
|
77
|
+
*/
|
|
78
|
+
.zx-view-settings__row {
|
|
79
|
+
position: relative;
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
gap: var(--zx-space-2);
|
|
83
|
+
min-block-size: var(--zx-control-height);
|
|
84
|
+
padding-inline: var(--zx-space-1);
|
|
85
|
+
border-radius: var(--zx-control-radius);
|
|
86
|
+
background: var(--zx-color-bg-surface);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.zx-view-settings__row:hover {
|
|
90
|
+
background: var(--zx-color-bg-hover);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.zx-view-settings__handle {
|
|
94
|
+
order: -2;
|
|
95
|
+
flex: none;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.zx-view-settings__type {
|
|
99
|
+
order: -1;
|
|
100
|
+
display: inline-flex;
|
|
101
|
+
flex: none;
|
|
102
|
+
align-items: center;
|
|
103
|
+
justify-content: center;
|
|
104
|
+
inline-size: 1.25rem;
|
|
105
|
+
color: var(--zx-color-text-subtle);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.zx-view-settings__label {
|
|
109
|
+
flex: 1 1 auto;
|
|
110
|
+
min-inline-size: 0;
|
|
111
|
+
overflow: hidden;
|
|
112
|
+
text-overflow: ellipsis;
|
|
113
|
+
white-space: nowrap;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Handle and hide buttons: flat until hovered or focused, square at the control height. */
|
|
117
|
+
.zx-view-settings__handle,
|
|
118
|
+
.zx-view-settings__hide {
|
|
119
|
+
display: inline-grid;
|
|
120
|
+
place-items: center;
|
|
121
|
+
inline-size: var(--zx-control-height);
|
|
122
|
+
block-size: var(--zx-control-height);
|
|
123
|
+
min-block-size: 0;
|
|
124
|
+
padding: 0;
|
|
125
|
+
border: 1px solid transparent;
|
|
126
|
+
border-radius: var(--zx-control-radius);
|
|
127
|
+
background: none;
|
|
128
|
+
color: var(--zx-color-text-subtle);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.zx-view-settings__handle[data-placeholder] {
|
|
132
|
+
visibility: hidden;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
button.zx-view-settings__handle {
|
|
136
|
+
cursor: grab;
|
|
137
|
+
touch-action: none;
|
|
138
|
+
-webkit-user-select: none;
|
|
139
|
+
user-select: none;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.zx-view-settings__handle:hover,
|
|
143
|
+
.zx-view-settings__hide:hover:not(:disabled) {
|
|
144
|
+
background: var(--zx-color-bg-hover);
|
|
145
|
+
color: var(--zx-color-text);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.zx-view-settings__handle:focus-visible,
|
|
149
|
+
.zx-view-settings__hide:focus-visible {
|
|
150
|
+
box-shadow: var(--zx-focus-ring);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.zx-view-settings__hide:disabled {
|
|
154
|
+
color: var(--zx-color-text-placeholder);
|
|
155
|
+
cursor: default;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.zx-view-settings__lock {
|
|
159
|
+
display: inline-grid;
|
|
160
|
+
flex: none;
|
|
161
|
+
place-items: center;
|
|
162
|
+
inline-size: var(--zx-control-height);
|
|
163
|
+
color: var(--zx-color-text-placeholder);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.zx-view-settings__add {
|
|
167
|
+
flex: none;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* Picked up from the keyboard: marked by the border as well as the fill, never by colour alone. */
|
|
171
|
+
.zx-view-settings__row[data-state="grabbed"] {
|
|
172
|
+
outline: 2px solid var(--zx-color-ring);
|
|
173
|
+
outline-offset: -2px;
|
|
174
|
+
background: var(--zx-color-bg-selected);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.zx-view-settings[data-dragging="true"],
|
|
178
|
+
.zx-view-settings[data-dragging="true"] .zx-view-settings__handle {
|
|
179
|
+
cursor: grabbing;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.zx-view-settings__row[data-dragging="true"] {
|
|
183
|
+
z-index: 1;
|
|
184
|
+
background: var(--zx-color-bg-raised);
|
|
185
|
+
box-shadow: var(--zx-overlay-shadow);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.zx-view-settings__drop-indicator {
|
|
189
|
+
position: absolute;
|
|
190
|
+
inset-inline: 0;
|
|
191
|
+
inset-block-start: 0;
|
|
192
|
+
z-index: 2;
|
|
193
|
+
block-size: 2px;
|
|
194
|
+
margin-block-start: -1px;
|
|
195
|
+
border-radius: var(--zx-radius-full);
|
|
196
|
+
background: var(--zx-color-accent);
|
|
197
|
+
pointer-events: none;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.zx-view-settings__drop-indicator[hidden] {
|
|
201
|
+
display: none;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.zx-view-settings__empty {
|
|
205
|
+
margin: 0;
|
|
206
|
+
padding-block: var(--zx-space-1);
|
|
207
|
+
color: var(--zx-color-text-muted);
|
|
208
|
+
font-size: var(--zx-text-sm);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.zx-view-settings__empty[hidden] {
|
|
212
|
+
display: none;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.zx-view-settings__option {
|
|
216
|
+
display: inline-flex;
|
|
217
|
+
align-items: center;
|
|
218
|
+
gap: var(--zx-space-2);
|
|
219
|
+
min-inline-size: 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.zx-view-settings__option > .zx-icon,
|
|
223
|
+
.zx-view-settings .zx-select__value-adornment > .zx-icon {
|
|
224
|
+
flex: none;
|
|
225
|
+
color: var(--zx-color-text-subtle);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.zx-view-settings__group {
|
|
229
|
+
inline-size: 100%;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Board settings: label beside its select where there is room, above it where there is not. */
|
|
233
|
+
.zx-view-settings__setting {
|
|
234
|
+
display: grid;
|
|
235
|
+
grid-template-columns: minmax(6rem, auto) minmax(0, 1fr);
|
|
236
|
+
align-items: center;
|
|
237
|
+
gap: var(--zx-space-2);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.zx-view-settings__setting-label {
|
|
241
|
+
color: var(--zx-color-text-muted);
|
|
242
|
+
font-size: var(--zx-text-sm);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
@container (max-width: 18rem) {
|
|
246
|
+
.zx-view-settings__setting {
|
|
247
|
+
grid-template-columns: minmax(0, 1fr);
|
|
248
|
+
gap: var(--zx-space-1);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.zx-view-settings__toggle {
|
|
253
|
+
justify-self: start;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.zx-view-settings__instructions[hidden] {
|
|
257
|
+
display: none;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.zx-view-settings__sr,
|
|
261
|
+
.zx-view-settings__status {
|
|
262
|
+
position: absolute;
|
|
263
|
+
inline-size: 1px;
|
|
264
|
+
block-size: 1px;
|
|
265
|
+
margin: -1px;
|
|
266
|
+
padding: 0;
|
|
267
|
+
overflow: hidden;
|
|
268
|
+
clip-path: inset(50%);
|
|
269
|
+
white-space: nowrap;
|
|
270
|
+
border: 0;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
274
|
+
.zx-view-settings__row,
|
|
275
|
+
.zx-view-settings__handle,
|
|
276
|
+
.zx-view-settings__hide {
|
|
277
|
+
transition: background var(--zx-dur-fast) var(--zx-ease),
|
|
278
|
+
color var(--zx-dur-fast) var(--zx-ease),
|
|
279
|
+
box-shadow var(--zx-dur-fast) var(--zx-ease);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.zx-view-settings__drop-indicator {
|
|
283
|
+
transition: inset-block-start var(--zx-dur-fast) var(--zx-ease);
|
|
284
|
+
}
|
|
285
|
+
}
|