@zeyos/zx 2.3.0 → 4.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 +138 -0
- package/DESIGN-SYSTEM.md +96 -0
- package/README.md +29 -39
- package/dist/types/components/account-menu/account-menu.d.ts +144 -0
- package/dist/types/components/app-sidebar/app-sidebar.d.ts +243 -0
- package/dist/types/components/avatar/avatar.d.ts +100 -0
- package/dist/types/components/card/card.d.ts +128 -0
- package/dist/types/components/chart/chart.d.ts +322 -0
- package/dist/types/components/date-picker/month-picker.d.ts +6 -0
- package/dist/types/components/dialog/dialog.d.ts +9 -0
- package/dist/types/components/dock/dock.d.ts +495 -0
- package/dist/types/components/dropdown/dropdown.d.ts +2 -2
- package/dist/types/components/field/field.d.ts +14 -0
- package/dist/types/components/field-upload/field-upload.d.ts +37 -4
- package/dist/types/components/form/form.d.ts +25 -3
- package/dist/types/components/grid/grid.d.ts +172 -0
- package/dist/types/components/launcher/launcher.d.ts +359 -0
- package/dist/types/components/layout/layout.d.ts +4 -4
- package/dist/types/components/menu-button/menu-button.d.ts +10 -2
- package/dist/types/components/modal/modal.d.ts +25 -0
- package/dist/types/components/multi-value-editor/multi-value-editor.d.ts +7 -1
- package/dist/types/components/questionnaire/items.d.ts +327 -0
- package/dist/types/components/questionnaire/questionnaire.d.ts +449 -0
- package/dist/types/components/select/select.d.ts +51 -0
- package/dist/types/components/sheet/sheet-stack.d.ts +167 -0
- package/dist/types/components/sheet/sheet.d.ts +294 -0
- package/dist/types/components/split-view/split-view.d.ts +2 -21
- package/dist/types/components/table/table.d.ts +223 -15
- package/dist/types/components/tooltip/tooltip.d.ts +2 -2
- package/dist/types/components/value-list/value-list.d.ts +24 -3
- package/dist/types/core/dom.d.ts +122 -0
- package/dist/types/core/drag-axis.d.ts +111 -0
- package/dist/types/core/position.d.ts +4 -2
- package/dist/types/index.d.ts +13 -2
- package/dist/types/internal/app-rail.d.ts +265 -0
- package/dist/types/zeyos/icons.d.ts +5 -0
- package/dist/types/zeyos/index.d.ts +2 -1
- package/dist/types/zeyos/launcher.d.ts +147 -0
- package/dist/types/zeyos/select.d.ts +39 -0
- package/dist/zx-zeyos.esm.js +1870 -133
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +2006 -267
- package/dist/zx.esm.js +8987 -2757
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +8987 -2757
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +4396 -2344
- package/docs/llms.md +463 -86
- package/docs/llms.txt +32 -19
- package/package.json +10 -17
- package/src/components/account-menu/account-menu.css +70 -0
- package/src/components/account-menu/account-menu.js +208 -0
- package/src/components/app-rail/app-rail.css +107 -0
- package/src/components/app-sidebar/app-sidebar.css +80 -0
- package/src/components/app-sidebar/app-sidebar.js +588 -0
- package/src/components/avatar/avatar.css +61 -0
- package/src/components/avatar/avatar.js +222 -0
- package/src/components/button/button.css +0 -1
- package/src/components/card/card.css +149 -0
- package/src/components/card/card.js +285 -0
- package/src/components/chart/chart.css +61 -0
- package/src/components/chart/chart.js +523 -0
- package/src/components/date-picker/month-picker.js +9 -0
- package/src/components/dialog/dialog.js +11 -4
- package/src/components/dock/dock.css +228 -0
- package/src/components/dock/dock.js +977 -0
- package/src/components/dropdown/dropdown.js +1 -1
- package/src/components/field/field.js +2 -0
- package/src/components/field-upload/field-upload.js +9 -4
- package/src/components/form/form.js +6 -3
- package/src/components/grid/grid.css +6 -0
- package/src/components/grid/grid.js +220 -0
- package/src/components/launcher/launcher.css +210 -0
- package/src/components/launcher/launcher.js +765 -0
- package/src/components/layout/layout.js +2 -2
- package/src/components/menu-button/menu-button.js +27 -2
- package/src/components/modal/modal.css +5 -1
- package/src/components/modal/modal.js +37 -2
- package/src/components/multi-value-editor/multi-value-editor.js +2 -1
- package/src/components/questionnaire/items.js +370 -0
- package/src/components/questionnaire/questionnaire.css +387 -0
- package/src/components/questionnaire/questionnaire.js +1033 -0
- package/src/components/select/field-adapter.js +9 -4
- package/src/components/select/select.css +144 -11
- package/src/components/select/select.js +259 -27
- package/src/components/sheet/sheet-stack.js +304 -0
- package/src/components/sheet/sheet.css +351 -0
- package/src/components/sheet/sheet.js +551 -0
- package/src/components/split-view/split-view.js +76 -161
- package/src/components/table/table.css +43 -0
- package/src/components/table/table.js +531 -52
- package/src/components/tooltip/tooltip.js +5 -2
- package/src/components/value-list/value-list.js +6 -3
- package/src/core/dom.js +322 -0
- package/src/core/drag-axis.js +203 -0
- package/src/core/position.js +68 -22
- package/src/index.js +20 -2
- package/src/internal/app-rail.js +718 -0
- package/src/zeyos/icons.js +8 -3
- package/src/zeyos/index.js +7 -1
- package/src/zeyos/launcher.js +338 -0
- package/src/zeyos/modules.js +1 -0
- package/src/zeyos/select.js +115 -1
- package/styles/tokens/dark.css +2 -3
- package/styles/tokens/global.css +6 -6
- package/styles/tokens/modules.css +1 -0
- package/styles/tokens/semantic.css +5 -3
- package/styles/tokens/themes.css +1 -1
- package/styles/zx.css +11 -1
- package/MIGRATION.md +0 -598
- package/dist/types/compat/base.d.ts +0 -156
- package/dist/types/compat/globals.d.ts +0 -20
- package/dist/types/compat/index.d.ts +0 -128
- package/dist/types/compat/map/basic.d.ts +0 -175
- package/dist/types/compat/map/data.d.ts +0 -102
- package/dist/types/compat/map/date-time.d.ts +0 -80
- package/dist/types/compat/map/factory.d.ts +0 -9
- package/dist/types/compat/map/form.d.ts +0 -98
- package/dist/types/compat/map/helpers.d.ts +0 -12
- package/dist/types/compat/map/http.d.ts +0 -36
- package/dist/types/compat/map/layout.d.ts +0 -106
- package/dist/types/compat/map/options.d.ts +0 -80
- package/dist/types/compat/map/select.d.ts +0 -95
- package/dist/types/compat/map/stubs.d.ts +0 -32
- package/dist/types/compat/map/utilities.d.ts +0 -37
- package/dist/types/compat-entry.d.ts +0 -5
- package/dist/types/components/permission/permission.d.ts +0 -96
- package/dist/zx-compat.esm.js +0 -17484
- package/dist/zx-compat.esm.min.js +0 -1
- package/dist/zx-compat.global.js +0 -23635
- package/dist/zx-compat.global.min.js +0 -3
- package/src/compat/base.js +0 -291
- package/src/compat/globals.js +0 -165
- package/src/compat/index.js +0 -84
- package/src/compat/map/basic.js +0 -464
- package/src/compat/map/data.js +0 -232
- package/src/compat/map/date-time.js +0 -177
- package/src/compat/map/factory.js +0 -45
- package/src/compat/map/form.js +0 -330
- package/src/compat/map/helpers.js +0 -46
- package/src/compat/map/http.js +0 -117
- package/src/compat/map/layout.js +0 -256
- package/src/compat/map/options.js +0 -267
- package/src/compat/map/select.js +0 -198
- package/src/compat/map/stubs.js +0 -26
- package/src/compat/map/utilities.js +0 -113
- package/src/compat-entry.js +0 -11
- package/src/components/permission/permission.css +0 -47
- package/src/components/permission/permission.js +0 -201
package/dist/zx-zeyos.esm.js
CHANGED
|
@@ -136,12 +136,12 @@ function registerFieldType(typeOrFormat, strategy) {
|
|
|
136
136
|
function fieldToZxField(meta) {
|
|
137
137
|
const normalized = normalizeMeta(meta);
|
|
138
138
|
const strategy = strategyFor(normalized);
|
|
139
|
-
const
|
|
139
|
+
const fragment2 = strategyPart(strategy.field, normalized);
|
|
140
140
|
const descriptor = {
|
|
141
141
|
id: normalized.id,
|
|
142
142
|
type: "text",
|
|
143
143
|
label: fieldLabel(normalized),
|
|
144
|
-
...
|
|
144
|
+
...fragment2
|
|
145
145
|
};
|
|
146
146
|
if (normalized.description != null) descriptor.description = String(normalized.description);
|
|
147
147
|
if (normalized.required === true || normalized.nullable === false) descriptor.required = true;
|
|
@@ -524,6 +524,18 @@ function cloneValue(value) {
|
|
|
524
524
|
|
|
525
525
|
// src/core/dom.js
|
|
526
526
|
var activeRefs = null;
|
|
527
|
+
function safeHref(value) {
|
|
528
|
+
if (typeof value !== "string") return null;
|
|
529
|
+
const href = value.trim();
|
|
530
|
+
if (!href) return null;
|
|
531
|
+
try {
|
|
532
|
+
const protocol = new URL(href, globalThis.location?.href ?? "https://zx.invalid/").protocol;
|
|
533
|
+
if (["javascript:", "data:", "vbscript:"].includes(protocol.toLowerCase())) return null;
|
|
534
|
+
} catch {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
return href;
|
|
538
|
+
}
|
|
527
539
|
function h(tag, props, ...children) {
|
|
528
540
|
const element = document.createElement(tag);
|
|
529
541
|
let properties = props;
|
|
@@ -2970,6 +2982,14 @@ var MonthPicker = class extends DatePicker {
|
|
|
2970
2982
|
static cssName = "month-picker";
|
|
2971
2983
|
/** @type {MonthPickerOptions} */
|
|
2972
2984
|
static defaults = { value: null, min: null, max: null };
|
|
2985
|
+
/**
|
|
2986
|
+
* Creates a month picker with its narrower month-based option contract.
|
|
2987
|
+
* @param {Element|string|null} target Existing element, selector, or null.
|
|
2988
|
+
* @param {MonthPickerOptions} [options={}] Month-picker options.
|
|
2989
|
+
*/
|
|
2990
|
+
constructor(target, options = {}) {
|
|
2991
|
+
super(target, options);
|
|
2992
|
+
}
|
|
2973
2993
|
/** @returns {HTMLElement} */
|
|
2974
2994
|
render() {
|
|
2975
2995
|
const root = (
|
|
@@ -3189,7 +3209,13 @@ var PLACEMENTS = /* @__PURE__ */ new Set([
|
|
|
3189
3209
|
"top-start",
|
|
3190
3210
|
"top-end",
|
|
3191
3211
|
"bottom",
|
|
3192
|
-
"top"
|
|
3212
|
+
"top",
|
|
3213
|
+
"right-start",
|
|
3214
|
+
"right-end",
|
|
3215
|
+
"left-start",
|
|
3216
|
+
"left-end",
|
|
3217
|
+
"right",
|
|
3218
|
+
"left"
|
|
3193
3219
|
]);
|
|
3194
3220
|
function position(anchor, floating, options = {}) {
|
|
3195
3221
|
if (!(anchor instanceof Element) || !(floating instanceof HTMLElement)) {
|
|
@@ -3217,13 +3243,15 @@ function position(anchor, floating, options = {}) {
|
|
|
3217
3243
|
floating.style.inset = "auto";
|
|
3218
3244
|
floating.style.margin = "0";
|
|
3219
3245
|
floating.style.setProperty("position-anchor", anchorName);
|
|
3220
|
-
|
|
3221
|
-
floating.style.setProperty("
|
|
3246
|
+
const area = positionArea(placement, getComputedStyle(anchor).direction === "rtl");
|
|
3247
|
+
floating.style.setProperty("position-area", area);
|
|
3248
|
+
floating.style.setProperty("inset-area", area);
|
|
3222
3249
|
floating.style.setProperty(
|
|
3223
3250
|
"position-try-fallbacks",
|
|
3224
3251
|
flip ? "flip-block, flip-inline, flip-block flip-inline" : "none"
|
|
3225
3252
|
);
|
|
3226
|
-
|
|
3253
|
+
const margin = placement.startsWith("top") ? "margin-bottom" : placement.startsWith("bottom") ? "margin-top" : placement.startsWith("left") ? "margin-right" : "margin-left";
|
|
3254
|
+
floating.style.setProperty(margin, `${offset}px`);
|
|
3227
3255
|
update = () => {
|
|
3228
3256
|
if (matchWidth) floating.style.minWidth = `${anchor.getBoundingClientRect().width}px`;
|
|
3229
3257
|
};
|
|
@@ -3267,14 +3295,20 @@ function position(anchor, floating, options = {}) {
|
|
|
3267
3295
|
}
|
|
3268
3296
|
};
|
|
3269
3297
|
}
|
|
3270
|
-
function positionArea(placement) {
|
|
3298
|
+
function positionArea(placement, rtl) {
|
|
3271
3299
|
return {
|
|
3272
|
-
"bottom-start": "
|
|
3273
|
-
"bottom-end": "
|
|
3274
|
-
"top-start": "
|
|
3275
|
-
"top-end": "
|
|
3276
|
-
|
|
3277
|
-
|
|
3300
|
+
"bottom-start": rtl ? "bottom span-left" : "bottom span-right",
|
|
3301
|
+
"bottom-end": rtl ? "bottom span-right" : "bottom span-left",
|
|
3302
|
+
"top-start": rtl ? "top span-left" : "top span-right",
|
|
3303
|
+
"top-end": rtl ? "top span-right" : "top span-left",
|
|
3304
|
+
"right-start": "right span-bottom",
|
|
3305
|
+
"right-end": "right span-top",
|
|
3306
|
+
"left-start": "left span-bottom",
|
|
3307
|
+
"left-end": "left span-top",
|
|
3308
|
+
bottom: "bottom",
|
|
3309
|
+
top: "top",
|
|
3310
|
+
right: "right",
|
|
3311
|
+
left: "left"
|
|
3278
3312
|
}[placement];
|
|
3279
3313
|
}
|
|
3280
3314
|
function updateFallback(anchor, floating, { placement, offset, flip, matchWidth }) {
|
|
@@ -3284,26 +3318,54 @@ function updateFallback(anchor, floating, { placement, offset, flip, matchWidth
|
|
|
3284
3318
|
const viewportWidth = document.documentElement.clientWidth || window.innerWidth;
|
|
3285
3319
|
const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
|
|
3286
3320
|
let actualPlacement = placement;
|
|
3287
|
-
const
|
|
3321
|
+
const side = placementSide(placement);
|
|
3288
3322
|
const bottomY = anchorRect.bottom + offset;
|
|
3289
3323
|
const topY = anchorRect.top - offset - floatingRect.height;
|
|
3324
|
+
const rightX = anchorRect.right + offset;
|
|
3325
|
+
const leftX = anchorRect.left - offset - floatingRect.width;
|
|
3290
3326
|
if (flip) {
|
|
3291
|
-
if (
|
|
3292
|
-
actualPlacement =
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
placement
|
|
3327
|
+
if (side === "bottom" && bottomY + floatingRect.height > viewportHeight && topY >= 0) {
|
|
3328
|
+
actualPlacement = flipPlacement(placement, "bottom", "top");
|
|
3329
|
+
} else if (side === "top" && topY < 0 && bottomY + floatingRect.height <= viewportHeight) {
|
|
3330
|
+
actualPlacement = flipPlacement(placement, "top", "bottom");
|
|
3331
|
+
} else if (side === "right" && rightX + floatingRect.width > viewportWidth && leftX >= 0) {
|
|
3332
|
+
actualPlacement = flipPlacement(placement, "right", "left");
|
|
3333
|
+
} else if (side === "left" && leftX < 0 && rightX + floatingRect.width <= viewportWidth) {
|
|
3334
|
+
actualPlacement = flipPlacement(placement, "left", "right");
|
|
3297
3335
|
}
|
|
3298
3336
|
}
|
|
3337
|
+
const actualSide = placementSide(actualPlacement);
|
|
3299
3338
|
let left;
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3339
|
+
let top;
|
|
3340
|
+
if (actualSide === "bottom" || actualSide === "top") {
|
|
3341
|
+
const rtl = getComputedStyle(anchor).direction === "rtl";
|
|
3342
|
+
if (actualPlacement.endsWith("-end")) {
|
|
3343
|
+
left = rtl ? anchorRect.left : anchorRect.right - floatingRect.width;
|
|
3344
|
+
} else if (actualPlacement.endsWith("-start")) {
|
|
3345
|
+
left = rtl ? anchorRect.right - floatingRect.width : anchorRect.left;
|
|
3346
|
+
} else left = anchorRect.left + (anchorRect.width - floatingRect.width) / 2;
|
|
3347
|
+
top = actualSide === "bottom" ? bottomY : topY;
|
|
3348
|
+
} else {
|
|
3349
|
+
left = actualSide === "right" ? rightX : leftX;
|
|
3350
|
+
if (actualPlacement.endsWith("-end")) top = anchorRect.bottom - floatingRect.height;
|
|
3351
|
+
else if (actualPlacement.endsWith("-start")) top = anchorRect.top;
|
|
3352
|
+
else top = anchorRect.top + (anchorRect.height - floatingRect.height) / 2;
|
|
3353
|
+
}
|
|
3304
3354
|
floating.style.left = `${Math.round(clamp(left, 0, Math.max(0, viewportWidth - floatingRect.width)))}px`;
|
|
3305
3355
|
floating.style.top = `${Math.round(clamp(top, 0, Math.max(0, viewportHeight - floatingRect.height)))}px`;
|
|
3306
3356
|
}
|
|
3357
|
+
function placementSide(placement) {
|
|
3358
|
+
return (
|
|
3359
|
+
/** @type {'top'|'bottom'|'left'|'right'} */
|
|
3360
|
+
placement.split("-")[0]
|
|
3361
|
+
);
|
|
3362
|
+
}
|
|
3363
|
+
function flipPlacement(placement, from, to) {
|
|
3364
|
+
return (
|
|
3365
|
+
/** @type {Placement} */
|
|
3366
|
+
placement.replace(from, to)
|
|
3367
|
+
);
|
|
3368
|
+
}
|
|
3307
3369
|
function showPopover(element) {
|
|
3308
3370
|
if (typeof element.showPopover !== "function") return;
|
|
3309
3371
|
try {
|
|
@@ -4136,31 +4198,31 @@ var PLACEHOLDER_TOKENS = {
|
|
|
4136
4198
|
"%Y": "YYYY",
|
|
4137
4199
|
"%y": "YY"
|
|
4138
4200
|
};
|
|
4139
|
-
function parseRangeText(text3,
|
|
4201
|
+
function parseRangeText(text3, format2, separator) {
|
|
4140
4202
|
const source = String(text3 ?? "").trim();
|
|
4141
4203
|
if (source === "") return { start: null, end: null };
|
|
4142
4204
|
const raw2 = String(separator ?? " \u2013 ");
|
|
4143
4205
|
const core = raw2.trim();
|
|
4144
|
-
const strict = core !== "" && String(
|
|
4206
|
+
const strict = core !== "" && String(format2).includes(core) && /^\s|\s$/.test(raw2);
|
|
4145
4207
|
const lead = strict ? "\\s+" : "\\s*";
|
|
4146
4208
|
const tail = strict ? "(?:\\s+|$)" : "\\s*";
|
|
4147
4209
|
const parts = core === "" ? [source] : source.split(new RegExp(`${lead}${escapeRegExp(core)}${tail}`));
|
|
4148
4210
|
if (parts.length > 2) return null;
|
|
4149
|
-
const first = parseDate(parts[0].trim(),
|
|
4211
|
+
const first = parseDate(parts[0].trim(), format2);
|
|
4150
4212
|
if (!first) return null;
|
|
4151
4213
|
if (parts.length === 1) return { start: first, end: new Date(first.getTime()) };
|
|
4152
4214
|
if (parts[1].trim() === "") return { start: first, end: null };
|
|
4153
|
-
const second = parseDate(parts[1].trim(),
|
|
4215
|
+
const second = parseDate(parts[1].trim(), format2);
|
|
4154
4216
|
if (!second) return null;
|
|
4155
4217
|
return normalizeRange(first, second);
|
|
4156
4218
|
}
|
|
4157
|
-
function formatRangeText(range,
|
|
4219
|
+
function formatRangeText(range, format2, separator) {
|
|
4158
4220
|
const start = range?.start ?? null;
|
|
4159
4221
|
const end = range?.end ?? null;
|
|
4160
4222
|
if (!start && !end) return "";
|
|
4161
|
-
if (!end) return formatDate(start,
|
|
4162
|
-
if (!start) return formatDate(end,
|
|
4163
|
-
return `${formatDate(start,
|
|
4223
|
+
if (!end) return formatDate(start, format2);
|
|
4224
|
+
if (!start) return formatDate(end, format2);
|
|
4225
|
+
return `${formatDate(start, format2)}${separator}${formatDate(end, format2)}`;
|
|
4164
4226
|
}
|
|
4165
4227
|
var DateRangeBox = class extends Component {
|
|
4166
4228
|
static cssName = "date-range-box";
|
|
@@ -4818,12 +4880,12 @@ var DateTimeBox = function DateTimeBox2(target, options = {}) {
|
|
|
4818
4880
|
function validDate4(value) {
|
|
4819
4881
|
return value instanceof Date && !Number.isNaN(value.getTime()) ? new Date(value.getTime()) : null;
|
|
4820
4882
|
}
|
|
4821
|
-
function effectiveFormat(
|
|
4822
|
-
const value = String(
|
|
4883
|
+
function effectiveFormat(format2, time) {
|
|
4884
|
+
const value = String(format2 || "%d.%m.%Y");
|
|
4823
4885
|
return time && !/%[HM]/.test(value) ? `${value} %H:%M` : value;
|
|
4824
4886
|
}
|
|
4825
|
-
function placeholderFromFormat(
|
|
4826
|
-
return
|
|
4887
|
+
function placeholderFromFormat(format2) {
|
|
4888
|
+
return format2.replace(/%[demYyHMS]/g, (token) => ({
|
|
4827
4889
|
"%d": "DD",
|
|
4828
4890
|
"%e": "D",
|
|
4829
4891
|
"%m": "MM",
|
|
@@ -6483,7 +6545,18 @@ var PRIORITY_KEYS = [
|
|
|
6483
6545
|
"priority.high",
|
|
6484
6546
|
"priority.highest"
|
|
6485
6547
|
];
|
|
6486
|
-
var PRIORITY_FALLBACKS = ["Lowest", "Low", "
|
|
6548
|
+
var PRIORITY_FALLBACKS = ["Lowest", "Low", "Medium", "High", "Highest"];
|
|
6549
|
+
var DEFAULT_STATUSES = Object.freeze([
|
|
6550
|
+
["backlog", "status.backlog", "Backlog", "dotted", "muted"],
|
|
6551
|
+
["todo", "status.todo", "Todo", "ring", "neutral"],
|
|
6552
|
+
["in-progress", "status.inProgress", "In Progress", "progress", "warning"],
|
|
6553
|
+
["in-review", "status.inReview", "In Review", "review", "info"],
|
|
6554
|
+
["ready", "status.ready", "Ready to Merge", "ready", "accent"],
|
|
6555
|
+
["waiting", "status.waiting", "Waiting for feedback", "waiting", "warning"],
|
|
6556
|
+
["done", "status.done", "Done", "done", "info"],
|
|
6557
|
+
["canceled", "status.canceled", "Canceled", "canceled", "muted"],
|
|
6558
|
+
["duplicate", "status.duplicate", "Duplicate", "duplicate", "muted"]
|
|
6559
|
+
]);
|
|
6487
6560
|
var Select = class _Select extends Component {
|
|
6488
6561
|
static cssName = "select";
|
|
6489
6562
|
/** @type {SelectOptions} */
|
|
@@ -6494,6 +6567,10 @@ var Select = class _Select extends Component {
|
|
|
6494
6567
|
labelKey: "name",
|
|
6495
6568
|
renderItem: null,
|
|
6496
6569
|
renderValue: null,
|
|
6570
|
+
renderValueAdornment: null,
|
|
6571
|
+
renderNone: null,
|
|
6572
|
+
noneLabel: null,
|
|
6573
|
+
actions: [],
|
|
6497
6574
|
value: null,
|
|
6498
6575
|
disabled: false,
|
|
6499
6576
|
placeholder: "",
|
|
@@ -6511,6 +6588,7 @@ var Select = class _Select extends Component {
|
|
|
6511
6588
|
this._visibleItems = [];
|
|
6512
6589
|
this._fixedItems = [];
|
|
6513
6590
|
this._visibleFixed = [];
|
|
6591
|
+
this._actions = [];
|
|
6514
6592
|
this._selected = null;
|
|
6515
6593
|
this._optionEntries = [];
|
|
6516
6594
|
this._activeIndex = -1;
|
|
@@ -6549,6 +6627,12 @@ var Select = class _Select extends Component {
|
|
|
6549
6627
|
placeholder: String(this.options.placeholder ?? ""),
|
|
6550
6628
|
readOnly: !this.options.filter
|
|
6551
6629
|
});
|
|
6630
|
+
const valueAdornment = h("span", {
|
|
6631
|
+
ref: "valueAdornment",
|
|
6632
|
+
class: "zx-select__value-adornment",
|
|
6633
|
+
ariaHidden: "true",
|
|
6634
|
+
hidden: true
|
|
6635
|
+
});
|
|
6552
6636
|
const clear = h("button", {
|
|
6553
6637
|
ref: "clear",
|
|
6554
6638
|
class: "zx-icon-btn zx-select__clear",
|
|
@@ -6574,6 +6658,7 @@ var Select = class _Select extends Component {
|
|
|
6574
6658
|
const control = h(
|
|
6575
6659
|
"div",
|
|
6576
6660
|
{ ref: "control", class: "zx-select__control" },
|
|
6661
|
+
valueAdornment,
|
|
6577
6662
|
input,
|
|
6578
6663
|
clear,
|
|
6579
6664
|
loading,
|
|
@@ -6596,9 +6681,10 @@ var Select = class _Select extends Component {
|
|
|
6596
6681
|
this._visibleItems = this._items.slice();
|
|
6597
6682
|
this._fixedItems = Array.isArray(this.options.fixedItems) ? this.options.fixedItems.slice() : [];
|
|
6598
6683
|
this._visibleFixed = this._fixedItems.slice();
|
|
6684
|
+
this._actions = normalizeSelectActions(this.options.actions);
|
|
6599
6685
|
this._disabled = Boolean(this.options.disabled);
|
|
6600
6686
|
this._handleTypeahead = typeahead(
|
|
6601
|
-
() => this._optionEntries.filter((entry) => !entry.clear).map((entry) => entry.element),
|
|
6687
|
+
() => this._optionEntries.filter((entry) => !entry.clear && !entry.action).map((entry) => entry.element),
|
|
6602
6688
|
(_option, index) => {
|
|
6603
6689
|
if (!this._open) this.open();
|
|
6604
6690
|
this._setActive(this._itemEntryIndex(index));
|
|
@@ -6693,6 +6779,13 @@ var Select = class _Select extends Component {
|
|
|
6693
6779
|
this._position?.update();
|
|
6694
6780
|
return this;
|
|
6695
6781
|
}
|
|
6782
|
+
/** Replaces command rows without changing the selected value. @param {SelectAction[]} actions @returns {this} */
|
|
6783
|
+
setActions(actions) {
|
|
6784
|
+
this._actions = normalizeSelectActions(actions);
|
|
6785
|
+
this._renderOptions();
|
|
6786
|
+
this._position?.update();
|
|
6787
|
+
return this;
|
|
6788
|
+
}
|
|
6696
6789
|
/**
|
|
6697
6790
|
* Restores the configured initial value.
|
|
6698
6791
|
* @param {{silent?: boolean}} [options={}] Reset behavior.
|
|
@@ -6777,26 +6870,51 @@ var Select = class _Select extends Component {
|
|
|
6777
6870
|
value: null,
|
|
6778
6871
|
items: [],
|
|
6779
6872
|
renderItem(item) {
|
|
6780
|
-
return
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
class: "zx-select__priority-icon",
|
|
6785
|
-
dataset: { level: item.level },
|
|
6786
|
-
ariaHidden: "true"
|
|
6787
|
-
}),
|
|
6788
|
-
h("span", {}, String(item.name))
|
|
6789
|
-
);
|
|
6873
|
+
return priorityContent(item, true);
|
|
6874
|
+
},
|
|
6875
|
+
renderValueAdornment(item) {
|
|
6876
|
+
return priorityIndicator(item.level);
|
|
6790
6877
|
}
|
|
6791
6878
|
});
|
|
6792
|
-
select.setItems(PRIORITY_KEYS.map((key, level) =>
|
|
6793
|
-
|
|
6794
|
-
name
|
|
6795
|
-
level
|
|
6796
|
-
|
|
6879
|
+
select.setItems(PRIORITY_KEYS.map((key, level) => {
|
|
6880
|
+
let name = level === 2 ? select.msg("priority.medium") : key;
|
|
6881
|
+
if (name === "priority.medium") name = select._message(key, PRIORITY_FALLBACKS[level]);
|
|
6882
|
+
else if (level !== 2) name = select._message(key, PRIORITY_FALLBACKS[level]);
|
|
6883
|
+
return { ID: level, name, level };
|
|
6884
|
+
}));
|
|
6797
6885
|
select.set(requestedValue, { silent: true });
|
|
6798
6886
|
return select;
|
|
6799
6887
|
}
|
|
6888
|
+
/**
|
|
6889
|
+
* Creates a status picker with shape- and color-distinct workflow indicators.
|
|
6890
|
+
* @param {Element|string|null} target Component target.
|
|
6891
|
+
* @param {SelectOptions & {items?: Array<Record<string, any>>}} [options={}] Select overrides and status items.
|
|
6892
|
+
* @returns {Select}
|
|
6893
|
+
*/
|
|
6894
|
+
static status(target, options = {}) {
|
|
6895
|
+
const { items = null, value = null, ...rest } = options;
|
|
6896
|
+
const select = new _Select(target, {
|
|
6897
|
+
...rest,
|
|
6898
|
+
value: null,
|
|
6899
|
+
items: [],
|
|
6900
|
+
renderItem(item) {
|
|
6901
|
+
return statusContent(item);
|
|
6902
|
+
},
|
|
6903
|
+
renderValueAdornment(item) {
|
|
6904
|
+
return statusIndicator(item);
|
|
6905
|
+
}
|
|
6906
|
+
});
|
|
6907
|
+
const statuses = Array.isArray(items) ? items.map(normalizeStatusItem).filter(Boolean) : DEFAULT_STATUSES.map(([ID, key, fallback, indicator, tone], index) => ({
|
|
6908
|
+
ID,
|
|
6909
|
+
name: select._message(key, fallback),
|
|
6910
|
+
indicator,
|
|
6911
|
+
tone,
|
|
6912
|
+
shortcut: String(index + 1)
|
|
6913
|
+
}));
|
|
6914
|
+
select.setItems(statuses);
|
|
6915
|
+
select.set(value, { silent: true });
|
|
6916
|
+
return select;
|
|
6917
|
+
}
|
|
6800
6918
|
/**
|
|
6801
6919
|
* Creates the record-permission picker: the fixed Private and Public choices above the groups a
|
|
6802
6920
|
* record can be shared with. The value is the tri-state ZeyOS stores — `false` for private,
|
|
@@ -6809,12 +6927,21 @@ var Select = class _Select extends Component {
|
|
|
6809
6927
|
* @returns {Select}
|
|
6810
6928
|
*/
|
|
6811
6929
|
static permission(target, options = {}) {
|
|
6812
|
-
const {
|
|
6930
|
+
const {
|
|
6931
|
+
groups = [],
|
|
6932
|
+
value = true,
|
|
6933
|
+
valueKey = "ID",
|
|
6934
|
+
labelKey = "name",
|
|
6935
|
+
filter = "local",
|
|
6936
|
+
...rest
|
|
6937
|
+
} = options;
|
|
6813
6938
|
const loader = typeof groups === "function" ? groups : null;
|
|
6814
6939
|
const shareable = loader ? [] : groups;
|
|
6815
6940
|
const choices = [{ ID: false, name: "Private" }, { ID: true, name: "Public" }];
|
|
6816
6941
|
const select = new _Select(target, {
|
|
6817
|
-
|
|
6942
|
+
// An async group loader is the data source and cannot be disconnected by an incidental
|
|
6943
|
+
// `filter` override. Array-backed groups still honor local/disabled filtering.
|
|
6944
|
+
filter: loader ?? filter,
|
|
6818
6945
|
...rest,
|
|
6819
6946
|
value: null,
|
|
6820
6947
|
items: shareable,
|
|
@@ -6850,6 +6977,18 @@ var Select = class _Select extends Component {
|
|
|
6850
6977
|
/** @param {KeyboardEvent} event @returns {void} */
|
|
6851
6978
|
_onKeydown(event) {
|
|
6852
6979
|
if (this._disabled) return;
|
|
6980
|
+
if (this._open && isPrintable(event)) {
|
|
6981
|
+
const shortcut = this._optionEntries.findIndex((entry) => !entry.clear && !entry.action && String(
|
|
6982
|
+
/** @type {Record<string, any>|null} */
|
|
6983
|
+
entry.item?.shortcut ?? ""
|
|
6984
|
+
) === event.key);
|
|
6985
|
+
if (shortcut >= 0) {
|
|
6986
|
+
event.preventDefault();
|
|
6987
|
+
this._setActive(shortcut);
|
|
6988
|
+
this._chooseEntry(this._optionEntries[shortcut]);
|
|
6989
|
+
return;
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6853
6992
|
if (event.key === "ArrowDown") {
|
|
6854
6993
|
event.preventDefault();
|
|
6855
6994
|
if (!this._open) this.open();
|
|
@@ -6897,6 +7036,7 @@ var Select = class _Select extends Component {
|
|
|
6897
7036
|
/** @type {HTMLInputElement} */
|
|
6898
7037
|
this.refs.input.value
|
|
6899
7038
|
);
|
|
7039
|
+
this._syncValueAdornment();
|
|
6900
7040
|
if (!this._open) this.open();
|
|
6901
7041
|
if (this.options.filter === "local") {
|
|
6902
7042
|
this._visibleFixed = this._matchFixed(query);
|
|
@@ -6920,6 +7060,22 @@ var Select = class _Select extends Component {
|
|
|
6920
7060
|
/** @param {SelectOptionEntry|undefined} entry @returns {void} */
|
|
6921
7061
|
_chooseEntry(entry) {
|
|
6922
7062
|
if (!entry) return;
|
|
7063
|
+
if (entry.action) {
|
|
7064
|
+
const detail = {
|
|
7065
|
+
id: entry.action.id,
|
|
7066
|
+
action: entry.action,
|
|
7067
|
+
query: (
|
|
7068
|
+
/** @type {HTMLInputElement} */
|
|
7069
|
+
this.refs.input.value
|
|
7070
|
+
),
|
|
7071
|
+
select: this
|
|
7072
|
+
};
|
|
7073
|
+
const action = this.emit("action", detail);
|
|
7074
|
+
if (!action.defaultPrevented) entry.action.invoke?.(detail);
|
|
7075
|
+
this.close();
|
|
7076
|
+
this.focus();
|
|
7077
|
+
return;
|
|
7078
|
+
}
|
|
6923
7079
|
this.set(entry.clear ? null : entry.item);
|
|
6924
7080
|
this.close();
|
|
6925
7081
|
this.focus();
|
|
@@ -6953,7 +7109,7 @@ var Select = class _Select extends Component {
|
|
|
6953
7109
|
this._optionEntries = [];
|
|
6954
7110
|
let lastGroup = /* @__PURE__ */ Symbol("initial-group");
|
|
6955
7111
|
if (this.options.clearable) {
|
|
6956
|
-
this._appendOption(null, true, this.options.
|
|
7112
|
+
this._appendOption(null, true, this.options.noneLabel ?? this._message("select.none", "No selection"));
|
|
6957
7113
|
}
|
|
6958
7114
|
for (const item of this._visibleFixed) this._appendOption(item, false);
|
|
6959
7115
|
if (this._visibleFixed.length > 0 && this._visibleItems.length > 0 && !this.options.groupKey) {
|
|
@@ -6972,7 +7128,23 @@ var Select = class _Select extends Component {
|
|
|
6972
7128
|
}
|
|
6973
7129
|
this._appendOption(item, false);
|
|
6974
7130
|
}
|
|
6975
|
-
if (this.
|
|
7131
|
+
if (this._actions.length > 0) {
|
|
7132
|
+
if (this._visibleFixed.length > 0 || this._visibleItems.length > 0 || this.options.clearable) {
|
|
7133
|
+
list.append(h("div", { class: "zx-select__separator", role: "presentation" }));
|
|
7134
|
+
}
|
|
7135
|
+
let actionGroup = /* @__PURE__ */ Symbol("initial-action-group");
|
|
7136
|
+
for (const action of this._actions) {
|
|
7137
|
+
if (action.group && action.group !== actionGroup) {
|
|
7138
|
+
list.append(h("div", {
|
|
7139
|
+
class: "zx-select__group",
|
|
7140
|
+
role: "presentation"
|
|
7141
|
+
}, action.group));
|
|
7142
|
+
actionGroup = action.group;
|
|
7143
|
+
}
|
|
7144
|
+
this._appendAction(action);
|
|
7145
|
+
}
|
|
7146
|
+
}
|
|
7147
|
+
if (this._visibleItems.length === 0 && this._visibleFixed.length === 0 && this._actions.length === 0 && !this._loading) {
|
|
6976
7148
|
list.append(h("div", {
|
|
6977
7149
|
class: "zx-select__empty",
|
|
6978
7150
|
role: "status"
|
|
@@ -6984,7 +7156,7 @@ var Select = class _Select extends Component {
|
|
|
6984
7156
|
_appendOption(item, clear, label) {
|
|
6985
7157
|
const selected = clear ? this._selected === null : this._selected !== null && Object.is(this._itemValue(item), this.value);
|
|
6986
7158
|
const index = this._optionEntries.length;
|
|
6987
|
-
const content = clear ? String(label ?? "") : this._renderItem(item);
|
|
7159
|
+
const content = clear && typeof this.options.renderNone === "function" ? normalizeRenderedContent(this.options.renderNone(String(label ?? ""))) : clear ? String(label ?? "") : this._renderItem(item);
|
|
6988
7160
|
const option = h("div", {
|
|
6989
7161
|
class: "zx-select__option",
|
|
6990
7162
|
id: uid("zx-select-option"),
|
|
@@ -6994,16 +7166,37 @@ var Select = class _Select extends Component {
|
|
|
6994
7166
|
}, h("span", { class: "zx-select__option-content" }, content));
|
|
6995
7167
|
if (selected) option.append(icon("check", { size: 15 }));
|
|
6996
7168
|
this.refs.list.append(option);
|
|
6997
|
-
this._optionEntries.push({ item, clear, element: option });
|
|
7169
|
+
this._optionEntries.push({ item, clear, action: null, element: option });
|
|
7170
|
+
}
|
|
7171
|
+
/** @param {SelectAction} action @returns {void} */
|
|
7172
|
+
_appendAction(action) {
|
|
7173
|
+
const index = this._optionEntries.length;
|
|
7174
|
+
const visual = selectActionIcon(action.icon);
|
|
7175
|
+
const option = h("div", {
|
|
7176
|
+
class: "zx-select__option zx-select__action",
|
|
7177
|
+
id: uid("zx-select-option"),
|
|
7178
|
+
role: "option",
|
|
7179
|
+
ariaSelected: "false",
|
|
7180
|
+
dataset: { index, kind: "action" }
|
|
7181
|
+
}, h(
|
|
7182
|
+
"span",
|
|
7183
|
+
{ class: "zx-select__option-content zx-select__action-content" },
|
|
7184
|
+
visual ? h("span", { class: "zx-select__action-icon", ariaHidden: "true" }, visual) : null,
|
|
7185
|
+
h(
|
|
7186
|
+
"span",
|
|
7187
|
+
{ class: "zx-select__action-copy" },
|
|
7188
|
+
h("span", { class: "zx-select__action-label" }, action.label),
|
|
7189
|
+
action.description ? h("span", { class: "zx-select__action-description" }, action.description) : null
|
|
7190
|
+
)
|
|
7191
|
+
));
|
|
7192
|
+
this.refs.list.append(option);
|
|
7193
|
+
this._optionEntries.push({ item: null, clear: false, action, element: option });
|
|
6998
7194
|
}
|
|
6999
7195
|
/** @param {SelectItem|null} item @returns {Node|string} */
|
|
7000
7196
|
_renderItem(item) {
|
|
7001
7197
|
if (typeof this.options.renderItem === "function") {
|
|
7002
7198
|
const rendered = this.options.renderItem(item);
|
|
7003
|
-
return rendered
|
|
7004
|
-
/** @type {Node} */
|
|
7005
|
-
rendered
|
|
7006
|
-
) : String(rendered ?? "");
|
|
7199
|
+
return normalizeRenderedContent(rendered);
|
|
7007
7200
|
}
|
|
7008
7201
|
return this._itemLabel(item);
|
|
7009
7202
|
}
|
|
@@ -7074,9 +7267,29 @@ var Select = class _Select extends Component {
|
|
|
7074
7267
|
this.refs.input
|
|
7075
7268
|
);
|
|
7076
7269
|
input.value = this._selected === null ? "" : typeof this.options.renderValue === "function" ? String(this.options.renderValue(this._selected) ?? "") : this._itemLabel(this._selected);
|
|
7270
|
+
this._syncValueAdornment();
|
|
7077
7271
|
this.refs.clear.hidden = !this.options.clearable || this._selected === null;
|
|
7078
7272
|
}
|
|
7079
7273
|
/** @returns {void} */
|
|
7274
|
+
_syncValueAdornment() {
|
|
7275
|
+
const host = this.refs.valueAdornment;
|
|
7276
|
+
if (!host) return;
|
|
7277
|
+
const selectedLabel = this._selected === null ? "" : typeof this.options.renderValue === "function" ? String(this.options.renderValue(this._selected) ?? "") : this._itemLabel(this._selected);
|
|
7278
|
+
const query = String(
|
|
7279
|
+
/** @type {HTMLInputElement} */
|
|
7280
|
+
this.refs.input?.value ?? ""
|
|
7281
|
+
);
|
|
7282
|
+
if (this._selected === null || typeof this.options.renderValueAdornment !== "function" || Boolean(this.options.filter && query !== selectedLabel)) {
|
|
7283
|
+
host.replaceChildren();
|
|
7284
|
+
host.hidden = true;
|
|
7285
|
+
return;
|
|
7286
|
+
}
|
|
7287
|
+
const rendered = this.options.renderValueAdornment(this._selected);
|
|
7288
|
+
const content = normalizeRenderedContent(rendered);
|
|
7289
|
+
host.replaceChildren(content instanceof Node ? content : document.createTextNode(content));
|
|
7290
|
+
host.hidden = rendered == null || typeof content === "string" && content === "";
|
|
7291
|
+
}
|
|
7292
|
+
/** @returns {void} */
|
|
7080
7293
|
_syncDisabled() {
|
|
7081
7294
|
const input = (
|
|
7082
7295
|
/** @type {HTMLInputElement} */
|
|
@@ -7153,6 +7366,82 @@ var Select = class _Select extends Component {
|
|
|
7153
7366
|
return message === key ? fallback : message;
|
|
7154
7367
|
}
|
|
7155
7368
|
};
|
|
7369
|
+
function normalizeRenderedContent(rendered) {
|
|
7370
|
+
return rendered && typeof rendered === "object" && "nodeType" in rendered ? (
|
|
7371
|
+
/** @type {Node} */
|
|
7372
|
+
rendered
|
|
7373
|
+
) : String(rendered ?? "");
|
|
7374
|
+
}
|
|
7375
|
+
function normalizeSelectActions(actions) {
|
|
7376
|
+
if (!Array.isArray(actions)) return [];
|
|
7377
|
+
return actions.filter((action) => action && typeof action === "object" && action.id != null && action.label != null).map((action) => ({
|
|
7378
|
+
...action,
|
|
7379
|
+
label: String(action.label),
|
|
7380
|
+
description: action.description == null ? void 0 : String(action.description),
|
|
7381
|
+
group: action.group == null ? void 0 : String(action.group)
|
|
7382
|
+
}));
|
|
7383
|
+
}
|
|
7384
|
+
function selectActionIcon(visual) {
|
|
7385
|
+
if (typeof visual === "string") return icon(visual);
|
|
7386
|
+
if (visual instanceof Node) return visual.cloneNode(true);
|
|
7387
|
+
return null;
|
|
7388
|
+
}
|
|
7389
|
+
function priorityContent(item, label) {
|
|
7390
|
+
return h(
|
|
7391
|
+
"span",
|
|
7392
|
+
{ class: "zx-select__priority" },
|
|
7393
|
+
priorityIndicator(item.level),
|
|
7394
|
+
label ? h("span", {}, String(item.name)) : null
|
|
7395
|
+
);
|
|
7396
|
+
}
|
|
7397
|
+
function priorityIndicator(level) {
|
|
7398
|
+
const normalized = Math.max(0, Math.min(4, Math.trunc(Number(level) || 0)));
|
|
7399
|
+
return h("span", {
|
|
7400
|
+
class: "zx-select__priority-icon",
|
|
7401
|
+
dataset: { level: normalized },
|
|
7402
|
+
ariaHidden: "true"
|
|
7403
|
+
}, Array.from({ length: 5 }, (_, index) => h("span", {
|
|
7404
|
+
dataset: { filled: String(index <= normalized) }
|
|
7405
|
+
})));
|
|
7406
|
+
}
|
|
7407
|
+
function normalizeStatusItem(input) {
|
|
7408
|
+
if (!input || typeof input !== "object" || input.ID == null || input.name == null) return null;
|
|
7409
|
+
const indicators = ["dotted", "ring", "progress", "review", "ready", "waiting", "done", "canceled", "duplicate"];
|
|
7410
|
+
const tones = ["muted", "neutral", "warning", "info", "accent", "success", "danger"];
|
|
7411
|
+
return {
|
|
7412
|
+
...input,
|
|
7413
|
+
name: String(input.name),
|
|
7414
|
+
indicator: indicators.includes(String(input.indicator)) ? String(input.indicator) : "ring",
|
|
7415
|
+
tone: tones.includes(String(input.tone)) ? String(input.tone) : "muted",
|
|
7416
|
+
description: input.description == null ? "" : String(input.description),
|
|
7417
|
+
shortcut: input.shortcut == null ? "" : String(input.shortcut)
|
|
7418
|
+
};
|
|
7419
|
+
}
|
|
7420
|
+
function statusContent(item) {
|
|
7421
|
+
return h(
|
|
7422
|
+
"span",
|
|
7423
|
+
{ class: "zx-select__status" },
|
|
7424
|
+
statusIndicator(item),
|
|
7425
|
+
h(
|
|
7426
|
+
"span",
|
|
7427
|
+
{ class: "zx-select__status-copy" },
|
|
7428
|
+
h("span", { class: "zx-select__status-label" }, String(item.name)),
|
|
7429
|
+
item.description ? h("span", { class: "zx-select__status-description" }, item.description) : null
|
|
7430
|
+
),
|
|
7431
|
+
item.shortcut ? h("kbd", { class: "zx-select__status-shortcut" }, item.shortcut) : null
|
|
7432
|
+
);
|
|
7433
|
+
}
|
|
7434
|
+
function statusIndicator(item) {
|
|
7435
|
+
const indicator = String(item.indicator ?? "ring");
|
|
7436
|
+
const marker = h("span", {
|
|
7437
|
+
class: "zx-select__status-indicator",
|
|
7438
|
+
dataset: { indicator, tone: String(item.tone ?? "muted") },
|
|
7439
|
+
ariaHidden: "true"
|
|
7440
|
+
});
|
|
7441
|
+
if (indicator === "done") marker.append(icon("check", { size: 8 }));
|
|
7442
|
+
if (indicator === "canceled") marker.append(icon("x", { size: 7 }));
|
|
7443
|
+
return marker;
|
|
7444
|
+
}
|
|
7156
7445
|
function readItem(item, reader) {
|
|
7157
7446
|
if (typeof reader === "function") return reader(item);
|
|
7158
7447
|
if (item === null || typeof item !== "object") return item;
|
|
@@ -7165,10 +7454,11 @@ function isPrintable(event) {
|
|
|
7165
7454
|
// src/components/select/field-adapter.js
|
|
7166
7455
|
function registerSelectFieldAdapter() {
|
|
7167
7456
|
Field.register("zxselect", (field, options) => {
|
|
7168
|
-
const
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7457
|
+
const props = options.props ?? {};
|
|
7458
|
+
const preset = props.preset;
|
|
7459
|
+
const selectOptions = { ...props, value: options.value };
|
|
7460
|
+
delete selectOptions.preset;
|
|
7461
|
+
const select = field.own(preset === "priority" ? Select.priority(null, selectOptions) : preset === "status" ? Select.status(null, selectOptions) : new Select(null, selectOptions));
|
|
7172
7462
|
field.listen(select.el, "change", (event) => event.stopImmediatePropagation());
|
|
7173
7463
|
select.on("change", (event) => field.emit("change", { value: event.detail.value }));
|
|
7174
7464
|
return {
|
|
@@ -9074,6 +9364,82 @@ function onBreakpoint(handler, options = {}) {
|
|
|
9074
9364
|
};
|
|
9075
9365
|
}
|
|
9076
9366
|
|
|
9367
|
+
// src/core/format.js
|
|
9368
|
+
var MIN_DIGITS = 0;
|
|
9369
|
+
var MAX_DIGITS = 20;
|
|
9370
|
+
var DEFAULT_MAX_DIGITS = 3;
|
|
9371
|
+
var numberFormatters = /* @__PURE__ */ new Map();
|
|
9372
|
+
function formatNumber(value, options = {}) {
|
|
9373
|
+
const number = toFiniteNumber(value);
|
|
9374
|
+
if (number === null) return "";
|
|
9375
|
+
const { locale = getLanguage(), decimals, minDecimals, maxDecimals, group = true } = options;
|
|
9376
|
+
return format(locale, digitOptions({ decimals, minDecimals, maxDecimals, group }), number);
|
|
9377
|
+
}
|
|
9378
|
+
function formatCurrency(value, currency, options = {}) {
|
|
9379
|
+
const number = toFiniteNumber(value);
|
|
9380
|
+
if (number === null) return "";
|
|
9381
|
+
const { locale = getLanguage(), decimals } = options;
|
|
9382
|
+
const code = currencyCode(currency);
|
|
9383
|
+
if (code === null) return formatNumber(number, { locale, decimals });
|
|
9384
|
+
const digits = digitOptions({ decimals, group: true });
|
|
9385
|
+
return format(locale, { ...digits, style: "currency", currency: code }, number);
|
|
9386
|
+
}
|
|
9387
|
+
function formatPercent2(value, options = {}) {
|
|
9388
|
+
const number = toFiniteNumber(value);
|
|
9389
|
+
if (number === null) return "";
|
|
9390
|
+
const { locale = getLanguage(), decimals = 0 } = options;
|
|
9391
|
+
return format(locale, { ...digitOptions({ decimals, group: true }), style: "percent" }, number);
|
|
9392
|
+
}
|
|
9393
|
+
function format(locale, options, value) {
|
|
9394
|
+
const key = `${locale}\0${JSON.stringify(options)}`;
|
|
9395
|
+
let formatter = numberFormatters.get(key);
|
|
9396
|
+
if (formatter === void 0) {
|
|
9397
|
+
formatter = createNumberFormat(locale, options);
|
|
9398
|
+
numberFormatters.set(key, formatter);
|
|
9399
|
+
}
|
|
9400
|
+
return formatter.format(value);
|
|
9401
|
+
}
|
|
9402
|
+
function createNumberFormat(locale, options) {
|
|
9403
|
+
try {
|
|
9404
|
+
return new Intl.NumberFormat(locale, options);
|
|
9405
|
+
} catch {
|
|
9406
|
+
try {
|
|
9407
|
+
return new Intl.NumberFormat(void 0, options);
|
|
9408
|
+
} catch {
|
|
9409
|
+
return new Intl.NumberFormat();
|
|
9410
|
+
}
|
|
9411
|
+
}
|
|
9412
|
+
}
|
|
9413
|
+
function digitOptions({ decimals, minDecimals, maxDecimals, group = true }) {
|
|
9414
|
+
const result = { useGrouping: group !== false };
|
|
9415
|
+
const min = digitCount(minDecimals ?? decimals);
|
|
9416
|
+
const max = digitCount(maxDecimals ?? decimals);
|
|
9417
|
+
if (min !== null) result.minimumFractionDigits = min;
|
|
9418
|
+
if (max !== null) {
|
|
9419
|
+
result.maximumFractionDigits = max;
|
|
9420
|
+
if (min !== null && min > max) result.minimumFractionDigits = max;
|
|
9421
|
+
} else if (min !== null) {
|
|
9422
|
+
result.maximumFractionDigits = Math.max(min, DEFAULT_MAX_DIGITS);
|
|
9423
|
+
}
|
|
9424
|
+
return result;
|
|
9425
|
+
}
|
|
9426
|
+
function digitCount(value) {
|
|
9427
|
+
if (value == null) return null;
|
|
9428
|
+
const number = Number(value);
|
|
9429
|
+
if (!Number.isFinite(number)) return null;
|
|
9430
|
+
return Math.min(MAX_DIGITS, Math.max(MIN_DIGITS, Math.round(number)));
|
|
9431
|
+
}
|
|
9432
|
+
function toFiniteNumber(value) {
|
|
9433
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
9434
|
+
const number = typeof value === "number" ? value : Number(value);
|
|
9435
|
+
return Number.isFinite(number) ? number : null;
|
|
9436
|
+
}
|
|
9437
|
+
function currencyCode(value) {
|
|
9438
|
+
if (typeof value !== "string") return null;
|
|
9439
|
+
const code = value.trim().toUpperCase();
|
|
9440
|
+
return /^[A-Z]{3}$/.test(code) ? code : null;
|
|
9441
|
+
}
|
|
9442
|
+
|
|
9077
9443
|
// src/components/table/sort.js
|
|
9078
9444
|
function compareStrings(left, right) {
|
|
9079
9445
|
return String(left).localeCompare(String(right));
|
|
@@ -9129,7 +9495,8 @@ var Table = class extends Component {
|
|
|
9129
9495
|
emptyText: null,
|
|
9130
9496
|
rowClass: null,
|
|
9131
9497
|
zebra: true,
|
|
9132
|
-
editMode: false
|
|
9498
|
+
editMode: false,
|
|
9499
|
+
hierarchy: false
|
|
9133
9500
|
};
|
|
9134
9501
|
/**
|
|
9135
9502
|
* Creates a table around a target or creates a new root when target is null.
|
|
@@ -9141,12 +9508,14 @@ var Table = class extends Component {
|
|
|
9141
9508
|
}
|
|
9142
9509
|
/** @returns {HTMLElement} Table root. */
|
|
9143
9510
|
render() {
|
|
9511
|
+
const created = this.el === null;
|
|
9144
9512
|
const root = (
|
|
9145
9513
|
/** @type {HTMLElement} */
|
|
9146
9514
|
this.el ?? h("div")
|
|
9147
9515
|
);
|
|
9148
9516
|
this.el = root;
|
|
9149
|
-
this.
|
|
9517
|
+
this._createdRoot = created;
|
|
9518
|
+
this._original = created ? null : snapshotTarget(root);
|
|
9150
9519
|
this._columns = Array.isArray(this.options.columns) ? this.options.columns.map((column) => ({ ...column })) : [];
|
|
9151
9520
|
this._data = Array.isArray(this.options.data) ? [...this.options.data] : [];
|
|
9152
9521
|
this._selected = /* @__PURE__ */ new Set();
|
|
@@ -9166,8 +9535,17 @@ var Table = class extends Component {
|
|
|
9166
9535
|
this._rovingKey = null;
|
|
9167
9536
|
this._rovingCell = null;
|
|
9168
9537
|
this._rovingMatched = false;
|
|
9538
|
+
this._hierarchy = normalizeHierarchy(this.options.hierarchy, this._columns);
|
|
9539
|
+
this._expanded = initialExpanded(this._hierarchy?.expanded);
|
|
9169
9540
|
if (this._sort && this.options.sortMode === "local") this._sortData();
|
|
9541
|
+
if (this._hierarchy?.expanded === true) {
|
|
9542
|
+
for (const id of hierarchyBranchIds(this._data, this.options.rowId, this._hierarchy.parentId)) {
|
|
9543
|
+
this._expanded.add(id);
|
|
9544
|
+
}
|
|
9545
|
+
}
|
|
9546
|
+
this._reconcileExpanded();
|
|
9170
9547
|
this._table = h("table", { class: "zx-table__table" });
|
|
9548
|
+
if (this._hierarchy) this._table.setAttribute("role", "treegrid");
|
|
9171
9549
|
this._colgroup = h("colgroup", { class: "zx-table__columns" });
|
|
9172
9550
|
this._thead = h("thead", { class: "zx-table__head" });
|
|
9173
9551
|
this._tbody = h("tbody", { class: "zx-table__body" });
|
|
@@ -9212,8 +9590,8 @@ var Table = class extends Component {
|
|
|
9212
9590
|
}
|
|
9213
9591
|
/**
|
|
9214
9592
|
* Toggles a loading state. While loading, the body is dimmed, an indeterminate progress bar is
|
|
9215
|
-
* shown, and `aria-busy` is set — a lightweight
|
|
9216
|
-
*
|
|
9593
|
+
* shown, and `aria-busy` is set — a lightweight enterprise-table busy and skeleton pattern.
|
|
9594
|
+
* Call `setLoading(false)` (or `setData`) when done.
|
|
9217
9595
|
* @param {boolean} [loading=true] Whether the table is loading.
|
|
9218
9596
|
* @returns {this}
|
|
9219
9597
|
*/
|
|
@@ -9236,6 +9614,7 @@ var Table = class extends Component {
|
|
|
9236
9614
|
assertRows(rows);
|
|
9237
9615
|
this._data = [...rows];
|
|
9238
9616
|
if (this._sort && this.options.sortMode === "local") this._sortData();
|
|
9617
|
+
this._reconcileExpanded();
|
|
9239
9618
|
const selectionChanged = this._pruneSelection();
|
|
9240
9619
|
this._renderBody();
|
|
9241
9620
|
if (selectionChanged) this._emitSelectionChange();
|
|
@@ -9253,6 +9632,7 @@ var Table = class extends Component {
|
|
|
9253
9632
|
assertRows(rows);
|
|
9254
9633
|
this._data.push(...rows);
|
|
9255
9634
|
if (this._sort && this.options.sortMode === "local") this._sortData();
|
|
9635
|
+
this._reconcileExpanded();
|
|
9256
9636
|
this._renderBody();
|
|
9257
9637
|
this._emitDataChange();
|
|
9258
9638
|
return this;
|
|
@@ -9272,6 +9652,7 @@ var Table = class extends Component {
|
|
|
9272
9652
|
this._data[index] = row;
|
|
9273
9653
|
if (wasSelected) this._selected.add(this._idFor(row));
|
|
9274
9654
|
if (this._sort && this.options.sortMode === "local") this._sortData();
|
|
9655
|
+
this._reconcileExpanded();
|
|
9275
9656
|
this._renderBody();
|
|
9276
9657
|
if (wasSelected && !Object.is(id, this._idFor(row))) this._emitSelectionChange();
|
|
9277
9658
|
this._emitDataChange();
|
|
@@ -9289,6 +9670,7 @@ var Table = class extends Component {
|
|
|
9289
9670
|
const index = this._data.findIndex((candidate) => Object.is(this._idFor(candidate), id));
|
|
9290
9671
|
if (index < 0) return this;
|
|
9291
9672
|
this._data.splice(index, 1);
|
|
9673
|
+
this._reconcileExpanded();
|
|
9292
9674
|
const selectionChanged = this._selected.delete(id);
|
|
9293
9675
|
if (Object.is(this._selectionAnchorId, id)) this._selectionAnchorId = null;
|
|
9294
9676
|
this._renderBody();
|
|
@@ -9304,7 +9686,10 @@ var Table = class extends Component {
|
|
|
9304
9686
|
getRow(id) {
|
|
9305
9687
|
return this._data.find((row) => Object.is(this._idFor(row), id)) ?? null;
|
|
9306
9688
|
}
|
|
9307
|
-
/**
|
|
9689
|
+
/**
|
|
9690
|
+
* Returns a shallow copy of the current rows in display order.
|
|
9691
|
+
* @returns {TableRow[]} Current table rows.
|
|
9692
|
+
*/
|
|
9308
9693
|
getData() {
|
|
9309
9694
|
return [...this._data];
|
|
9310
9695
|
}
|
|
@@ -9318,6 +9703,7 @@ var Table = class extends Component {
|
|
|
9318
9703
|
empty() {
|
|
9319
9704
|
const selectionChanged = this._selected.size > 0;
|
|
9320
9705
|
this._data = [];
|
|
9706
|
+
this._expanded.clear();
|
|
9321
9707
|
this._selected.clear();
|
|
9322
9708
|
this._selectionAnchorId = null;
|
|
9323
9709
|
this._renderBody();
|
|
@@ -9352,7 +9738,10 @@ var Table = class extends Component {
|
|
|
9352
9738
|
if (!options.silent) this.emit("sort", { id, dir: direction });
|
|
9353
9739
|
return this;
|
|
9354
9740
|
}
|
|
9355
|
-
/**
|
|
9741
|
+
/**
|
|
9742
|
+
* Returns selected rows in current display order.
|
|
9743
|
+
* @returns {TableRow[]} Selected rows.
|
|
9744
|
+
*/
|
|
9356
9745
|
getSelection() {
|
|
9357
9746
|
return this._data.filter((row) => this._selected.has(this._idFor(row)));
|
|
9358
9747
|
}
|
|
@@ -9391,6 +9780,75 @@ var Table = class extends Component {
|
|
|
9391
9780
|
this._emitSelectionChange();
|
|
9392
9781
|
return this;
|
|
9393
9782
|
}
|
|
9783
|
+
/**
|
|
9784
|
+
* Toggles one hierarchy branch. A leaf or an id absent from the table is a no-op.
|
|
9785
|
+
* @param {unknown} id Row id.
|
|
9786
|
+
* @param {{silent?: boolean}} [options={}] Event behavior.
|
|
9787
|
+
* @returns {this}
|
|
9788
|
+
* @fires Table#rowtoggle
|
|
9789
|
+
*/
|
|
9790
|
+
toggleRow(id, options = {}) {
|
|
9791
|
+
return this._expanded.has(id) ? this.collapseRow(id, options) : this.expandRow(id, options);
|
|
9792
|
+
}
|
|
9793
|
+
/**
|
|
9794
|
+
* Expands one hierarchy branch.
|
|
9795
|
+
* @param {unknown} id Row id.
|
|
9796
|
+
* @param {{silent?: boolean}} [options={}] Event behavior.
|
|
9797
|
+
* @returns {this}
|
|
9798
|
+
* @fires Table#rowtoggle
|
|
9799
|
+
*/
|
|
9800
|
+
expandRow(id, options = {}) {
|
|
9801
|
+
return this._setRowExpanded(id, true, options);
|
|
9802
|
+
}
|
|
9803
|
+
/**
|
|
9804
|
+
* Collapses one hierarchy branch without discarding its descendants or their expansion state.
|
|
9805
|
+
* @param {unknown} id Row id.
|
|
9806
|
+
* @param {{silent?: boolean}} [options={}] Event behavior.
|
|
9807
|
+
* @returns {this}
|
|
9808
|
+
* @fires Table#rowtoggle
|
|
9809
|
+
*/
|
|
9810
|
+
collapseRow(id, options = {}) {
|
|
9811
|
+
return this._setRowExpanded(id, false, options);
|
|
9812
|
+
}
|
|
9813
|
+
/**
|
|
9814
|
+
* Expands every row that currently has children.
|
|
9815
|
+
* @param {{silent?: boolean}} [options={}] Event behavior. When not silent, one `rowtoggle`
|
|
9816
|
+
* event is emitted per branch whose state changed.
|
|
9817
|
+
* @returns {this}
|
|
9818
|
+
*/
|
|
9819
|
+
expandAll(options = {}) {
|
|
9820
|
+
if (!this._hierarchy) return this;
|
|
9821
|
+
const branchIds = hierarchyBranchIds(this._data, this.options.rowId, this._hierarchy.parentId);
|
|
9822
|
+
const changed = branchIds.filter((id) => !this._expanded.has(id));
|
|
9823
|
+
for (const id of changed) this._expanded.add(id);
|
|
9824
|
+
if (changed.length) this._renderBody();
|
|
9825
|
+
if (!options.silent) {
|
|
9826
|
+
for (const id of changed) this._emitRowToggle(id, true);
|
|
9827
|
+
}
|
|
9828
|
+
return this;
|
|
9829
|
+
}
|
|
9830
|
+
/**
|
|
9831
|
+
* Collapses every hierarchy branch.
|
|
9832
|
+
* @param {{silent?: boolean}} [options={}] Event behavior.
|
|
9833
|
+
* @returns {this}
|
|
9834
|
+
*/
|
|
9835
|
+
collapseAll(options = {}) {
|
|
9836
|
+
if (!this._hierarchy || this._expanded.size === 0) return this;
|
|
9837
|
+
const changed = [...this._expanded];
|
|
9838
|
+
this._expanded.clear();
|
|
9839
|
+
this._renderBody();
|
|
9840
|
+
if (!options.silent) {
|
|
9841
|
+
for (const id of changed) this._emitRowToggle(id, false);
|
|
9842
|
+
}
|
|
9843
|
+
return this;
|
|
9844
|
+
}
|
|
9845
|
+
/**
|
|
9846
|
+
* Returns expanded branch ids in current flat-data order.
|
|
9847
|
+
* @returns {unknown[]} Expanded branch ids.
|
|
9848
|
+
*/
|
|
9849
|
+
getExpanded() {
|
|
9850
|
+
return this._data.map((row) => this._idFor(row)).filter((id) => this._expanded.has(id));
|
|
9851
|
+
}
|
|
9394
9852
|
/**
|
|
9395
9853
|
* Opens the editor for a cell. No-op unless the table runs with `editMode` and the column
|
|
9396
9854
|
* resolves to an editable type for that row. In `'row'` mode every editable cell of the row is
|
|
@@ -9498,11 +9956,17 @@ var Table = class extends Component {
|
|
|
9498
9956
|
this._abortEdit({ focus: true });
|
|
9499
9957
|
return this;
|
|
9500
9958
|
}
|
|
9501
|
-
/**
|
|
9959
|
+
/**
|
|
9960
|
+
* Reports whether a cell or row is currently open for editing.
|
|
9961
|
+
* @returns {boolean} Whether an edit is open.
|
|
9962
|
+
*/
|
|
9502
9963
|
isEditing() {
|
|
9503
9964
|
return this._edit !== null;
|
|
9504
9965
|
}
|
|
9505
|
-
/**
|
|
9966
|
+
/**
|
|
9967
|
+
* Returns the row id and focused column id for the open edit.
|
|
9968
|
+
* @returns {{id: unknown, columnId: string}|null} Editing location, or null.
|
|
9969
|
+
*/
|
|
9506
9970
|
getEditing() {
|
|
9507
9971
|
return this._edit ? { id: this._edit.id, columnId: this._edit.columnId } : null;
|
|
9508
9972
|
}
|
|
@@ -9511,7 +9975,31 @@ var Table = class extends Component {
|
|
|
9511
9975
|
* @returns {TableRow[]}
|
|
9512
9976
|
*/
|
|
9513
9977
|
_visibleData() {
|
|
9514
|
-
return this.
|
|
9978
|
+
return this._visibleEntries().map((entry) => entry.row);
|
|
9979
|
+
}
|
|
9980
|
+
/** @returns {TableHierarchyEntry[]} Rows visible after hierarchy expansion, before growing. */
|
|
9981
|
+
_projectedData() {
|
|
9982
|
+
if (!this._hierarchy) {
|
|
9983
|
+
return this._data.map((row, index) => ({
|
|
9984
|
+
row,
|
|
9985
|
+
id: this._idFor(row),
|
|
9986
|
+
index,
|
|
9987
|
+
depth: 0,
|
|
9988
|
+
hasChildren: false,
|
|
9989
|
+
parentId: null
|
|
9990
|
+
}));
|
|
9991
|
+
}
|
|
9992
|
+
return projectHierarchyRows(
|
|
9993
|
+
this._data,
|
|
9994
|
+
this.options.rowId,
|
|
9995
|
+
this._hierarchy.parentId,
|
|
9996
|
+
this._expanded
|
|
9997
|
+
);
|
|
9998
|
+
}
|
|
9999
|
+
/** @returns {TableHierarchyEntry[]} Rows that currently have DOM entries. */
|
|
10000
|
+
_visibleEntries() {
|
|
10001
|
+
const projected = this._projectedData();
|
|
10002
|
+
return this._rendered === Infinity ? projected : projected.slice(0, this._rendered);
|
|
9515
10003
|
}
|
|
9516
10004
|
/**
|
|
9517
10005
|
* Renders the next batch of rows.
|
|
@@ -9523,10 +10011,11 @@ var Table = class extends Component {
|
|
|
9523
10011
|
if (!this._growStep) return this;
|
|
9524
10012
|
const step = Number(count) > 0 ? Math.trunc(Number(count)) : this._growStep;
|
|
9525
10013
|
const before = this._rendered;
|
|
9526
|
-
|
|
10014
|
+
const total = this._projectedData().length;
|
|
10015
|
+
this._rendered = Math.min(total, this._rendered + step);
|
|
9527
10016
|
if (this._rendered === before) return this;
|
|
9528
10017
|
this._renderBody();
|
|
9529
|
-
this.emit("grow", { rendered: this._rendered, total
|
|
10018
|
+
this.emit("grow", { rendered: this._rendered, total });
|
|
9530
10019
|
return this;
|
|
9531
10020
|
}
|
|
9532
10021
|
/**
|
|
@@ -9535,14 +10024,14 @@ var Table = class extends Component {
|
|
|
9535
10024
|
* @fires Table#grow
|
|
9536
10025
|
*/
|
|
9537
10026
|
showAll() {
|
|
9538
|
-
return this.growBy(this.
|
|
10027
|
+
return this.growBy(this._projectedData().length);
|
|
9539
10028
|
}
|
|
9540
10029
|
/**
|
|
9541
10030
|
* How many rows are currently rendered.
|
|
9542
10031
|
* @returns {number}
|
|
9543
10032
|
*/
|
|
9544
10033
|
getRenderedCount() {
|
|
9545
|
-
return Math.min(this._rendered, this.
|
|
10034
|
+
return Math.min(this._rendered, this._projectedData().length);
|
|
9546
10035
|
}
|
|
9547
10036
|
/**
|
|
9548
10037
|
* Keeps the "show more" control in step with what is rendered.
|
|
@@ -9554,7 +10043,7 @@ var Table = class extends Component {
|
|
|
9554
10043
|
*/
|
|
9555
10044
|
_syncGrowing() {
|
|
9556
10045
|
if (!this._growStep) return;
|
|
9557
|
-
const remaining = this.
|
|
10046
|
+
const remaining = this._projectedData().length - this.getRenderedCount();
|
|
9558
10047
|
if (remaining <= 0) {
|
|
9559
10048
|
this._more?.remove();
|
|
9560
10049
|
this._more = null;
|
|
@@ -9625,10 +10114,11 @@ var Table = class extends Component {
|
|
|
9625
10114
|
* @returns {void}
|
|
9626
10115
|
*/
|
|
9627
10116
|
_applyStackedRoles(stacked) {
|
|
9628
|
-
const parts = [[this._table, "table"], [this._thead, "rowgroup"], [this._tbody, "rowgroup"]];
|
|
10117
|
+
const parts = [[this._table, this._hierarchy ? "treegrid" : "table"], [this._thead, "rowgroup"], [this._tbody, "rowgroup"]];
|
|
9629
10118
|
for (const [element, role] of parts) {
|
|
9630
10119
|
if (!element) continue;
|
|
9631
10120
|
if (stacked) element.setAttribute("role", role);
|
|
10121
|
+
else if (element === this._table && this._hierarchy) element.setAttribute("role", "treegrid");
|
|
9632
10122
|
else element.removeAttribute("role");
|
|
9633
10123
|
}
|
|
9634
10124
|
for (const [selector, role] of [["tr", "row"], ["td", "cell"], ["th", "columnheader"]]) {
|
|
@@ -9650,18 +10140,17 @@ var Table = class extends Component {
|
|
|
9650
10140
|
this._teardownEdit();
|
|
9651
10141
|
this._width?.destroy();
|
|
9652
10142
|
this._width = null;
|
|
9653
|
-
if (this.
|
|
9654
|
-
|
|
9655
|
-
this.el.replaceChildren(...this._originalChildren);
|
|
10143
|
+
if (!this._restored && !this._createdRoot) {
|
|
10144
|
+
restoreTarget(this.el, this._original);
|
|
9656
10145
|
this._restored = true;
|
|
9657
10146
|
}
|
|
9658
10147
|
super.destroy();
|
|
9659
10148
|
}
|
|
9660
10149
|
/** @returns {void} */
|
|
9661
10150
|
_renderColumns() {
|
|
9662
|
-
const
|
|
10151
|
+
const fragment2 = document.createDocumentFragment();
|
|
9663
10152
|
if (this.options.selectable === "multi") {
|
|
9664
|
-
|
|
10153
|
+
fragment2.append(h("col", { class: "zx-table__selection-column" }));
|
|
9665
10154
|
}
|
|
9666
10155
|
const fractions = this._columns.map((column) => parseFraction(column.width));
|
|
9667
10156
|
const total = fractions.reduce((sum, value) => sum + value, 0);
|
|
@@ -9670,9 +10159,9 @@ var Table = class extends Component {
|
|
|
9670
10159
|
const col = h("col");
|
|
9671
10160
|
if (fractions[index] > 0) col.style.width = `${fractions[index] / total * 100}%`;
|
|
9672
10161
|
else if (column.width && column.width !== "auto") col.style.width = String(column.width);
|
|
9673
|
-
|
|
10162
|
+
fragment2.append(col);
|
|
9674
10163
|
});
|
|
9675
|
-
this._colgroup.replaceChildren(
|
|
10164
|
+
this._colgroup.replaceChildren(fragment2);
|
|
9676
10165
|
}
|
|
9677
10166
|
/** @returns {void} */
|
|
9678
10167
|
_renderHeader() {
|
|
@@ -9705,7 +10194,8 @@ var Table = class extends Component {
|
|
|
9705
10194
|
this._headers = /* @__PURE__ */ new Map();
|
|
9706
10195
|
for (const column of this._columns) {
|
|
9707
10196
|
const th = h("th", { scope: "col" });
|
|
9708
|
-
|
|
10197
|
+
const align = column.align ?? (isNumericColumn(column) ? "end" : null);
|
|
10198
|
+
if (align) th.dataset.align = align;
|
|
9709
10199
|
if (column.headerTitle) th.title = column.headerTitle;
|
|
9710
10200
|
if (column.sortable) {
|
|
9711
10201
|
const button = h("button", {
|
|
@@ -9729,13 +10219,15 @@ var Table = class extends Component {
|
|
|
9729
10219
|
/** @returns {void} @fires Table#editcancel */
|
|
9730
10220
|
_renderBody() {
|
|
9731
10221
|
this._abortEdit({ focus: false });
|
|
9732
|
-
const
|
|
10222
|
+
const fragment2 = document.createDocumentFragment();
|
|
9733
10223
|
this._rowMeta = /* @__PURE__ */ new WeakMap();
|
|
9734
10224
|
this._rowElements = /* @__PURE__ */ new Map();
|
|
9735
10225
|
this._rovingCell = null;
|
|
9736
10226
|
this._rovingMatched = false;
|
|
10227
|
+
const projected = this._projectedData();
|
|
10228
|
+
this._hierarchyMeta = new Map(projected.map((entry) => [entry.id, entry]));
|
|
9737
10229
|
if (this._data.length === 0) {
|
|
9738
|
-
|
|
10230
|
+
fragment2.append(h(
|
|
9739
10231
|
"tr",
|
|
9740
10232
|
{ class: "zx-table__empty-row" },
|
|
9741
10233
|
h("td", {
|
|
@@ -9744,10 +10236,12 @@ var Table = class extends Component {
|
|
|
9744
10236
|
}, this._emptyContent())
|
|
9745
10237
|
));
|
|
9746
10238
|
} else {
|
|
9747
|
-
this.
|
|
10239
|
+
this._visibleEntries().forEach((entry, visibleIndex) => {
|
|
10240
|
+
fragment2.append(this._createRow(entry.row, entry.index, { ...entry, visibleIndex }));
|
|
10241
|
+
});
|
|
9748
10242
|
}
|
|
9749
|
-
this._tbody.replaceChildren(
|
|
9750
|
-
if (this._growStep) this._table.setAttribute("aria-rowcount", String(
|
|
10243
|
+
this._tbody.replaceChildren(fragment2);
|
|
10244
|
+
if (this._growStep) this._table.setAttribute("aria-rowcount", String(projected.length + 1));
|
|
9751
10245
|
this._syncSelectAll();
|
|
9752
10246
|
this._syncGrowing();
|
|
9753
10247
|
if (this._stacked) this._applyStackedRoles(true);
|
|
@@ -9766,18 +10260,27 @@ var Table = class extends Component {
|
|
|
9766
10260
|
if (empty == null) return this.msg("table.empty");
|
|
9767
10261
|
return typeof empty === "function" ? empty() : empty;
|
|
9768
10262
|
}
|
|
9769
|
-
/**
|
|
9770
|
-
|
|
10263
|
+
/**
|
|
10264
|
+
* @param {TableRow} row
|
|
10265
|
+
* @param {number} index
|
|
10266
|
+
* @param {TableHierarchyEntry & {visibleIndex: number}} tree
|
|
10267
|
+
* @returns {HTMLTableRowElement}
|
|
10268
|
+
*/
|
|
10269
|
+
_createRow(row, index, tree) {
|
|
9771
10270
|
const id = this._idFor(row);
|
|
9772
10271
|
const tr = (
|
|
9773
10272
|
/** @type {HTMLTableRowElement} */
|
|
9774
10273
|
h("tr", { dataset: { row: "" } })
|
|
9775
10274
|
);
|
|
9776
|
-
if (this._growStep) tr.setAttribute("aria-rowindex", String(
|
|
10275
|
+
if (this._growStep) tr.setAttribute("aria-rowindex", String(tree.visibleIndex + 2));
|
|
9777
10276
|
const rowClass = typeof this.options.rowClass === "function" ? this.options.rowClass(row) : "";
|
|
9778
10277
|
if (rowClass) tr.classList.add(...String(rowClass).split(/\s+/).filter(Boolean));
|
|
9779
10278
|
if (this.options.selectable !== false) tr.setAttribute("aria-selected", String(this._selected.has(id)));
|
|
9780
|
-
this.
|
|
10279
|
+
if (this._hierarchy) {
|
|
10280
|
+
tr.setAttribute("aria-level", String(tree.depth + 1));
|
|
10281
|
+
if (tree.hasChildren) tr.setAttribute("aria-expanded", String(this._expanded.has(id)));
|
|
10282
|
+
}
|
|
10283
|
+
this._rowMeta.set(tr, { row, id, index, visibleIndex: tree.visibleIndex });
|
|
9781
10284
|
const elements = this._rowElements.get(id) ?? [];
|
|
9782
10285
|
elements.push(tr);
|
|
9783
10286
|
this._rowElements.set(id, elements);
|
|
@@ -9795,7 +10298,8 @@ var Table = class extends Component {
|
|
|
9795
10298
|
/** @type {HTMLTableCellElement} */
|
|
9796
10299
|
h("td")
|
|
9797
10300
|
);
|
|
9798
|
-
|
|
10301
|
+
const align = column.align ?? (isNumericColumn(column) ? "end" : null);
|
|
10302
|
+
if (align) cell.dataset.align = align;
|
|
9799
10303
|
cell.dataset.label = column.label;
|
|
9800
10304
|
if (column.popin === false) cell.dataset.popin = "false";
|
|
9801
10305
|
if (this._editMode !== false) {
|
|
@@ -9806,7 +10310,7 @@ var Table = class extends Component {
|
|
|
9806
10310
|
this._offerRovingCell(cell, id, column.id);
|
|
9807
10311
|
}
|
|
9808
10312
|
}
|
|
9809
|
-
this._renderCellContent(cell, column, row, index);
|
|
10313
|
+
this._renderCellContent(cell, column, row, index, tree);
|
|
9810
10314
|
tr.append(cell);
|
|
9811
10315
|
});
|
|
9812
10316
|
return tr;
|
|
@@ -9817,13 +10321,33 @@ var Table = class extends Component {
|
|
|
9817
10321
|
* @param {TableColumn} column Column definition.
|
|
9818
10322
|
* @param {TableRow} row Row data.
|
|
9819
10323
|
* @param {number} index Row index.
|
|
10324
|
+
* @param {TableHierarchyEntry} [tree] Hierarchy metadata for the row.
|
|
9820
10325
|
* @returns {void}
|
|
9821
10326
|
*/
|
|
9822
|
-
_renderCellContent(cell, column, row, index) {
|
|
9823
|
-
const value =
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
else
|
|
10327
|
+
_renderCellContent(cell, column, row, index, tree = this._hierarchyMeta?.get(this._idFor(row))) {
|
|
10328
|
+
const value = formatTableCell(column, row, index);
|
|
10329
|
+
const content = h("span", { class: "zx-table__cell-content" });
|
|
10330
|
+
if (value && typeof value === "object" && typeof value.nodeType === "number") content.append(value);
|
|
10331
|
+
else if (value != null) content.append(document.createTextNode(String(value)));
|
|
10332
|
+
if (!this._hierarchy || column.id !== this._hierarchy.column || !tree) {
|
|
10333
|
+
cell.replaceChildren(...content.childNodes);
|
|
10334
|
+
return;
|
|
10335
|
+
}
|
|
10336
|
+
const branch = h("span", { class: "zx-table__tree-cell" });
|
|
10337
|
+
branch.style.setProperty("--zx-table-depth", String(tree.depth));
|
|
10338
|
+
if (tree.hasChildren) {
|
|
10339
|
+
const expanded = this._expanded.has(tree.id);
|
|
10340
|
+
branch.append(h("button", {
|
|
10341
|
+
class: "zx-table__tree-toggle",
|
|
10342
|
+
type: "button",
|
|
10343
|
+
ariaExpanded: String(expanded),
|
|
10344
|
+
ariaLabel: `${expanded ? "Collapse" : "Expand"} row ${index + 1}`
|
|
10345
|
+
}, icon(expanded ? "chevron-down" : "chevron-right", { size: 14 })));
|
|
10346
|
+
} else {
|
|
10347
|
+
branch.append(h("span", { class: "zx-table__tree-spacer", ariaHidden: "true" }));
|
|
10348
|
+
}
|
|
10349
|
+
branch.append(content);
|
|
10350
|
+
cell.replaceChildren(branch);
|
|
9827
10351
|
}
|
|
9828
10352
|
/** @param {MouseEvent} event @returns {void} */
|
|
9829
10353
|
_handleBodyClick(event) {
|
|
@@ -9836,7 +10360,15 @@ var Table = class extends Component {
|
|
|
9836
10360
|
if (!rowElement || !this._tbody.contains(rowElement)) return;
|
|
9837
10361
|
const meta = this._rowMeta.get(rowElement);
|
|
9838
10362
|
if (!meta) return;
|
|
9839
|
-
if (this._edit)
|
|
10363
|
+
if (this._edit) {
|
|
10364
|
+
this.commitEdit();
|
|
10365
|
+
if (this._edit) return;
|
|
10366
|
+
}
|
|
10367
|
+
const disclosure = target.closest(".zx-table__tree-toggle");
|
|
10368
|
+
if (disclosure) {
|
|
10369
|
+
this.toggleRow(meta.id);
|
|
10370
|
+
return;
|
|
10371
|
+
}
|
|
9840
10372
|
const checkbox = target.closest(".zx-table__row-checkbox");
|
|
9841
10373
|
if (checkbox && this.options.selectable === "multi") {
|
|
9842
10374
|
this._toggleMultiSelection(
|
|
@@ -9932,7 +10464,15 @@ var Table = class extends Component {
|
|
|
9932
10464
|
return entry;
|
|
9933
10465
|
}
|
|
9934
10466
|
if (type === "number") {
|
|
9935
|
-
const
|
|
10467
|
+
const locale = resolveColumnOption(column.locale, row);
|
|
10468
|
+
const decimals = resolveColumnDecimals(column, row);
|
|
10469
|
+
const suffix = column.type === "currency" ? resolveColumnOption(column.currency, row) : column.type === "unit" ? resolveColumnOption(column.unit, row) : null;
|
|
10470
|
+
const inferred = {
|
|
10471
|
+
...locale != null && !Object.prototype.hasOwnProperty.call(props, "locale") ? { locale } : {},
|
|
10472
|
+
...decimals != null && !Object.prototype.hasOwnProperty.call(props, "precision") ? { precision: decimals } : {},
|
|
10473
|
+
...suffix != null && !Object.prototype.hasOwnProperty.call(props, "unit") ? { unit: String(suffix) } : {}
|
|
10474
|
+
};
|
|
10475
|
+
const field = new NumberField(null, { ...inferred, ...props, value: value ?? null });
|
|
9936
10476
|
host.append(field.el);
|
|
9937
10477
|
entry.component = field;
|
|
9938
10478
|
entry.control = field.getInput();
|
|
@@ -10163,9 +10703,12 @@ var Table = class extends Component {
|
|
|
10163
10703
|
_nextEditableCell(rowIndex, columnId, direction) {
|
|
10164
10704
|
const columnCount = this._columns.length;
|
|
10165
10705
|
if (rowIndex < 0 || columnCount === 0) return null;
|
|
10166
|
-
|
|
10706
|
+
const visible = this._visibleData();
|
|
10707
|
+
const startingId = this._idFor(this._data[rowIndex]);
|
|
10708
|
+
let row = visible.findIndex((candidate) => Object.is(this._idFor(candidate), startingId));
|
|
10709
|
+
if (row < 0) return null;
|
|
10167
10710
|
let column = this._columns.findIndex((candidate) => candidate.id === columnId);
|
|
10168
|
-
const limit =
|
|
10711
|
+
const limit = visible.length * columnCount + columnCount;
|
|
10169
10712
|
for (let step = 0; step < limit; step += 1) {
|
|
10170
10713
|
column += direction;
|
|
10171
10714
|
if (column >= columnCount) {
|
|
@@ -10175,11 +10718,15 @@ var Table = class extends Component {
|
|
|
10175
10718
|
row -= 1;
|
|
10176
10719
|
column = columnCount - 1;
|
|
10177
10720
|
}
|
|
10178
|
-
if (row < 0 || row >=
|
|
10179
|
-
const candidateRow =
|
|
10721
|
+
if (row < 0 || row >= visible.length) return null;
|
|
10722
|
+
const candidateRow = visible[row];
|
|
10180
10723
|
const candidateColumn = this._columns[column];
|
|
10181
10724
|
if (resolveEditable(candidateColumn, candidateRow) !== false) {
|
|
10182
|
-
return {
|
|
10725
|
+
return {
|
|
10726
|
+
id: this._idFor(candidateRow),
|
|
10727
|
+
columnId: candidateColumn.id,
|
|
10728
|
+
index: this._data.indexOf(candidateRow)
|
|
10729
|
+
};
|
|
10183
10730
|
}
|
|
10184
10731
|
}
|
|
10185
10732
|
return null;
|
|
@@ -10246,14 +10793,16 @@ var Table = class extends Component {
|
|
|
10246
10793
|
const message = this.msg(key);
|
|
10247
10794
|
return message === key ? fallback : message;
|
|
10248
10795
|
}
|
|
10249
|
-
/** @param {{row: TableRow, id: unknown, index: number}} meta @param {boolean} checked @param {boolean} shiftKey @returns {void} */
|
|
10796
|
+
/** @param {{row: TableRow, id: unknown, index: number, visibleIndex?: number}} meta @param {boolean} checked @param {boolean} shiftKey @returns {void} */
|
|
10250
10797
|
_toggleMultiSelection(meta, checked, shiftKey) {
|
|
10251
|
-
const
|
|
10798
|
+
const visible = this._visibleData();
|
|
10799
|
+
const anchorIndex = visible.findIndex((row) => Object.is(this._idFor(row), this._selectionAnchorId));
|
|
10252
10800
|
if (shiftKey && anchorIndex >= 0) {
|
|
10253
|
-
const
|
|
10254
|
-
const
|
|
10801
|
+
const currentIndex = meta.visibleIndex ?? visible.findIndex((row) => Object.is(this._idFor(row), meta.id));
|
|
10802
|
+
const start = Math.min(anchorIndex, currentIndex);
|
|
10803
|
+
const end = Math.max(anchorIndex, currentIndex);
|
|
10255
10804
|
for (let index = start; index <= end; index += 1) {
|
|
10256
|
-
const id = this._idFor(
|
|
10805
|
+
const id = this._idFor(visible[index]);
|
|
10257
10806
|
if (checked) this._selected.add(id);
|
|
10258
10807
|
else this._selected.delete(id);
|
|
10259
10808
|
}
|
|
@@ -10304,7 +10853,34 @@ var Table = class extends Component {
|
|
|
10304
10853
|
const column = this._columns.find((candidate) => candidate.id === this._sort.id);
|
|
10305
10854
|
if (!column) return;
|
|
10306
10855
|
const getValue = typeof column.sortValue === "function" ? column.sortValue : (row) => row?.[column.id];
|
|
10307
|
-
this._data = sortRows(this._data, getValue, this._sort.dir);
|
|
10856
|
+
this._data = this._hierarchy ? sortHierarchyRows(this._data, this.options.rowId, this._hierarchy.parentId, getValue, this._sort.dir) : sortRows(this._data, getValue, this._sort.dir);
|
|
10857
|
+
}
|
|
10858
|
+
/** @param {unknown} id @param {boolean} expanded @param {{silent?: boolean}} options @returns {this} */
|
|
10859
|
+
_setRowExpanded(id, expanded, options) {
|
|
10860
|
+
if (!this._hierarchy) return this;
|
|
10861
|
+
const branchIds = hierarchyBranchIds(this._data, this.options.rowId, this._hierarchy.parentId);
|
|
10862
|
+
if (!branchIds.some((candidate) => Object.is(candidate, id))) return this;
|
|
10863
|
+
if (this._expanded.has(id) === expanded) return this;
|
|
10864
|
+
if (expanded) this._expanded.add(id);
|
|
10865
|
+
else this._expanded.delete(id);
|
|
10866
|
+
this._renderBody();
|
|
10867
|
+
if (!options.silent) this._emitRowToggle(id, expanded);
|
|
10868
|
+
return this;
|
|
10869
|
+
}
|
|
10870
|
+
/** @param {unknown} id @param {boolean} expanded @returns {void} */
|
|
10871
|
+
_emitRowToggle(id, expanded) {
|
|
10872
|
+
const row = this.getRow(id);
|
|
10873
|
+
if (row) this.emit("rowtoggle", { row, id, expanded });
|
|
10874
|
+
}
|
|
10875
|
+
/** Keeps the reader's current expansion state limited to branches still present in the data. */
|
|
10876
|
+
_reconcileExpanded() {
|
|
10877
|
+
if (!this._hierarchy) {
|
|
10878
|
+
this._expanded.clear();
|
|
10879
|
+
return;
|
|
10880
|
+
}
|
|
10881
|
+
const branches = hierarchyBranchIds(this._data, this.options.rowId, this._hierarchy.parentId);
|
|
10882
|
+
const valid = new Set(branches);
|
|
10883
|
+
for (const id of this._expanded) if (!valid.has(id)) this._expanded.delete(id);
|
|
10308
10884
|
}
|
|
10309
10885
|
/** @returns {boolean} Whether selection changed. */
|
|
10310
10886
|
_pruneSelection() {
|
|
@@ -10328,6 +10904,65 @@ var Table = class extends Component {
|
|
|
10328
10904
|
this.emit("datachange", { rows: this.getData() });
|
|
10329
10905
|
}
|
|
10330
10906
|
};
|
|
10907
|
+
function formatTableCell(column, row, index = 0) {
|
|
10908
|
+
if (typeof column?.render === "function") return column.render(row, index);
|
|
10909
|
+
const value = row?.[column?.id];
|
|
10910
|
+
const type = column?.type ?? "text";
|
|
10911
|
+
if (type === "text") return value;
|
|
10912
|
+
const locale = resolveColumnOption(column.locale, row);
|
|
10913
|
+
const decimals = resolveColumnDecimals(column, row);
|
|
10914
|
+
const options = {
|
|
10915
|
+
...locale != null ? { locale: String(locale) } : {},
|
|
10916
|
+
...decimals != null ? { decimals } : {}
|
|
10917
|
+
};
|
|
10918
|
+
if (type === "number") return formatNumber(value, options);
|
|
10919
|
+
if (type === "currency") {
|
|
10920
|
+
const currency = resolveColumnOption(column.currency, row);
|
|
10921
|
+
return formatCurrency(value, currency == null ? null : String(currency), options);
|
|
10922
|
+
}
|
|
10923
|
+
if (type === "percent") return formatPercent2(value, options);
|
|
10924
|
+
if (type === "unit") {
|
|
10925
|
+
const number = formatNumber(value, options);
|
|
10926
|
+
const unit = resolveColumnOption(column.unit, row);
|
|
10927
|
+
return number && unit != null && String(unit) !== "" ? `${number}\xA0${unit}` : number;
|
|
10928
|
+
}
|
|
10929
|
+
return value;
|
|
10930
|
+
}
|
|
10931
|
+
function projectHierarchyRows(rows, rowId, parentId, expanded = []) {
|
|
10932
|
+
const forest = hierarchyForest(rows, rowId, parentId);
|
|
10933
|
+
const open = expanded instanceof Set ? expanded : new Set(expanded);
|
|
10934
|
+
const result = [];
|
|
10935
|
+
const stack = forest.roots.slice().reverse().map((node) => ({ node, depth: 0 }));
|
|
10936
|
+
while (stack.length) {
|
|
10937
|
+
const { node, depth } = stack.pop();
|
|
10938
|
+
result.push({
|
|
10939
|
+
row: node.row,
|
|
10940
|
+
id: node.id,
|
|
10941
|
+
index: node.index,
|
|
10942
|
+
depth,
|
|
10943
|
+
hasChildren: node.children.length > 0,
|
|
10944
|
+
parentId: node.parent?.id ?? null
|
|
10945
|
+
});
|
|
10946
|
+
if (!open.has(node.id)) continue;
|
|
10947
|
+
for (let index = node.children.length - 1; index >= 0; index -= 1) {
|
|
10948
|
+
stack.push({ node: node.children[index], depth: depth + 1 });
|
|
10949
|
+
}
|
|
10950
|
+
}
|
|
10951
|
+
return result;
|
|
10952
|
+
}
|
|
10953
|
+
function sortHierarchyRows(rows, rowId, parentId, getValue, direction) {
|
|
10954
|
+
const forest = hierarchyForest(rows, rowId, parentId);
|
|
10955
|
+
const sorted = (nodes) => sortRows(nodes, (node) => getValue(node.row), direction);
|
|
10956
|
+
const result = [];
|
|
10957
|
+
const stack = sorted(forest.roots).reverse();
|
|
10958
|
+
while (stack.length) {
|
|
10959
|
+
const node = stack.pop();
|
|
10960
|
+
result.push(node.row);
|
|
10961
|
+
const children = sorted(node.children);
|
|
10962
|
+
for (let index = children.length - 1; index >= 0; index -= 1) stack.push(children[index]);
|
|
10963
|
+
}
|
|
10964
|
+
return result;
|
|
10965
|
+
}
|
|
10331
10966
|
function resolveEditable(column, row) {
|
|
10332
10967
|
if (!column || typeof column !== "object") return false;
|
|
10333
10968
|
if (typeof column.editor === "function") {
|
|
@@ -10335,7 +10970,7 @@ function resolveEditable(column, row) {
|
|
|
10335
10970
|
return "custom";
|
|
10336
10971
|
}
|
|
10337
10972
|
const declared = typeof column.editable === "function" ? column.editable(row) : column.editable;
|
|
10338
|
-
if (declared === true) return "text";
|
|
10973
|
+
if (declared === true) return isNumericColumn(column) ? "number" : "text";
|
|
10339
10974
|
if (typeof declared === "string" && EDITOR_TYPES.has(declared)) return (
|
|
10340
10975
|
/** @type {TableEditorType} */
|
|
10341
10976
|
declared
|
|
@@ -10384,6 +11019,71 @@ function firstValidationError(values, columns, row, fallbackMessage = "Invalid v
|
|
|
10384
11019
|
}
|
|
10385
11020
|
return null;
|
|
10386
11021
|
}
|
|
11022
|
+
function resolveColumnOption(option, row) {
|
|
11023
|
+
return typeof option === "function" ? option(row) : option;
|
|
11024
|
+
}
|
|
11025
|
+
function resolveColumnDecimals(column, row) {
|
|
11026
|
+
const value = resolveColumnOption(column?.decimals, row);
|
|
11027
|
+
if (value == null) return null;
|
|
11028
|
+
const number = Number(value);
|
|
11029
|
+
return Number.isFinite(number) ? Math.max(0, Math.trunc(number)) : null;
|
|
11030
|
+
}
|
|
11031
|
+
function isNumericColumn(column) {
|
|
11032
|
+
return ["number", "currency", "percent", "unit"].includes(column?.type);
|
|
11033
|
+
}
|
|
11034
|
+
function hierarchyForest(rows, rowId, parentId) {
|
|
11035
|
+
const nodes = (Array.isArray(rows) ? rows : []).map((row, index) => ({
|
|
11036
|
+
row,
|
|
11037
|
+
id: readRowValue(rowId, row),
|
|
11038
|
+
index,
|
|
11039
|
+
parent: null,
|
|
11040
|
+
children: []
|
|
11041
|
+
}));
|
|
11042
|
+
const byId = /* @__PURE__ */ new Map();
|
|
11043
|
+
for (const node of nodes) if (!byId.has(node.id)) byId.set(node.id, node);
|
|
11044
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
11045
|
+
for (const node of nodes) {
|
|
11046
|
+
const parent = byId.get(readRowValue(parentId, node.row)) ?? null;
|
|
11047
|
+
parentOf.set(node, parent === node ? null : parent);
|
|
11048
|
+
}
|
|
11049
|
+
for (const node of nodes) {
|
|
11050
|
+
const seen = /* @__PURE__ */ new Set([node]);
|
|
11051
|
+
let cursor = parentOf.get(node) ?? null;
|
|
11052
|
+
while (cursor) {
|
|
11053
|
+
if (seen.has(cursor)) {
|
|
11054
|
+
parentOf.set(cursor, null);
|
|
11055
|
+
break;
|
|
11056
|
+
}
|
|
11057
|
+
seen.add(cursor);
|
|
11058
|
+
cursor = parentOf.get(cursor) ?? null;
|
|
11059
|
+
}
|
|
11060
|
+
}
|
|
11061
|
+
const roots = [];
|
|
11062
|
+
for (const node of nodes) {
|
|
11063
|
+
node.parent = parentOf.get(node) ?? null;
|
|
11064
|
+
if (node.parent) node.parent.children.push(node);
|
|
11065
|
+
else roots.push(node);
|
|
11066
|
+
}
|
|
11067
|
+
return { roots, nodes };
|
|
11068
|
+
}
|
|
11069
|
+
function hierarchyBranchIds(rows, rowId, parentId) {
|
|
11070
|
+
return hierarchyForest(rows, rowId, parentId).nodes.filter((node) => node.children.length > 0).map((node) => node.id);
|
|
11071
|
+
}
|
|
11072
|
+
function readRowValue(accessor, row) {
|
|
11073
|
+
return typeof accessor === "function" ? accessor(row) : row?.[accessor];
|
|
11074
|
+
}
|
|
11075
|
+
function normalizeHierarchy(option, columns) {
|
|
11076
|
+
if (!option || typeof option !== "object") return null;
|
|
11077
|
+
const parentId = option.parentId;
|
|
11078
|
+
if (typeof parentId !== "string" && typeof parentId !== "function") return null;
|
|
11079
|
+
const requested = typeof option.column === "string" ? option.column : null;
|
|
11080
|
+
const column = columns.some((candidate) => candidate.id === requested) ? requested : columns[0]?.id;
|
|
11081
|
+
if (!column) return null;
|
|
11082
|
+
return { ...option, parentId, column };
|
|
11083
|
+
}
|
|
11084
|
+
function initialExpanded(option) {
|
|
11085
|
+
return new Set(Array.isArray(option) ? option : []);
|
|
11086
|
+
}
|
|
10387
11087
|
function normalizeEditMode(mode) {
|
|
10388
11088
|
if (mode === "cell" || mode === "row") return mode;
|
|
10389
11089
|
return mode === true ? "cell" : false;
|
|
@@ -10425,6 +11125,630 @@ function growStep(growing) {
|
|
|
10425
11125
|
return Number.isFinite(step) && step > 0 ? step : null;
|
|
10426
11126
|
}
|
|
10427
11127
|
|
|
11128
|
+
// src/components/launcher/launcher.js
|
|
11129
|
+
var Launcher = class extends Component {
|
|
11130
|
+
static cssName = "launcher";
|
|
11131
|
+
/** @type {Readonly<LauncherOptions>} */
|
|
11132
|
+
static defaults = {
|
|
11133
|
+
items: [],
|
|
11134
|
+
sources: [],
|
|
11135
|
+
query: "",
|
|
11136
|
+
debounce: 250,
|
|
11137
|
+
minQuery: 0,
|
|
11138
|
+
maxResults: 100,
|
|
11139
|
+
placeholder: "Search applications and records",
|
|
11140
|
+
label: "Launcher",
|
|
11141
|
+
closeLabel: "Close launcher",
|
|
11142
|
+
resultsLabel: "Launcher results",
|
|
11143
|
+
emptyText: "No results",
|
|
11144
|
+
loadingText: "Searching\u2026",
|
|
11145
|
+
shortcut: "mod+k",
|
|
11146
|
+
hints: { move: "Move", open: "Open", close: "Close" }
|
|
11147
|
+
};
|
|
11148
|
+
/**
|
|
11149
|
+
* Creates an owned launcher dialog or enhances an existing `<dialog>` target.
|
|
11150
|
+
* @param {HTMLDialogElement|string|null} [target=null] Dialog target, selector, or null.
|
|
11151
|
+
* @param {LauncherOptions} [options={}] Launcher options.
|
|
11152
|
+
*/
|
|
11153
|
+
constructor(target = null, options = {}) {
|
|
11154
|
+
super(target, options);
|
|
11155
|
+
}
|
|
11156
|
+
/** @returns {HTMLDialogElement} */
|
|
11157
|
+
render() {
|
|
11158
|
+
if (this.el !== null && !(this.el instanceof HTMLDialogElement)) {
|
|
11159
|
+
throw new TypeError("Launcher target must be a <dialog> element");
|
|
11160
|
+
}
|
|
11161
|
+
this._createdRoot = this.el === null;
|
|
11162
|
+
const dialog = (
|
|
11163
|
+
/** @type {HTMLDialogElement} */
|
|
11164
|
+
this.el ?? h("dialog")
|
|
11165
|
+
);
|
|
11166
|
+
this.el = dialog;
|
|
11167
|
+
this._original = this._createdRoot ? null : {
|
|
11168
|
+
attributes: Array.from(dialog.attributes, (attribute) => [attribute.name, attribute.value]),
|
|
11169
|
+
children: Array.from(dialog.childNodes)
|
|
11170
|
+
};
|
|
11171
|
+
this._items = normalizeItems(this.options.items);
|
|
11172
|
+
this._sources = normalizeSources(this.options.sources);
|
|
11173
|
+
this._sourceItems = /* @__PURE__ */ new Map();
|
|
11174
|
+
this._query = String(this.options.query ?? "");
|
|
11175
|
+
this._renderedQuery = null;
|
|
11176
|
+
this._results = [];
|
|
11177
|
+
this._active = -1;
|
|
11178
|
+
this._request = null;
|
|
11179
|
+
this._requestSequence = 0;
|
|
11180
|
+
this._queryTimer = null;
|
|
11181
|
+
this._busy = false;
|
|
11182
|
+
this._destroyed = false;
|
|
11183
|
+
const listId = uid("zx-launcher-results");
|
|
11184
|
+
const input = h("input", {
|
|
11185
|
+
ref: "input",
|
|
11186
|
+
class: "zx-launcher__input",
|
|
11187
|
+
type: "search",
|
|
11188
|
+
role: "combobox",
|
|
11189
|
+
autocomplete: "off",
|
|
11190
|
+
spellcheck: false,
|
|
11191
|
+
ariaAutocomplete: "list",
|
|
11192
|
+
ariaControls: listId,
|
|
11193
|
+
ariaExpanded: "false",
|
|
11194
|
+
ariaLabel: this.options.label,
|
|
11195
|
+
placeholder: this.options.placeholder,
|
|
11196
|
+
value: this._query
|
|
11197
|
+
});
|
|
11198
|
+
const close = h("button", {
|
|
11199
|
+
ref: "close",
|
|
11200
|
+
class: "zx-icon-btn zx-launcher__close",
|
|
11201
|
+
type: "button",
|
|
11202
|
+
ariaLabel: this.options.closeLabel
|
|
11203
|
+
}, icon("x"));
|
|
11204
|
+
const status = h("div", {
|
|
11205
|
+
ref: "status",
|
|
11206
|
+
class: "zx-launcher__status",
|
|
11207
|
+
role: "status",
|
|
11208
|
+
ariaLive: "polite"
|
|
11209
|
+
});
|
|
11210
|
+
const results = h("div", {
|
|
11211
|
+
ref: "results",
|
|
11212
|
+
class: "zx-launcher__results",
|
|
11213
|
+
id: listId,
|
|
11214
|
+
role: "listbox",
|
|
11215
|
+
ariaLabel: this.options.resultsLabel
|
|
11216
|
+
});
|
|
11217
|
+
const shortcut = this.options.shortcut === "mod+k" ? h("kbd", {
|
|
11218
|
+
class: "zx-launcher__shortcut",
|
|
11219
|
+
ariaHidden: "true"
|
|
11220
|
+
}, launcherShortcutLabel()) : null;
|
|
11221
|
+
const footer = launcherHints(this.options.hints);
|
|
11222
|
+
dialog.setAttribute("aria-label", String(this.options.label));
|
|
11223
|
+
dialog.replaceChildren(
|
|
11224
|
+
h(
|
|
11225
|
+
"div",
|
|
11226
|
+
{ class: "zx-launcher__surface" },
|
|
11227
|
+
h("div", {
|
|
11228
|
+
class: "zx-launcher__search",
|
|
11229
|
+
dataset: { shortcut: shortcut ? "true" : "false" }
|
|
11230
|
+
}, icon("search"), input, shortcut, close),
|
|
11231
|
+
status,
|
|
11232
|
+
results,
|
|
11233
|
+
footer
|
|
11234
|
+
)
|
|
11235
|
+
);
|
|
11236
|
+
dialog.dataset.state = "closed";
|
|
11237
|
+
if (this._createdRoot) document.body.append(dialog);
|
|
11238
|
+
this.listen(input, "input", () => {
|
|
11239
|
+
this._query = /** @type {HTMLInputElement} */
|
|
11240
|
+
input.value;
|
|
11241
|
+
this.emit("query", { query: this._query.trim() });
|
|
11242
|
+
this._refresh();
|
|
11243
|
+
});
|
|
11244
|
+
this.listen(input, "keydown", (event) => this._onKeydown(
|
|
11245
|
+
/** @type {KeyboardEvent} */
|
|
11246
|
+
event
|
|
11247
|
+
));
|
|
11248
|
+
this.listen(close, "click", () => this.close());
|
|
11249
|
+
this.listen(results, "click", (event) => {
|
|
11250
|
+
const option = (
|
|
11251
|
+
/** @type {Element|null} */
|
|
11252
|
+
event.target?.closest?.("[data-launcher-index]")
|
|
11253
|
+
);
|
|
11254
|
+
if (!option || !results.contains(option)) return;
|
|
11255
|
+
this._activate(Number(option.getAttribute("data-launcher-index")), event);
|
|
11256
|
+
});
|
|
11257
|
+
this.listen(results, "pointermove", (event) => {
|
|
11258
|
+
const option = (
|
|
11259
|
+
/** @type {Element|null} */
|
|
11260
|
+
event.target?.closest?.("[data-launcher-index]")
|
|
11261
|
+
);
|
|
11262
|
+
if (!option || !results.contains(option)) return;
|
|
11263
|
+
const index = Number(option.getAttribute("data-launcher-index"));
|
|
11264
|
+
if (!Number.isInteger(index) || this._results[index]?.disabled || index === this._active) return;
|
|
11265
|
+
this._active = index;
|
|
11266
|
+
this._syncActive();
|
|
11267
|
+
});
|
|
11268
|
+
this.listen(dialog, "cancel", (event) => {
|
|
11269
|
+
event.preventDefault();
|
|
11270
|
+
this.close();
|
|
11271
|
+
});
|
|
11272
|
+
this.listen(dialog, "close", () => {
|
|
11273
|
+
dialog.dataset.state = "closed";
|
|
11274
|
+
input.setAttribute("aria-expanded", "false");
|
|
11275
|
+
this._abortRequest();
|
|
11276
|
+
this.emit("close");
|
|
11277
|
+
});
|
|
11278
|
+
this.listen(document, "keydown", (event) => this._onDocumentKeydown(
|
|
11279
|
+
/** @type {KeyboardEvent} */
|
|
11280
|
+
event
|
|
11281
|
+
));
|
|
11282
|
+
this._renderResults();
|
|
11283
|
+
return dialog;
|
|
11284
|
+
}
|
|
11285
|
+
/** Opens the launcher and focuses/selects its query. @returns {this} @fires Launcher#open */
|
|
11286
|
+
open() {
|
|
11287
|
+
if (this.isOpen() || this._destroyed) return this;
|
|
11288
|
+
this.el.showModal();
|
|
11289
|
+
this.el.dataset.state = "open";
|
|
11290
|
+
this.refs.input.setAttribute("aria-expanded", "true");
|
|
11291
|
+
this._refresh();
|
|
11292
|
+
queueMicrotask(() => {
|
|
11293
|
+
if (!this.isOpen()) return;
|
|
11294
|
+
this.refs.input.focus();
|
|
11295
|
+
this.refs.input.select();
|
|
11296
|
+
});
|
|
11297
|
+
this.emit("open");
|
|
11298
|
+
return this;
|
|
11299
|
+
}
|
|
11300
|
+
/** Closes the launcher. @returns {this} */
|
|
11301
|
+
close() {
|
|
11302
|
+
if (this.isOpen()) this.el.close();
|
|
11303
|
+
return this;
|
|
11304
|
+
}
|
|
11305
|
+
/** Toggles the launcher. @returns {this} */
|
|
11306
|
+
toggle() {
|
|
11307
|
+
return this.isOpen() ? this.close() : this.open();
|
|
11308
|
+
}
|
|
11309
|
+
/** Reports whether the launcher is open. @returns {boolean} */
|
|
11310
|
+
isOpen() {
|
|
11311
|
+
return Boolean(this.el.open);
|
|
11312
|
+
}
|
|
11313
|
+
/** Focuses and selects the query field. @returns {this} */
|
|
11314
|
+
focus() {
|
|
11315
|
+
this.refs.input.focus();
|
|
11316
|
+
this.refs.input.select();
|
|
11317
|
+
return this;
|
|
11318
|
+
}
|
|
11319
|
+
/** Replaces the query and refreshes local and remote results. @param {string} query @returns {this} */
|
|
11320
|
+
setQuery(query) {
|
|
11321
|
+
this._query = String(query ?? "");
|
|
11322
|
+
this.refs.input.value = this._query;
|
|
11323
|
+
this.emit("query", { query: this._query.trim() });
|
|
11324
|
+
this._refresh();
|
|
11325
|
+
return this;
|
|
11326
|
+
}
|
|
11327
|
+
/** Returns the untrimmed query. @returns {string} */
|
|
11328
|
+
getQuery() {
|
|
11329
|
+
return this._query;
|
|
11330
|
+
}
|
|
11331
|
+
/** Replaces local catalogue items. @param {LauncherItem[]} items @returns {this} */
|
|
11332
|
+
setItems(items) {
|
|
11333
|
+
this._items = normalizeItems(items);
|
|
11334
|
+
this._renderResults();
|
|
11335
|
+
return this;
|
|
11336
|
+
}
|
|
11337
|
+
/** Marks one local item as the current destination. @param {string|number|null} id Item id. @returns {this} */
|
|
11338
|
+
setCurrent(id) {
|
|
11339
|
+
const key = id == null ? null : String(id);
|
|
11340
|
+
this._items = this._items.map((item) => ({ ...item, current: key !== null && String(item.id) === key }));
|
|
11341
|
+
this._renderResults();
|
|
11342
|
+
return this;
|
|
11343
|
+
}
|
|
11344
|
+
/** Replaces asynchronous sources and aborts work from the old set. @param {LauncherSource[]} sources @returns {this} */
|
|
11345
|
+
setSources(sources) {
|
|
11346
|
+
this._abortRequest();
|
|
11347
|
+
this._sources = normalizeSources(sources);
|
|
11348
|
+
this._sourceItems.clear();
|
|
11349
|
+
this._refresh();
|
|
11350
|
+
return this;
|
|
11351
|
+
}
|
|
11352
|
+
/** Aborts sources and removes the owned dialog. @returns {void} */
|
|
11353
|
+
destroy() {
|
|
11354
|
+
if (this._destroyed) return;
|
|
11355
|
+
this._destroyed = true;
|
|
11356
|
+
const original = this._original;
|
|
11357
|
+
this._abortRequest();
|
|
11358
|
+
if (this.isOpen()) this.el.close();
|
|
11359
|
+
super.destroy();
|
|
11360
|
+
if (!this._createdRoot && original) {
|
|
11361
|
+
for (const attribute of Array.from(this.el.attributes)) this.el.removeAttribute(attribute.name);
|
|
11362
|
+
for (const [name, value] of original.attributes) this.el.setAttribute(name, value);
|
|
11363
|
+
this.el.replaceChildren(...original.children);
|
|
11364
|
+
}
|
|
11365
|
+
}
|
|
11366
|
+
/** @returns {void} */
|
|
11367
|
+
_refresh() {
|
|
11368
|
+
this._sourceItems.clear();
|
|
11369
|
+
this._renderResults();
|
|
11370
|
+
this._abortRequest();
|
|
11371
|
+
if (this._sources.length === 0) return;
|
|
11372
|
+
const delay = Math.max(0, Number(this.options.debounce) || 0);
|
|
11373
|
+
this._queryTimer = setTimeout(() => this._loadSources(), delay);
|
|
11374
|
+
}
|
|
11375
|
+
/** @returns {Promise<void>} */
|
|
11376
|
+
async _loadSources() {
|
|
11377
|
+
this._queryTimer = null;
|
|
11378
|
+
const query = this._query.trim();
|
|
11379
|
+
const sources = this._sources.filter((source) => visibleForQuery(source.when, query) && query.length >= Math.max(Number(this.options.minQuery) || 0, Number(source.minQuery) || 0));
|
|
11380
|
+
if (sources.length === 0) return;
|
|
11381
|
+
const request = new AbortController();
|
|
11382
|
+
const sequence = ++this._requestSequence;
|
|
11383
|
+
this._request = request;
|
|
11384
|
+
this._setBusy(true);
|
|
11385
|
+
await Promise.all(sources.map(async (source) => {
|
|
11386
|
+
try {
|
|
11387
|
+
const loaded = await source.load(query, { signal: request.signal });
|
|
11388
|
+
if (request.signal.aborted || sequence !== this._requestSequence) return;
|
|
11389
|
+
this._sourceItems.set(source.id, normalizeItems(loaded).map((item, itemOrder) => ({
|
|
11390
|
+
...item,
|
|
11391
|
+
_source: source.id,
|
|
11392
|
+
_sourceOrder: source.order === "source",
|
|
11393
|
+
group: item.group ?? source.label ?? "",
|
|
11394
|
+
itemOrder: item.itemOrder ?? (source.order === "source" ? itemOrder : void 0)
|
|
11395
|
+
})));
|
|
11396
|
+
this._renderResults();
|
|
11397
|
+
} catch (error) {
|
|
11398
|
+
if (request.signal.aborted || sequence !== this._requestSequence) return;
|
|
11399
|
+
this.emit("error", { source, error });
|
|
11400
|
+
}
|
|
11401
|
+
}));
|
|
11402
|
+
if (request.signal.aborted || sequence !== this._requestSequence) return;
|
|
11403
|
+
this._request = null;
|
|
11404
|
+
this._setBusy(false);
|
|
11405
|
+
}
|
|
11406
|
+
/** @param {boolean} busy @returns {void} */
|
|
11407
|
+
_setBusy(busy) {
|
|
11408
|
+
this._busy = busy;
|
|
11409
|
+
this.el.setAttribute("aria-busy", String(busy));
|
|
11410
|
+
this._renderStatus();
|
|
11411
|
+
}
|
|
11412
|
+
/** @returns {void} */
|
|
11413
|
+
_abortRequest() {
|
|
11414
|
+
if (this._queryTimer !== null) clearTimeout(this._queryTimer);
|
|
11415
|
+
this._queryTimer = null;
|
|
11416
|
+
this._request?.abort();
|
|
11417
|
+
this._request = null;
|
|
11418
|
+
this._requestSequence += 1;
|
|
11419
|
+
if (this._busy) this._setBusy(false);
|
|
11420
|
+
}
|
|
11421
|
+
/** @returns {void} */
|
|
11422
|
+
_renderResults() {
|
|
11423
|
+
const queryKey = this._query;
|
|
11424
|
+
const activeId = this._renderedQuery === queryKey && this._active >= 0 && this._results[this._active] ? String(this._results[this._active].id) : null;
|
|
11425
|
+
const query = this._query.trim();
|
|
11426
|
+
const local = this._items.filter((item) => visibleForQuery(item.when, query));
|
|
11427
|
+
const rankedLocal = rankLauncherItems(local, query, this.options.maxResults);
|
|
11428
|
+
const remote = this._sources.flatMap((source) => {
|
|
11429
|
+
const loaded = this._sourceItems.get(String(source.id)) ?? [];
|
|
11430
|
+
return source.order === "source" ? loaded.filter((item) => visibleForQuery(item.when, query)) : rankLauncherItems(
|
|
11431
|
+
loaded.filter((item) => visibleForQuery(item.when, query)),
|
|
11432
|
+
query,
|
|
11433
|
+
this.options.maxResults
|
|
11434
|
+
);
|
|
11435
|
+
});
|
|
11436
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11437
|
+
const combined = [...rankedLocal.map((item) => ({ ...item, _source: null })), ...remote].filter((item) => {
|
|
11438
|
+
const key = String(item.id);
|
|
11439
|
+
if (seen.has(key)) return false;
|
|
11440
|
+
seen.add(key);
|
|
11441
|
+
return true;
|
|
11442
|
+
}).slice(0, Math.max(0, Number(this.options.maxResults) || 0));
|
|
11443
|
+
const groups = /* @__PURE__ */ new Map();
|
|
11444
|
+
for (const item of combined) {
|
|
11445
|
+
const group = String(item.group ?? "");
|
|
11446
|
+
if (!groups.has(group)) groups.set(group, []);
|
|
11447
|
+
groups.get(group).push(item);
|
|
11448
|
+
}
|
|
11449
|
+
this._results = [...groups.values()].flat();
|
|
11450
|
+
this._renderedQuery = queryKey;
|
|
11451
|
+
const preserved = activeId === null ? -1 : this._results.findIndex((item) => !item.disabled && String(item.id) === activeId);
|
|
11452
|
+
this._active = preserved >= 0 ? preserved : this._results.findIndex((item) => !item.disabled);
|
|
11453
|
+
const nodes = [];
|
|
11454
|
+
let index = 0;
|
|
11455
|
+
for (const [group, items] of groups) {
|
|
11456
|
+
const options = items.map((item) => this._option(item, index++));
|
|
11457
|
+
if (group) nodes.push(h("div", {
|
|
11458
|
+
class: "zx-launcher__group",
|
|
11459
|
+
role: "group",
|
|
11460
|
+
ariaLabel: group,
|
|
11461
|
+
dataset: { layout: items.every((item) => item.kind === "application") ? "grid" : "list" }
|
|
11462
|
+
}, h("div", { class: "zx-launcher__group-label", ariaHidden: "true" }, group), options));
|
|
11463
|
+
else nodes.push(...options);
|
|
11464
|
+
}
|
|
11465
|
+
this.refs.results.replaceChildren(...nodes);
|
|
11466
|
+
this._syncActive();
|
|
11467
|
+
this._renderStatus();
|
|
11468
|
+
}
|
|
11469
|
+
/** @param {RankedLauncherItem} item @param {number} index @returns {HTMLElement} */
|
|
11470
|
+
_option(item, index) {
|
|
11471
|
+
const href = safeHref(item.href);
|
|
11472
|
+
const tag = href ? "a" : "button";
|
|
11473
|
+
const children = [];
|
|
11474
|
+
const visual = launcherItemIcon(item);
|
|
11475
|
+
if (visual) children.push(h("span", {
|
|
11476
|
+
class: "zx-launcher__icon",
|
|
11477
|
+
ariaHidden: "true"
|
|
11478
|
+
}, visual));
|
|
11479
|
+
children.push(h(
|
|
11480
|
+
"span",
|
|
11481
|
+
{ class: "zx-launcher__copy" },
|
|
11482
|
+
h("span", { class: "zx-launcher__label" }, item.label),
|
|
11483
|
+
item.description ? h("span", { class: "zx-launcher__description" }, item.description) : null
|
|
11484
|
+
));
|
|
11485
|
+
if (item.badge != null) children.push(h("span", { class: "zx-launcher__badge" }, String(item.badge)));
|
|
11486
|
+
return (
|
|
11487
|
+
/** @type {HTMLElement} */
|
|
11488
|
+
h(tag, {
|
|
11489
|
+
class: "zx-launcher__option",
|
|
11490
|
+
type: tag === "button" ? "button" : null,
|
|
11491
|
+
href,
|
|
11492
|
+
target: item.target ?? null,
|
|
11493
|
+
rel: tag === "a" ? "noopener" : null,
|
|
11494
|
+
role: "option",
|
|
11495
|
+
id: uid("zx-launcher-option"),
|
|
11496
|
+
tabIndex: -1,
|
|
11497
|
+
ariaSelected: "false",
|
|
11498
|
+
ariaDisabled: item.disabled ? "true" : null,
|
|
11499
|
+
ariaCurrent: item.current ? "page" : null,
|
|
11500
|
+
dataset: {
|
|
11501
|
+
launcherIndex: index,
|
|
11502
|
+
pinned: item.pinned !== void 0 && item.pinned !== false ? "true" : null,
|
|
11503
|
+
kind: normalizeLauncherKind(item.kind)
|
|
11504
|
+
}
|
|
11505
|
+
}, children)
|
|
11506
|
+
);
|
|
11507
|
+
}
|
|
11508
|
+
/** @returns {void} */
|
|
11509
|
+
_renderStatus() {
|
|
11510
|
+
if (this._busy) this.refs.status.textContent = String(this.options.loadingText);
|
|
11511
|
+
else if (this._results.length === 0) this.refs.status.textContent = String(this.options.emptyText);
|
|
11512
|
+
else this.refs.status.textContent = "";
|
|
11513
|
+
this.refs.status.hidden = !this.refs.status.textContent;
|
|
11514
|
+
}
|
|
11515
|
+
/** @returns {void} */
|
|
11516
|
+
_syncActive() {
|
|
11517
|
+
const options = [...this.refs.results.querySelectorAll("[data-launcher-index]")];
|
|
11518
|
+
for (const option of options) {
|
|
11519
|
+
const selected = Number(option.getAttribute("data-launcher-index")) === this._active;
|
|
11520
|
+
option.setAttribute("aria-selected", String(selected));
|
|
11521
|
+
}
|
|
11522
|
+
const active = options.find((option) => Number(option.getAttribute("data-launcher-index")) === this._active);
|
|
11523
|
+
if (active) this.refs.input.setAttribute("aria-activedescendant", active.id);
|
|
11524
|
+
else this.refs.input.removeAttribute("aria-activedescendant");
|
|
11525
|
+
}
|
|
11526
|
+
/** @param {KeyboardEvent} event @returns {void} */
|
|
11527
|
+
_onKeydown(event) {
|
|
11528
|
+
if (event.key === "Escape") {
|
|
11529
|
+
this.close();
|
|
11530
|
+
return;
|
|
11531
|
+
}
|
|
11532
|
+
if (event.key === "Enter") {
|
|
11533
|
+
if (this._active < 0) return;
|
|
11534
|
+
event.preventDefault();
|
|
11535
|
+
const option = this.refs.results.querySelector(`[data-launcher-index="${this._active}"]`);
|
|
11536
|
+
option?.click();
|
|
11537
|
+
return;
|
|
11538
|
+
}
|
|
11539
|
+
const enabled = this._results.map((item, index) => item.disabled ? -1 : index).filter((index) => index >= 0);
|
|
11540
|
+
if (enabled.length === 0) return;
|
|
11541
|
+
if (/^Arrow(?:Left|Right|Up|Down)$/.test(event.key)) {
|
|
11542
|
+
const next = this._spatialIndex(event.key, enabled);
|
|
11543
|
+
if (next !== null) {
|
|
11544
|
+
event.preventDefault();
|
|
11545
|
+
this._active = next;
|
|
11546
|
+
this._syncActive();
|
|
11547
|
+
this.refs.results.querySelector(`[data-launcher-index="${this._active}"]`)?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
11548
|
+
return;
|
|
11549
|
+
}
|
|
11550
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") return;
|
|
11551
|
+
}
|
|
11552
|
+
let position2 = enabled.indexOf(this._active);
|
|
11553
|
+
if (event.key === "Home" || event.key === "PageUp") position2 = 0;
|
|
11554
|
+
else if (event.key === "End" || event.key === "PageDown") position2 = enabled.length - 1;
|
|
11555
|
+
else if (event.key === "ArrowDown") position2 = (position2 + 1 + enabled.length) % enabled.length;
|
|
11556
|
+
else if (event.key === "ArrowUp") position2 = (position2 - 1 + enabled.length) % enabled.length;
|
|
11557
|
+
else return;
|
|
11558
|
+
event.preventDefault();
|
|
11559
|
+
this._active = enabled[position2];
|
|
11560
|
+
this._syncActive();
|
|
11561
|
+
this.refs.results.querySelector(`[data-launcher-index="${this._active}"]`)?.scrollIntoView({ block: "nearest" });
|
|
11562
|
+
}
|
|
11563
|
+
/** @param {string} direction @param {number[]} enabled @returns {number|null} */
|
|
11564
|
+
_spatialIndex(direction, enabled) {
|
|
11565
|
+
const current = this.refs.results.querySelector(`[data-launcher-index="${this._active}"]`);
|
|
11566
|
+
if (!(current instanceof HTMLElement)) return null;
|
|
11567
|
+
if ((direction === "ArrowLeft" || direction === "ArrowRight") && this._results[this._active]?.kind !== "application") return null;
|
|
11568
|
+
const candidates = [...this.refs.results.querySelectorAll("[data-launcher-index]")].filter((option) => enabled.includes(Number(option.getAttribute("data-launcher-index"))) && (direction !== "ArrowLeft" && direction !== "ArrowRight" || option.getAttribute("data-kind") === "application"));
|
|
11569
|
+
const next = findSpatialLauncherOption(candidates, current, direction);
|
|
11570
|
+
return next ? Number(next.getAttribute("data-launcher-index")) : null;
|
|
11571
|
+
}
|
|
11572
|
+
/** @param {number} index @param {Event} event @returns {void} */
|
|
11573
|
+
_activate(index, event) {
|
|
11574
|
+
const item = this._results[index];
|
|
11575
|
+
if (!item || item.disabled) {
|
|
11576
|
+
event.preventDefault();
|
|
11577
|
+
return;
|
|
11578
|
+
}
|
|
11579
|
+
const selected = this.emit("select", {
|
|
11580
|
+
item,
|
|
11581
|
+
value: item.value ?? item.id,
|
|
11582
|
+
source: item._source ?? null,
|
|
11583
|
+
query: this._query.trim()
|
|
11584
|
+
});
|
|
11585
|
+
if (selected.defaultPrevented) {
|
|
11586
|
+
event.preventDefault();
|
|
11587
|
+
return;
|
|
11588
|
+
}
|
|
11589
|
+
if (!item.href) {
|
|
11590
|
+
event.preventDefault();
|
|
11591
|
+
item.invoke?.();
|
|
11592
|
+
}
|
|
11593
|
+
this.close();
|
|
11594
|
+
}
|
|
11595
|
+
/** @param {KeyboardEvent} event @returns {void} */
|
|
11596
|
+
_onDocumentKeydown(event) {
|
|
11597
|
+
if (this.options.shortcut !== "mod+k" || event.isComposing || event.key.toLowerCase() !== "k" || Number(event.metaKey) + Number(event.ctrlKey) !== 1 || event.altKey || event.shiftKey) return;
|
|
11598
|
+
const active = document.activeElement;
|
|
11599
|
+
if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement || active instanceof HTMLSelectElement || active instanceof HTMLElement && active.isContentEditable) return;
|
|
11600
|
+
const modal = document.querySelector("dialog:modal");
|
|
11601
|
+
if (modal && modal !== this.el) return;
|
|
11602
|
+
event.preventDefault();
|
|
11603
|
+
this.open();
|
|
11604
|
+
}
|
|
11605
|
+
};
|
|
11606
|
+
function launcherShortcutLabel() {
|
|
11607
|
+
const platform = String(
|
|
11608
|
+
/** @type {any} */
|
|
11609
|
+
navigator.userAgentData?.platform ?? navigator.platform ?? ""
|
|
11610
|
+
);
|
|
11611
|
+
return /mac|iphone|ipad/i.test(platform) ? "\u2318 K" : "Ctrl K";
|
|
11612
|
+
}
|
|
11613
|
+
function launcherHints(hints) {
|
|
11614
|
+
if (hints === false) return null;
|
|
11615
|
+
const labels = hints && typeof hints === "object" ? hints : {};
|
|
11616
|
+
return h(
|
|
11617
|
+
"footer",
|
|
11618
|
+
{ class: "zx-launcher__hints", ariaHidden: "true" },
|
|
11619
|
+
h("span", {}, h("kbd", {}, "\u2191\u2193\u2190\u2192"), labels.move ?? "Move"),
|
|
11620
|
+
h("span", {}, h("kbd", {}, "\u21B5"), labels.open ?? "Open"),
|
|
11621
|
+
h("span", {}, h("kbd", {}, "Esc"), labels.close ?? "Close")
|
|
11622
|
+
);
|
|
11623
|
+
}
|
|
11624
|
+
function launcherItemIcon(item) {
|
|
11625
|
+
let visual = item.icon;
|
|
11626
|
+
if (typeof visual === "function") visual = visual(item);
|
|
11627
|
+
if (typeof visual === "string") return icon(visual);
|
|
11628
|
+
if (visual instanceof Node) return visual.cloneNode(true);
|
|
11629
|
+
if (visual && typeof visual === "object" && typeof /** @type {any} */
|
|
11630
|
+
visual.toElement === "function") {
|
|
11631
|
+
const element = (
|
|
11632
|
+
/** @type {any} */
|
|
11633
|
+
visual.toElement()
|
|
11634
|
+
);
|
|
11635
|
+
return element instanceof Node ? element.cloneNode(true) : null;
|
|
11636
|
+
}
|
|
11637
|
+
return null;
|
|
11638
|
+
}
|
|
11639
|
+
function normalizeLauncherKind(kind) {
|
|
11640
|
+
return kind === "application" || kind === "record" ? kind : "action";
|
|
11641
|
+
}
|
|
11642
|
+
function visibleForQuery(when, query) {
|
|
11643
|
+
if (when === "empty") return query === "";
|
|
11644
|
+
if (when === "query") return query !== "";
|
|
11645
|
+
return true;
|
|
11646
|
+
}
|
|
11647
|
+
function findSpatialLauncherOption(options, current, direction) {
|
|
11648
|
+
const origin = current.getBoundingClientRect();
|
|
11649
|
+
if (origin.width === 0 && origin.height === 0) return null;
|
|
11650
|
+
const originX = origin.left + origin.width / 2;
|
|
11651
|
+
const originY = origin.top + origin.height / 2;
|
|
11652
|
+
let best = null;
|
|
11653
|
+
let bestScore = Infinity;
|
|
11654
|
+
for (const option of options) {
|
|
11655
|
+
if (option === current) continue;
|
|
11656
|
+
const rect = option.getBoundingClientRect();
|
|
11657
|
+
const dx = rect.left + rect.width / 2 - originX;
|
|
11658
|
+
const dy = rect.top + rect.height / 2 - originY;
|
|
11659
|
+
let valid = false;
|
|
11660
|
+
let score = Infinity;
|
|
11661
|
+
if (direction === "ArrowLeft") {
|
|
11662
|
+
valid = dx < -2 && Math.abs(dy) < Math.max(origin.height, rect.height) / 2;
|
|
11663
|
+
score = Math.abs(dx) + Math.abs(dy) * 4;
|
|
11664
|
+
} else if (direction === "ArrowRight") {
|
|
11665
|
+
valid = dx > 2 && Math.abs(dy) < Math.max(origin.height, rect.height) / 2;
|
|
11666
|
+
score = Math.abs(dx) + Math.abs(dy) * 4;
|
|
11667
|
+
} else if (direction === "ArrowUp") {
|
|
11668
|
+
valid = dy < -2;
|
|
11669
|
+
score = Math.abs(dy) * 10 + Math.abs(dx);
|
|
11670
|
+
} else if (direction === "ArrowDown") {
|
|
11671
|
+
valid = dy > 2;
|
|
11672
|
+
score = Math.abs(dy) * 10 + Math.abs(dx);
|
|
11673
|
+
}
|
|
11674
|
+
if (valid && score < bestScore) {
|
|
11675
|
+
best = option;
|
|
11676
|
+
bestScore = score;
|
|
11677
|
+
}
|
|
11678
|
+
}
|
|
11679
|
+
return best;
|
|
11680
|
+
}
|
|
11681
|
+
function rankLauncherItems(items, query, limit = 100) {
|
|
11682
|
+
const needle = searchable(query).trim();
|
|
11683
|
+
const ranked = items.map((item, order) => ({ item, order, score: launcherScore(item, needle) })).filter(({ score }) => score < Infinity);
|
|
11684
|
+
ranked.sort((a, b) => {
|
|
11685
|
+
const aGroup = finiteOrder(a.item.groupOrder);
|
|
11686
|
+
const bGroup = finiteOrder(b.item.groupOrder);
|
|
11687
|
+
if (aGroup !== bGroup) return aGroup - bGroup;
|
|
11688
|
+
if (needle === "") {
|
|
11689
|
+
const aPinned = pinOrder(a.item.pinned, a.order);
|
|
11690
|
+
const bPinned = pinOrder(b.item.pinned, b.order);
|
|
11691
|
+
if (aPinned !== bPinned) return aPinned - bPinned;
|
|
11692
|
+
}
|
|
11693
|
+
return a.score - b.score || finiteOrder(a.item.itemOrder) - finiteOrder(b.item.itemOrder) || compareLauncherText(a.item.label, b.item.label) || a.order - b.order;
|
|
11694
|
+
});
|
|
11695
|
+
const maximum = Math.max(0, Number(limit) || 0);
|
|
11696
|
+
return ranked.slice(0, maximum || 0).map(({ item, score }) => ({ ...item, _score: score }));
|
|
11697
|
+
}
|
|
11698
|
+
function launcherScore(item, needle) {
|
|
11699
|
+
if (!needle) return item.pinned ? -1 : 0;
|
|
11700
|
+
const label = searchable(item.label);
|
|
11701
|
+
const identifier = searchable(item.id);
|
|
11702
|
+
const keywords = searchable(Array.isArray(item.keywords) ? item.keywords.join(" ") : item.keywords ?? "");
|
|
11703
|
+
if (label === needle) return 0;
|
|
11704
|
+
if (label.startsWith(needle)) return 10;
|
|
11705
|
+
if (label.split(/[^\p{L}\p{N}]+/u).some((word) => word.startsWith(needle))) return 20;
|
|
11706
|
+
if (label.includes(needle) || keywords.includes(needle)) return 30;
|
|
11707
|
+
if (identifier.includes(needle)) return 40;
|
|
11708
|
+
const acronym = label.split(/[^\p{L}\p{N}]+/u).filter(Boolean).map((word) => word[0]).join("");
|
|
11709
|
+
if (acronym.startsWith(needle)) return 50;
|
|
11710
|
+
return Infinity;
|
|
11711
|
+
}
|
|
11712
|
+
function pinOrder(pinned, order) {
|
|
11713
|
+
if (typeof pinned === "number" && Number.isFinite(pinned)) return pinned;
|
|
11714
|
+
if (pinned) return order;
|
|
11715
|
+
return Number.MAX_SAFE_INTEGER;
|
|
11716
|
+
}
|
|
11717
|
+
function finiteOrder(value) {
|
|
11718
|
+
return typeof value === "number" && Number.isFinite(value) ? value : Number.MAX_SAFE_INTEGER;
|
|
11719
|
+
}
|
|
11720
|
+
function searchable(value) {
|
|
11721
|
+
return String(value ?? "").normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase();
|
|
11722
|
+
}
|
|
11723
|
+
function compareLauncherText(left, right) {
|
|
11724
|
+
const a = searchable(left);
|
|
11725
|
+
const b = searchable(right);
|
|
11726
|
+
if (a < b) return -1;
|
|
11727
|
+
if (a > b) return 1;
|
|
11728
|
+
const rawA = String(left ?? "").normalize("NFC");
|
|
11729
|
+
const rawB = String(right ?? "").normalize("NFC");
|
|
11730
|
+
return rawA < rawB ? -1 : rawA > rawB ? 1 : 0;
|
|
11731
|
+
}
|
|
11732
|
+
function normalizeItems(items) {
|
|
11733
|
+
if (!Array.isArray(items)) return [];
|
|
11734
|
+
return items.filter((item) => item && typeof item === "object" && item.id != null && item.label != null).map((item) => normalizeLauncherItem(item));
|
|
11735
|
+
}
|
|
11736
|
+
function normalizeLauncherItem(item) {
|
|
11737
|
+
const normalized = { ...item, label: String(item.label) };
|
|
11738
|
+
if (item.href == null) return normalized;
|
|
11739
|
+
const href = safeHref(item.href);
|
|
11740
|
+
if (href !== null) normalized.href = href;
|
|
11741
|
+
else {
|
|
11742
|
+
delete normalized.href;
|
|
11743
|
+
if (typeof normalized.invoke !== "function") normalized.disabled = true;
|
|
11744
|
+
}
|
|
11745
|
+
return normalized;
|
|
11746
|
+
}
|
|
11747
|
+
function normalizeSources(sources) {
|
|
11748
|
+
if (!Array.isArray(sources)) return [];
|
|
11749
|
+
return sources.filter((source) => source && typeof source === "object" && source.id != null && typeof source.load === "function").map((source) => ({ ...source, id: String(source.id) }));
|
|
11750
|
+
}
|
|
11751
|
+
|
|
10428
11752
|
// src/components/message/message.js
|
|
10429
11753
|
var MessageQueue = class {
|
|
10430
11754
|
#limit;
|
|
@@ -11534,6 +12858,7 @@ var zeyosModules = Object.freeze({
|
|
|
11534
12858
|
weblets: { label: "Weblets", icon: "zeyos-weblets", color: "#4f46e5", fa: "window-restore" }
|
|
11535
12859
|
});
|
|
11536
12860
|
var moduleAliases = Object.freeze({
|
|
12861
|
+
main: "zeyos",
|
|
11537
12862
|
addresses: "contacts",
|
|
11538
12863
|
clocking: "actionsteps",
|
|
11539
12864
|
comments: "notes",
|
|
@@ -11640,15 +12965,23 @@ function moduleIcon(name, options = {}) {
|
|
|
11640
12965
|
return icon(standard ? `fa:${info.fa}` : `kit:${info.icon}`, rest);
|
|
11641
12966
|
}
|
|
11642
12967
|
function moduleChip(name, options = {}) {
|
|
11643
|
-
const {
|
|
12968
|
+
const {
|
|
12969
|
+
size = 24,
|
|
12970
|
+
iconSize = "1em",
|
|
12971
|
+
label = null,
|
|
12972
|
+
title = false,
|
|
12973
|
+
standard = false,
|
|
12974
|
+
color = null
|
|
12975
|
+
} = options;
|
|
11644
12976
|
const key = normalizeModuleName(name);
|
|
11645
12977
|
const info = moduleInfo(name);
|
|
12978
|
+
const background = color || info.color;
|
|
11646
12979
|
const chip = h("span", {
|
|
11647
12980
|
class: ["zx-module-icon", options.class].flat().filter(Boolean),
|
|
11648
12981
|
dataset: { module: key },
|
|
11649
12982
|
style: {
|
|
11650
|
-
"--zx-module-color":
|
|
11651
|
-
"--zx-module-glyph": moduleGlyphColor(
|
|
12983
|
+
"--zx-module-color": background,
|
|
12984
|
+
"--zx-module-glyph": moduleGlyphColor(background),
|
|
11652
12985
|
"--zx-module-icon-size": typeof size === "number" ? `${size}px` : String(size)
|
|
11653
12986
|
},
|
|
11654
12987
|
title: title ? info.label : null
|
|
@@ -11662,6 +12995,307 @@ function moduleChip(name, options = {}) {
|
|
|
11662
12995
|
return chip;
|
|
11663
12996
|
}
|
|
11664
12997
|
|
|
12998
|
+
// src/zeyos/launcher.js
|
|
12999
|
+
var ZEYOS_LAUNCHER_APPLICATIONS = Object.freeze([
|
|
13000
|
+
"accounts",
|
|
13001
|
+
"actionsteps",
|
|
13002
|
+
"admin",
|
|
13003
|
+
"auth",
|
|
13004
|
+
"billing",
|
|
13005
|
+
"calendar",
|
|
13006
|
+
"campaigns",
|
|
13007
|
+
"collection",
|
|
13008
|
+
"contacts",
|
|
13009
|
+
"contracts",
|
|
13010
|
+
"control",
|
|
13011
|
+
"coupons",
|
|
13012
|
+
"dev",
|
|
13013
|
+
"documents",
|
|
13014
|
+
"inventory",
|
|
13015
|
+
"mailinglists",
|
|
13016
|
+
"main",
|
|
13017
|
+
"messages",
|
|
13018
|
+
"links",
|
|
13019
|
+
"notes",
|
|
13020
|
+
"objects",
|
|
13021
|
+
"opportunities",
|
|
13022
|
+
"payments",
|
|
13023
|
+
"pricelists",
|
|
13024
|
+
"procurement",
|
|
13025
|
+
"production",
|
|
13026
|
+
"projects",
|
|
13027
|
+
"tasks",
|
|
13028
|
+
"tickets"
|
|
13029
|
+
]);
|
|
13030
|
+
function buildZeyosLauncherConfig(state = {}, options = {}) {
|
|
13031
|
+
const {
|
|
13032
|
+
standardIcons = false,
|
|
13033
|
+
items: extraItems = [],
|
|
13034
|
+
sources: extraSources = [],
|
|
13035
|
+
...launcherOptions
|
|
13036
|
+
} = options;
|
|
13037
|
+
const labels = launcherLabels(state.labels);
|
|
13038
|
+
const iconOptions = {
|
|
13039
|
+
standardIcons,
|
|
13040
|
+
renderApplicationIcon: state.renderApplicationIcon,
|
|
13041
|
+
renderWebletIcon: state.renderWebletIcon
|
|
13042
|
+
};
|
|
13043
|
+
const applications = normalizeZeyosLauncherApplications(state, iconOptions);
|
|
13044
|
+
const recent = typeof state.loadRecent === "function" ? [] : normalizeZeyosLauncherRecords(state.recent ?? [], {
|
|
13045
|
+
query: "",
|
|
13046
|
+
labels,
|
|
13047
|
+
resolveRecord: state.resolveRecord,
|
|
13048
|
+
renderRecordIcon: state.renderRecordIcon,
|
|
13049
|
+
standardIcons
|
|
13050
|
+
});
|
|
13051
|
+
const sources = [];
|
|
13052
|
+
if (typeof state.loadRecent === "function") {
|
|
13053
|
+
sources.push({
|
|
13054
|
+
id: "zeyos-recent",
|
|
13055
|
+
minQuery: 0,
|
|
13056
|
+
when: "empty",
|
|
13057
|
+
order: "source",
|
|
13058
|
+
load: async (_query, context) => normalizeZeyosLauncherRecords(
|
|
13059
|
+
await state.loadRecent(context),
|
|
13060
|
+
{
|
|
13061
|
+
query: "",
|
|
13062
|
+
labels,
|
|
13063
|
+
resolveRecord: state.resolveRecord,
|
|
13064
|
+
renderRecordIcon: state.renderRecordIcon,
|
|
13065
|
+
standardIcons
|
|
13066
|
+
}
|
|
13067
|
+
)
|
|
13068
|
+
});
|
|
13069
|
+
}
|
|
13070
|
+
if (typeof state.search === "function") {
|
|
13071
|
+
sources.push({
|
|
13072
|
+
id: "zeyos-records",
|
|
13073
|
+
minQuery: 1,
|
|
13074
|
+
when: "query",
|
|
13075
|
+
order: "source",
|
|
13076
|
+
load: async (query, context) => normalizeZeyosLauncherRecords(
|
|
13077
|
+
await state.search(query, context),
|
|
13078
|
+
{
|
|
13079
|
+
query,
|
|
13080
|
+
labels,
|
|
13081
|
+
resolveRecord: state.resolveRecord,
|
|
13082
|
+
renderRecordIcon: state.renderRecordIcon,
|
|
13083
|
+
standardIcons
|
|
13084
|
+
}
|
|
13085
|
+
)
|
|
13086
|
+
});
|
|
13087
|
+
}
|
|
13088
|
+
sources.push(...extraSources);
|
|
13089
|
+
return {
|
|
13090
|
+
...launcherOptions,
|
|
13091
|
+
items: [...applications, ...recent, ...extraItems],
|
|
13092
|
+
sources,
|
|
13093
|
+
hints: options.hints ?? {
|
|
13094
|
+
move: labels.move,
|
|
13095
|
+
open: labels.open,
|
|
13096
|
+
close: labels.close
|
|
13097
|
+
}
|
|
13098
|
+
};
|
|
13099
|
+
}
|
|
13100
|
+
function zeyosLauncher(state = {}, options = {}) {
|
|
13101
|
+
return new Launcher(null, buildZeyosLauncherConfig(state, options));
|
|
13102
|
+
}
|
|
13103
|
+
function normalizeZeyosLauncherApplications(state = {}, options = {}) {
|
|
13104
|
+
const labels = launcherLabels(state.labels);
|
|
13105
|
+
const pinned = Array.isArray(state.pinned) ? state.pinned.map(String) : [];
|
|
13106
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13107
|
+
const items = [];
|
|
13108
|
+
const add = (raw2, fallbackId = null, fallbackLabel = null) => {
|
|
13109
|
+
const app = normalizeApplication(raw2, fallbackId, fallbackLabel);
|
|
13110
|
+
if (!app || seen.has(app.identifier)) return;
|
|
13111
|
+
if (app.type === "module" && typeof state.canUseModule === "function" && !state.canUseModule(app.module)) return;
|
|
13112
|
+
seen.add(app.identifier);
|
|
13113
|
+
const resolved = mergeDestination(app.raw, state.resolveApplication);
|
|
13114
|
+
const pin = pinned.indexOf(app.identifier);
|
|
13115
|
+
const icon2 = () => {
|
|
13116
|
+
const custom = typeof options.renderApplicationIcon === "function" ? options.renderApplicationIcon(app.raw, app) : app.type === "weblet" && typeof options.renderWebletIcon === "function" ? options.renderWebletIcon(app.raw, app) : null;
|
|
13117
|
+
return custom ?? moduleChip(app.module, {
|
|
13118
|
+
size: 36,
|
|
13119
|
+
standard: Boolean(options.standardIcons),
|
|
13120
|
+
color: app.color
|
|
13121
|
+
});
|
|
13122
|
+
};
|
|
13123
|
+
items.push({
|
|
13124
|
+
id: app.identifier,
|
|
13125
|
+
label: app.label,
|
|
13126
|
+
keywords: [app.identifier, app.module],
|
|
13127
|
+
group: labels.applications,
|
|
13128
|
+
groupOrder: 0,
|
|
13129
|
+
kind: "application",
|
|
13130
|
+
icon: icon2,
|
|
13131
|
+
pinned: pin >= 0 ? pin : void 0,
|
|
13132
|
+
current: app.identifier === state.activeIdentifier,
|
|
13133
|
+
disabled: !resolved.href && typeof resolved.invoke !== "function",
|
|
13134
|
+
...resolved,
|
|
13135
|
+
_zeyos: app
|
|
13136
|
+
});
|
|
13137
|
+
};
|
|
13138
|
+
for (const raw2 of Array.isArray(state.menuApps) ? state.menuApps : []) add(raw2);
|
|
13139
|
+
if (Array.isArray(state.modules)) {
|
|
13140
|
+
for (const module of state.modules) {
|
|
13141
|
+
if (typeof module === "string") add([module, moduleInfo(module).label]);
|
|
13142
|
+
else add(module, module?.id ?? module?.module, module?.label);
|
|
13143
|
+
}
|
|
13144
|
+
} else {
|
|
13145
|
+
for (const [module, label] of Object.entries(state.modules ?? {})) add([module, label]);
|
|
13146
|
+
}
|
|
13147
|
+
return items;
|
|
13148
|
+
}
|
|
13149
|
+
function normalizeZeyosLauncherRecords(payload, options = {}) {
|
|
13150
|
+
const query = String(options.query ?? "");
|
|
13151
|
+
const labels = launcherLabels(options.labels);
|
|
13152
|
+
const records = flattenRecords(payload, query);
|
|
13153
|
+
const groupOrders = /* @__PURE__ */ new Map();
|
|
13154
|
+
return records.flatMap(({ raw: raw2, group }, itemOrder) => {
|
|
13155
|
+
const record = normalizeRecord(raw2, group, query);
|
|
13156
|
+
if (!record) return [];
|
|
13157
|
+
const groupName = query === "" ? labels.recent : record.group === "" ? labels.results : labels.entities[record.group] ?? record.group;
|
|
13158
|
+
if (!groupOrders.has(groupName)) groupOrders.set(groupName, groupOrders.size + 1);
|
|
13159
|
+
const resolved = mergeDestination(record.raw, options.resolveRecord);
|
|
13160
|
+
return [{
|
|
13161
|
+
id: `record:${record.entity}:${record.fork ?? ""}:${record.ID}`,
|
|
13162
|
+
label: record.name,
|
|
13163
|
+
description: record.sec,
|
|
13164
|
+
keywords: [record.entity, record.ID, record.fork ?? ""],
|
|
13165
|
+
group: groupName,
|
|
13166
|
+
groupOrder: groupOrders.get(groupName),
|
|
13167
|
+
itemOrder,
|
|
13168
|
+
kind: "record",
|
|
13169
|
+
when: query === "" ? "empty" : "query",
|
|
13170
|
+
icon: () => {
|
|
13171
|
+
const custom = typeof options.renderRecordIcon === "function" ? options.renderRecordIcon(record.raw, record) : null;
|
|
13172
|
+
return custom ?? moduleChip(record.entity, {
|
|
13173
|
+
size: 28,
|
|
13174
|
+
standard: Boolean(options.standardIcons)
|
|
13175
|
+
});
|
|
13176
|
+
},
|
|
13177
|
+
disabled: !resolved.href && typeof resolved.invoke !== "function",
|
|
13178
|
+
...resolved,
|
|
13179
|
+
_zeyos: record
|
|
13180
|
+
}];
|
|
13181
|
+
});
|
|
13182
|
+
}
|
|
13183
|
+
function zeyosActiveIdentifier(state = {}) {
|
|
13184
|
+
if (state.weblet != null && state.weblet !== "") return `weblet#${state.weblet}`;
|
|
13185
|
+
if (state.fork != null && state.fork !== "") return `fork#${state.fork}`;
|
|
13186
|
+
return String(state.module ?? "");
|
|
13187
|
+
}
|
|
13188
|
+
function normalizeApplication(raw2, fallbackId, fallbackLabel) {
|
|
13189
|
+
if (Array.isArray(raw2)) {
|
|
13190
|
+
if (raw2.length < 2) return null;
|
|
13191
|
+
if (raw2.length === 2) {
|
|
13192
|
+
const module = String(raw2[0]);
|
|
13193
|
+
return { raw: raw2, identifier: module, label: String(raw2[1]), module, type: "module", color: null };
|
|
13194
|
+
}
|
|
13195
|
+
if (raw2.length === 4) {
|
|
13196
|
+
const module = String(raw2[2] || "default");
|
|
13197
|
+
return {
|
|
13198
|
+
raw: raw2,
|
|
13199
|
+
identifier: `fork#${raw2[0]}`,
|
|
13200
|
+
label: String(raw2[1]),
|
|
13201
|
+
module,
|
|
13202
|
+
type: "fork",
|
|
13203
|
+
color: normalizeServerColor(raw2[3])
|
|
13204
|
+
};
|
|
13205
|
+
}
|
|
13206
|
+
return {
|
|
13207
|
+
raw: raw2,
|
|
13208
|
+
identifier: `weblet#${raw2[0]}`,
|
|
13209
|
+
label: String(raw2[1]),
|
|
13210
|
+
module: "weblets",
|
|
13211
|
+
type: "weblet",
|
|
13212
|
+
color: normalizeServerColor(raw2[8])
|
|
13213
|
+
};
|
|
13214
|
+
}
|
|
13215
|
+
if (!raw2 || typeof raw2 !== "object") return null;
|
|
13216
|
+
const identifier = String(raw2.identifier ?? raw2.id ?? fallbackId ?? "");
|
|
13217
|
+
if (!identifier) return null;
|
|
13218
|
+
const type = identifier.startsWith("fork#") ? "fork" : identifier.startsWith("weblet#") ? "weblet" : "module";
|
|
13219
|
+
return {
|
|
13220
|
+
raw: raw2,
|
|
13221
|
+
identifier,
|
|
13222
|
+
label: String(raw2.label ?? fallbackLabel ?? moduleInfo(identifier).label),
|
|
13223
|
+
module: String(raw2.module ?? (type === "module" ? identifier : type === "fork" ? "default" : "weblets")),
|
|
13224
|
+
type,
|
|
13225
|
+
color: normalizeServerColor(raw2.color)
|
|
13226
|
+
};
|
|
13227
|
+
}
|
|
13228
|
+
function flattenRecords(payload, query) {
|
|
13229
|
+
if (Array.isArray(payload)) return payload.map((raw2) => ({ raw: raw2, group: raw2?.group ?? "" }));
|
|
13230
|
+
if (!payload || typeof payload !== "object") return [];
|
|
13231
|
+
const result = [];
|
|
13232
|
+
for (const [group, rows] of Object.entries(payload)) {
|
|
13233
|
+
if (!Array.isArray(rows)) continue;
|
|
13234
|
+
for (const row of rows) {
|
|
13235
|
+
if (!Array.isArray(row)) {
|
|
13236
|
+
result.push({ raw: row, group });
|
|
13237
|
+
continue;
|
|
13238
|
+
}
|
|
13239
|
+
const raw2 = group === "" && row.length >= 5 ? { ID: row[0], fork: row[1], name: row[2], sec: row[3], entity: row[4], group } : { ID: row[0], fork: row[1], name: row[2], sec: row[3], entity: group, group };
|
|
13240
|
+
result.push({ raw: raw2, group });
|
|
13241
|
+
}
|
|
13242
|
+
}
|
|
13243
|
+
return result;
|
|
13244
|
+
}
|
|
13245
|
+
function normalizeRecord(raw2, group, query) {
|
|
13246
|
+
if (Array.isArray(raw2)) {
|
|
13247
|
+
if (query === "" && raw2.length >= 4) {
|
|
13248
|
+
return {
|
|
13249
|
+
raw: raw2,
|
|
13250
|
+
entity: String(raw2[0]),
|
|
13251
|
+
ID: raw2[1],
|
|
13252
|
+
fork: raw2[2] ?? null,
|
|
13253
|
+
name: String(raw2[3]),
|
|
13254
|
+
sec: String(raw2[4] ?? ""),
|
|
13255
|
+
group: ""
|
|
13256
|
+
};
|
|
13257
|
+
}
|
|
13258
|
+
return null;
|
|
13259
|
+
}
|
|
13260
|
+
if (!raw2 || typeof raw2 !== "object" || raw2.ID == null || raw2.name == null) return null;
|
|
13261
|
+
return {
|
|
13262
|
+
raw: raw2,
|
|
13263
|
+
entity: String(raw2.entity ?? group ?? "records"),
|
|
13264
|
+
ID: raw2.ID,
|
|
13265
|
+
fork: raw2.fork ?? null,
|
|
13266
|
+
name: String(raw2.name),
|
|
13267
|
+
sec: String(raw2.sec ?? ""),
|
|
13268
|
+
group: String(raw2.group ?? group ?? "")
|
|
13269
|
+
};
|
|
13270
|
+
}
|
|
13271
|
+
function mergeDestination(raw2, resolver) {
|
|
13272
|
+
const direct = raw2 && typeof raw2 === "object" && !Array.isArray(raw2) ? raw2 : {};
|
|
13273
|
+
const resolved = typeof resolver === "function" ? resolver(raw2) : null;
|
|
13274
|
+
const destination = resolved && typeof resolved === "object" ? resolved : {};
|
|
13275
|
+
return Object.fromEntries(["href", "target", "invoke", "value"].flatMap((key) => {
|
|
13276
|
+
const value = destination[key] ?? direct[key];
|
|
13277
|
+
return value === void 0 ? [] : [[key, value]];
|
|
13278
|
+
}));
|
|
13279
|
+
}
|
|
13280
|
+
function normalizeServerColor(value) {
|
|
13281
|
+
const source = String(value ?? "").trim();
|
|
13282
|
+
if (!source) return null;
|
|
13283
|
+
const color = source.startsWith("#") ? source : `#${source}`;
|
|
13284
|
+
return /^#[\da-f]{3}(?:[\da-f]{3})?$/i.test(color) ? color : null;
|
|
13285
|
+
}
|
|
13286
|
+
function launcherLabels(input) {
|
|
13287
|
+
const labels = input && typeof input === "object" ? input : {};
|
|
13288
|
+
return {
|
|
13289
|
+
applications: String(labels.applications ?? "Applications"),
|
|
13290
|
+
recent: String(labels.recent ?? "Recent"),
|
|
13291
|
+
results: String(labels.results ?? "Top results"),
|
|
13292
|
+
entities: labels.entities && typeof labels.entities === "object" ? labels.entities : {},
|
|
13293
|
+
move: String(labels.move ?? "Move"),
|
|
13294
|
+
open: String(labels.open ?? "Open"),
|
|
13295
|
+
close: String(labels.close ?? "Close")
|
|
13296
|
+
};
|
|
13297
|
+
}
|
|
13298
|
+
|
|
11665
13299
|
// src/zeyos/select.js
|
|
11666
13300
|
function buildZeyosSelectConfig(client, resource, opts = {}) {
|
|
11667
13301
|
const {
|
|
@@ -11703,6 +13337,90 @@ function zeyosSelect(client, resource, opts = {}) {
|
|
|
11703
13337
|
select.ready = hydrateInitialValue(select, client, resource, requestedValue);
|
|
11704
13338
|
return select;
|
|
11705
13339
|
}
|
|
13340
|
+
function buildZeyosEntitySelectConfig(client, resource, opts = {}) {
|
|
13341
|
+
const {
|
|
13342
|
+
fields,
|
|
13343
|
+
labelKey = inferLabelKey(client, resource),
|
|
13344
|
+
valueKey = "ID",
|
|
13345
|
+
searchFields = inferSearchFields(client, resource),
|
|
13346
|
+
subtitleKey = null,
|
|
13347
|
+
moduleKey = () => resource,
|
|
13348
|
+
groupKey = null,
|
|
13349
|
+
none = true,
|
|
13350
|
+
create = false,
|
|
13351
|
+
renderIcon = defaultEntityIcon,
|
|
13352
|
+
renderItem,
|
|
13353
|
+
renderValueAdornment,
|
|
13354
|
+
...rest
|
|
13355
|
+
} = opts;
|
|
13356
|
+
const projection = fields ?? defaultProjection(valueKey, labelKey, [
|
|
13357
|
+
...searchFields,
|
|
13358
|
+
...[subtitleKey, moduleKey, groupKey].filter((reader) => typeof reader === "string")
|
|
13359
|
+
]);
|
|
13360
|
+
const base = buildZeyosSelectConfig(client, resource, {
|
|
13361
|
+
...rest,
|
|
13362
|
+
fields: projection,
|
|
13363
|
+
labelKey,
|
|
13364
|
+
valueKey,
|
|
13365
|
+
searchFields,
|
|
13366
|
+
groupKey,
|
|
13367
|
+
clearable: none === false ? Boolean(rest.clearable) : true
|
|
13368
|
+
});
|
|
13369
|
+
const renderModuleIcon = (item, selected = false) => {
|
|
13370
|
+
if (renderIcon === null) return null;
|
|
13371
|
+
const module = readRecord(item, moduleKey) ?? resource;
|
|
13372
|
+
return renderIcon(item, { resource, module: String(module), selected });
|
|
13373
|
+
};
|
|
13374
|
+
base.renderItem = renderItem ?? ((item) => {
|
|
13375
|
+
const visual = renderModuleIcon(item);
|
|
13376
|
+
return h(
|
|
13377
|
+
"span",
|
|
13378
|
+
{ class: "zx-select__entity" },
|
|
13379
|
+
visual ? h("span", {
|
|
13380
|
+
class: "zx-select__entity-icon",
|
|
13381
|
+
ariaHidden: "true"
|
|
13382
|
+
}, visual) : null,
|
|
13383
|
+
h(
|
|
13384
|
+
"span",
|
|
13385
|
+
{ class: "zx-select__entity-copy" },
|
|
13386
|
+
h("span", { class: "zx-select__entity-label" }, String(readRecord(item, labelKey) ?? "")),
|
|
13387
|
+
subtitleKey && readRecord(item, subtitleKey) != null ? h("span", { class: "zx-select__entity-subtitle" }, String(readRecord(item, subtitleKey))) : null
|
|
13388
|
+
)
|
|
13389
|
+
);
|
|
13390
|
+
});
|
|
13391
|
+
base.renderValueAdornment = renderValueAdornment ?? ((item) => renderModuleIcon(item, true));
|
|
13392
|
+
if (none !== false) {
|
|
13393
|
+
base.noneLabel = typeof none === "string" ? none : rest.noneLabel ?? "No selection";
|
|
13394
|
+
base.renderNone = rest.renderNone ?? ((label) => h(
|
|
13395
|
+
"span",
|
|
13396
|
+
{ class: "zx-select__entity-none" },
|
|
13397
|
+
h("span", { class: "zx-select__entity-none-icon", ariaHidden: "true" }),
|
|
13398
|
+
h("span", {}, label)
|
|
13399
|
+
));
|
|
13400
|
+
}
|
|
13401
|
+
if (create) {
|
|
13402
|
+
const descriptor = create === true ? {} : create;
|
|
13403
|
+
const invoke = typeof descriptor.oninvoke === "function" ? descriptor.oninvoke : null;
|
|
13404
|
+
base.actions = [...Array.isArray(base.actions) ? base.actions : [], {
|
|
13405
|
+
id: descriptor.id ?? "create",
|
|
13406
|
+
label: descriptor.label ?? "Create new\u2026",
|
|
13407
|
+
group: descriptor.group ?? "New",
|
|
13408
|
+
icon: descriptor.icon ?? "plus",
|
|
13409
|
+
invoke: (detail) => invoke?.({ ...detail, resource })
|
|
13410
|
+
}];
|
|
13411
|
+
}
|
|
13412
|
+
return base;
|
|
13413
|
+
}
|
|
13414
|
+
function zeyosEntitySelect(client, resource, opts = {}) {
|
|
13415
|
+
const config2 = buildZeyosEntitySelectConfig(client, resource, opts);
|
|
13416
|
+
const requestedValue = opts.value ?? null;
|
|
13417
|
+
const select = (
|
|
13418
|
+
/** @type {Select & {ready: Promise<Select>}} */
|
|
13419
|
+
new Select(null, config2)
|
|
13420
|
+
);
|
|
13421
|
+
select.ready = hydrateInitialValue(select, client, resource, requestedValue);
|
|
13422
|
+
return select;
|
|
13423
|
+
}
|
|
11706
13424
|
function normalizeZeyosListResult(client, result) {
|
|
11707
13425
|
if (typeof client?.normalizeListResult === "function") {
|
|
11708
13426
|
return normalizeLocalListResult(client.normalizeListResult(result));
|
|
@@ -11719,12 +13437,12 @@ function normalizeLocalListResult(result) {
|
|
|
11719
13437
|
async function hydrateInitialValue(select, client, resource, value) {
|
|
11720
13438
|
if (value == null) return select;
|
|
11721
13439
|
const record = value && typeof value === "object" ? value : await connect(client).get(resource, value);
|
|
11722
|
-
const normalized =
|
|
13440
|
+
const normalized = normalizeRecord2(record);
|
|
11723
13441
|
select.setItems([normalized]);
|
|
11724
13442
|
select.set(normalized, { silent: true });
|
|
11725
13443
|
return select;
|
|
11726
13444
|
}
|
|
11727
|
-
function
|
|
13445
|
+
function normalizeRecord2(result) {
|
|
11728
13446
|
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
11729
13447
|
const nested = result.data;
|
|
11730
13448
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) return nested;
|
|
@@ -11732,6 +13450,16 @@ function normalizeRecord(result) {
|
|
|
11732
13450
|
}
|
|
11733
13451
|
throw new TypeError("ZeyOS get operation did not return a record");
|
|
11734
13452
|
}
|
|
13453
|
+
function readRecord(item, reader) {
|
|
13454
|
+
if (typeof reader === "function") return reader(item);
|
|
13455
|
+
if (item && typeof item === "object" && typeof reader === "string") return item[reader];
|
|
13456
|
+
return null;
|
|
13457
|
+
}
|
|
13458
|
+
function defaultEntityIcon(_item, context) {
|
|
13459
|
+
const chip = moduleChip(String(context.module ?? "default"), { size: 20 });
|
|
13460
|
+
chip.replaceChildren(icon("square", { size: 10 }));
|
|
13461
|
+
return chip;
|
|
13462
|
+
}
|
|
11735
13463
|
function defaultProjection(valueKey, labelKey, searchFields) {
|
|
11736
13464
|
const keys = [valueKey, labelKey, ...searchFields].filter((key) => typeof key === "string" && key);
|
|
11737
13465
|
return keys.length ? [...new Set(keys)] : void 0;
|
|
@@ -11811,7 +13539,7 @@ function zeyosForm(client, resource, opts = {}) {
|
|
|
11811
13539
|
await Promise.all(config2.entityFields.map(async ({ id, resource: referencedResource2 }) => {
|
|
11812
13540
|
const value = record[id];
|
|
11813
13541
|
if (value == null) return;
|
|
11814
|
-
const selected = value && typeof value === "object" ? value :
|
|
13542
|
+
const selected = value && typeof value === "object" ? value : normalizeRecord3(await facade.get(referencedResource2, value));
|
|
11815
13543
|
const select = Select.from(form.getField(id)?.getInput());
|
|
11816
13544
|
if (select instanceof Select) select.setItems([selected]);
|
|
11817
13545
|
}));
|
|
@@ -11820,7 +13548,7 @@ function zeyosForm(client, resource, opts = {}) {
|
|
|
11820
13548
|
};
|
|
11821
13549
|
const load = async (id) => {
|
|
11822
13550
|
if (id == null || id === "") throw new TypeError("zeyosForm.load() requires an id");
|
|
11823
|
-
const record =
|
|
13551
|
+
const record = normalizeRecord3(await facade.get(resource, id));
|
|
11824
13552
|
currentId = record.ID ?? id;
|
|
11825
13553
|
await applyRecord(record);
|
|
11826
13554
|
return record;
|
|
@@ -11839,7 +13567,7 @@ function zeyosForm(client, resource, opts = {}) {
|
|
|
11839
13567
|
throw error;
|
|
11840
13568
|
}
|
|
11841
13569
|
const result = currentId == null ? await facade.create(resource, payload) : await facade.update(resource, { ID: currentId, ...payload });
|
|
11842
|
-
const record =
|
|
13570
|
+
const record = normalizeRecord3(result);
|
|
11843
13571
|
currentId = record.ID ?? currentId;
|
|
11844
13572
|
if (opts.onSaved) {
|
|
11845
13573
|
try {
|
|
@@ -11917,7 +13645,7 @@ function normalizeColumns(value) {
|
|
|
11917
13645
|
[1, 2, 3].includes(number) ? number : 2
|
|
11918
13646
|
);
|
|
11919
13647
|
}
|
|
11920
|
-
function
|
|
13648
|
+
function normalizeRecord3(result) {
|
|
11921
13649
|
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
11922
13650
|
const nested = result.data;
|
|
11923
13651
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) return nested;
|
|
@@ -11951,7 +13679,7 @@ function buildZeyosTableConfig(client, resource, opts = {}) {
|
|
|
11951
13679
|
const monetary = isMonetaryField(meta);
|
|
11952
13680
|
const column = fieldToZxColumn(monetary ? { ...meta, format: "money" } : meta);
|
|
11953
13681
|
if (monetary) {
|
|
11954
|
-
column.render = (row) =>
|
|
13682
|
+
column.render = (row) => formatCurrency2(row?.[meta.id], row?.currency);
|
|
11955
13683
|
if (schemaHasField(client, resource, "currency")) projection.currency = "currency";
|
|
11956
13684
|
}
|
|
11957
13685
|
return column;
|
|
@@ -12076,10 +13804,10 @@ function inferLabelKey2(client, resource) {
|
|
|
12076
13804
|
return names[0] ?? "ID";
|
|
12077
13805
|
}
|
|
12078
13806
|
function isMonetaryField(meta) {
|
|
12079
|
-
const
|
|
12080
|
-
return /money|currency|price/.test(
|
|
13807
|
+
const format2 = String(meta.format ?? "").toLowerCase();
|
|
13808
|
+
return /money|currency|price/.test(format2) || /(?:amount|price)$/.test(String(meta.id).toLowerCase());
|
|
12081
13809
|
}
|
|
12082
|
-
function
|
|
13810
|
+
function formatCurrency2(value, currency) {
|
|
12083
13811
|
if (value == null || value === "") return "";
|
|
12084
13812
|
const number = Number(value);
|
|
12085
13813
|
if (!Number.isFinite(number)) return "";
|
|
@@ -12112,7 +13840,11 @@ function copyFilters(filters) {
|
|
|
12112
13840
|
}
|
|
12113
13841
|
export {
|
|
12114
13842
|
ZEYOS_ICON_KIT,
|
|
13843
|
+
ZEYOS_LAUNCHER_APPLICATIONS,
|
|
12115
13844
|
buildListQuery,
|
|
13845
|
+
buildZeyosEntitySelectConfig,
|
|
13846
|
+
buildZeyosLauncherConfig,
|
|
13847
|
+
buildZeyosSelectConfig,
|
|
12116
13848
|
connect,
|
|
12117
13849
|
dataFilterStateToFilters,
|
|
12118
13850
|
dateToUnixSeconds,
|
|
@@ -12127,13 +13859,18 @@ export {
|
|
|
12127
13859
|
moduleInfo,
|
|
12128
13860
|
moduleKeys,
|
|
12129
13861
|
normalizeModuleName,
|
|
13862
|
+
normalizeZeyosLauncherApplications,
|
|
13863
|
+
normalizeZeyosLauncherRecords,
|
|
12130
13864
|
registerFieldType,
|
|
12131
13865
|
registerModules,
|
|
12132
13866
|
resolveFields,
|
|
12133
13867
|
tableSortToQuery,
|
|
12134
13868
|
unixSecondsToDate,
|
|
12135
13869
|
useZeyosIcons,
|
|
13870
|
+
zeyosActiveIdentifier,
|
|
13871
|
+
zeyosEntitySelect,
|
|
12136
13872
|
zeyosForm,
|
|
13873
|
+
zeyosLauncher,
|
|
12137
13874
|
zeyosModules,
|
|
12138
13875
|
zeyosSelect,
|
|
12139
13876
|
zeyosTable
|