@zeyos/zx 4.3.4 → 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 +438 -0
- package/README.md +49 -10
- package/dist/types/components/activity-item/activity-item.d.ts +351 -0
- package/dist/types/components/activity-list/activity-list.d.ts +217 -0
- package/dist/types/components/app-sidebar/app-sidebar.d.ts +33 -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 +23 -0
- package/dist/types/components/calendar/calendar.d.ts +19 -0
- package/dist/types/components/card/card.d.ts +7 -0
- package/dist/types/components/card-view/card-view.d.ts +11 -3
- package/dist/types/components/card-view/record-card.d.ts +7 -1
- 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 +149 -0
- package/dist/types/components/code-editor/field-adapter.d.ts +5 -0
- package/dist/types/components/context-menu/context-menu.d.ts +18 -2
- package/dist/types/components/date-picker/date-picker.d.ts +8 -0
- package/dist/types/components/date-picker/time-picker.d.ts +7 -0
- 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 +75 -6
- package/dist/types/components/dropdown/dropdown.d.ts +2 -2
- package/dist/types/components/entity-ref/entity-ref.d.ts +331 -0
- 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 +262 -0
- package/dist/types/components/file-list/file-list.d.ts +171 -0
- package/dist/types/components/filter/filter.d.ts +91 -36
- package/dist/types/components/filter-panel/filter-panel.d.ts +449 -0
- package/dist/types/components/form/form.d.ts +12 -1
- package/dist/types/components/kanban-view/kanban-card.d.ts +146 -0
- package/dist/types/components/kanban-view/kanban-view.d.ts +170 -4
- package/dist/types/components/launcher/launcher.d.ts +278 -26
- package/dist/types/components/list-toolbar/list-toolbar.d.ts +599 -0
- package/dist/types/components/menu-button/menu-button.d.ts +37 -42
- package/dist/types/components/message/message.d.ts +126 -17
- package/dist/types/components/modal/modal.d.ts +61 -2
- package/dist/types/components/navigation-bar/navigation-bar.d.ts +105 -21
- package/dist/types/components/panel/panel.d.ts +7 -0
- package/dist/types/components/search/search.d.ts +7 -0
- 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 +15 -3
- package/dist/types/components/sort-control/sort-control.d.ts +105 -0
- package/dist/types/components/stat-tile/stat-tile.d.ts +288 -0
- package/dist/types/components/tabbox/tabbox.d.ts +16 -0
- package/dist/types/components/table/table.d.ts +190 -1
- package/dist/types/components/table-view/table-view.d.ts +11 -5
- package/dist/types/components/tag-picker/tag-picker.d.ts +21 -4
- package/dist/types/components/timebox/timebox.d.ts +7 -0
- package/dist/types/components/tree/tree.d.ts +13 -0
- package/dist/types/components/value-list/value-list.d.ts +8 -0
- package/dist/types/components/view/record-view.d.ts +96 -5
- package/dist/types/core/breakpoint.d.ts +1 -1
- package/dist/types/core/icons.d.ts +37 -22
- 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 +16 -3
- package/dist/types/internal/app-rail.d.ts +10 -0
- package/dist/types/internal/menu-items.d.ts +134 -0
- 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 +2206 -489
- package/dist/zx-zeyos.esm.min.js +56 -1
- package/dist/zx.css +3322 -724
- package/dist/zx.esm.js +12244 -5809
- package/dist/zx.esm.min.js +58 -3
- package/dist/zx.global.js +12246 -5811
- package/dist/zx.global.min.js +58 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +1683 -146
- package/docs/llms.md +777 -110
- package/docs/llms.txt +14 -8
- package/package.json +1 -1
- package/src/components/account-menu/account-menu.css +35 -10
- package/src/components/activity-item/activity-item.css +137 -0
- package/src/components/activity-item/activity-item.js +602 -0
- package/src/components/activity-list/activity-list.css +99 -0
- package/src/components/activity-list/activity-list.js +409 -0
- package/src/components/app-icon/app-icon.css +1 -1
- package/src/components/app-rail/app-rail.css +191 -22
- package/src/components/app-sidebar/app-sidebar.css +349 -24
- package/src/components/app-sidebar/app-sidebar.js +86 -11
- 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 +74 -8
- package/src/components/button/button.js +44 -1
- package/src/components/calendar/calendar.css +46 -46
- package/src/components/calendar/calendar.js +33 -3
- package/src/components/card/card.css +2 -2
- package/src/components/card/card.js +12 -1
- package/src/components/card-view/card-view.css +10 -7
- package/src/components/card-view/card-view.js +25 -9
- package/src/components/card-view/record-card.js +7 -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 +68 -0
- package/src/components/code-editor/code-editor.js +306 -0
- package/src/components/code-editor/field-adapter.js +34 -0
- package/src/components/context-menu/context-menu.css +87 -5
- package/src/components/context-menu/context-menu.js +22 -30
- 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-picker.js +22 -8
- package/src/components/date-picker/date-range-picker.css +3 -3
- package/src/components/date-picker/month-picker.js +3 -3
- package/src/components/date-picker/time-picker.css +1 -1
- package/src/components/date-picker/time-picker.js +20 -8
- 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 +174 -70
- package/src/components/dropdown/dropdown.css +1 -1
- package/src/components/dropdown/dropdown.js +7 -5
- package/src/components/empty-state/empty-state.css +2 -2
- package/src/components/entity-ref/entity-ref.css +182 -0
- package/src/components/entity-ref/entity-ref.js +496 -0
- 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.css +88 -0
- package/src/components/file-item/file-item.js +505 -0
- package/src/components/file-list/file-list.css +51 -0
- package/src/components/file-list/file-list.js +320 -0
- package/src/components/filter/filter.css +27 -1
- package/src/components/filter/filter.js +205 -47
- package/src/components/filter-panel/filter-panel.css +104 -0
- package/src/components/filter-panel/filter-panel.js +964 -0
- 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 +247 -0
- package/src/components/kanban-view/kanban-view.css +226 -8
- package/src/components/kanban-view/kanban-view.js +255 -12
- package/src/components/launcher/launcher.css +107 -25
- package/src/components/launcher/launcher.js +557 -63
- package/src/components/list-toolbar/list-toolbar.css +194 -0
- package/src/components/list-toolbar/list-toolbar.js +851 -0
- package/src/components/master-panel/master-panel.css +2 -0
- package/src/components/menu-button/menu-button.css +87 -5
- package/src/components/menu-button/menu-button.js +86 -46
- package/src/components/message/message.css +38 -5
- package/src/components/message/message.js +230 -17
- package/src/components/modal/modal.css +15 -0
- package/src/components/modal/modal.js +159 -3
- package/src/components/navigation-bar/navigation-bar.css +95 -11
- package/src/components/navigation-bar/navigation-bar.js +171 -4
- 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/panel/panel.js +13 -2
- package/src/components/rating/rating.css +7 -0
- package/src/components/search/search.js +17 -5
- package/src/components/select/color.js +22 -0
- package/src/components/select/field-adapter.js +6 -1
- package/src/components/select/glyphs.js +165 -0
- package/src/components/select/select.css +138 -45
- package/src/components/select/select.js +86 -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 +20 -0
- package/src/components/sort-control/sort-control.js +140 -0
- package/src/components/stat-tile/stat-tile.css +208 -0
- package/src/components/stat-tile/stat-tile.js +601 -0
- package/src/components/tabbox/tabbox.css +32 -27
- package/src/components/tabbox/tabbox.js +17 -2
- package/src/components/table/table.css +155 -31
- package/src/components/table/table.js +386 -33
- package/src/components/table-view/table-view.css +8 -2
- package/src/components/table-view/table-view.js +21 -6
- package/src/components/tag-picker/tag-picker.css +86 -6
- package/src/components/tag-picker/tag-picker.js +25 -8
- package/src/components/timebox/timebox.css +22 -2
- package/src/components/timebox/timebox.js +18 -5
- 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/value-list/value-list.js +22 -6
- package/src/components/view/record-view.css +5 -0
- package/src/components/view/record-view.js +100 -7
- package/src/core/breakpoint.js +1 -1
- package/src/core/icons.js +259 -57
- package/src/core/motion.js +147 -0
- package/src/core/overlay-host.js +21 -2
- package/src/index.js +30 -3
- package/src/internal/app-rail.js +18 -1
- package/src/internal/menu-items.js +142 -0
- 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/print.css +192 -0
- 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 +11 -1
- package/styles/tokens/density.css +0 -9
package/src/zeyos/schema.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dateToUnixSeconds, unixSecondsToDate } from './query.js';
|
|
1
|
+
import { calendarZone, dateToUnixSeconds, normalizeTimeZone, unixSecondsToDate } from './query.js';
|
|
2
2
|
|
|
3
3
|
/** @typedef {Record<string, unknown>} FieldMeta */
|
|
4
4
|
/**
|
|
@@ -11,6 +11,13 @@ import { dateToUnixSeconds, unixSecondsToDate } from './query.js';
|
|
|
11
11
|
* @property {string[]} [fields] Curated allow-list; its order becomes the output order.
|
|
12
12
|
* @property {string[]} [exclude] Field ids to omit.
|
|
13
13
|
* @property {Record<string, string>} [labels] Per-field label overrides.
|
|
14
|
+
* @property {Record<string, string>} [formats] Per-field semantic format overrides (`date`,
|
|
15
|
+
* `datetime`, `money`, or any registered key). The schema types a timestamp only by its storage,
|
|
16
|
+
* so this is how a Unix-second field the built-in rules misread is declared a calendar day
|
|
17
|
+
* (`date`) or an instant (`datetime`).
|
|
18
|
+
* @property {string} [timeZone] IANA time zone of the ZeyOS instance, such as `Europe/Vienna`.
|
|
19
|
+
* Date-only fields store midnight of a calendar day in this zone; it is copied onto every field's
|
|
20
|
+
* metadata as `timeZone`. Defaults to the runtime's own zone.
|
|
14
21
|
*/
|
|
15
22
|
|
|
16
23
|
/**
|
|
@@ -22,6 +29,49 @@ import { dateToUnixSeconds, unixSecondsToDate } from './query.js';
|
|
|
22
29
|
*/
|
|
23
30
|
export const TYPE_MAP = Object.create(null);
|
|
24
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Unix-second fields ZeyOS stores as calendar days (midnight in the instance's zone) rather than
|
|
34
|
+
* instants: document and due dates, validity ranges, birthdays. `date` counts only when indexed —
|
|
35
|
+
* the unindexed ones (`imports`, `couponcodes`) record when something happened.
|
|
36
|
+
*/
|
|
37
|
+
const DATE_ONLY_FIELDS = new Set(['date', 'duedate', 'birthdate', 'datefrom', 'dateto', 'datecancel', 'lastbilling']);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Fields with a date-only name that hold instants on their resource: appointment and event times,
|
|
41
|
+
* and when a stream record, comment, or document version was written.
|
|
42
|
+
*/
|
|
43
|
+
const INSTANT_FIELDS = new Set([
|
|
44
|
+
'appointments.datefrom', 'appointments.dateto', 'events.datefrom', 'events.dateto',
|
|
45
|
+
'comments.date', 'documentversions.date', 'records.date'
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
/** Readable words for the ZeyOS enum constants that run several words together. */
|
|
49
|
+
const ENUM_WORDS = Object.freeze({
|
|
50
|
+
AWAITINGACCEPTANCE: 'awaiting acceptance',
|
|
51
|
+
AWAITINGAPPROVAL: 'awaiting approval',
|
|
52
|
+
BILLINGONLY: 'billing only',
|
|
53
|
+
CUSTOMERANDSUPPLIER: 'customer and supplier',
|
|
54
|
+
FEEDBACKREQUIRED: 'feedback required',
|
|
55
|
+
INEVALUATION: 'in evaluation',
|
|
56
|
+
INNEGOTIATION: 'in negotiation',
|
|
57
|
+
INREVISION: 'in revision',
|
|
58
|
+
NOSTOCK: 'no stock',
|
|
59
|
+
NOTSTARTED: 'not started',
|
|
60
|
+
PARTLYDELIVERED: 'partly delivered',
|
|
61
|
+
PARTLYINVOICED: 'partly invoiced',
|
|
62
|
+
PARTLYORDERED: 'partly ordered',
|
|
63
|
+
PARTLYPAID: 'partly paid',
|
|
64
|
+
PROCUREMENTONLY: 'procurement only',
|
|
65
|
+
REMOTECALL: 'remote call',
|
|
66
|
+
SERIALSANDCHARGES: 'serials and charges'
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/** Labels for a priority field whose schema carries no enum. */
|
|
70
|
+
const PRIORITY_LABELS = Object.freeze({ 0: 'Lowest', 1: 'Low', 2: 'Medium', 3: 'High', 4: 'Highest' });
|
|
71
|
+
|
|
72
|
+
/** An enum name in the constant style ZeyOS uses for stored values (`INVOICED`, `BILLING_QUOTE`). */
|
|
73
|
+
const ENUM_CONSTANT = /^[A-Z][A-Z\d]*(?:_[A-Z\d]+)*$/;
|
|
74
|
+
|
|
25
75
|
const warnedUnknownTypes = new Set();
|
|
26
76
|
|
|
27
77
|
/**
|
|
@@ -74,7 +124,8 @@ export function fieldToZxField(meta) {
|
|
|
74
124
|
/**
|
|
75
125
|
* Converts normalized ZeyOS field metadata to a Zx Table column configuration. Renderers return
|
|
76
126
|
* strings only (never DOM): references/enums show labels, numeric columns are right-aligned,
|
|
77
|
-
* booleans show a check, and Unix seconds are formatted as dates
|
|
127
|
+
* booleans show a check, and Unix seconds are formatted as dates — a date-only field as the
|
|
128
|
+
* calendar day it names in `meta.timeZone`, with no time; a date-time field with its time.
|
|
78
129
|
* @param {FieldMeta} meta Normalized metadata containing at least `id` or `name`.
|
|
79
130
|
* @returns {Record<string, unknown>} Plain Zx Table column descriptor.
|
|
80
131
|
*/
|
|
@@ -94,6 +145,8 @@ export function fieldToZxColumn(meta) {
|
|
|
94
145
|
* client returns names from `schema.fields(resource)` and definitions from
|
|
95
146
|
* `schema.describe(resource).fields`; embedded `enum` data is authoritative. A compatible
|
|
96
147
|
* `schema.enums(resource, field)` is used when supplied by another client version/stub.
|
|
148
|
+
* `formats` overrides a field's semantic `format`, and `timeZone` is validated once and copied
|
|
149
|
+
* onto every field.
|
|
97
150
|
* @param {Record<string, any>} client Injected `@zeyos/client` instance.
|
|
98
151
|
* @param {string} resource Schema resource name such as `tickets`.
|
|
99
152
|
* @param {ResolveFieldOptions} [opts={}] Curation options.
|
|
@@ -119,6 +172,8 @@ export function resolveFields(client, resource, opts = {}) {
|
|
|
119
172
|
const requested = Array.isArray(opts.fields) ? opts.fields.map(String) : [...available.keys()];
|
|
120
173
|
const excluded = new Set(Array.isArray(opts.exclude) ? opts.exclude.map(String) : []);
|
|
121
174
|
const labels = opts.labels && typeof opts.labels === 'object' ? opts.labels : {};
|
|
175
|
+
const formats = opts.formats && typeof opts.formats === 'object' ? opts.formats : {};
|
|
176
|
+
const timeZone = normalizeTimeZone(opts.timeZone);
|
|
122
177
|
const result = [];
|
|
123
178
|
|
|
124
179
|
for (const id of requested) {
|
|
@@ -131,6 +186,8 @@ export function resolveFields(client, resource, opts = {}) {
|
|
|
131
186
|
if (meta.enum == null) meta.enum = enumValues;
|
|
132
187
|
}
|
|
133
188
|
if (Object.prototype.hasOwnProperty.call(labels, id)) meta.label = String(labels[id]);
|
|
189
|
+
if (Object.prototype.hasOwnProperty.call(formats, id) && formats[id]) meta.format = String(formats[id]);
|
|
190
|
+
if (timeZone) meta.timeZone = timeZone;
|
|
134
191
|
result.push(meta);
|
|
135
192
|
}
|
|
136
193
|
return result;
|
|
@@ -164,7 +221,7 @@ const enumColumn = {
|
|
|
164
221
|
};
|
|
165
222
|
const priorityColumn = {
|
|
166
223
|
field: { type: 'zxselect', props: { preset: 'priority' } },
|
|
167
|
-
column: (meta) => ({ render: enumRenderer(meta,
|
|
224
|
+
column: (meta) => ({ render: enumRenderer(meta, PRIORITY_LABELS) })
|
|
168
225
|
};
|
|
169
226
|
const percentColumn = {
|
|
170
227
|
field: { type: 'float', props: { min: 0, max: 100 } },
|
|
@@ -212,10 +269,13 @@ for (const type of ['email', 'tel', 'url', 'uri']) {
|
|
|
212
269
|
function dateStrategy(type) {
|
|
213
270
|
return {
|
|
214
271
|
field: { type },
|
|
215
|
-
column: (meta) =>
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
272
|
+
column: (meta) => {
|
|
273
|
+
const format = type === 'datetime' ? instantFormatter() : calendarDayFormatter(meta.timeZone);
|
|
274
|
+
return {
|
|
275
|
+
render: (row) => format(rowValue(row, meta)),
|
|
276
|
+
sortValue: (row) => unixSortValue(rowValue(row, meta))
|
|
277
|
+
};
|
|
278
|
+
}
|
|
219
279
|
};
|
|
220
280
|
}
|
|
221
281
|
|
|
@@ -241,9 +301,8 @@ function strategyKey(meta) {
|
|
|
241
301
|
|
|
242
302
|
const raw = normalizeType(meta.type);
|
|
243
303
|
if (raw === 'bigint' || raw === 'int64') {
|
|
244
|
-
const
|
|
245
|
-
if (
|
|
246
|
-
if (/(?:date|time)$/.test(id)) return 'datetime';
|
|
304
|
+
const temporal = timestampKind(meta);
|
|
305
|
+
if (temporal) return temporal;
|
|
247
306
|
}
|
|
248
307
|
if (isLongText(meta, raw)) return 'longtext';
|
|
249
308
|
if (raw && TYPE_MAP[raw]) return raw;
|
|
@@ -255,6 +314,22 @@ function strategyKey(meta) {
|
|
|
255
314
|
return raw || semantic;
|
|
256
315
|
}
|
|
257
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Tells a calendar-day timestamp from an instant by the field's name and resource, since the schema
|
|
319
|
+
* types both as `bigint`.
|
|
320
|
+
* @param {FieldMeta} meta
|
|
321
|
+
* @returns {'date'|'datetime'|null}
|
|
322
|
+
*/
|
|
323
|
+
function timestampKind(meta) {
|
|
324
|
+
const id = String(meta.id).toLowerCase();
|
|
325
|
+
const resource = String(meta.resource ?? '').toLowerCase();
|
|
326
|
+
if (DATE_ONLY_FIELDS.has(id) && !INSTANT_FIELDS.has(`${resource}.${id}`)) {
|
|
327
|
+
if (id !== 'date' || meta.indexed) return 'date';
|
|
328
|
+
}
|
|
329
|
+
if (INSTANT_FIELDS.has(`${resource}.${id}`) || /(?:date|time)$|^lastmodified$/.test(id)) return 'datetime';
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
|
|
258
333
|
/** @param {FieldMeta} meta @returns {FieldMeta & {id: string}} */
|
|
259
334
|
function normalizeMeta(meta) {
|
|
260
335
|
if (!meta || typeof meta !== 'object' || Array.isArray(meta)) throw new TypeError('Field metadata must be an object');
|
|
@@ -362,31 +437,41 @@ function normalizeEnumOptions(values) {
|
|
|
362
437
|
return Object.fromEntries(values.map((entry, index) => {
|
|
363
438
|
if (Array.isArray(entry)) {
|
|
364
439
|
const [value = index, label = value] = entry;
|
|
365
|
-
return [String(value),
|
|
440
|
+
return [String(value), enumLabel(label)];
|
|
366
441
|
}
|
|
367
442
|
if (entry && typeof entry === 'object') {
|
|
368
443
|
const value = entry.value ?? entry.id ?? index;
|
|
369
|
-
return [String(value), String(entry.label
|
|
444
|
+
return [String(value), entry.label != null ? String(entry.label) : enumLabel(entry.name ?? value)];
|
|
370
445
|
}
|
|
371
|
-
return [String(index),
|
|
446
|
+
return [String(index), enumLabel(entry)];
|
|
372
447
|
}));
|
|
373
448
|
}
|
|
374
449
|
if (values && typeof values === 'object') {
|
|
375
|
-
return Object.fromEntries(Object.entries(values).map(([value, label]) => [value,
|
|
450
|
+
return Object.fromEntries(Object.entries(values).map(([value, label]) => [value, enumLabel(label)]));
|
|
376
451
|
}
|
|
377
452
|
return {};
|
|
378
453
|
}
|
|
379
454
|
|
|
455
|
+
/**
|
|
456
|
+
* Turns a stored enum name into its label. A label the schema gives (`{value, label}`) never reaches
|
|
457
|
+
* this; what does is usually a constant such as `INVOICED` or `PARTLYPAID_CANCELLED`, shown in
|
|
458
|
+
* sentence case with its words separated ("Invoiced", "Partly paid cancelled"). Anything else is
|
|
459
|
+
* already a label and is kept as it is.
|
|
460
|
+
* @param {unknown} name Stored enum name.
|
|
461
|
+
* @returns {string}
|
|
462
|
+
*/
|
|
463
|
+
function enumLabel(name) {
|
|
464
|
+
const text = String(name ?? '');
|
|
465
|
+
if (!ENUM_CONSTANT.test(text)) return text;
|
|
466
|
+
const words = text.split('_').map((part) => ENUM_WORDS[part] ?? part.toLowerCase()).join(' ');
|
|
467
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
468
|
+
}
|
|
469
|
+
|
|
380
470
|
/** @param {FieldMeta} meta @returns {'optionlist'|'select'} */
|
|
381
471
|
function enumFieldType(meta) {
|
|
382
472
|
return Object.keys(enumOptions(meta)).length <= 5 ? 'optionlist' : 'select';
|
|
383
473
|
}
|
|
384
474
|
|
|
385
|
-
/** @returns {Record<string, string>} */
|
|
386
|
-
function priorityOptions() {
|
|
387
|
-
return { '0': 'LOWEST', '1': 'LOW', '2': 'MEDIUM', '3': 'HIGH', '4': 'HIGHEST' };
|
|
388
|
-
}
|
|
389
|
-
|
|
390
475
|
/** @param {FieldMeta} meta @param {Record<string, string>} [fallback] @returns {(row: Record<string, unknown>) => string} */
|
|
391
476
|
function enumRenderer(meta, fallback = {}) {
|
|
392
477
|
const labels = { ...fallback, ...enumOptions(meta) };
|
|
@@ -426,17 +511,49 @@ function formatPercent(value) {
|
|
|
426
511
|
return number == null ? '' : `${new Intl.NumberFormat().format(number)}%`;
|
|
427
512
|
}
|
|
428
513
|
|
|
429
|
-
/**
|
|
430
|
-
|
|
431
|
-
|
|
514
|
+
/**
|
|
515
|
+
* Formats a date-time field: the instant, in the viewer's zone, with its time.
|
|
516
|
+
* @returns {(value: unknown) => string}
|
|
517
|
+
*/
|
|
518
|
+
function instantFormatter() {
|
|
519
|
+
const formatter = new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' });
|
|
520
|
+
return (value) => {
|
|
521
|
+
const date = timestampDate(value);
|
|
522
|
+
return date ? formatter.format(date) : '';
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Formats a date-only field: the calendar day the stored midnight names in the instance's zone,
|
|
528
|
+
* with no time, so neither the viewer's zone nor daylight saving can move it.
|
|
529
|
+
* @param {unknown} timeZone Instance zone from the field metadata; the runtime's own when absent.
|
|
530
|
+
* @returns {(value: unknown) => string}
|
|
531
|
+
*/
|
|
532
|
+
function calendarDayFormatter(timeZone) {
|
|
533
|
+
const zone = calendarZone(/** @type {string|undefined} */ (timeZone ?? undefined));
|
|
534
|
+
// The day is formatted as a UTC date so the formatter cannot shift it a second time.
|
|
535
|
+
const formatter = new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeZone: 'UTC' });
|
|
536
|
+
return (value) => {
|
|
537
|
+
const date = timestampDate(value);
|
|
538
|
+
if (!date) return '';
|
|
539
|
+
// A Date value is already a local calendar day (a form's own value, say); seconds are stored.
|
|
540
|
+
const { year, month, day } = value instanceof Date
|
|
541
|
+
? { year: date.getFullYear(), month: date.getMonth(), day: date.getDate() }
|
|
542
|
+
: zone.dayOf(date.getTime());
|
|
543
|
+
const utc = new Date(0);
|
|
544
|
+
utc.setUTCFullYear(year, month, day);
|
|
545
|
+
return formatter.format(utc);
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/** @param {unknown} value Unix seconds or a Date. @returns {Date|null} */
|
|
550
|
+
function timestampDate(value) {
|
|
551
|
+
if (value == null || value === '') return null;
|
|
432
552
|
try {
|
|
433
553
|
const date = value instanceof Date ? value : unixSecondsToDate(/** @type {number|string} */ (value));
|
|
434
|
-
|
|
435
|
-
return new Intl.DateTimeFormat(undefined, includeTime
|
|
436
|
-
? { dateStyle: 'medium', timeStyle: 'short' }
|
|
437
|
-
: { dateStyle: 'medium' }).format(date);
|
|
554
|
+
return date && Number.isFinite(date.getTime()) ? date : null;
|
|
438
555
|
} catch {
|
|
439
|
-
return
|
|
556
|
+
return null;
|
|
440
557
|
}
|
|
441
558
|
}
|
|
442
559
|
|
package/src/zeyos/table.js
CHANGED
|
@@ -4,10 +4,16 @@ import { buildListQuery } from './query.js';
|
|
|
4
4
|
import { fieldToZxColumn, resolveFields } from './schema.js';
|
|
5
5
|
import { normalizeZeyosListResult } from './select.js';
|
|
6
6
|
|
|
7
|
+
/** @typedef {import('../components/table/table.js').TableOptions} TableOptions */
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* @typedef {Object} ZeyosTableOptions
|
|
9
11
|
* @property {string[]} [fields] Curated ordered field allow-list.
|
|
10
12
|
* @property {Record<string, string>} [labels] Per-field label overrides.
|
|
13
|
+
* @property {Record<string, string>} [formats] Per-field semantic format overrides, such as
|
|
14
|
+
* `{ duedate: 'date' }` for a Unix-second field that holds a calendar day.
|
|
15
|
+
* @property {string} [timeZone] IANA time zone of the ZeyOS instance. Date-only columns show the
|
|
16
|
+
* calendar day their stored midnight names in this zone; defaults to the runtime's own zone.
|
|
11
17
|
* @property {string|((row: Record<string, unknown>) => unknown)} [rowId='ID'] Row id accessor.
|
|
12
18
|
* @property {number} [pageSize=50] Server page size.
|
|
13
19
|
* @property {{id: string, dir: 'asc'|'desc'}|null} [sort=null] Initial server sort.
|
|
@@ -16,8 +22,42 @@ import { normalizeZeyosListResult } from './select.js';
|
|
|
16
22
|
* @property {false|'single'|'multi'} [selectable=false] Row selection mode.
|
|
17
23
|
* @property {number|string|null} [height=null] Table scroll-region height.
|
|
18
24
|
* @property {(row: Record<string, unknown>, event: CustomEvent<Record<string, unknown>>) => void} [onRowClick] Row activation callback.
|
|
25
|
+
* @property {TableOptions['variant']} [variant='default'] Table frame treatment; `'plain'` for a
|
|
26
|
+
* table inside a pane or card that already draws the box.
|
|
27
|
+
* @property {boolean} [zebra=true] Whether alternate rows use the zebra background.
|
|
28
|
+
* @property {boolean} [stickyHeader=true] Whether column headers stick while scrolling.
|
|
29
|
+
* @property {TableOptions['responsive']} [responsive=false] Width below which rows stack.
|
|
30
|
+
* @property {TableOptions['growing']} [growing=false] Rows rendered at first from a loaded page.
|
|
31
|
+
* @property {TableOptions['emptyText']} [emptyText=null] What the table shows with no rows.
|
|
32
|
+
* @property {TableOptions['rowClass']} [rowClass=null] Additional row class callback.
|
|
33
|
+
* @property {TableOptions['rowKind']} [rowKind=null] Row property or callback naming each row's kind.
|
|
34
|
+
* @property {TableOptions['rowKinds']} [rowKinds=null] Presentation per row kind.
|
|
35
|
+
* @property {boolean} [columnVisibility=false] Whether a column visibility chooser is rendered.
|
|
36
|
+
* @property {boolean} [columnReorder=false] Whether the column chooser exposes reorder controls.
|
|
37
|
+
* @property {string[]} [hiddenColumns=[]] Initially hidden column ids.
|
|
38
|
+
* @property {TableOptions['onrowclick']} [onrowclick] Table `rowclick` listener, beside `onRowClick`.
|
|
39
|
+
* @property {TableOptions['onrowdblclick']} [onrowdblclick] Table `rowdblclick` listener.
|
|
40
|
+
* @property {TableOptions['onsort']} [onsort] Table `sort` listener; the binding reloads either way.
|
|
41
|
+
* @property {TableOptions['onselectionchange']} [onselectionchange] Table `selectionchange` listener.
|
|
42
|
+
* @property {TableOptions['ondatachange']} [ondatachange] Table `datachange` listener.
|
|
43
|
+
* @property {TableOptions['onstackedchange']} [onstackedchange] Table `stackedchange` listener.
|
|
44
|
+
* @property {TableOptions['ongrow']} [ongrow] Table `grow` listener.
|
|
45
|
+
* @property {TableOptions['oncolumnvisibilitychange']} [oncolumnvisibilitychange] Table `columnvisibilitychange` listener.
|
|
46
|
+
* @property {TableOptions['oncolumnorderchange']} [oncolumnorderchange] Table `columnorderchange` listener.
|
|
19
47
|
*/
|
|
20
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Table options a binding passes through unchanged: presentation and listeners. The binding owns
|
|
51
|
+
* `columns`, `data`, and `sortMode`; editing, row reordering, and `hierarchy` are left out because
|
|
52
|
+
* they rearrange or change rows the server pages and never persists.
|
|
53
|
+
*/
|
|
54
|
+
const TABLE_OPTION_KEYS = [
|
|
55
|
+
'variant', 'zebra', 'stickyHeader', 'responsive', 'growing', 'emptyText', 'rowClass', 'rowKind',
|
|
56
|
+
'rowKinds', 'columnVisibility', 'columnReorder', 'hiddenColumns', 'onrowdblclick', 'onsort',
|
|
57
|
+
'onselectionchange', 'ondatachange', 'onstackedchange', 'ongrow', 'oncolumnvisibilitychange',
|
|
58
|
+
'oncolumnorderchange'
|
|
59
|
+
];
|
|
60
|
+
|
|
21
61
|
/**
|
|
22
62
|
* @typedef {Object} ZeyosTableConfig
|
|
23
63
|
* @property {Record<string, any>[]} fieldMeta Ordered normalized schema metadata.
|
|
@@ -59,7 +99,8 @@ import { normalizeZeyosListResult } from './select.js';
|
|
|
59
99
|
* Builds the DOM-free schema-derived columns and Table options used by {@link zeyosTable}. Entity
|
|
60
100
|
* projections include an `<field>_label` alias from a real label field on the referenced resource.
|
|
61
101
|
* Amount fields use each row's real `currency` value for display while retaining the WP14 typed
|
|
62
|
-
* column strategy and right alignment.
|
|
102
|
+
* column strategy and right alignment. Enum columns show labels, date-only columns calendar days in
|
|
103
|
+
* `timeZone`, and Table presentation options and listeners pass through to `tableOptions`.
|
|
63
104
|
* @param {Record<string, any>} client Injected ZeyOS client instance.
|
|
64
105
|
* @param {string} resource Resource name such as `transactions`.
|
|
65
106
|
* @param {ZeyosTableOptions} [opts={}] Schema, table, and initial query options.
|
|
@@ -68,7 +109,9 @@ import { normalizeZeyosListResult } from './select.js';
|
|
|
68
109
|
export function buildZeyosTableConfig(client, resource, opts = {}) {
|
|
69
110
|
const fieldMeta = resolveFields(client, resource, {
|
|
70
111
|
fields: opts.fields,
|
|
71
|
-
labels: opts.labels
|
|
112
|
+
labels: opts.labels,
|
|
113
|
+
formats: opts.formats,
|
|
114
|
+
timeZone: opts.timeZone
|
|
72
115
|
});
|
|
73
116
|
const projection = {};
|
|
74
117
|
const columns = fieldMeta.map((sourceMeta) => {
|
|
@@ -94,11 +137,16 @@ export function buildZeyosTableConfig(client, resource, opts = {}) {
|
|
|
94
137
|
if (typeof rowId === 'string' && !Object.prototype.hasOwnProperty.call(projection, rowId)) {
|
|
95
138
|
projection[rowId] = rowId;
|
|
96
139
|
}
|
|
140
|
+
const passed = {};
|
|
141
|
+
for (const key of TABLE_OPTION_KEYS) {
|
|
142
|
+
if (opts[key] !== undefined) passed[key] = opts[key];
|
|
143
|
+
}
|
|
97
144
|
return {
|
|
98
145
|
fieldMeta,
|
|
99
146
|
columns,
|
|
100
147
|
projection,
|
|
101
148
|
tableOptions: {
|
|
149
|
+
...passed,
|
|
102
150
|
columns,
|
|
103
151
|
data: [],
|
|
104
152
|
rowId,
|
|
@@ -106,9 +154,7 @@ export function buildZeyosTableConfig(client, resource, opts = {}) {
|
|
|
106
154
|
sortMode: 'server',
|
|
107
155
|
selectable: opts.selectable ?? false,
|
|
108
156
|
height: opts.height ?? null,
|
|
109
|
-
onrowclick:
|
|
110
|
-
? (event) => opts.onRowClick(event.detail.row, event)
|
|
111
|
-
: undefined
|
|
157
|
+
onrowclick: rowClickListener(opts)
|
|
112
158
|
}
|
|
113
159
|
};
|
|
114
160
|
}
|
|
@@ -117,6 +163,8 @@ export function buildZeyosTableConfig(client, resource, opts = {}) {
|
|
|
117
163
|
* Generates a server-backed Zx Table. Sorting automatically reloads page zero. `load({page: 0})`
|
|
118
164
|
* replaces data, higher pages append it, and `loadMore()` uses `limit`/`offset` plus the returned
|
|
119
165
|
* count. DataFilter callers can map state with `dataFilterStateToFilters` before `setFilters()`.
|
|
166
|
+
* Table presentation options (`variant`, `zebra`, `stickyHeader`, `responsive`, `emptyText`, …) and
|
|
167
|
+
* listeners are passed through to the generated Table.
|
|
120
168
|
* @param {Record<string, any>} client Injected ZeyOS client instance.
|
|
121
169
|
* @param {string} resource Resource name such as `transactions`.
|
|
122
170
|
* @param {ZeyosTableOptions} [opts={}] Schema, table, and initial query options.
|
|
@@ -204,6 +252,21 @@ export function zeyosTable(client, resource, opts = {}) {
|
|
|
204
252
|
return binding;
|
|
205
253
|
}
|
|
206
254
|
|
|
255
|
+
/**
|
|
256
|
+
* Combines the Table-style `onrowclick` listener with the binding's row-first `onRowClick`.
|
|
257
|
+
* @param {ZeyosTableOptions} opts
|
|
258
|
+
* @returns {((event: CustomEvent<Record<string, any>>) => void)|undefined}
|
|
259
|
+
*/
|
|
260
|
+
function rowClickListener(opts) {
|
|
261
|
+
const listener = typeof opts.onrowclick === 'function' ? opts.onrowclick : null;
|
|
262
|
+
const callback = typeof opts.onRowClick === 'function' ? opts.onRowClick : null;
|
|
263
|
+
if (!listener && !callback) return undefined;
|
|
264
|
+
return (event) => {
|
|
265
|
+
listener?.(event);
|
|
266
|
+
callback?.(event.detail.row, event);
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
207
270
|
/** @param {Record<string, any>} meta @returns {string|null} */
|
|
208
271
|
function referencedResourceFor(meta) {
|
|
209
272
|
for (const value of [meta.entity, meta.fk, meta.relation, meta.reference, meta.references]) {
|
package/src/zeyos/view.js
CHANGED
|
@@ -26,6 +26,10 @@ import { buildZeyosTableConfig } from './table.js';
|
|
|
26
26
|
* @property {string[]} [fields] Curated ordered schema field allow-list.
|
|
27
27
|
* @property {string[]} [exclude] Schema fields to omit.
|
|
28
28
|
* @property {Record<string, string>} [labels] Per-field label overrides.
|
|
29
|
+
* @property {Record<string, string>} [formats] Per-field semantic format overrides, such as
|
|
30
|
+
* `{ duedate: 'date' }` for a Unix-second field that holds a calendar day.
|
|
31
|
+
* @property {string} [timeZone] IANA time zone of the ZeyOS instance; date-only fields show the
|
|
32
|
+
* calendar day their stored midnight names in it. Defaults to the runtime's own zone.
|
|
29
33
|
* @property {Record<string, Partial<ViewField>>} [fieldOverrides] View descriptor overrides.
|
|
30
34
|
* @property {string|((record:Record<string,unknown>)=>unknown)} [recordId='ID'] Record id accessor.
|
|
31
35
|
* @property {number} [pageSize=50] Server page size.
|
|
@@ -134,6 +138,8 @@ export function buildZeyosViewConfig(client, resource, opts = {}) {
|
|
|
134
138
|
const table = buildZeyosTableConfig(client, resource, {
|
|
135
139
|
fields: requested,
|
|
136
140
|
labels: opts.labels,
|
|
141
|
+
formats: opts.formats,
|
|
142
|
+
timeZone: opts.timeZone,
|
|
137
143
|
rowId: opts.recordId ?? 'ID',
|
|
138
144
|
sort: opts.sort ?? null
|
|
139
145
|
});
|
package/styles/base.css
CHANGED
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
color: var(--zx-color-text);
|
|
13
13
|
font-family: var(--zx-font-sans);
|
|
14
14
|
font-size: var(--zx-text-md);
|
|
15
|
+
/* Inter at 13–14px reads better a touch tight. `cv11` is Inter's single-storey a: at interface
|
|
16
|
+
sizes it is rounder and calmer than the double-storey form, and it tells a from o and e
|
|
17
|
+
apart at least as well. Fonts without the feature ignore it. */
|
|
18
|
+
font-feature-settings: "cv11";
|
|
19
|
+
letter-spacing: var(--zx-tracking-tight);
|
|
15
20
|
line-height: 1.5;
|
|
16
21
|
-webkit-font-smoothing: antialiased;
|
|
17
22
|
scrollbar-gutter: stable;
|
|
@@ -67,6 +72,41 @@
|
|
|
67
72
|
background: var(--zx-color-bg-hover);
|
|
68
73
|
}
|
|
69
74
|
|
|
75
|
+
/* Quiet fields. Inside `[data-zx-fields="quiet"]` — a property panel, an inspector, an inline
|
|
76
|
+
record — native text inputs, selects, and textareas drop their border and fill so the values
|
|
77
|
+
read as text, and show that they are editable on hover (the hover fill) and on focus (the
|
|
78
|
+
normal border, the control fill, and the focus ring). Read-only and disabled fields never
|
|
79
|
+
light up. Zero specificity, like the defaults above: a component that styles its own inner
|
|
80
|
+
input keeps doing so. Composite controls opt in through their own stylesheets. */
|
|
81
|
+
:where(.zx-scope[data-zx-fields="quiet"], .zx-scope [data-zx-fields="quiet"]) :where(
|
|
82
|
+
input:not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"],
|
|
83
|
+
[type="button"], [type="submit"], [type="reset"], [type="image"]),
|
|
84
|
+
select,
|
|
85
|
+
textarea
|
|
86
|
+
) {
|
|
87
|
+
border-color: transparent;
|
|
88
|
+
background: transparent;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
:where(.zx-scope[data-zx-fields="quiet"], .zx-scope [data-zx-fields="quiet"]) :where(
|
|
92
|
+
input:not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"],
|
|
93
|
+
[type="button"], [type="submit"], [type="reset"], [type="image"]),
|
|
94
|
+
select,
|
|
95
|
+
textarea
|
|
96
|
+
):where(:hover:not(:focus, :disabled, [readonly])) {
|
|
97
|
+
background: var(--zx-color-bg-hover);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
:where(.zx-scope[data-zx-fields="quiet"], .zx-scope [data-zx-fields="quiet"]) :where(
|
|
101
|
+
input:not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"],
|
|
102
|
+
[type="button"], [type="submit"], [type="reset"], [type="image"]),
|
|
103
|
+
select,
|
|
104
|
+
textarea
|
|
105
|
+
):where(:focus:not([readonly])) {
|
|
106
|
+
border-color: var(--zx-color-border-control);
|
|
107
|
+
background: var(--zx-color-bg-control);
|
|
108
|
+
}
|
|
109
|
+
|
|
70
110
|
/* Ghost icon button: a borderless, transparent hit target for small glyph actions
|
|
71
111
|
(notification close, closable tab ×, field clear). Square, no chrome until hovered. */
|
|
72
112
|
.zx-scope .zx-icon-btn {
|
|
@@ -122,3 +162,19 @@
|
|
|
122
162
|
transition-duration: 0.01ms !important;
|
|
123
163
|
}
|
|
124
164
|
}
|
|
165
|
+
|
|
166
|
+
/* Motion as a setting: the same stop inside any `data-zx-motion="reduced"` region, whatever the
|
|
167
|
+
operating system says (`tokens/global.css` zeroes the duration tokens there too). Only movement
|
|
168
|
+
stops — every transition and animation still runs to its end state, at once, and still fires its
|
|
169
|
+
end event, so a menu still opens and a toast still leaves; a spinner stops turning. There is no
|
|
170
|
+
value that re-enables motion inside a reduced region. */
|
|
171
|
+
[data-zx-motion="reduced"],
|
|
172
|
+
[data-zx-motion="reduced"] *,
|
|
173
|
+
[data-zx-motion="reduced"] *::before,
|
|
174
|
+
[data-zx-motion="reduced"] *::after,
|
|
175
|
+
[data-zx-motion="reduced"] *::backdrop {
|
|
176
|
+
scroll-behavior: auto !important;
|
|
177
|
+
animation-duration: 0.01ms !important;
|
|
178
|
+
animation-iteration-count: 1 !important;
|
|
179
|
+
transition-duration: 0.01ms !important;
|
|
180
|
+
}
|
package/styles/icons.css
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
/* Icons.
|
|
2
2
|
Every icon `icon()` builds carries `.zx-icon`, whether it is one of the bundled inline SVG
|
|
3
3
|
glyphs or a Font Awesome element, so the rules below (and component rules) style both. Font
|
|
4
|
-
Awesome icons are sized by font-size and occupy exactly one em.
|
|
4
|
+
Awesome icons are sized by font-size and occupy exactly one em.
|
|
5
|
+
|
|
6
|
+
The bundled glyphs are outlines: the `<svg>` carries `fill="none"`, `stroke="currentColor"`,
|
|
7
|
+
round caps and joins, and a stroke width in view-box units (1.75 of 24, never under one CSS
|
|
8
|
+
pixel), and the path inherits all of it. The width therefore scales with the box an icon is
|
|
9
|
+
drawn in and needs no `vector-effect`. Glyphs registered as `[viewBox, path]` pairs stay filled.
|
|
10
|
+
Paint is presentation attributes, so plain CSS on `.zx-icon` overrides it: `fill: currentColor`
|
|
11
|
+
turns an outline into its solid form (a set star, a liked heart). */
|
|
5
12
|
|
|
6
13
|
.zx-icon {
|
|
7
14
|
flex: none;
|
|
@@ -17,6 +24,9 @@ i.zx-icon {
|
|
|
17
24
|
|
|
18
25
|
svg.zx-icon {
|
|
19
26
|
vertical-align: middle;
|
|
27
|
+
/* A stroke centred on a path near the view-box edge reaches past it; small icons thicken the
|
|
28
|
+
line to stay at one pixel, so let it draw instead of clipping the outer half. */
|
|
29
|
+
overflow: visible;
|
|
20
30
|
}
|
|
21
31
|
|
|
22
32
|
/* ZeyOS identity hook. `moduleChip()` now delegates vessel geometry and material entirely to
|