@zeyos/zx 5.0.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 +164 -0
- package/README.md +2 -1
- package/dist/types/components/account-menu/account-menu.d.ts +8 -0
- package/dist/types/components/activity-list/activity-list.d.ts +9 -1
- package/dist/types/components/app-icon/app-icon.d.ts +14 -2
- package/dist/types/components/app-sidebar/app-sidebar.d.ts +288 -14
- 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/context-menu/context-menu.d.ts +6 -2
- package/dist/types/components/data-filter/data-filter.d.ts +4 -4
- package/dist/types/components/dropdown/dropdown.d.ts +22 -7
- package/dist/types/components/field/field.d.ts +30 -0
- package/dist/types/components/fieldset/external-adapter.d.ts +83 -0
- package/dist/types/components/fieldset/fieldset.d.ts +7 -0
- 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/launcher/launcher.d.ts +8 -0
- package/dist/types/components/list-toolbar/list-toolbar.d.ts +15 -6
- package/dist/types/components/menu-button/menu-button.d.ts +6 -0
- package/dist/types/components/message/message.d.ts +14 -1
- package/dist/types/components/modal/modal.d.ts +30 -0
- 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/select.d.ts +6 -0
- package/dist/types/components/sheet/sheet.d.ts +15 -0
- package/dist/types/components/table/table.d.ts +265 -17
- 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/component.d.ts +12 -2
- package/dist/types/core/i18n.d.ts +27 -3
- package/dist/types/core/keyboard.d.ts +98 -1
- package/dist/types/core/messages.d.ts +382 -0
- package/dist/types/core/overlay-host.d.ts +28 -1
- package/dist/types/core/overlay-stack.d.ts +260 -0
- package/dist/types/index.d.ts +5 -2
- package/dist/types/internal/app-rail.d.ts +38 -0
- package/dist/types/internal/menu-items.d.ts +71 -3
- package/dist/types/zeyos/index.d.ts +1 -0
- 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 +31 -5
- package/dist/types/zeyos/table.d.ts +245 -8
- package/dist/types/zeyos/view.d.ts +16 -2
- package/dist/zx-zeyos.esm.js +4422 -430
- package/dist/zx-zeyos.esm.min.js +5 -5
- package/dist/zx.css +1417 -134
- package/dist/zx.esm.js +6986 -830
- package/dist/zx.esm.min.js +8 -8
- package/dist/zx.global.js +6986 -830
- package/dist/zx.global.min.js +8 -8
- package/dist/zx.min.css +1 -1
- package/docs/api.json +794 -17
- package/docs/llms.md +503 -63
- package/docs/llms.txt +6 -2
- package/package.json +1 -1
- package/src/components/account-menu/account-menu.js +12 -0
- package/src/components/activity-list/activity-list.js +27 -1
- package/src/components/app-icon/app-icon.css +13 -2
- package/src/components/app-icon/app-icon.js +11 -1
- package/src/components/app-rail/app-rail.css +19 -0
- package/src/components/app-sidebar/app-sidebar.css +204 -3
- package/src/components/app-sidebar/app-sidebar.js +646 -40
- package/src/components/breadcrumb/breadcrumb.css +1 -1
- package/src/components/calendar/calendar.css +1 -1
- package/src/components/calendar/calendar.js +26 -52
- package/src/components/card/card.css +2 -2
- package/src/components/card-view/card-view.css +5 -5
- package/src/components/card-view/card-view.js +13 -9
- package/src/components/card-view/record-card.js +14 -3
- package/src/components/code-editor/code-editor.css +1 -1
- package/src/components/code-editor/field-adapter.js +9 -2
- package/src/components/context-menu/context-menu.js +8 -2
- package/src/components/copy/copy-input.css +1 -1
- package/src/components/data-filter/data-filter.js +2 -2
- package/src/components/date-picker/time-picker.css +2 -2
- package/src/components/dock/dock.css +5 -4
- package/src/components/dropdown/dropdown.js +157 -72
- package/src/components/field/field.js +33 -3
- package/src/components/fieldset/external-adapter.js +363 -0
- package/src/components/fieldset/fieldset.js +10 -59
- package/src/components/file-list/file-list.js +144 -21
- package/src/components/filter/filter.css +86 -2
- package/src/components/filter/filter.js +300 -8
- package/src/components/filter-panel/filter-panel.js +149 -32
- package/src/components/finder/finder.css +1 -1
- package/src/components/kanban-view/kanban-view.css +3 -3
- package/src/components/kanban-view/kanban-view.js +5 -2
- package/src/components/launcher/launcher.css +1 -1
- package/src/components/launcher/launcher.js +29 -3
- package/src/components/list-toolbar/list-toolbar.js +39 -13
- package/src/components/master-panel/master-panel.js +0 -1
- package/src/components/menu-button/menu-button.css +63 -0
- package/src/components/menu-button/menu-button.js +97 -11
- package/src/components/message/message.js +60 -20
- package/src/components/modal/modal.css +1 -1
- package/src/components/modal/modal.js +66 -13
- 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/number-field/field-adapter.js +2 -1
- package/src/components/pagination/pagination.css +5 -5
- package/src/components/pagination/pagination.js +20 -4
- package/src/components/panel/panel.css +3 -2
- package/src/components/questionnaire/questionnaire.css +4 -4
- package/src/components/rating/field-adapter.js +2 -1
- package/src/components/rating/rating.css +1 -1
- package/src/components/search/search.css +4 -1
- package/src/components/select/select.js +9 -0
- package/src/components/sheet/sheet.css +1 -1
- package/src/components/sheet/sheet.js +74 -32
- package/src/components/slider/field-adapter.js +6 -1
- package/src/components/slider/slider.css +2 -2
- package/src/components/split-view/split-view.css +1 -1
- package/src/components/stat-tile/stat-tile.css +1 -1
- package/src/components/stepper/stepper.css +1 -1
- package/src/components/tabbox/tabbox.css +9 -5
- package/src/components/table/table.css +86 -11
- package/src/components/table/table.js +628 -57
- package/src/components/tag-picker/field-adapter.js +2 -1
- package/src/components/tooltip/tooltip.js +35 -17
- package/src/components/tree/tree.css +1 -1
- 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/component.js +88 -17
- package/src/core/fontawesome.js +5 -1
- package/src/core/i18n.js +47 -4
- package/src/core/icons.js +12 -2
- package/src/core/keyboard.js +206 -11
- package/src/core/messages.js +427 -0
- package/src/core/overlay-host.js +64 -9
- package/src/core/overlay-stack.js +487 -0
- package/src/core/position.js +22 -3
- package/src/index.js +5 -2
- package/src/internal/app-rail.js +134 -53
- package/src/internal/menu-items.js +61 -3
- package/src/zeyos/form.js +5 -3
- package/src/zeyos/index.js +3 -0
- package/src/zeyos/launcher.js +8 -3
- package/src/zeyos/module-bar.js +181 -0
- package/src/zeyos/query.js +44 -13
- package/src/zeyos/table.js +220 -80
- package/src/zeyos/view.js +43 -93
- package/styles/base.css +16 -1
- package/styles/print.css +1 -1
- package/styles/tokens/dark.css +4 -0
- package/styles/tokens/semantic.css +56 -28
- package/styles/zx.css +3 -0
|
@@ -35,7 +35,8 @@ export function registerNumberFieldAdapter() {
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
focus: () => numberField.focus(),
|
|
38
|
-
setDisabled: (value) => (value ? numberField.disable() : numberField.enable())
|
|
38
|
+
setDisabled: (value) => (value ? numberField.disable() : numberField.enable()),
|
|
39
|
+
setReadonly: (value) => { numberField.setReadonly(value); }
|
|
39
40
|
};
|
|
40
41
|
});
|
|
41
42
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Pagination — the footer bar of a server-backed list.
|
|
3
3
|
*
|
|
4
|
-
* One row that wraps: the summary
|
|
5
|
-
* at the end. Page buttons are ordinary `.zx-btn` controls so they inherit the library's control
|
|
4
|
+
* One row that wraps: the summary and the page-size select describe the slice and sit at the
|
|
5
|
+
* start, the pager sits alone at the end. Page buttons are ordinary `.zx-btn` controls so they inherit the library's control
|
|
6
6
|
* height, radius, and focus ring; the current page takes the neutral current fill — it is where
|
|
7
7
|
* you are, not a call to action.
|
|
8
8
|
*/
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
white-space: nowrap;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
/* Absorbs the slack, so summary and
|
|
26
|
-
stack when there is no room. */
|
|
25
|
+
/* Absorbs the slack, so the summary and page size end up at the start and the pager at the end of
|
|
26
|
+
a wide bar, and they simply stack when there is no room. */
|
|
27
27
|
.zx-pagination__spacer {
|
|
28
28
|
flex: 1 1 var(--zx-space-4);
|
|
29
29
|
}
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
.zx-pagination__select:focus-visible {
|
|
46
|
-
outline:
|
|
46
|
+
outline-color: transparent;
|
|
47
47
|
box-shadow: var(--zx-focus-ring);
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -95,9 +95,23 @@ export function paginationRange({ page = 1, pages = 1, siblings = 1, boundaries
|
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* The pager for a server-backed list. It owns no data: it reports which slice the user asked for
|
|
98
|
-
* and
|
|
99
|
-
*
|
|
100
|
-
*
|
|
98
|
+
* — `page`, `pageSize`, and the zero-based row `offset = (page - 1) * pageSize` — and lets the
|
|
99
|
+
* application fetch it.
|
|
100
|
+
*
|
|
101
|
+
* The pager is one-based and defaults to 25 rows per page. The ZeyOS bindings are zero-based,
|
|
102
|
+
* default to 50 rows, and append pages above zero unless told to replace, so connecting the two
|
|
103
|
+
* takes a small mapping rather than a direct hand-off:
|
|
104
|
+
*
|
|
105
|
+
* ```js
|
|
106
|
+
* const binding = zeyosTable(client, 'contacts', { pageSize: 25 });
|
|
107
|
+
* const pager = new Pagination(host, { pageSize: binding.pageSize });
|
|
108
|
+
* pager.on('change', async ({ detail }) => {
|
|
109
|
+
* const result = await binding.load({ page: detail.page - 1, pageSize: detail.pageSize, replace: true });
|
|
110
|
+
* pager.setTotal(result.count ?? 0);
|
|
111
|
+
* });
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* In `loadmore` mode each `change` stands for one `binding.loadMore()`, which appends.
|
|
101
115
|
*
|
|
102
116
|
* `page` is clamped into `[1, pages]` after every state change, so shrinking `total` under the
|
|
103
117
|
* user's feet can never leave the pager pointing past the end of the data.
|
|
@@ -163,8 +177,10 @@ export class Pagination extends Component {
|
|
|
163
177
|
if (this.options.showSummary) {
|
|
164
178
|
children.push(h('span', { class: 'zx-pagination__summary', ref: 'summary', ariaLive: 'polite' }));
|
|
165
179
|
}
|
|
166
|
-
|
|
180
|
+
// The summary and the page size describe the slice and sit together at the start; the pager
|
|
181
|
+
// walks the slice and sits alone at the end, where the next-page action is reached last.
|
|
167
182
|
if (this.options.showPageSize) children.push(this._renderPageSize());
|
|
183
|
+
children.push(h('span', { class: 'zx-pagination__spacer' }));
|
|
168
184
|
children.push(this.options.mode === 'pages' ? this._renderPages() : this._renderLoadMore());
|
|
169
185
|
root.replaceChildren(...children);
|
|
170
186
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
border: 1px solid var(--zx-color-border);
|
|
4
4
|
border-radius: var(--zx-radius-lg);
|
|
5
5
|
background: var(--zx-color-bg-surface);
|
|
6
|
-
box-shadow: var(--zx-shadow
|
|
6
|
+
box-shadow: var(--zx-surface-shadow);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
/* The header is a row, not one big button: the collapse control takes the title area and the
|
|
@@ -49,7 +49,8 @@ button.zx-panel__toggle:hover {
|
|
|
49
49
|
|
|
50
50
|
/* Inset, because the panel clips its own overflow and would cut an outside ring. */
|
|
51
51
|
button.zx-panel__toggle:focus-visible {
|
|
52
|
-
outline:
|
|
52
|
+
outline-color: transparent;
|
|
53
|
+
outline-offset: -2px;
|
|
53
54
|
box-shadow: inset var(--zx-focus-ring);
|
|
54
55
|
}
|
|
55
56
|
|
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
.zx-questionnaire__item:focus {
|
|
54
|
-
outline:
|
|
54
|
+
outline-color: transparent;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
.zx-questionnaire__item:focus-visible {
|
|
58
|
-
outline:
|
|
58
|
+
outline-color: transparent;
|
|
59
59
|
box-shadow: var(--zx-focus-ring);
|
|
60
60
|
border-radius: var(--zx-questionnaire-radius);
|
|
61
61
|
}
|
|
@@ -275,7 +275,7 @@
|
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
.zx-questionnaire__freeform-input:focus-visible {
|
|
278
|
-
outline:
|
|
278
|
+
outline-color: transparent;
|
|
279
279
|
border-color: var(--zx-color-accent);
|
|
280
280
|
box-shadow: var(--zx-focus-ring);
|
|
281
281
|
}
|
|
@@ -301,7 +301,7 @@
|
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
.zx-questionnaire__review:focus {
|
|
304
|
-
outline:
|
|
304
|
+
outline-color: transparent;
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
|
|
@@ -28,7 +28,8 @@ export function registerRatingFieldAdapter() {
|
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
focus: () => rating.focus(),
|
|
31
|
-
setDisabled: (value) => (value ? rating.disable() : rating.enable())
|
|
31
|
+
setDisabled: (value) => (value ? rating.disable() : rating.enable()),
|
|
32
|
+
setReadonly: (value) => { rating.setReadonly(value); }
|
|
32
33
|
};
|
|
33
34
|
});
|
|
34
35
|
}
|
|
@@ -13,8 +13,11 @@
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/* The input reserves room for the clear button, which floats over its trailing edge, so showing
|
|
16
|
-
or hiding the button never changes the control's total width.
|
|
16
|
+
or hiding the button never changes the control's total width. It takes every bit of width the
|
|
17
|
+
field is given — a toolbar stretches the field, and the submit glyph must stay on its trailing
|
|
18
|
+
edge rather than stopping where the input's own width ran out. */
|
|
17
19
|
.zx-search__input {
|
|
20
|
+
flex: 1 1 auto;
|
|
18
21
|
min-inline-size: 12rem;
|
|
19
22
|
min-block-size: 0;
|
|
20
23
|
border: 0;
|
|
@@ -251,6 +251,15 @@ export class Select extends Component {
|
|
|
251
251
|
this.set(id);
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Returns the selected item ID, or null: the same as reading `value`, under the `get()`/`set()`
|
|
256
|
+
* pair the other value components share.
|
|
257
|
+
* @returns {unknown}
|
|
258
|
+
*/
|
|
259
|
+
get() {
|
|
260
|
+
return this.value;
|
|
261
|
+
}
|
|
262
|
+
|
|
254
263
|
/** Selected item, or null. @returns {SelectItem|null} */
|
|
255
264
|
get selected() {
|
|
256
265
|
return this._selected;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { STEP, STEP_LARGE, dragAxis, resolveSize } from '../../core/drag-axis.js';
|
|
2
2
|
import { focusTrap } from '../../core/keyboard.js';
|
|
3
|
-
import { h } from '../../core/dom.js';
|
|
3
|
+
import { h, resolveElement } from '../../core/dom.js';
|
|
4
4
|
import { clamp } from '../../core/util.js';
|
|
5
5
|
import { Dialog } from '../dialog/dialog.js';
|
|
6
6
|
|
|
@@ -112,36 +112,24 @@ export class Sheet extends Dialog {
|
|
|
112
112
|
* field would be undefined at best and a private one would throw.
|
|
113
113
|
*/
|
|
114
114
|
this._dock = null;
|
|
115
|
+
/** @type {Element|null} The element focus was on when the current `open()` began. */
|
|
116
|
+
this._openedFrom = null;
|
|
115
117
|
this.setSide(this.options.side);
|
|
116
118
|
this.el.dataset.backdrop = BACKDROPS.has(this.options.backdrop) ? this.options.backdrop : 'dim';
|
|
117
119
|
this.el.dataset.modality = modality(this.options.modal);
|
|
118
120
|
this.refs.close.hidden = !this.options.closeButton;
|
|
119
121
|
|
|
120
|
-
if (this.options.modal !== true) {
|
|
121
|
-
/*
|
|
122
|
-
* Escape and outside-click are free for a modal dialog and absent for a non-modal one: the
|
|
123
|
-
* browser only wires `cancel` for `showModal()`, and there is no `::backdrop` to click.
|
|
124
|
-
*/
|
|
125
|
-
this.listen(this.el, 'keydown', (event) => {
|
|
126
|
-
const keyboardEvent = /** @type {KeyboardEvent} */ (event);
|
|
127
|
-
if (keyboardEvent.key !== 'Escape' || !this.options.closable || this._dock) return;
|
|
128
|
-
keyboardEvent.preventDefault();
|
|
129
|
-
if (!this.emit('cancel').defaultPrevented) this.close();
|
|
130
|
-
});
|
|
131
|
-
if (this.options.lightDismiss) {
|
|
132
|
-
this.listen(document, 'pointerdown', (event) => {
|
|
133
|
-
const target = /** @type {Node} */ (event.target);
|
|
134
|
-
if (!this.isOpen() || this._dock || this.el.contains(target)) return;
|
|
135
|
-
this.close();
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
122
|
/*
|
|
141
123
|
* Registered after Dialog's own restore listener, so focus lands on the opener either way:
|
|
142
124
|
* the trap captured the same element, because `open()` activates it before the sheet shows.
|
|
143
125
|
*/
|
|
144
126
|
this.on('close', () => this.#trap?.deactivate());
|
|
127
|
+
// A sheet that was hosted inside a modal it was opened from goes home once it closes, so it
|
|
128
|
+
// neither sits in that dialog's focus search nor goes with the dialog when it is removed.
|
|
129
|
+
this.on('close', () => {
|
|
130
|
+
const home = super.mountTarget();
|
|
131
|
+
if (!this._dock && this.el.parentElement !== home && this.el.parentElement?.closest('dialog')) home.append(this.el);
|
|
132
|
+
});
|
|
145
133
|
|
|
146
134
|
if (this.#wantsHandle()) this.#buildHandle();
|
|
147
135
|
}
|
|
@@ -320,7 +308,19 @@ export class Sheet extends Dialog {
|
|
|
320
308
|
* @returns {Element}
|
|
321
309
|
*/
|
|
322
310
|
mountTarget() {
|
|
323
|
-
|
|
311
|
+
if (this._dock) return this._dock.el;
|
|
312
|
+
/*
|
|
313
|
+
* A floating non-modal sheet opened from inside a modal dialog lives inside that dialog: its
|
|
314
|
+
* popover still rises above it, but outside the dialog's subtree `showModal()` would leave it
|
|
315
|
+
* inert. Only asked while opening — `this.el` is unset during `render()` — so a sheet at rest
|
|
316
|
+
* stays in its theme scope. A modal sheet stacks above any dialog wherever it lives.
|
|
317
|
+
*/
|
|
318
|
+
if (this.el && this.options.modal !== true) {
|
|
319
|
+
const opener = this._openedFrom ?? resolveElement(this.options.scope);
|
|
320
|
+
const modal = modalAround(opener, this.el);
|
|
321
|
+
if (modal) return modal;
|
|
322
|
+
}
|
|
323
|
+
return super.mountTarget();
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
/**
|
|
@@ -362,11 +362,43 @@ export class Sheet extends Dialog {
|
|
|
362
362
|
this.el.returnValue = '';
|
|
363
363
|
this._present();
|
|
364
364
|
this.el.dataset.state = 'open';
|
|
365
|
-
|
|
366
|
-
else this.el.dataset.zxOverlayOrder = String(nextOverlayOrder());
|
|
365
|
+
this._syncOverlay();
|
|
367
366
|
}
|
|
368
367
|
|
|
369
368
|
|
|
369
|
+
/**
|
|
370
|
+
* Describes the presentation for the overlay stack. A docked sheet is part of the layout and
|
|
371
|
+
* stays off it.
|
|
372
|
+
*
|
|
373
|
+
* Escape and outside-click are free for a modal dialog and absent for a non-modal one — the
|
|
374
|
+
* browser only wires `cancel` for `showModal()`, and there is no `::backdrop` to click — so a
|
|
375
|
+
* floating non-modal sheet takes both from the stack. That is what makes it aware of what it
|
|
376
|
+
* opened: Escape reaches it only once no popup above it takes the key, and a press inside a menu
|
|
377
|
+
* anchored in the sheet counts as inside the sheet, wherever the menu's panel lives.
|
|
378
|
+
* @returns {import('../../core/overlay-stack.js').OverlayEntryOptions|null}
|
|
379
|
+
* @protected
|
|
380
|
+
*/
|
|
381
|
+
_overlayEntry() {
|
|
382
|
+
if (this._dock) return null;
|
|
383
|
+
const entry = super._overlayEntry();
|
|
384
|
+
if (!entry || this.options.modal === true) return entry;
|
|
385
|
+
return {
|
|
386
|
+
...entry,
|
|
387
|
+
// An Escape pressed outside the sheet and everything it opened is not addressed to it.
|
|
388
|
+
onEscape: (event) => {
|
|
389
|
+
if (!this.options.closable || !this._overlayContains(/** @type {Node|null} */ (event.target))) return false;
|
|
390
|
+
if (!this.emit('cancel').defaultPrevented) this.close();
|
|
391
|
+
return undefined;
|
|
392
|
+
},
|
|
393
|
+
onOutsidePointer: this.options.lightDismiss ? () => { this.close(); } : undefined,
|
|
394
|
+
// Hosted inside a modal that has now closed, the sheet would stay open but never be seen.
|
|
395
|
+
onParentClose: () => {
|
|
396
|
+
const holder = this.el.parentElement?.closest('dialog');
|
|
397
|
+
if (holder && !holder.open && !holder.matches(':popover-open')) this.close();
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
370
402
|
/**
|
|
371
403
|
* Opens the sheet, trapping focus first where the modality asks for it.
|
|
372
404
|
* @returns {this}
|
|
@@ -374,11 +406,17 @@ export class Sheet extends Dialog {
|
|
|
374
406
|
*/
|
|
375
407
|
open() {
|
|
376
408
|
if (this.isOpen()) return this;
|
|
409
|
+
// Read before the trap moves focus: the element the reader was on decides where the sheet lives.
|
|
410
|
+
this._openedFrom = document.activeElement;
|
|
377
411
|
if (this.options.modal === 'trap-focus') {
|
|
378
412
|
this.#trap ??= focusTrap(this.el);
|
|
379
413
|
this.#trap.activate();
|
|
380
414
|
}
|
|
381
|
-
|
|
415
|
+
try {
|
|
416
|
+
super.open();
|
|
417
|
+
} finally {
|
|
418
|
+
this._openedFrom = null;
|
|
419
|
+
}
|
|
382
420
|
// The handle is built while the sheet is still closed, where it measures zero; only now is
|
|
383
421
|
// there a real size to report to a screen reader.
|
|
384
422
|
this.#syncHandleAria();
|
|
@@ -507,14 +545,18 @@ export class Sheet extends Dialog {
|
|
|
507
545
|
}
|
|
508
546
|
|
|
509
547
|
/**
|
|
510
|
-
*
|
|
511
|
-
*
|
|
512
|
-
* @
|
|
548
|
+
* The open modal dialog around an element, other than the sheet itself, or null.
|
|
549
|
+
* @param {Element|null|undefined} element
|
|
550
|
+
* @param {Element} self
|
|
551
|
+
* @returns {Element|null}
|
|
513
552
|
*/
|
|
514
|
-
function
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
553
|
+
function modalAround(element, self) {
|
|
554
|
+
if (!element || typeof element.closest !== 'function' || self.contains(element)) return null;
|
|
555
|
+
try {
|
|
556
|
+
return element.closest('dialog:modal');
|
|
557
|
+
} catch {
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
518
560
|
}
|
|
519
561
|
|
|
520
562
|
/**
|
|
@@ -15,9 +15,13 @@ export function registerSliderFieldAdapter() {
|
|
|
15
15
|
...(options.props ?? {})
|
|
16
16
|
}));
|
|
17
17
|
let setting = false;
|
|
18
|
+
// The range input's native `change` bubbles out of the slider after the slider has reported
|
|
19
|
+
// the same commit; without this the Field would emit `change` twice per edit.
|
|
20
|
+
field.listen(slider.el, 'change', (event) => event.stopImmediatePropagation());
|
|
18
21
|
slider.on('change', (event) => {
|
|
19
22
|
if (!setting) field.emit('change', { value: event.detail.value });
|
|
20
23
|
});
|
|
24
|
+
slider.on('input', (event) => field.emit('input', { value: event.detail.value }));
|
|
21
25
|
return {
|
|
22
26
|
el: slider.el,
|
|
23
27
|
get: () => slider.get(),
|
|
@@ -30,7 +34,8 @@ export function registerSliderFieldAdapter() {
|
|
|
30
34
|
}
|
|
31
35
|
},
|
|
32
36
|
focus: () => slider.focus(),
|
|
33
|
-
setDisabled: (value) => (value ? slider.disable() : slider.enable())
|
|
37
|
+
setDisabled: (value) => (value ? slider.disable() : slider.enable()),
|
|
38
|
+
setReadonly: (value) => { slider.setReadonly(value); }
|
|
34
39
|
};
|
|
35
40
|
});
|
|
36
41
|
}
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
border: 2px solid var(--zx-color-bg-surface);
|
|
120
120
|
border-radius: var(--zx-radius-full);
|
|
121
121
|
background: var(--zx-color-accent);
|
|
122
|
-
box-shadow: var(--zx-shadow
|
|
122
|
+
box-shadow: var(--zx-surface-shadow);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
.zx-slider__input::-moz-range-thumb {
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
border: 2px solid var(--zx-color-bg-surface);
|
|
129
129
|
border-radius: var(--zx-radius-full);
|
|
130
130
|
background: var(--zx-color-accent);
|
|
131
|
-
box-shadow: var(--zx-shadow
|
|
131
|
+
box-shadow: var(--zx-surface-shadow);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
.zx-slider__input:focus-visible {
|
|
@@ -113,7 +113,8 @@
|
|
|
113
113
|
|
|
114
114
|
.zx-tabbox[data-variant="divided"] .zx-tabbox__tab:focus-visible {
|
|
115
115
|
z-index: 1;
|
|
116
|
-
outline:
|
|
116
|
+
outline-color: transparent;
|
|
117
|
+
outline-offset: -2px;
|
|
117
118
|
box-shadow: inset var(--zx-focus-ring);
|
|
118
119
|
}
|
|
119
120
|
|
|
@@ -155,7 +156,8 @@
|
|
|
155
156
|
|
|
156
157
|
.zx-tabbox[data-variant="bracket"] .zx-tabbox__tab:focus-visible {
|
|
157
158
|
z-index: 1;
|
|
158
|
-
outline:
|
|
159
|
+
outline-color: transparent;
|
|
160
|
+
outline-offset: -2px;
|
|
159
161
|
box-shadow: inset var(--zx-focus-ring);
|
|
160
162
|
}
|
|
161
163
|
|
|
@@ -183,7 +185,8 @@
|
|
|
183
185
|
|
|
184
186
|
.zx-tabbox[data-variant="line"] .zx-tabbox__tab:focus-visible {
|
|
185
187
|
z-index: 1;
|
|
186
|
-
outline:
|
|
188
|
+
outline-color: transparent;
|
|
189
|
+
outline-offset: -2px;
|
|
187
190
|
box-shadow: inset var(--zx-focus-ring);
|
|
188
191
|
}
|
|
189
192
|
|
|
@@ -220,7 +223,7 @@
|
|
|
220
223
|
|
|
221
224
|
.zx-tabbox[data-variant="segmented"] .zx-tabbox__tab:focus-visible {
|
|
222
225
|
z-index: 1;
|
|
223
|
-
outline:
|
|
226
|
+
outline-color: transparent;
|
|
224
227
|
box-shadow: var(--zx-focus-ring);
|
|
225
228
|
}
|
|
226
229
|
|
|
@@ -291,6 +294,7 @@
|
|
|
291
294
|
}
|
|
292
295
|
|
|
293
296
|
.zx-tabbox__panel:focus-visible {
|
|
294
|
-
outline:
|
|
297
|
+
outline-color: transparent;
|
|
298
|
+
outline-offset: -2px;
|
|
295
299
|
box-shadow: inset var(--zx-focus-ring);
|
|
296
300
|
}
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
.zx-table__column-move:focus-visible {
|
|
111
|
-
outline:
|
|
111
|
+
outline-color: transparent;
|
|
112
112
|
box-shadow: var(--zx-focus-ring);
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -298,17 +298,21 @@
|
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
.zx-table__sort-button {
|
|
301
|
+
/* At least 24px tall (WCAG 2.5.8 target size) without making the header any taller: the height
|
|
302
|
+
the label's line lacks is padding, handed back by a negative margin of the same size. */
|
|
303
|
+
--zx-table-sort-slack: max(0px, (24px - 1lh) / 2);
|
|
301
304
|
display: flex;
|
|
302
305
|
align-items: center;
|
|
303
306
|
justify-content: space-between;
|
|
304
307
|
gap: var(--zx-space-2);
|
|
305
308
|
inline-size: 100%;
|
|
306
|
-
min-block-size:
|
|
309
|
+
min-block-size: 24px;
|
|
307
310
|
border: 0;
|
|
308
311
|
border-radius: var(--zx-radius-sm);
|
|
309
312
|
background: inherit;
|
|
310
313
|
color: inherit;
|
|
311
|
-
|
|
314
|
+
margin-block: calc(-1 * var(--zx-table-sort-slack));
|
|
315
|
+
padding: var(--zx-table-sort-slack) 0;
|
|
312
316
|
/* The header's type, not a control's: base.css sets buttons in the control size and weight, and
|
|
313
317
|
the browser resets their tracking, so a sortable label would not match its unsortable
|
|
314
318
|
neighbours. */
|
|
@@ -405,13 +409,77 @@
|
|
|
405
409
|
}
|
|
406
410
|
|
|
407
411
|
|
|
412
|
+
/*
|
|
413
|
+
* The selection checkboxes are drawn rather than native. A native checkbox paints its glyph across
|
|
414
|
+
* its whole box, so the only way to give it a 24px target (WCAG 2.5.8) would be a 24px glyph. Here
|
|
415
|
+
* the box stays the --zx-space-4 square it always was and a transparent border around it makes up
|
|
416
|
+
* the rest of the target — the inset shadow that draws the box's edge sits inside that border.
|
|
417
|
+
* Forced colours get the native control back, which the system paints in its own colours.
|
|
418
|
+
*/
|
|
408
419
|
.zx-table__checkbox {
|
|
409
|
-
|
|
420
|
+
--zx-table-checkbox-slack: max(0px, (24px - var(--zx-space-4)) / 2);
|
|
421
|
+
appearance: none;
|
|
422
|
+
display: grid;
|
|
423
|
+
place-content: center;
|
|
424
|
+
box-sizing: content-box;
|
|
410
425
|
inline-size: var(--zx-space-4);
|
|
411
|
-
min-block-size: auto;
|
|
412
426
|
block-size: var(--zx-space-4);
|
|
427
|
+
min-block-size: auto;
|
|
413
428
|
margin: 0 auto;
|
|
414
|
-
|
|
429
|
+
border: var(--zx-table-checkbox-slack) solid transparent;
|
|
430
|
+
border-radius: calc(var(--zx-radius-sm) + var(--zx-table-checkbox-slack));
|
|
431
|
+
padding: 0;
|
|
432
|
+
background: var(--zx-color-bg-control) padding-box;
|
|
433
|
+
box-shadow: inset 0 0 0 1px var(--zx-color-text-subtle);
|
|
434
|
+
color: var(--zx-color-on-accent);
|
|
435
|
+
cursor: pointer;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.zx-table__checkbox::before {
|
|
439
|
+
content: "";
|
|
440
|
+
inline-size: calc(var(--zx-space-4) * .625);
|
|
441
|
+
block-size: calc(var(--zx-space-4) * .625);
|
|
442
|
+
background: currentColor;
|
|
443
|
+
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0, 43% 62%);
|
|
444
|
+
visibility: hidden;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.zx-table__checkbox:hover:not(:disabled) {
|
|
448
|
+
box-shadow: inset 0 0 0 1px var(--zx-color-text-muted);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.zx-table__checkbox:is(:checked, :indeterminate) {
|
|
452
|
+
background-color: var(--zx-color-accent);
|
|
453
|
+
box-shadow: inset 0 0 0 1px var(--zx-color-accent);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.zx-table__checkbox:is(:checked, :indeterminate)::before {
|
|
457
|
+
visibility: visible;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.zx-table__checkbox:indeterminate::before {
|
|
461
|
+
clip-path: inset(40% 5%);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.zx-table__checkbox:focus-visible {
|
|
465
|
+
outline-color: transparent;
|
|
466
|
+
outline-offset: calc(-1 * var(--zx-table-checkbox-slack));
|
|
467
|
+
box-shadow: inset 0 0 0 1px var(--zx-color-accent), var(--zx-focus-ring);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.zx-table__checkbox:disabled {
|
|
471
|
+
opacity: .5;
|
|
472
|
+
cursor: not-allowed;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
@media (forced-colors: active) {
|
|
476
|
+
.zx-table__checkbox {
|
|
477
|
+
appearance: auto;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.zx-table__checkbox::before {
|
|
481
|
+
content: none;
|
|
482
|
+
}
|
|
415
483
|
}
|
|
416
484
|
|
|
417
485
|
.zx-table__empty {
|
|
@@ -455,7 +523,7 @@
|
|
|
455
523
|
}
|
|
456
524
|
|
|
457
525
|
.zx-table__tree-toggle:focus-visible {
|
|
458
|
-
outline:
|
|
526
|
+
outline-color: transparent;
|
|
459
527
|
box-shadow: var(--zx-focus-ring);
|
|
460
528
|
}
|
|
461
529
|
|
|
@@ -519,13 +587,20 @@
|
|
|
519
587
|
cursor: cell;
|
|
520
588
|
}
|
|
521
589
|
|
|
522
|
-
/*
|
|
523
|
-
be painted over by the neighbouring cell.
|
|
524
|
-
.
|
|
525
|
-
|
|
590
|
+
/* Rows and cells carry the grid's roving tabindex, so their ring sits inside the box — an outer
|
|
591
|
+
ring would be painted over by the neighbouring row or cell, or clipped by the scroll region. The
|
|
592
|
+
outline stays, transparent, for forced colours to paint. */
|
|
593
|
+
.zx-table__body :is(tr[data-row], td, th)[tabindex]:focus-visible {
|
|
594
|
+
outline-color: transparent;
|
|
595
|
+
outline-offset: -2px;
|
|
526
596
|
box-shadow: inset var(--zx-focus-ring);
|
|
527
597
|
}
|
|
528
598
|
|
|
599
|
+
/* A row moved to by keyboard scrolls clear of the sticky header rather than behind it. */
|
|
600
|
+
.zx-table__body > tr[data-row] {
|
|
601
|
+
scroll-margin-block: calc(var(--zx-control-height) + var(--zx-space-2)) var(--zx-space-2);
|
|
602
|
+
}
|
|
603
|
+
|
|
529
604
|
.zx-table__body td[data-editing="true"] {
|
|
530
605
|
position: relative;
|
|
531
606
|
}
|