@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
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
padding-inline: var(--zx-space-2);
|
|
30
30
|
color: var(--zx-color-accent);
|
|
31
31
|
font-size: var(--zx-text-xl);
|
|
32
|
-
font-weight:
|
|
32
|
+
font-weight: 650;
|
|
33
|
+
letter-spacing: var(--zx-tracking-title);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
.zx-navigation-bar__navigation {
|
|
@@ -51,10 +52,11 @@
|
|
|
51
52
|
gap: var(--zx-space-2);
|
|
52
53
|
border: 0;
|
|
53
54
|
border-radius: var(--zx-control-radius);
|
|
54
|
-
background:
|
|
55
|
+
background: transparent;
|
|
55
56
|
padding-inline: var(--zx-control-pad-x);
|
|
56
57
|
color: var(--zx-color-text-muted);
|
|
57
58
|
font: inherit;
|
|
59
|
+
font-weight: var(--zx-weight-medium);
|
|
58
60
|
text-decoration: none;
|
|
59
61
|
cursor: pointer;
|
|
60
62
|
}
|
|
@@ -64,11 +66,12 @@
|
|
|
64
66
|
color: var(--zx-color-text);
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
/* Where you are, not a pick: the neutral current fill, the full text colour, a heavier label. */
|
|
67
70
|
.zx-navigation-bar__item[aria-current="page"],
|
|
68
71
|
.zx-navigation-bar__item[aria-selected="true"] {
|
|
69
|
-
background: var(--zx-color-bg-
|
|
70
|
-
color: var(--zx-color-
|
|
71
|
-
font-weight:
|
|
72
|
+
background: var(--zx-color-bg-current);
|
|
73
|
+
color: var(--zx-color-text);
|
|
74
|
+
font-weight: var(--zx-weight-semibold);
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
.zx-navigation-bar__item[aria-disabled="true"] {
|
|
@@ -81,18 +84,24 @@
|
|
|
81
84
|
box-shadow: var(--zx-focus-ring);
|
|
82
85
|
}
|
|
83
86
|
|
|
87
|
+
/* A count beside a label, not a call to action: neutral, like the Tabbox count. */
|
|
84
88
|
.zx-navigation-bar__badge {
|
|
85
89
|
min-inline-size: var(--zx-space-4);
|
|
86
90
|
border-radius: var(--zx-radius-full);
|
|
87
|
-
background: var(--zx-color-
|
|
91
|
+
background: var(--zx-color-bg-current);
|
|
88
92
|
padding-inline: var(--zx-space-1);
|
|
89
|
-
color: var(--zx-color-
|
|
90
|
-
font-size: var(--zx-text-
|
|
91
|
-
font-
|
|
93
|
+
color: var(--zx-color-text-muted);
|
|
94
|
+
font-size: var(--zx-text-xs);
|
|
95
|
+
font-variant-numeric: tabular-nums;
|
|
96
|
+
font-weight: var(--zx-weight-medium);
|
|
92
97
|
line-height: var(--zx-space-4);
|
|
93
98
|
text-align: center;
|
|
94
99
|
}
|
|
95
100
|
|
|
101
|
+
.zx-navigation-bar__item:is([aria-current="page"], [aria-selected="true"]) .zx-navigation-bar__badge {
|
|
102
|
+
color: var(--zx-color-text);
|
|
103
|
+
}
|
|
104
|
+
|
|
96
105
|
.zx-navigation-bar__more {
|
|
97
106
|
display: none;
|
|
98
107
|
}
|
|
@@ -11,6 +11,7 @@ export function registerNumberFieldAdapter() {
|
|
|
11
11
|
label: options.label ? String(options.label) : undefined,
|
|
12
12
|
placeholder: options.placeholder,
|
|
13
13
|
required: options.required,
|
|
14
|
+
name: field.name || null,
|
|
14
15
|
...(options.props ?? {})
|
|
15
16
|
}));
|
|
16
17
|
let setting = false;
|
|
@@ -34,7 +35,8 @@ export function registerNumberFieldAdapter() {
|
|
|
34
35
|
}
|
|
35
36
|
},
|
|
36
37
|
focus: () => numberField.focus(),
|
|
37
|
-
setDisabled: (value) => (value ? numberField.disable() : numberField.enable())
|
|
38
|
+
setDisabled: (value) => (value ? numberField.disable() : numberField.enable()),
|
|
39
|
+
setReadonly: (value) => { numberField.setReadonly(value); }
|
|
38
40
|
};
|
|
39
41
|
});
|
|
40
42
|
}
|
|
@@ -75,3 +75,73 @@
|
|
|
75
75
|
border-end-end-radius: var(--zx-control-radius);
|
|
76
76
|
border-inline-start: 1px solid var(--zx-color-border-control);
|
|
77
77
|
}
|
|
78
|
+
|
|
79
|
+
/* In a Field the control fills its column, as a text input does, and gives way in a narrow one. */
|
|
80
|
+
.zx-field__control > .zx-number-field {
|
|
81
|
+
display: flex;
|
|
82
|
+
inline-size: 100%;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.zx-field__control > .zx-number-field .zx-number-field__input {
|
|
86
|
+
min-inline-size: 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* Quiet fields. Inside `[data-zx-fields="quiet"]` the number reads as text: no border, no fill,
|
|
91
|
+
* and the value at the start, where the text of the quiet fields around it starts. The steppers
|
|
92
|
+
* move after it, both at the trailing end, and stay hidden until hover or focus — kept in the
|
|
93
|
+
* layout, so nothing moves when they return. Focus brings back the border, the control fill and
|
|
94
|
+
* the focus ring. Disabled and read-only fields never light up.
|
|
95
|
+
*/
|
|
96
|
+
[data-zx-fields="quiet"] .zx-number-field {
|
|
97
|
+
border-color: transparent;
|
|
98
|
+
background: transparent;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
[data-zx-fields="quiet"] .zx-number-field__input {
|
|
102
|
+
padding-inline-start: var(--zx-control-pad-x);
|
|
103
|
+
text-align: start;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
[data-zx-fields="quiet"] .zx-number-field__step {
|
|
107
|
+
border-color: transparent;
|
|
108
|
+
opacity: 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
[data-zx-fields="quiet"] .zx-number-field__step[data-step="down"] {
|
|
112
|
+
order: 1;
|
|
113
|
+
border-inline-end: 0;
|
|
114
|
+
border-inline-start: 1px solid transparent;
|
|
115
|
+
border-radius: 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
[data-zx-fields="quiet"] .zx-number-field__step[data-step="up"] {
|
|
119
|
+
order: 2;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
[data-zx-fields="quiet"] .zx-number-field:not([data-disabled], [data-readonly]):hover {
|
|
123
|
+
background: var(--zx-color-bg-hover);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
[data-zx-fields="quiet"] .zx-number-field:not([data-disabled], [data-readonly]):is(:hover, :focus-within) .zx-number-field__step {
|
|
127
|
+
opacity: 1;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
[data-zx-fields="quiet"] .zx-number-field:not([data-disabled], [data-readonly]):focus-within {
|
|
131
|
+
border-color: var(--zx-color-border-control);
|
|
132
|
+
background: var(--zx-color-bg-control);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
[data-zx-fields="quiet"] .zx-number-field:not([data-disabled], [data-readonly]):focus-within .zx-number-field__step {
|
|
136
|
+
border-color: var(--zx-color-border-control);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
140
|
+
[data-zx-fields="quiet"] .zx-number-field {
|
|
141
|
+
transition: background-color var(--zx-dur-fast) var(--zx-ease), border-color var(--zx-dur-fast) var(--zx-ease);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
[data-zx-fields="quiet"] .zx-number-field__step {
|
|
145
|
+
transition: opacity var(--zx-dur-fast) var(--zx-ease), border-color var(--zx-dur-fast) var(--zx-ease);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -336,6 +336,8 @@ export class NumberField extends Component {
|
|
|
336
336
|
/** @param {KeyboardEvent} event @returns {void} */
|
|
337
337
|
_onKeydown(event) {
|
|
338
338
|
if (this._disabled || this._readonly) return;
|
|
339
|
+
// Chords belong to the page — Mod+Enter submits the form or dialog the field is in.
|
|
340
|
+
if (event.altKey || event.ctrlKey || event.metaKey) return;
|
|
339
341
|
const large = Math.max(1, Math.trunc(Number(this.options.largeStep) || 1));
|
|
340
342
|
const actions = {
|
|
341
343
|
ArrowUp: () => this._stepBy(1),
|
|
@@ -1,9 +1,10 @@
|
|
|
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
|
|
6
|
-
* height, radius, and focus ring;
|
|
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
|
+
* height, radius, and focus ring; the current page takes the neutral current fill — it is where
|
|
7
|
+
* you are, not a call to action.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
.zx-pagination {
|
|
@@ -21,8 +22,8 @@
|
|
|
21
22
|
white-space: nowrap;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
/* Absorbs the slack, so summary and
|
|
25
|
-
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. */
|
|
26
27
|
.zx-pagination__spacer {
|
|
27
28
|
flex: 1 1 var(--zx-space-4);
|
|
28
29
|
}
|
|
@@ -42,7 +43,7 @@
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
.zx-pagination__select:focus-visible {
|
|
45
|
-
outline:
|
|
46
|
+
outline-color: transparent;
|
|
46
47
|
box-shadow: var(--zx-focus-ring);
|
|
47
48
|
}
|
|
48
49
|
|
|
@@ -70,15 +71,16 @@
|
|
|
70
71
|
font-variant-numeric: tabular-nums;
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
/* The page you are on: the neutral current fill and a heavier figure, not a solid accent. */
|
|
73
75
|
.zx-pagination__page[aria-current="page"] {
|
|
74
|
-
border-color:
|
|
75
|
-
background: var(--zx-color-
|
|
76
|
-
color: var(--zx-color-
|
|
76
|
+
border-color: transparent;
|
|
77
|
+
background: var(--zx-color-bg-current);
|
|
78
|
+
color: var(--zx-color-text);
|
|
79
|
+
font-weight: var(--zx-weight-semibold);
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
.zx-pagination__page[aria-current="page"]:hover:not(:disabled) {
|
|
80
|
-
|
|
81
|
-
background: var(--zx-color-accent-hover);
|
|
83
|
+
background: var(--zx-color-bg-current);
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
/* Decorative gap marker: never focusable, never announced. */
|
|
@@ -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
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
padding: var(--zx-space-3) var(--zx-space-4);
|
|
36
36
|
color: var(--zx-color-text);
|
|
37
37
|
font: inherit;
|
|
38
|
-
font-weight:
|
|
38
|
+
font-weight: var(--zx-weight-semibold);
|
|
39
39
|
text-align: start;
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -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
|
}
|
|
@@ -30,6 +30,13 @@
|
|
|
30
30
|
clip-path: inset(0 calc(100% - var(--zx-rating-fill)) 0 0);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/* The icons are outlines; a chosen star is a solid one, so the two states differ in shape as well
|
|
34
|
+
as in colour. */
|
|
35
|
+
.zx-rating__glyph[data-part="on"],
|
|
36
|
+
.zx-rating__glyph[data-part="on"] .zx-icon {
|
|
37
|
+
fill: currentColor;
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
.zx-rating__symbol[data-preview] .zx-rating__glyph[data-part="on"] {
|
|
34
41
|
color: var(--zx-color-warning);
|
|
35
42
|
opacity: 0.75;
|
|
@@ -69,7 +76,7 @@
|
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
.zx-rating__radio:focus-visible {
|
|
72
|
-
outline:
|
|
79
|
+
outline-color: transparent;
|
|
73
80
|
box-shadow: var(--zx-focus-ring);
|
|
74
81
|
}
|
|
75
82
|
|
|
@@ -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;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isCssColor } from '../../core/util.js';
|
|
2
|
+
|
|
3
|
+
/** A reference to one custom property, such as `var(--zx-color-tone-blue-fg)`. */
|
|
4
|
+
const TOKEN_REFERENCE = /^var\(\s*--[\w-]+\s*\)$/;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Accepts an item colour for an inline custom property: a CSS colour literal (`#0b7285`, `rgb(…)`,
|
|
8
|
+
* `oklch(…)`, a named colour) or a reference to a design token (`var(--zx-color-tone-blue-fg)`),
|
|
9
|
+
* so an item can follow the theme rather than fix one colour for light and dark alike. Anything
|
|
10
|
+
* else — other functions, declarations, a `var()` with a fallback — is refused.
|
|
11
|
+
*
|
|
12
|
+
* Shared by `Select.entity()` and `TagPicker`, the two components that take a colour per item.
|
|
13
|
+
*
|
|
14
|
+
* @param {unknown} value Candidate colour.
|
|
15
|
+
* @returns {string|null} The trimmed colour, or null when the value is not one.
|
|
16
|
+
*/
|
|
17
|
+
export function itemColor(value) {
|
|
18
|
+
if (typeof value !== 'string') return null;
|
|
19
|
+
const text = value.trim();
|
|
20
|
+
if (TOKEN_REFERENCE.test(text)) return text;
|
|
21
|
+
return isCssColor(text) ? text : null;
|
|
22
|
+
}
|
|
@@ -14,6 +14,8 @@ export function registerSelectFieldAdapter() {
|
|
|
14
14
|
if (event.target.closest?.('.zx-field__label')) field.adapter.focus();
|
|
15
15
|
});
|
|
16
16
|
delete selectOptions.preset;
|
|
17
|
+
// The priority preset offers "No priority" (null) by default; a required field cannot hold it.
|
|
18
|
+
if (preset === 'priority' && props.clearable === undefined) selectOptions.clearable = !options.required;
|
|
17
19
|
const select = field.own(preset === 'priority'
|
|
18
20
|
? Select.priority(null, selectOptions)
|
|
19
21
|
: preset === 'status'
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { h } from '../../core/dom.js';
|
|
2
|
+
import { icon } from '../../core/icons.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The shapes a workflow-status glyph can take. Each status reads by shape first and colour
|
|
6
|
+
* second, so the set stays legible to readers who do not see the colour.
|
|
7
|
+
* @typedef {'dotted'|'ring'|'progress'|'review'|'ready'|'waiting'|'done'|'canceled'|'duplicate'} StatusIndicator
|
|
8
|
+
*/
|
|
9
|
+
/** @typedef {'muted'|'neutral'|'warning'|'info'|'accent'|'success'|'danger'} StatusTone */
|
|
10
|
+
/**
|
|
11
|
+
* The built-in workflow statuses. `review` and `cancelled` are accepted as spellings of
|
|
12
|
+
* `in-review` and `canceled`.
|
|
13
|
+
* @typedef {'backlog'|'todo'|'in-progress'|'in-review'|'review'|'ready'|'waiting'|'done'|'canceled'|'cancelled'|'duplicate'} StatusId
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} StatusIconOptions
|
|
17
|
+
* @property {StatusIndicator} [indicator] Shape, overriding the status's own.
|
|
18
|
+
* @property {StatusTone} [tone] Colour, overriding the status's own.
|
|
19
|
+
* @property {string|null} [label=null] Accessible name. Without one the glyph is decorative
|
|
20
|
+
* (`aria-hidden`) and must sit beside text, or inside a control with a tooltip, that says the
|
|
21
|
+
* status.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {Object} PriorityIconOptions
|
|
25
|
+
* @property {string|null} [label=null] Accessible name. Without one the glyph is decorative
|
|
26
|
+
* (`aria-hidden`) and must sit beside text, or inside a control with a tooltip, that says the
|
|
27
|
+
* priority.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/** Every shape `statusIcon()` draws. */
|
|
31
|
+
export const STATUS_INDICATORS = Object.freeze([
|
|
32
|
+
'dotted', 'ring', 'progress', 'review', 'ready', 'waiting', 'done', 'canceled', 'duplicate'
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
/** Every colour a status glyph takes, from the semantic set. */
|
|
36
|
+
export const STATUS_TONES = Object.freeze([
|
|
37
|
+
'muted', 'neutral', 'warning', 'info', 'accent', 'success', 'danger'
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The built-in statuses in workflow order, as `[id, message key, English name, indicator, tone]`.
|
|
42
|
+
* `Select.status()` lists them; `statusIcon()` draws them.
|
|
43
|
+
*/
|
|
44
|
+
export const DEFAULT_STATUSES = Object.freeze([
|
|
45
|
+
['backlog', 'status.backlog', 'Backlog', 'dotted', 'muted'],
|
|
46
|
+
['todo', 'status.todo', 'Todo', 'ring', 'neutral'],
|
|
47
|
+
['in-progress', 'status.inProgress', 'In Progress', 'progress', 'warning'],
|
|
48
|
+
['in-review', 'status.inReview', 'In Review', 'review', 'info'],
|
|
49
|
+
['ready', 'status.ready', 'Ready to Merge', 'ready', 'accent'],
|
|
50
|
+
['waiting', 'status.waiting', 'Waiting for feedback', 'waiting', 'warning'],
|
|
51
|
+
['done', 'status.done', 'Done', 'done', 'info'],
|
|
52
|
+
['canceled', 'status.canceled', 'Canceled', 'canceled', 'muted'],
|
|
53
|
+
['duplicate', 'status.duplicate', 'Duplicate', 'duplicate', 'muted']
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
/** Other spellings of the built-in status ids. */
|
|
57
|
+
const STATUS_ALIASES = Object.freeze({ review: 'in-review', cancelled: 'canceled' });
|
|
58
|
+
|
|
59
|
+
/** The top priority level, the one coloured level: a danger square with an exclamation mark. */
|
|
60
|
+
export const PRIORITY_URGENT = 4;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resolves the shape and colour of a status glyph.
|
|
64
|
+
*
|
|
65
|
+
* A status is a built-in id (or one of its other spellings), or a status item of the kind
|
|
66
|
+
* `Select.status({items})` takes, whose own `indicator` and `tone` win over the built-in status
|
|
67
|
+
* its `ID` names. Options win over both. An unknown shape falls back to the empty ring and an
|
|
68
|
+
* unknown colour to muted, as `Select.status()` does with a custom item.
|
|
69
|
+
* @param {StatusId|string|{ID?: unknown, id?: unknown, indicator?: unknown, tone?: unknown}|null|undefined} status
|
|
70
|
+
* @param {StatusIconOptions} [options={}] Overrides.
|
|
71
|
+
* @returns {{indicator: StatusIndicator, tone: StatusTone}}
|
|
72
|
+
*/
|
|
73
|
+
export function resolveStatusGlyph(status, options = {}) {
|
|
74
|
+
const item = status && typeof status === 'object' ? status : null;
|
|
75
|
+
const id = normalizeStatusId(item ? (item.ID ?? item.id) : status);
|
|
76
|
+
const preset = DEFAULT_STATUSES.find(([key]) => key === id);
|
|
77
|
+
const indicator = String(options.indicator ?? item?.indicator ?? preset?.[3] ?? 'ring');
|
|
78
|
+
const tone = String(options.tone ?? item?.tone ?? preset?.[4] ?? 'muted');
|
|
79
|
+
return {
|
|
80
|
+
indicator: /** @type {StatusIndicator} */ (STATUS_INDICATORS.includes(indicator) ? indicator : 'ring'),
|
|
81
|
+
tone: /** @type {StatusTone} */ (STATUS_TONES.includes(tone) ? tone : 'muted')
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Maps a status id to its built-in spelling: trimmed, lower-cased, and `review` / `cancelled`
|
|
87
|
+
* read as `in-review` / `canceled`.
|
|
88
|
+
* @param {unknown} id Status id.
|
|
89
|
+
* @returns {string}
|
|
90
|
+
*/
|
|
91
|
+
export function normalizeStatusId(id) {
|
|
92
|
+
const key = String(id ?? '').trim().toLowerCase();
|
|
93
|
+
return STATUS_ALIASES[key] ?? key;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Settles a priority level: an integer from 0 (lowest) to 4 (highest), or `null` for no priority.
|
|
98
|
+
* Out-of-range numbers clamp; anything that is not a number reads as 0.
|
|
99
|
+
* @param {unknown} level Priority level.
|
|
100
|
+
* @returns {number|null}
|
|
101
|
+
*/
|
|
102
|
+
export function normalizePriorityLevel(level) {
|
|
103
|
+
if (level === null || level === undefined || level === '') return null;
|
|
104
|
+
return Math.max(0, Math.min(PRIORITY_URGENT, Math.trunc(Number(level) || 0)));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Draws the workflow-status glyph `Select.status()` shows, for a list row, a board column, a
|
|
109
|
+
* filter chip, or a heading — so every place a status appears reads as the picker that changes it.
|
|
110
|
+
*
|
|
111
|
+
* Built-in statuses: `backlog` (dotted ring), `todo` (empty ring), `in-progress` (ring with a
|
|
112
|
+
* centre dot), `in-review` (half filled; also `review`), `ready` (a third filled), `waiting` (two
|
|
113
|
+
* thirds filled), `done` (filled, with a check), `canceled` (filled, with a cross; also
|
|
114
|
+
* `cancelled`), and `duplicate` (ring with a slash). Colour only repeats what the shape says.
|
|
115
|
+
* @param {StatusId|string|{ID?: unknown, indicator?: unknown, tone?: unknown}} status Built-in id,
|
|
116
|
+
* or a status item with its own `indicator` and `tone`.
|
|
117
|
+
* @param {StatusIconOptions} [options={}] Shape, colour, and accessible-name overrides.
|
|
118
|
+
* @returns {HTMLSpanElement} A `.zx-status-icon` element, decorative unless `label` is given.
|
|
119
|
+
*/
|
|
120
|
+
export function statusIcon(status, options = {}) {
|
|
121
|
+
const { indicator, tone } = resolveStatusGlyph(status, options ?? {});
|
|
122
|
+
const marker = /** @type {HTMLSpanElement} */ (h('span', {
|
|
123
|
+
class: 'zx-status-icon',
|
|
124
|
+
dataset: { indicator, tone }
|
|
125
|
+
}));
|
|
126
|
+
if (indicator === 'done') marker.append(icon('check', { size: 8 }));
|
|
127
|
+
if (indicator === 'canceled') marker.append(icon('x', { size: 7 }));
|
|
128
|
+
return name(marker, options?.label);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Draws the priority glyph `Select.priority()` shows. Levels 0–3 are four signal bars in muted
|
|
133
|
+
* ink, filled up to the level; level 4, the highest, is a filled danger square whose "!" the
|
|
134
|
+
* stylesheet draws, so it never joins the text beside it; no priority (`null`) is a row of dashes.
|
|
135
|
+
* @param {number|null} level Priority level, 0 (lowest) to 4 (highest), or `null` for none.
|
|
136
|
+
* @param {PriorityIconOptions} [options={}] Accessible-name option.
|
|
137
|
+
* @returns {HTMLSpanElement} A `.zx-priority-icon` element, decorative unless `label` is given.
|
|
138
|
+
*/
|
|
139
|
+
export function priorityIcon(level, options = {}) {
|
|
140
|
+
const normalized = normalizePriorityLevel(level);
|
|
141
|
+
const glyph = /** @type {HTMLSpanElement} */ (h('span', {
|
|
142
|
+
class: 'zx-priority-icon',
|
|
143
|
+
dataset: { level: normalized === null ? 'none' : normalized }
|
|
144
|
+
}, normalized === PRIORITY_URGENT ? null : Array.from({ length: PRIORITY_URGENT }, (_, index) => h('span', {
|
|
145
|
+
dataset: { filled: String(normalized !== null && index <= normalized) }
|
|
146
|
+
}))));
|
|
147
|
+
return name(glyph, options?.label);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Makes a glyph an image with an accessible name, or hides it from assistive technology.
|
|
152
|
+
* @param {HTMLSpanElement} glyph Glyph element.
|
|
153
|
+
* @param {unknown} label Accessible name, if any.
|
|
154
|
+
* @returns {HTMLSpanElement}
|
|
155
|
+
*/
|
|
156
|
+
function name(glyph, label) {
|
|
157
|
+
const text = label == null ? '' : String(label).trim();
|
|
158
|
+
if (text) {
|
|
159
|
+
glyph.setAttribute('role', 'img');
|
|
160
|
+
glyph.setAttribute('aria-label', text);
|
|
161
|
+
} else {
|
|
162
|
+
glyph.setAttribute('aria-hidden', 'true');
|
|
163
|
+
}
|
|
164
|
+
return glyph;
|
|
165
|
+
}
|