@zeyos/zx 4.4.0 → 5.0.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 +283 -0
- package/README.md +42 -10
- package/dist/types/components/activity-list/activity-list.d.ts +22 -0
- package/dist/types/components/app-sidebar/app-sidebar.d.ts +5 -3
- 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/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/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 +2 -2
- package/dist/types/components/entity-ref/entity-ref.d.ts +2 -2
- package/dist/types/components/field/field.d.ts +24 -2
- package/dist/types/components/fieldset/fieldset.d.ts +19 -1
- package/dist/types/components/file-item/file-item.d.ts +34 -6
- 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 +260 -15
- package/dist/types/components/list-toolbar/list-toolbar.d.ts +1 -1
- package/dist/types/components/menu-button/menu-button.d.ts +33 -5
- package/dist/types/components/message/message.d.ts +119 -17
- package/dist/types/components/modal/modal.d.ts +13 -2
- 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 +11 -3
- 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 +46 -1
- 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/core/breakpoint.d.ts +1 -1
- package/dist/types/core/icons.d.ts +23 -25
- package/dist/types/core/motion.d.ts +59 -0
- package/dist/types/core/overlay-host.d.ts +15 -1
- package/dist/types/index.d.ts +5 -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 +1 -1
- package/dist/types/zeyos/query.d.ts +79 -0
- package/dist/types/zeyos/schema.d.ts +24 -1
- package/dist/types/zeyos/table.d.ts +100 -14
- package/dist/types/zeyos/view.d.ts +10 -0
- package/dist/zx-zeyos.esm.js +1621 -443
- package/dist/zx-zeyos.esm.min.js +53 -5
- package/dist/zx.css +1411 -660
- package/dist/zx.esm.js +1976 -703
- package/dist/zx.esm.min.js +56 -8
- package/dist/zx.global.js +1978 -705
- package/dist/zx.global.min.js +56 -8
- package/dist/zx.min.css +1 -1
- package/docs/api.json +219 -34
- package/docs/llms.md +472 -110
- package/docs/llms.txt +3 -3
- package/package.json +1 -1
- package/src/components/account-menu/account-menu.css +35 -10
- 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 +34 -0
- package/src/components/app-icon/app-icon.css +1 -1
- package/src/components/app-rail/app-rail.css +40 -79
- package/src/components/app-sidebar/app-sidebar.css +109 -174
- package/src/components/app-sidebar/app-sidebar.js +21 -6
- 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/button/button.css +67 -9
- package/src/components/button/button.js +38 -0
- package/src/components/calendar/calendar.css +46 -46
- package/src/components/card/card.css +2 -2
- package/src/components/card-view/card-view.css +9 -7
- 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 +6 -5
- package/src/components/code-editor/code-editor.js +19 -1
- package/src/components/context-menu/context-menu.css +9 -4
- package/src/components/copy/copy-button.css +6 -0
- package/src/components/copy/copy-input.css +8 -3
- package/src/components/data-filter/data-filter.css +5 -4
- 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 +1 -1
- 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/dropdown/dropdown.css +1 -1
- package/src/components/dropdown/dropdown.js +5 -3
- 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 +68 -26
- package/src/components/field-upload/field-upload.css +1 -1
- package/src/components/fieldset/fieldset.css +5 -3
- package/src/components/fieldset/fieldset.js +28 -5
- package/src/components/file-item/file-item.js +144 -10
- package/src/components/filter/filter.js +7 -3
- package/src/components/filter-panel/filter-panel.css +1 -1
- package/src/components/finder/finder.css +15 -13
- 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 +39 -15
- package/src/components/kanban-view/kanban-view.js +4 -4
- package/src/components/launcher/launcher.css +107 -25
- package/src/components/launcher/launcher.js +503 -41
- package/src/components/list-toolbar/list-toolbar.css +13 -8
- package/src/components/list-toolbar/list-toolbar.js +1 -1
- package/src/components/master-panel/master-panel.css +2 -0
- package/src/components/menu-button/menu-button.css +9 -4
- package/src/components/menu-button/menu-button.js +65 -9
- package/src/components/message/message.css +33 -3
- package/src/components/message/message.js +217 -15
- package/src/components/modal/modal.js +18 -3
- package/src/components/navigation-bar/navigation-bar.css +18 -9
- package/src/components/number-field/field-adapter.js +1 -0
- 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 +8 -6
- package/src/components/panel/panel.css +1 -1
- package/src/components/rating/rating.css +7 -0
- 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 +83 -47
- package/src/components/sheet/sheet.css +11 -8
- package/src/components/slider/slider.css +16 -2
- package/src/components/slider/slider.js +1 -1
- package/src/components/sort-control/sort-control.css +4 -1
- package/src/components/stat-tile/stat-tile.css +41 -46
- package/src/components/stat-tile/stat-tile.js +17 -6
- package/src/components/tabbox/tabbox.css +32 -27
- package/src/components/tabbox/tabbox.js +5 -1
- package/src/components/table/table.css +104 -29
- package/src/components/table/table.js +90 -16
- 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 +10 -3
- package/src/components/tree/tree.css +25 -16
- package/src/components/tree/tree.js +31 -0
- package/src/components/value-list/value-list.css +3 -3
- package/src/components/view/record-view.js +1 -1
- package/src/core/breakpoint.js +1 -1
- package/src/core/icons.js +193 -60
- package/src/core/motion.js +147 -0
- package/src/core/overlay-host.js +21 -2
- package/src/index.js +5 -3
- package/src/internal/app-rail.js +1 -2
- package/src/zeyos/connect.js +6 -2
- package/src/zeyos/form.js +67 -27
- package/src/zeyos/index.js +1 -1
- package/src/zeyos/query.js +153 -0
- package/src/zeyos/schema.js +144 -27
- package/src/zeyos/table.js +68 -5
- package/src/zeyos/view.js +6 -0
- package/styles/base.css +56 -0
- package/styles/icons.css +11 -1
- package/styles/tokens/dark.css +113 -37
- package/styles/tokens/global.css +121 -27
- package/styles/tokens/semantic.css +100 -31
- package/styles/tokens/themes.css +7 -6
- package/styles/zx.css +0 -1
- package/styles/tokens/density.css +0 -9
|
@@ -2,15 +2,18 @@
|
|
|
2
2
|
* Four tab appearances, all square-cornered by default:
|
|
3
3
|
*
|
|
4
4
|
* divided (default) flat blocks on a muted track, split by hairline dividers, the active
|
|
5
|
-
* block lifted onto the panel surface
|
|
6
|
-
*
|
|
5
|
+
* block lifted onto the panel surface it opens — the workhorse for a record page or
|
|
6
|
+
* a panel that owns its content box.
|
|
7
7
|
* bracket folder tabs that outline and fuse into the panel below, the active tab bridging
|
|
8
8
|
* the seam — for document-like views where the tabs belong to the sheet.
|
|
9
|
-
* segmented a compact group
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* line a full-width rule with the active tab underlined — for page- and
|
|
13
|
-
* navigation, where the row spans the content it switches.
|
|
9
|
+
* segmented a compact framed group, the active tab on the neutral current fill — for switching
|
|
10
|
+
* a view inside a toolbar or card header, where the row reads as one control rather
|
|
11
|
+
* than as navigation.
|
|
12
|
+
* line a full-width rule with the active tab underlined in the text colour — for page- and
|
|
13
|
+
* section-level navigation, where the row spans the content it switches.
|
|
14
|
+
*
|
|
15
|
+
* The active tab is where you are, not something you picked, so no variant marks it with the
|
|
16
|
+
* accent: it takes the text colour and a heavier label, and its own variant's neutral ground.
|
|
14
17
|
*
|
|
15
18
|
* `divided` and `bracket` draw the panel box themselves; `line` and `segmented` leave the panel
|
|
16
19
|
* bare so the surrounding layout supplies the chrome.
|
|
@@ -57,7 +60,7 @@
|
|
|
57
60
|
color: var(--zx-color-text-muted);
|
|
58
61
|
font: inherit;
|
|
59
62
|
font-size: var(--zx-control-font-size);
|
|
60
|
-
font-weight:
|
|
63
|
+
font-weight: var(--zx-weight-medium);
|
|
61
64
|
white-space: nowrap;
|
|
62
65
|
cursor: pointer;
|
|
63
66
|
}
|
|
@@ -68,6 +71,7 @@
|
|
|
68
71
|
|
|
69
72
|
.zx-tabbox__tab[aria-selected="true"] {
|
|
70
73
|
color: var(--zx-color-text);
|
|
74
|
+
font-weight: var(--zx-weight-semibold);
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
.zx-tabbox__tab[aria-disabled="true"] {
|
|
@@ -90,8 +94,6 @@
|
|
|
90
94
|
|
|
91
95
|
.zx-tabbox[data-variant="divided"] .zx-tabbox__tab {
|
|
92
96
|
block-size: calc(var(--zx-control-height) + 8px);
|
|
93
|
-
/* The top rule is the selected marker; transparent here keeps every block the same height. */
|
|
94
|
-
border-block-start: 2px solid transparent;
|
|
95
97
|
border-inline-end: 1px solid var(--zx-color-border);
|
|
96
98
|
padding-inline: var(--zx-space-4);
|
|
97
99
|
}
|
|
@@ -104,10 +106,9 @@
|
|
|
104
106
|
background: var(--zx-color-bg-hover);
|
|
105
107
|
}
|
|
106
108
|
|
|
109
|
+
/* The active block is lifted onto the panel surface it opens; nothing is docked to its edge. */
|
|
107
110
|
.zx-tabbox[data-variant="divided"] .zx-tabbox__tab[aria-selected="true"] {
|
|
108
|
-
border-block-start-color: var(--zx-color-accent);
|
|
109
111
|
background: var(--zx-color-bg-surface);
|
|
110
|
-
font-weight: 650;
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
.zx-tabbox[data-variant="divided"] .zx-tabbox__tab:focus-visible {
|
|
@@ -141,7 +142,6 @@
|
|
|
141
142
|
.zx-tabbox[data-variant="bracket"] .zx-tabbox__tab[aria-selected="true"] {
|
|
142
143
|
border-color: var(--zx-color-border);
|
|
143
144
|
background: var(--zx-color-bg-surface);
|
|
144
|
-
font-weight: 650;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
.zx-tabbox[data-variant="bracket"] .zx-tabbox__tab[aria-selected="true"]::after {
|
|
@@ -175,9 +175,10 @@
|
|
|
175
175
|
border-block-end-color: var(--zx-color-border-strong);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
/* The underline is the text colour, not the accent: a tab row is navigation, and the screen's
|
|
179
|
+
one solid accent belongs to its primary action. */
|
|
178
180
|
.zx-tabbox[data-variant="line"] .zx-tabbox__tab[aria-selected="true"] {
|
|
179
|
-
border-block-end-color: var(--zx-color-
|
|
180
|
-
font-weight: 600;
|
|
181
|
+
border-block-end-color: var(--zx-color-text);
|
|
181
182
|
}
|
|
182
183
|
|
|
183
184
|
.zx-tabbox[data-variant="line"] .zx-tabbox__tab:focus-visible {
|
|
@@ -188,22 +189,22 @@
|
|
|
188
189
|
|
|
189
190
|
/* ------------------------------------------------------------- segmented variant -- */
|
|
190
191
|
|
|
192
|
+
/* One framed control: a hairline round the group, the segments flat inside it, and the active one
|
|
193
|
+
on the neutral current fill — the same recipe as the view switch in ListToolbar and Calendar. */
|
|
191
194
|
.zx-tabbox[data-variant="segmented"] .zx-tabbox__tablist {
|
|
192
195
|
inline-size: fit-content;
|
|
193
196
|
max-inline-size: 100%;
|
|
194
197
|
align-items: center;
|
|
195
198
|
gap: 2px;
|
|
196
199
|
block-size: var(--zx-control-height);
|
|
200
|
+
border: 1px solid var(--zx-color-border-control);
|
|
197
201
|
border-radius: var(--zx-tabbox-radius);
|
|
198
|
-
|
|
199
|
-
padding: 3px;
|
|
202
|
+
padding: 2px;
|
|
200
203
|
}
|
|
201
204
|
|
|
202
205
|
.zx-tabbox[data-variant="segmented"] .zx-tabbox__tab {
|
|
203
206
|
block-size: 100%;
|
|
204
|
-
/*
|
|
205
|
-
border: 1px solid transparent;
|
|
206
|
-
/* Nested inside the track's 3px padding, so the inner corner is one step tighter. */
|
|
207
|
+
/* Nested inside the frame's 2px padding, so the inner corner is one step tighter. */
|
|
207
208
|
border-radius: max(0px, calc(var(--zx-tabbox-radius) - 2px));
|
|
208
209
|
padding-inline: var(--zx-space-3);
|
|
209
210
|
font-size: var(--zx-text-sm);
|
|
@@ -214,15 +215,12 @@
|
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
.zx-tabbox[data-variant="segmented"] .zx-tabbox__tab[aria-selected="true"] {
|
|
217
|
-
|
|
218
|
-
background: var(--zx-color-bg-control);
|
|
219
|
-
box-shadow: var(--zx-shadow-2);
|
|
218
|
+
background: var(--zx-color-bg-current);
|
|
220
219
|
}
|
|
221
220
|
|
|
222
221
|
.zx-tabbox[data-variant="segmented"] .zx-tabbox__tab:focus-visible {
|
|
223
222
|
z-index: 1;
|
|
224
223
|
outline: 0;
|
|
225
|
-
border-color: var(--zx-color-ring);
|
|
226
224
|
box-shadow: var(--zx-focus-ring);
|
|
227
225
|
}
|
|
228
226
|
|
|
@@ -237,18 +235,25 @@
|
|
|
237
235
|
|
|
238
236
|
/* ---------------------------------------------------------------- badge & close -- */
|
|
239
237
|
|
|
238
|
+
/* A count, not a call to action: a neutral overlay that reads on every variant's ground, in the
|
|
239
|
+
muted text colour, taking the full text colour with its tab. */
|
|
240
240
|
.zx-tabbox__badge {
|
|
241
241
|
min-inline-size: 1.25rem;
|
|
242
242
|
border-radius: var(--zx-radius-full);
|
|
243
|
-
background: var(--zx-color-
|
|
243
|
+
background: var(--zx-color-bg-current);
|
|
244
244
|
padding-inline: var(--zx-space-1);
|
|
245
|
-
color: var(--zx-color-
|
|
245
|
+
color: var(--zx-color-text-muted);
|
|
246
246
|
font-size: var(--zx-text-xs);
|
|
247
|
-
font-
|
|
247
|
+
font-variant-numeric: tabular-nums;
|
|
248
|
+
font-weight: var(--zx-weight-medium);
|
|
248
249
|
line-height: 1.25rem;
|
|
249
250
|
text-align: center;
|
|
250
251
|
}
|
|
251
252
|
|
|
253
|
+
.zx-tabbox__tab[aria-selected="true"] .zx-tabbox__badge {
|
|
254
|
+
color: var(--zx-color-text);
|
|
255
|
+
}
|
|
256
|
+
|
|
252
257
|
/* Chrome comes from the shared .zx-icon-btn ghost utility; only the box is reset here, because
|
|
253
258
|
a full control-height square would widen every closable tab. The extra .zx-tabbox raises the
|
|
254
259
|
specificity to match `.zx-scope .zx-icon-btn` in base.css, which sets the size. */
|
|
@@ -37,6 +37,9 @@ const VARIANTS = new Set(['divided', 'bracket', 'line', 'segmented']);
|
|
|
37
37
|
* the active tab across a full-width rule; `segmented` renders a compact group that reads as one
|
|
38
38
|
* control, for a toolbar or card header.
|
|
39
39
|
* @property {boolean} [keepAlive=true] Keep inactive panel elements mounted.
|
|
40
|
+
* @property {string} [label] Accessible name of the tab list — what the tabs switch between
|
|
41
|
+
* ("Record sections"). A tab list needs one whenever the page has more than one, and screen
|
|
42
|
+
* readers announce it on entry.
|
|
40
43
|
* @property {(event: CustomEvent<{name: string, previous: string|null}>) => void} [onchange] Change callback.
|
|
41
44
|
* @property {(event: CustomEvent<{name: string}>) => void} [onclose] Close callback, fired when the
|
|
42
45
|
* user dismisses a closable tab.
|
|
@@ -101,7 +104,8 @@ export class Tabbox extends Component {
|
|
|
101
104
|
class: 'zx-tabbox__tablist',
|
|
102
105
|
ref: 'tablist',
|
|
103
106
|
role: 'tablist',
|
|
104
|
-
ariaOrientation: 'horizontal'
|
|
107
|
+
ariaOrientation: 'horizontal',
|
|
108
|
+
ariaLabel: this.options.label || null
|
|
105
109
|
});
|
|
106
110
|
const panels = h('div', {
|
|
107
111
|
class: 'zx-tabbox__panels',
|
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
65
65
|
align-items: center;
|
|
66
66
|
gap: var(--zx-space-2);
|
|
67
|
-
min-block-size: var(--zx-control-height);
|
|
67
|
+
min-block-size: calc(var(--zx-control-height) - 2px);
|
|
68
68
|
border-radius: var(--zx-radius-sm);
|
|
69
|
-
padding
|
|
69
|
+
padding: var(--zx-space-1) var(--zx-space-2);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
.zx-table__column-option:hover { background: var(--zx-color-overlay-hover); }
|
|
@@ -132,16 +132,28 @@
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/*
|
|
136
|
+
* The reorder and selection columns are one control height wide, the control centred in the slot.
|
|
137
|
+
* Their cells drop the inline padding: a fixed layout holds the column to exactly that width, and
|
|
138
|
+
* the padding would push the control off its middle. The controls are blocks centred by their
|
|
139
|
+
* margins: inline, they would sit on the text baseline with the descender room left below them, a
|
|
140
|
+
* few pixels above the middle of the row.
|
|
141
|
+
*/
|
|
135
142
|
.zx-table__reorder-column,
|
|
136
|
-
.zx-
|
|
137
|
-
.zx-table__reorder-cell {
|
|
143
|
+
.zx-table__selection-column {
|
|
138
144
|
inline-size: var(--zx-control-height);
|
|
139
|
-
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.zx-table :is(.zx-table__reorder-header, .zx-table__reorder-cell,
|
|
148
|
+
.zx-table__selection-header, .zx-table__selection-cell) {
|
|
149
|
+
inline-size: var(--zx-control-height);
|
|
150
|
+
padding-inline: 0;
|
|
140
151
|
}
|
|
141
152
|
|
|
142
153
|
.zx-table__row-handle {
|
|
143
|
-
display:
|
|
154
|
+
display: grid;
|
|
144
155
|
place-items: center;
|
|
156
|
+
margin-inline: auto;
|
|
145
157
|
inline-size: 26px;
|
|
146
158
|
block-size: 26px;
|
|
147
159
|
min-block-size: 0;
|
|
@@ -171,7 +183,9 @@
|
|
|
171
183
|
border: 1px solid var(--zx-color-border);
|
|
172
184
|
border-radius: var(--zx-radius-md);
|
|
173
185
|
background: var(--zx-color-bg-surface);
|
|
174
|
-
|
|
186
|
+
/* No reserved gutter: a table that does not scroll would carry a blank strip beside its last
|
|
187
|
+
column wherever scrollbars take up room. One that scrolls gets its scrollbar when it does. */
|
|
188
|
+
scrollbar-gutter: auto;
|
|
175
189
|
}
|
|
176
190
|
|
|
177
191
|
/* Loading state (Table.setLoading): dim rows and show an indeterminate top progress bar. */
|
|
@@ -214,6 +228,8 @@
|
|
|
214
228
|
border-spacing: 0;
|
|
215
229
|
background: var(--zx-color-bg-surface);
|
|
216
230
|
color: var(--zx-color-text);
|
|
231
|
+
/* Figures line up down a column whatever the cell's alignment. Only digits change width. */
|
|
232
|
+
font-variant-numeric: tabular-nums;
|
|
217
233
|
}
|
|
218
234
|
|
|
219
235
|
/* Auto layout (intrinsic/px columns): let content set the width and scroll horizontally. */
|
|
@@ -227,23 +243,32 @@
|
|
|
227
243
|
table-layout: fixed;
|
|
228
244
|
}
|
|
229
245
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
246
|
+
/*
|
|
247
|
+
* Rows are separated by the subtle divider, the header from the body by the ordinary one. The
|
|
248
|
+
* header has no band of its own: it sits on the table's surface in the muted text colour at a
|
|
249
|
+
* medium weight, so it labels the columns without competing with the values in them — and stays
|
|
250
|
+
* opaque, which a sticky header has to be.
|
|
251
|
+
*/
|
|
234
252
|
.zx-table th,
|
|
235
253
|
.zx-table td {
|
|
236
|
-
border-block-end: 1px solid var(--zx-table-border-color, var(--zx-color-border));
|
|
254
|
+
border-block-end: 1px solid var(--zx-table-border-color, var(--zx-color-border-subtle));
|
|
237
255
|
padding: var(--zx-space-2) var(--zx-space-3);
|
|
238
256
|
text-align: start;
|
|
239
257
|
vertical-align: middle;
|
|
240
258
|
}
|
|
241
259
|
|
|
242
260
|
.zx-table th {
|
|
243
|
-
|
|
261
|
+
border-block-end-color: var(--zx-table-border-color, var(--zx-color-border));
|
|
262
|
+
background: var(--zx-table-header-bg, var(--zx-color-bg-surface));
|
|
244
263
|
color: var(--zx-color-text-muted);
|
|
245
264
|
font-size: var(--zx-text-sm);
|
|
246
|
-
font-weight:
|
|
265
|
+
font-weight: var(--zx-weight-medium);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* The column the rows are ordered by says so in its label as well as its arrow. */
|
|
269
|
+
.zx-table th[aria-sort="ascending"],
|
|
270
|
+
.zx-table th[aria-sort="descending"] {
|
|
271
|
+
color: var(--zx-color-text);
|
|
247
272
|
}
|
|
248
273
|
|
|
249
274
|
.zx-table__scroll[data-sticky-header="true"] th {
|
|
@@ -284,9 +309,25 @@
|
|
|
284
309
|
background: inherit;
|
|
285
310
|
color: inherit;
|
|
286
311
|
padding: 0;
|
|
312
|
+
/* The header's type, not a control's: base.css sets buttons in the control size and weight, and
|
|
313
|
+
the browser resets their tracking, so a sortable label would not match its unsortable
|
|
314
|
+
neighbours. */
|
|
315
|
+
font: inherit;
|
|
316
|
+
letter-spacing: inherit;
|
|
287
317
|
text-align: inherit;
|
|
288
318
|
}
|
|
289
319
|
|
|
320
|
+
/* A sortable figure column keeps its label over the figures: label at the end edge, the direction
|
|
321
|
+
arrow before it. */
|
|
322
|
+
.zx-table th:is([data-align="end"], [data-align="right"]) .zx-table__sort-button {
|
|
323
|
+
flex-direction: row-reverse;
|
|
324
|
+
justify-content: flex-start;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.zx-table th[data-align="center"] .zx-table__sort-button {
|
|
328
|
+
justify-content: center;
|
|
329
|
+
}
|
|
330
|
+
|
|
290
331
|
.zx-table__sort-button:hover {
|
|
291
332
|
background: var(--zx-table-row-hover-bg, var(--zx-color-bg-hover));
|
|
292
333
|
color: var(--zx-color-text);
|
|
@@ -298,12 +339,12 @@
|
|
|
298
339
|
|
|
299
340
|
.zx-table th[aria-sort="ascending"] .zx-table__sort-button::after {
|
|
300
341
|
content: "↑";
|
|
301
|
-
color: var(--zx-color-
|
|
342
|
+
color: var(--zx-color-text-muted);
|
|
302
343
|
}
|
|
303
344
|
|
|
304
345
|
.zx-table th[aria-sort="descending"] .zx-table__sort-button::after {
|
|
305
346
|
content: "↓";
|
|
306
|
-
color: var(--zx-color-
|
|
347
|
+
color: var(--zx-color-text-muted);
|
|
307
348
|
}
|
|
308
349
|
|
|
309
350
|
.zx-table__body tr:last-child td {
|
|
@@ -314,28 +355,65 @@
|
|
|
314
355
|
background: var(--zx-color-bg-page);
|
|
315
356
|
}
|
|
316
357
|
|
|
358
|
+
/* Checked rows of a multi-selection are picks and keep the accent tint. The selected row of a
|
|
359
|
+
single-selection table — no checkbox column — is the record you are on, the current row of a
|
|
360
|
+
master–detail list, and takes the neutral current fill instead. */
|
|
317
361
|
.zx-table__body tr[aria-selected="true"] {
|
|
318
362
|
background: var(--zx-table-row-selected-bg, var(--zx-color-bg-selected));
|
|
319
363
|
}
|
|
320
364
|
|
|
365
|
+
.zx-table__body tr[aria-selected="true"]:not(:has(> .zx-table__selection-cell)) {
|
|
366
|
+
background: var(--zx-table-row-selected-bg, var(--zx-color-bg-current));
|
|
367
|
+
}
|
|
368
|
+
|
|
321
369
|
.zx-table__body tr[data-row]:hover {
|
|
322
370
|
background: var(--zx-color-bg-hover);
|
|
323
371
|
}
|
|
324
372
|
|
|
373
|
+
/* ------------------------------------------------------------------ plain -- */
|
|
374
|
+
|
|
375
|
+
/*
|
|
376
|
+
* `variant: 'plain'` — for a table inside a card or panel that already draws the box: no outer
|
|
377
|
+
* frame, no header band, no zebra, hairline rows. The cells keep their padding, so the hover and
|
|
378
|
+
* selection fills never run up against the text.
|
|
379
|
+
*/
|
|
380
|
+
.zx-table[data-variant="plain"] .zx-table__scroll {
|
|
381
|
+
border: 0;
|
|
382
|
+
border-radius: 0;
|
|
383
|
+
background: transparent;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.zx-table[data-variant="plain"] .zx-table__table {
|
|
387
|
+
background: transparent;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.zx-table[data-variant="plain"] th {
|
|
391
|
+
background: var(--zx-table-header-bg, var(--zx-color-bg-surface));
|
|
392
|
+
border-block-end-color: var(--zx-table-border-color, var(--zx-color-border-subtle));
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.zx-table[data-variant="plain"] .zx-table__scroll[data-sticky-header="false"] th {
|
|
396
|
+
background: transparent;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.zx-table[data-variant="plain"] .zx-table__scroll[data-zebra="true"] .zx-table__body tr:nth-child(even):not([aria-selected="true"]) {
|
|
400
|
+
background: transparent;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.zx-table[data-variant="plain"] .zx-table__body tr:last-child td {
|
|
404
|
+
border-block-end: 0;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
|
|
325
408
|
.zx-table__checkbox {
|
|
409
|
+
display: block;
|
|
326
410
|
inline-size: var(--zx-space-4);
|
|
327
411
|
min-block-size: auto;
|
|
328
412
|
block-size: var(--zx-space-4);
|
|
329
|
-
margin: 0;
|
|
413
|
+
margin: 0 auto;
|
|
330
414
|
accent-color: var(--zx-color-accent);
|
|
331
415
|
}
|
|
332
416
|
|
|
333
|
-
.zx-table__selection-header,
|
|
334
|
-
.zx-table__selection-cell {
|
|
335
|
-
inline-size: var(--zx-control-height);
|
|
336
|
-
text-align: center;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
417
|
.zx-table__empty {
|
|
340
418
|
padding-block: var(--zx-space-6);
|
|
341
419
|
color: var(--zx-color-text-muted);
|
|
@@ -583,12 +661,9 @@
|
|
|
583
661
|
content: none;
|
|
584
662
|
}
|
|
585
663
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
.zx-table[data-stacked] .zx-table__reorder-cell {
|
|
591
|
-
justify-content: flex-start;
|
|
664
|
+
/* A card's checkbox and handle lead their line, where the auto margins would centre them. */
|
|
665
|
+
.zx-table[data-stacked] :is(.zx-table__checkbox, .zx-table__row-handle) {
|
|
666
|
+
margin-inline: 0;
|
|
592
667
|
}
|
|
593
668
|
|
|
594
669
|
|
|
@@ -15,6 +15,7 @@ import { sortRows } from './sort.js';
|
|
|
15
15
|
/** @typedef {'asc'|'desc'} TableSortDirection */
|
|
16
16
|
/** @typedef {false|'single'|'multi'} TableSelectionMode */
|
|
17
17
|
/** @typedef {false|'cell'|'row'} TableEditMode */
|
|
18
|
+
/** @typedef {'default'|'plain'} TableVariant */
|
|
18
19
|
/** @typedef {'text'|'number'|'select'|'date'|'checkbox'|'textarea'|'custom'} TableEditorType */
|
|
19
20
|
/** @typedef {'text'|'number'|'currency'|'percent'|'unit'} TableColumnType */
|
|
20
21
|
/** @typedef {{value: unknown, label: string}} TableEditorOption */
|
|
@@ -59,7 +60,11 @@ import { sortRows } from './sort.js';
|
|
|
59
60
|
/**
|
|
60
61
|
* @typedef {Object} TableColumn
|
|
61
62
|
* @property {string} id Row property and stable column identifier.
|
|
62
|
-
* @property {string} label Visible header label.
|
|
63
|
+
* @property {string} label Visible header label. An empty label leaves the header blank — an
|
|
64
|
+
* icon or actions column — and the header is then named by `ariaLabel`, or by the id.
|
|
65
|
+
* @property {string} [ariaLabel] Accessible name of the column header, and the name the column
|
|
66
|
+
* chooser and the cell editors use. Needed when `label` is empty or terse (`'#'`); include the
|
|
67
|
+
* visible label's words in it when there is one.
|
|
63
68
|
* @property {boolean} [sortable=false] Whether the header changes the active sort.
|
|
64
69
|
* @property {string} [width='auto'] CSS width, `auto`, or an `fr` proportion. `fr` values are
|
|
65
70
|
* converted into proportional percentages on `<col>` elements and enable `table-layout: fixed`;
|
|
@@ -128,6 +133,9 @@ import { sortRows } from './sort.js';
|
|
|
128
133
|
* `setSort()` is inert: sorting a grouped table scatters the positions out of their sections, and
|
|
129
134
|
* doing that silently is worse than not offering it. `sortMode: 'server'` still owns its own order.
|
|
130
135
|
* @property {boolean} [zebra=true] Whether alternate rows use the zebra background.
|
|
136
|
+
* @property {TableVariant} [variant='default'] Frame treatment. `'default'` draws the table's own
|
|
137
|
+
* bordered box; `'plain'` is for a table inside a card or panel that already draws one — no outer
|
|
138
|
+
* frame, no header band, no zebra, hairline row dividers. Reflected as `data-variant` on the root.
|
|
131
139
|
* @property {TableEditMode} [editMode=false] Inline editing mode. `'cell'` edits one cell, `'row'`
|
|
132
140
|
* opens every editable cell of the row together and commits them as a unit. Editing is completely
|
|
133
141
|
* inert while this is `false`.
|
|
@@ -288,6 +296,7 @@ export class Table extends Component {
|
|
|
288
296
|
rowKind: null,
|
|
289
297
|
rowKinds: null,
|
|
290
298
|
zebra: true,
|
|
299
|
+
variant: 'default',
|
|
291
300
|
editMode: false,
|
|
292
301
|
editTrigger: 'double',
|
|
293
302
|
hierarchy: false,
|
|
@@ -361,6 +370,8 @@ export class Table extends Component {
|
|
|
361
370
|
}
|
|
362
371
|
this._reconcileExpanded();
|
|
363
372
|
|
|
373
|
+
if (normalizeTableVariant(this.options.variant) === 'plain') root.dataset.variant = 'plain';
|
|
374
|
+
|
|
364
375
|
this._table = h('table', { class: 'zx-table__table' });
|
|
365
376
|
if (this._hierarchy) this._table.setAttribute('role', 'treegrid');
|
|
366
377
|
this._colgroup = h('colgroup', { class: 'zx-table__columns' });
|
|
@@ -638,7 +649,7 @@ export class Table extends Component {
|
|
|
638
649
|
const column = this._columns.find((candidate) => candidate.id === String(id));
|
|
639
650
|
const index = this.getColumnOrder().indexOf(String(id));
|
|
640
651
|
if (column && index >= 0) {
|
|
641
|
-
this._columnStatus.textContent = `Moved ${column
|
|
652
|
+
this._columnStatus.textContent = `Moved ${tableColumnName(column)} to position ${index + 1} of ${this._columns.length}.`;
|
|
642
653
|
}
|
|
643
654
|
}
|
|
644
655
|
return this;
|
|
@@ -1303,7 +1314,7 @@ export class Table extends Component {
|
|
|
1303
1314
|
class: 'zx-table__column-status', role: 'status', ariaLive: 'polite'
|
|
1304
1315
|
});
|
|
1305
1316
|
const details = h('details', { class: 'zx-table__column-controls' },
|
|
1306
|
-
h('summary', { class: 'zx-
|
|
1317
|
+
h('summary', { class: 'zx-btn', dataset: { size: 'sm' } }, icon('fields'),
|
|
1307
1318
|
this._message('table.columns', 'Columns', [])),
|
|
1308
1319
|
list);
|
|
1309
1320
|
this._renderColumnControls();
|
|
@@ -1330,21 +1341,23 @@ export class Table extends Component {
|
|
|
1330
1341
|
disabled: checked && visible.length === 1,
|
|
1331
1342
|
dataset: { columnId: column.id, columnAction: 'toggle' }
|
|
1332
1343
|
}) : null;
|
|
1344
|
+
// The chooser lists every column by name, a blank-headed one included.
|
|
1345
|
+
const name = tableColumnName(column);
|
|
1333
1346
|
const label = toggle
|
|
1334
|
-
? h('label', { for: controlId }, toggle, h('span', {},
|
|
1335
|
-
: h('span', { class: 'zx-table__column-label' },
|
|
1347
|
+
? h('label', { for: controlId }, toggle, h('span', {}, name))
|
|
1348
|
+
: h('span', { class: 'zx-table__column-label' }, name);
|
|
1336
1349
|
const order = this.options.columnReorder ? h('span', {
|
|
1337
1350
|
class: 'zx-table__column-order', role: 'group',
|
|
1338
|
-
ariaLabel: this._message('table.moveColumn', 'Move %1', [
|
|
1351
|
+
ariaLabel: this._message('table.moveColumn', 'Move %1', [name])
|
|
1339
1352
|
},
|
|
1340
1353
|
h('button', {
|
|
1341
1354
|
class: 'zx-table__column-move', type: 'button', disabled: index === 0,
|
|
1342
|
-
ariaLabel: `Move ${
|
|
1355
|
+
ariaLabel: `Move ${name} earlier`,
|
|
1343
1356
|
dataset: { columnId: column.id, columnAction: 'up' }
|
|
1344
1357
|
}, icon('chevron-up', { size: 11 })),
|
|
1345
1358
|
h('button', {
|
|
1346
1359
|
class: 'zx-table__column-move', type: 'button', disabled: index === this._columns.length - 1,
|
|
1347
|
-
ariaLabel: `Move ${
|
|
1360
|
+
ariaLabel: `Move ${name} later`,
|
|
1348
1361
|
dataset: { columnId: column.id, columnAction: 'down' }
|
|
1349
1362
|
}, icon('chevron-down', { size: 11 }))) : null;
|
|
1350
1363
|
return h('div', { class: 'zx-table__column-option', dataset: { columnId: column.id } }, label, order);
|
|
@@ -1602,16 +1615,23 @@ export class Table extends Component {
|
|
|
1602
1615
|
// What this header was built from, so `_renderBody()` can tell whether it is still the right one.
|
|
1603
1616
|
this._headerSpanning = this._spanning;
|
|
1604
1617
|
for (const column of this._visibleColumns()) {
|
|
1605
|
-
|
|
1618
|
+
// `data-column` on the header and on every body cell, in every mode, so a read-only table
|
|
1619
|
+
// can be styled per column as readily as an editable one.
|
|
1620
|
+
const th = h('th', { scope: 'col', dataset: { column: column.id } });
|
|
1606
1621
|
const align = column.align ?? (isNumericColumn(column) ? 'end' : null);
|
|
1607
1622
|
if (align) th.dataset.align = align;
|
|
1608
1623
|
if (column.headerTitle) th.title = column.headerTitle;
|
|
1624
|
+
// A blank header (an icon or actions column) still needs a name, or every cell beneath it
|
|
1625
|
+
// is announced with an empty column header.
|
|
1626
|
+
const name = tableHeaderName(column);
|
|
1627
|
+
if (name !== null) th.setAttribute('aria-label', name);
|
|
1609
1628
|
// Withheld, not disabled: a header that invites a sort the component will refuse is worse
|
|
1610
1629
|
// than a header that never offered one. The label still reads exactly the same.
|
|
1611
1630
|
if (column.sortable && !this._spanning) {
|
|
1612
1631
|
const button = h('button', {
|
|
1613
1632
|
class: 'zx-table__sort-button',
|
|
1614
|
-
type: 'button'
|
|
1633
|
+
type: 'button',
|
|
1634
|
+
ariaLabel: name ?? undefined
|
|
1615
1635
|
}, h('span', { class: 'zx-table__header-label' }, column.label));
|
|
1616
1636
|
this.listen(button, 'click', () => {
|
|
1617
1637
|
const direction = this._sort?.id === column.id && this._sort.dir === 'asc' ? 'desc' : 'asc';
|
|
@@ -1769,9 +1789,9 @@ export class Table extends Component {
|
|
|
1769
1789
|
// Written unconditionally: `responsive` can be switched on after the rows exist, and a
|
|
1770
1790
|
// stacked cell has no column header above it to say what its value means.
|
|
1771
1791
|
cell.dataset.label = column.label;
|
|
1792
|
+
cell.dataset.column = column.id;
|
|
1772
1793
|
if (column.popin === false) cell.dataset.popin = 'false';
|
|
1773
1794
|
if (this._editMode !== false) {
|
|
1774
|
-
cell.dataset.column = column.id;
|
|
1775
1795
|
if (resolveEditable(column, row) !== false) {
|
|
1776
1796
|
cell.dataset.editable = 'true';
|
|
1777
1797
|
cell.tabIndex = -1;
|
|
@@ -1850,7 +1870,9 @@ export class Table extends Component {
|
|
|
1850
1870
|
return;
|
|
1851
1871
|
}
|
|
1852
1872
|
|
|
1853
|
-
|
|
1873
|
+
// Only an editable table holds a click on a control inside a cell back from the row: there the
|
|
1874
|
+
// click could otherwise open an editor. A read-only table activates the row as it always has.
|
|
1875
|
+
if (this._editMode !== false && isInteractiveCellContent(target)) return;
|
|
1854
1876
|
|
|
1855
1877
|
if (this._editMode !== false && this._editTrigger === 'single') {
|
|
1856
1878
|
const cell = target?.closest('td[data-editable="true"]');
|
|
@@ -1881,7 +1903,7 @@ export class Table extends Component {
|
|
|
1881
1903
|
if (!rowElement || !this._tbody.contains(rowElement)) return;
|
|
1882
1904
|
const meta = this._rowMeta.get(rowElement);
|
|
1883
1905
|
if (!meta) return;
|
|
1884
|
-
if (isInteractiveCellContent(target)) return;
|
|
1906
|
+
if (this._editMode !== false && isInteractiveCellContent(target)) return;
|
|
1885
1907
|
if (this._editMode !== false && this._editTrigger === 'double') {
|
|
1886
1908
|
const cell = target?.closest('td[data-editable="true"]');
|
|
1887
1909
|
if (cell && this._tbody.contains(cell)) {
|
|
@@ -2027,7 +2049,7 @@ export class Table extends Component {
|
|
|
2027
2049
|
...props,
|
|
2028
2050
|
class: textarea ? 'zx-table__editor-input zx-table__editor-textarea' : 'zx-table__editor-input',
|
|
2029
2051
|
...(textarea ? {} : { type: 'text' }),
|
|
2030
|
-
ariaLabel: props.ariaLabel ?? `${column
|
|
2052
|
+
ariaLabel: props.ariaLabel ?? `${tableColumnName(column)} row ${index + 1}`,
|
|
2031
2053
|
value: formatCellValue(column, value, row)
|
|
2032
2054
|
}));
|
|
2033
2055
|
host.append(input);
|
|
@@ -2046,8 +2068,10 @@ export class Table extends Component {
|
|
|
2046
2068
|
const control = entry?.control;
|
|
2047
2069
|
if (!control || typeof control.focus !== 'function') return;
|
|
2048
2070
|
control.focus();
|
|
2049
|
-
// Opening a text editor selects its content, so typing replaces the old value
|
|
2050
|
-
|
|
2071
|
+
// Opening a text, number, or date editor selects its content, so typing replaces the old value
|
|
2072
|
+
// ("6" over "1.00" gives 6, not 1.006). Selected after focus: NumberField rewrites its text on
|
|
2073
|
+
// focus, from the grouped display to a plain re-parseable number.
|
|
2074
|
+
if (editorSelectsOnEntry(entry.type) && typeof control.select === 'function') control.select();
|
|
2051
2075
|
}
|
|
2052
2076
|
|
|
2053
2077
|
/**
|
|
@@ -3008,6 +3032,15 @@ function initialExpanded(option) {
|
|
|
3008
3032
|
return new Set(Array.isArray(option) ? option : []);
|
|
3009
3033
|
}
|
|
3010
3034
|
|
|
3035
|
+
/**
|
|
3036
|
+
* Resolves the `variant` option to one the stylesheet knows; anything else is the default frame.
|
|
3037
|
+
* @param {unknown} value Requested variant.
|
|
3038
|
+
* @returns {TableVariant}
|
|
3039
|
+
*/
|
|
3040
|
+
export function normalizeTableVariant(value) {
|
|
3041
|
+
return value === 'plain' ? 'plain' : 'default';
|
|
3042
|
+
}
|
|
3043
|
+
|
|
3011
3044
|
/** @param {unknown} mode @returns {TableEditMode} */
|
|
3012
3045
|
function normalizeEditMode(mode) {
|
|
3013
3046
|
if (mode === 'cell' || mode === 'row') return mode;
|
|
@@ -3059,6 +3092,47 @@ export function normalizeTableColumns(columns) {
|
|
|
3059
3092
|
});
|
|
3060
3093
|
}
|
|
3061
3094
|
|
|
3095
|
+
/**
|
|
3096
|
+
* The name a column goes by wherever it is named rather than shown: its `ariaLabel`, else its
|
|
3097
|
+
* visible label, else its id. The column chooser lists columns by it and cell editors are labelled
|
|
3098
|
+
* with it, so a column with a blank header still has a name in both.
|
|
3099
|
+
* @param {Partial<TableColumn>|null|undefined} column Column descriptor.
|
|
3100
|
+
* @returns {string} Non-empty name, unless the column has no id either.
|
|
3101
|
+
*/
|
|
3102
|
+
export function tableColumnName(column) {
|
|
3103
|
+
for (const candidate of [column?.ariaLabel, column?.label]) {
|
|
3104
|
+
const text = candidate == null ? '' : String(candidate).trim();
|
|
3105
|
+
if (text) return text;
|
|
3106
|
+
}
|
|
3107
|
+
return String(column?.id ?? '');
|
|
3108
|
+
}
|
|
3109
|
+
|
|
3110
|
+
/**
|
|
3111
|
+
* The accessible name a column header needs on top of its content, or null when its visible label
|
|
3112
|
+
* already names it. That is the `ariaLabel` when one is given, and the column id when the label is
|
|
3113
|
+
* blank — an icon or actions column — so no header is announced as an empty column.
|
|
3114
|
+
* @param {Partial<TableColumn>|null|undefined} column Column descriptor.
|
|
3115
|
+
* @returns {string|null} Name to set as `aria-label`, or null.
|
|
3116
|
+
*/
|
|
3117
|
+
export function tableHeaderName(column) {
|
|
3118
|
+
const label = column?.label == null ? '' : String(column.label).trim();
|
|
3119
|
+
const explicit = column?.ariaLabel == null ? '' : String(column.ariaLabel).trim();
|
|
3120
|
+
if (explicit) return explicit;
|
|
3121
|
+
return label ? null : tableColumnName(column) || null;
|
|
3122
|
+
}
|
|
3123
|
+
|
|
3124
|
+
/**
|
|
3125
|
+
* Whether opening an editor of this type selects its content, so the first keystroke replaces the
|
|
3126
|
+
* value instead of being appended to it. True for the single-line text, number, and date editors;
|
|
3127
|
+
* a textarea keeps its caret, and a select, checkbox, or custom editor keeps its own focus
|
|
3128
|
+
* behaviour.
|
|
3129
|
+
* @param {TableEditorType|string} type Resolved editor type.
|
|
3130
|
+
* @returns {boolean}
|
|
3131
|
+
*/
|
|
3132
|
+
export function editorSelectsOnEntry(type) {
|
|
3133
|
+
return type === 'text' || type === 'number' || type === 'date';
|
|
3134
|
+
}
|
|
3135
|
+
|
|
3062
3136
|
/**
|
|
3063
3137
|
* Reconciles saved or partial column IDs with every current column.
|
|
3064
3138
|
* @param {TableColumn[]} columns Current columns.
|