@zeyos/zx 2.2.1 → 3.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 +197 -0
- package/DESIGN-SYSTEM.md +136 -0
- package/MIGRATION.md +155 -4
- package/README.md +51 -14
- package/dist/types/compat/map/data.d.ts +3 -2
- package/dist/types/components/account-menu/account-menu.d.ts +144 -0
- package/dist/types/components/app-rail/app-rail.d.ts +250 -0
- package/dist/types/components/app-sidebar/app-sidebar.d.ts +169 -0
- package/dist/types/components/avatar/avatar.d.ts +100 -0
- package/dist/types/components/chart/chart.d.ts +321 -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 +300 -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 +74 -9
- 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 +36 -1
- 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/tabbox/tabbox.d.ts +12 -3
- package/dist/types/components/table/table.d.ts +219 -12
- 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 +114 -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 +12 -2
- package/dist/zx-compat.esm.js +943 -311
- package/dist/zx-compat.esm.min.js +1 -1
- package/dist/zx-compat.global.js +7893 -2252
- package/dist/zx-compat.global.min.js +3 -3
- package/dist/zx-zeyos.esm.js +826 -114
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +1720 -243
- package/dist/zx.esm.js +8227 -2599
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +8227 -2599
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +2597 -255
- package/docs/llms.md +475 -37
- package/docs/llms.txt +21 -9
- package/package.json +4 -2
- package/src/compat/globals.js +3 -1
- package/src/compat/map/data.js +31 -9
- 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-rail/app-rail.js +668 -0
- package/src/components/app-sidebar/app-sidebar.css +68 -0
- package/src/components/app-sidebar/app-sidebar.js +388 -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/chart/chart.css +61 -0
- package/src/components/chart/chart.js +518 -0
- package/src/components/date-picker/date-range-picker.css +7 -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/fieldset/fieldset.css +7 -0
- package/src/components/form/form.css +7 -0
- package/src/components/form/form.js +6 -3
- package/src/components/grid/grid.css +6 -0
- package/src/components/grid/grid.js +213 -0
- package/src/components/launcher/launcher.css +100 -0
- package/src/components/launcher/launcher.js +557 -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.css +99 -15
- package/src/components/multi-value-editor/multi-value-editor.js +237 -19
- package/src/components/navigation-bar/navigation-bar.css +7 -0
- 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/search/search.css +10 -2
- package/src/components/select/select.css +6 -1
- package/src/components/select/select.js +99 -11
- 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/stepper/stepper.css +12 -2
- package/src/components/tabbox/tabbox.css +13 -3
- package/src/components/tabbox/tabbox.js +34 -10
- package/src/components/table/table.css +43 -0
- package/src/components/table/table.js +523 -45
- package/src/components/tooltip/tooltip.js +5 -2
- package/src/components/value-list/value-list.js +6 -3
- package/src/core/dom.js +302 -0
- package/src/core/drag-axis.js +203 -0
- package/src/core/position.js +68 -22
- package/src/index.js +19 -2
- package/styles/tokens/dark.css +15 -13
- package/styles/tokens/global.css +17 -5
- package/styles/tokens/semantic.css +14 -8
- package/styles/tokens/themes.css +72 -0
- package/styles/zx.css +11 -1
- package/dist/types/components/permission/permission.d.ts +0 -93
- package/src/components/permission/permission.css +0 -47
- package/src/components/permission/permission.js +0 -198
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;
|
|
@@ -2970,6 +2970,14 @@ var MonthPicker = class extends DatePicker {
|
|
|
2970
2970
|
static cssName = "month-picker";
|
|
2971
2971
|
/** @type {MonthPickerOptions} */
|
|
2972
2972
|
static defaults = { value: null, min: null, max: null };
|
|
2973
|
+
/**
|
|
2974
|
+
* Creates a month picker with its narrower month-based option contract.
|
|
2975
|
+
* @param {Element|string|null} target Existing element, selector, or null.
|
|
2976
|
+
* @param {MonthPickerOptions} [options={}] Month-picker options.
|
|
2977
|
+
*/
|
|
2978
|
+
constructor(target, options = {}) {
|
|
2979
|
+
super(target, options);
|
|
2980
|
+
}
|
|
2973
2981
|
/** @returns {HTMLElement} */
|
|
2974
2982
|
render() {
|
|
2975
2983
|
const root = (
|
|
@@ -3189,7 +3197,13 @@ var PLACEMENTS = /* @__PURE__ */ new Set([
|
|
|
3189
3197
|
"top-start",
|
|
3190
3198
|
"top-end",
|
|
3191
3199
|
"bottom",
|
|
3192
|
-
"top"
|
|
3200
|
+
"top",
|
|
3201
|
+
"right-start",
|
|
3202
|
+
"right-end",
|
|
3203
|
+
"left-start",
|
|
3204
|
+
"left-end",
|
|
3205
|
+
"right",
|
|
3206
|
+
"left"
|
|
3193
3207
|
]);
|
|
3194
3208
|
function position(anchor, floating, options = {}) {
|
|
3195
3209
|
if (!(anchor instanceof Element) || !(floating instanceof HTMLElement)) {
|
|
@@ -3217,13 +3231,15 @@ function position(anchor, floating, options = {}) {
|
|
|
3217
3231
|
floating.style.inset = "auto";
|
|
3218
3232
|
floating.style.margin = "0";
|
|
3219
3233
|
floating.style.setProperty("position-anchor", anchorName);
|
|
3220
|
-
|
|
3221
|
-
floating.style.setProperty("
|
|
3234
|
+
const area = positionArea(placement, getComputedStyle(anchor).direction === "rtl");
|
|
3235
|
+
floating.style.setProperty("position-area", area);
|
|
3236
|
+
floating.style.setProperty("inset-area", area);
|
|
3222
3237
|
floating.style.setProperty(
|
|
3223
3238
|
"position-try-fallbacks",
|
|
3224
3239
|
flip ? "flip-block, flip-inline, flip-block flip-inline" : "none"
|
|
3225
3240
|
);
|
|
3226
|
-
|
|
3241
|
+
const margin = placement.startsWith("top") ? "margin-bottom" : placement.startsWith("bottom") ? "margin-top" : placement.startsWith("left") ? "margin-right" : "margin-left";
|
|
3242
|
+
floating.style.setProperty(margin, `${offset}px`);
|
|
3227
3243
|
update = () => {
|
|
3228
3244
|
if (matchWidth) floating.style.minWidth = `${anchor.getBoundingClientRect().width}px`;
|
|
3229
3245
|
};
|
|
@@ -3267,14 +3283,20 @@ function position(anchor, floating, options = {}) {
|
|
|
3267
3283
|
}
|
|
3268
3284
|
};
|
|
3269
3285
|
}
|
|
3270
|
-
function positionArea(placement) {
|
|
3286
|
+
function positionArea(placement, rtl) {
|
|
3271
3287
|
return {
|
|
3272
|
-
"bottom-start": "
|
|
3273
|
-
"bottom-end": "
|
|
3274
|
-
"top-start": "
|
|
3275
|
-
"top-end": "
|
|
3276
|
-
|
|
3277
|
-
|
|
3288
|
+
"bottom-start": rtl ? "bottom span-left" : "bottom span-right",
|
|
3289
|
+
"bottom-end": rtl ? "bottom span-right" : "bottom span-left",
|
|
3290
|
+
"top-start": rtl ? "top span-left" : "top span-right",
|
|
3291
|
+
"top-end": rtl ? "top span-right" : "top span-left",
|
|
3292
|
+
"right-start": "right span-bottom",
|
|
3293
|
+
"right-end": "right span-top",
|
|
3294
|
+
"left-start": "left span-bottom",
|
|
3295
|
+
"left-end": "left span-top",
|
|
3296
|
+
bottom: "bottom",
|
|
3297
|
+
top: "top",
|
|
3298
|
+
right: "right",
|
|
3299
|
+
left: "left"
|
|
3278
3300
|
}[placement];
|
|
3279
3301
|
}
|
|
3280
3302
|
function updateFallback(anchor, floating, { placement, offset, flip, matchWidth }) {
|
|
@@ -3284,26 +3306,54 @@ function updateFallback(anchor, floating, { placement, offset, flip, matchWidth
|
|
|
3284
3306
|
const viewportWidth = document.documentElement.clientWidth || window.innerWidth;
|
|
3285
3307
|
const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
|
|
3286
3308
|
let actualPlacement = placement;
|
|
3287
|
-
const
|
|
3309
|
+
const side = placementSide(placement);
|
|
3288
3310
|
const bottomY = anchorRect.bottom + offset;
|
|
3289
3311
|
const topY = anchorRect.top - offset - floatingRect.height;
|
|
3312
|
+
const rightX = anchorRect.right + offset;
|
|
3313
|
+
const leftX = anchorRect.left - offset - floatingRect.width;
|
|
3290
3314
|
if (flip) {
|
|
3291
|
-
if (
|
|
3292
|
-
actualPlacement =
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
placement
|
|
3315
|
+
if (side === "bottom" && bottomY + floatingRect.height > viewportHeight && topY >= 0) {
|
|
3316
|
+
actualPlacement = flipPlacement(placement, "bottom", "top");
|
|
3317
|
+
} else if (side === "top" && topY < 0 && bottomY + floatingRect.height <= viewportHeight) {
|
|
3318
|
+
actualPlacement = flipPlacement(placement, "top", "bottom");
|
|
3319
|
+
} else if (side === "right" && rightX + floatingRect.width > viewportWidth && leftX >= 0) {
|
|
3320
|
+
actualPlacement = flipPlacement(placement, "right", "left");
|
|
3321
|
+
} else if (side === "left" && leftX < 0 && rightX + floatingRect.width <= viewportWidth) {
|
|
3322
|
+
actualPlacement = flipPlacement(placement, "left", "right");
|
|
3297
3323
|
}
|
|
3298
3324
|
}
|
|
3325
|
+
const actualSide = placementSide(actualPlacement);
|
|
3299
3326
|
let left;
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3327
|
+
let top;
|
|
3328
|
+
if (actualSide === "bottom" || actualSide === "top") {
|
|
3329
|
+
const rtl = getComputedStyle(anchor).direction === "rtl";
|
|
3330
|
+
if (actualPlacement.endsWith("-end")) {
|
|
3331
|
+
left = rtl ? anchorRect.left : anchorRect.right - floatingRect.width;
|
|
3332
|
+
} else if (actualPlacement.endsWith("-start")) {
|
|
3333
|
+
left = rtl ? anchorRect.right - floatingRect.width : anchorRect.left;
|
|
3334
|
+
} else left = anchorRect.left + (anchorRect.width - floatingRect.width) / 2;
|
|
3335
|
+
top = actualSide === "bottom" ? bottomY : topY;
|
|
3336
|
+
} else {
|
|
3337
|
+
left = actualSide === "right" ? rightX : leftX;
|
|
3338
|
+
if (actualPlacement.endsWith("-end")) top = anchorRect.bottom - floatingRect.height;
|
|
3339
|
+
else if (actualPlacement.endsWith("-start")) top = anchorRect.top;
|
|
3340
|
+
else top = anchorRect.top + (anchorRect.height - floatingRect.height) / 2;
|
|
3341
|
+
}
|
|
3304
3342
|
floating.style.left = `${Math.round(clamp(left, 0, Math.max(0, viewportWidth - floatingRect.width)))}px`;
|
|
3305
3343
|
floating.style.top = `${Math.round(clamp(top, 0, Math.max(0, viewportHeight - floatingRect.height)))}px`;
|
|
3306
3344
|
}
|
|
3345
|
+
function placementSide(placement) {
|
|
3346
|
+
return (
|
|
3347
|
+
/** @type {'top'|'bottom'|'left'|'right'} */
|
|
3348
|
+
placement.split("-")[0]
|
|
3349
|
+
);
|
|
3350
|
+
}
|
|
3351
|
+
function flipPlacement(placement, from, to) {
|
|
3352
|
+
return (
|
|
3353
|
+
/** @type {Placement} */
|
|
3354
|
+
placement.replace(from, to)
|
|
3355
|
+
);
|
|
3356
|
+
}
|
|
3307
3357
|
function showPopover(element) {
|
|
3308
3358
|
if (typeof element.showPopover !== "function") return;
|
|
3309
3359
|
try {
|
|
@@ -4136,31 +4186,31 @@ var PLACEHOLDER_TOKENS = {
|
|
|
4136
4186
|
"%Y": "YYYY",
|
|
4137
4187
|
"%y": "YY"
|
|
4138
4188
|
};
|
|
4139
|
-
function parseRangeText(text3,
|
|
4189
|
+
function parseRangeText(text3, format2, separator) {
|
|
4140
4190
|
const source = String(text3 ?? "").trim();
|
|
4141
4191
|
if (source === "") return { start: null, end: null };
|
|
4142
4192
|
const raw2 = String(separator ?? " \u2013 ");
|
|
4143
4193
|
const core = raw2.trim();
|
|
4144
|
-
const strict = core !== "" && String(
|
|
4194
|
+
const strict = core !== "" && String(format2).includes(core) && /^\s|\s$/.test(raw2);
|
|
4145
4195
|
const lead = strict ? "\\s+" : "\\s*";
|
|
4146
4196
|
const tail = strict ? "(?:\\s+|$)" : "\\s*";
|
|
4147
4197
|
const parts = core === "" ? [source] : source.split(new RegExp(`${lead}${escapeRegExp(core)}${tail}`));
|
|
4148
4198
|
if (parts.length > 2) return null;
|
|
4149
|
-
const first = parseDate(parts[0].trim(),
|
|
4199
|
+
const first = parseDate(parts[0].trim(), format2);
|
|
4150
4200
|
if (!first) return null;
|
|
4151
4201
|
if (parts.length === 1) return { start: first, end: new Date(first.getTime()) };
|
|
4152
4202
|
if (parts[1].trim() === "") return { start: first, end: null };
|
|
4153
|
-
const second = parseDate(parts[1].trim(),
|
|
4203
|
+
const second = parseDate(parts[1].trim(), format2);
|
|
4154
4204
|
if (!second) return null;
|
|
4155
4205
|
return normalizeRange(first, second);
|
|
4156
4206
|
}
|
|
4157
|
-
function formatRangeText(range,
|
|
4207
|
+
function formatRangeText(range, format2, separator) {
|
|
4158
4208
|
const start = range?.start ?? null;
|
|
4159
4209
|
const end = range?.end ?? null;
|
|
4160
4210
|
if (!start && !end) return "";
|
|
4161
|
-
if (!end) return formatDate(start,
|
|
4162
|
-
if (!start) return formatDate(end,
|
|
4163
|
-
return `${formatDate(start,
|
|
4211
|
+
if (!end) return formatDate(start, format2);
|
|
4212
|
+
if (!start) return formatDate(end, format2);
|
|
4213
|
+
return `${formatDate(start, format2)}${separator}${formatDate(end, format2)}`;
|
|
4164
4214
|
}
|
|
4165
4215
|
var DateRangeBox = class extends Component {
|
|
4166
4216
|
static cssName = "date-range-box";
|
|
@@ -4818,12 +4868,12 @@ var DateTimeBox = function DateTimeBox2(target, options = {}) {
|
|
|
4818
4868
|
function validDate4(value) {
|
|
4819
4869
|
return value instanceof Date && !Number.isNaN(value.getTime()) ? new Date(value.getTime()) : null;
|
|
4820
4870
|
}
|
|
4821
|
-
function effectiveFormat(
|
|
4822
|
-
const value = String(
|
|
4871
|
+
function effectiveFormat(format2, time) {
|
|
4872
|
+
const value = String(format2 || "%d.%m.%Y");
|
|
4823
4873
|
return time && !/%[HM]/.test(value) ? `${value} %H:%M` : value;
|
|
4824
4874
|
}
|
|
4825
|
-
function placeholderFromFormat(
|
|
4826
|
-
return
|
|
4875
|
+
function placeholderFromFormat(format2) {
|
|
4876
|
+
return format2.replace(/%[demYyHMS]/g, (token) => ({
|
|
4827
4877
|
"%d": "DD",
|
|
4828
4878
|
"%e": "D",
|
|
4829
4879
|
"%m": "MM",
|
|
@@ -5250,18 +5300,34 @@ function registerFieldUploadAdapter() {
|
|
|
5250
5300
|
}
|
|
5251
5301
|
|
|
5252
5302
|
// src/components/multi-value-editor/multi-value-editor.js
|
|
5303
|
+
var REORDER = Object.freeze({
|
|
5304
|
+
both: { drag: true, buttons: true },
|
|
5305
|
+
buttons: { drag: false, buttons: true },
|
|
5306
|
+
drag: { drag: true, buttons: false },
|
|
5307
|
+
none: { drag: false, buttons: false }
|
|
5308
|
+
});
|
|
5309
|
+
var PARTS = Object.freeze({
|
|
5310
|
+
value: ".zx-multi-value-editor__value",
|
|
5311
|
+
handle: ".zx-multi-value-editor__handle",
|
|
5312
|
+
up: '[data-action="up"]',
|
|
5313
|
+
down: '[data-action="down"]'
|
|
5314
|
+
});
|
|
5253
5315
|
var MultiValueEditor = class extends Component {
|
|
5254
5316
|
static cssName = "multi-value-editor";
|
|
5255
5317
|
/** @type {MultiValueEditorOptions} */
|
|
5256
|
-
static defaults = { values: [], options: null, addLabel: "Add value" };
|
|
5318
|
+
static defaults = { values: [], options: null, addLabel: "Add value", reorder: "both" };
|
|
5257
5319
|
/** @returns {HTMLElement} */
|
|
5258
5320
|
render() {
|
|
5259
5321
|
const root = (
|
|
5260
5322
|
/** @type {HTMLElement} */
|
|
5261
5323
|
this.el ?? h("div")
|
|
5262
5324
|
);
|
|
5325
|
+
this.el = root;
|
|
5263
5326
|
this._values = Array.isArray(this.options.values) ? this.options.values.map(String) : [];
|
|
5264
5327
|
this._ownedNodes = [];
|
|
5328
|
+
this._initialState = root.getAttribute("data-state");
|
|
5329
|
+
this._dragIndex = -1;
|
|
5330
|
+
this._armed = null;
|
|
5265
5331
|
this._rows = h("div", { class: "zx-multi-value-editor__rows" });
|
|
5266
5332
|
this._add = h("button", {
|
|
5267
5333
|
class: "zx-btn zx-multi-value-editor__add",
|
|
@@ -5272,6 +5338,17 @@ var MultiValueEditor = class extends Component {
|
|
|
5272
5338
|
root.append(this._rows, this._add);
|
|
5273
5339
|
this.listen(root, "click", (event) => this._handleClick(event));
|
|
5274
5340
|
this.listen(this._rows, "change", (event) => this._handleChange(event));
|
|
5341
|
+
this.listen(this._rows, "keydown", (event) => this._handleKeydown(event));
|
|
5342
|
+
if (this._reorder().drag) {
|
|
5343
|
+
this.listen(this._rows, "pointerdown", (event) => this._handlePointerDown(event));
|
|
5344
|
+
this.listen(this._rows, "dragstart", (event) => this._handleDragStart(event));
|
|
5345
|
+
this.listen(this._rows, "dragover", (event) => this._handleDragOver(event));
|
|
5346
|
+
this.listen(this._rows, "dragleave", (event) => this._handleDragLeave(event));
|
|
5347
|
+
this.listen(this._rows, "drop", (event) => this._handleDrop(event));
|
|
5348
|
+
this.listen(this._rows, "dragend", () => this._finishDrag());
|
|
5349
|
+
this.listen(document, "pointerup", () => this._disarm());
|
|
5350
|
+
this.listen(document, "pointercancel", () => this._disarm());
|
|
5351
|
+
}
|
|
5275
5352
|
this._renderRows();
|
|
5276
5353
|
return root;
|
|
5277
5354
|
}
|
|
@@ -5294,16 +5371,27 @@ var MultiValueEditor = class extends Component {
|
|
|
5294
5371
|
if (!silent) this.emit("change", { values: this.getValues() });
|
|
5295
5372
|
return this;
|
|
5296
5373
|
}
|
|
5297
|
-
/** @
|
|
5298
|
-
|
|
5374
|
+
/** @returns {{drag: boolean, buttons: boolean}} */
|
|
5375
|
+
_reorder() {
|
|
5376
|
+
return REORDER[this.options.reorder] ?? REORDER.both;
|
|
5377
|
+
}
|
|
5378
|
+
/**
|
|
5379
|
+
* @param {number} [focusIndex=-1] Row to focus after the render, or -1 for none.
|
|
5380
|
+
* @param {keyof PARTS} [focusPart='value'] Which control of that row takes focus.
|
|
5381
|
+
* @returns {void}
|
|
5382
|
+
*/
|
|
5383
|
+
_renderRows(focusIndex = -1, focusPart = "value") {
|
|
5299
5384
|
const choices = normalizeOptions2(this.options.options);
|
|
5300
5385
|
const useSelect = this.options.options !== null;
|
|
5386
|
+
const { drag, buttons } = this._reorder();
|
|
5387
|
+
const last = this._values.length - 1;
|
|
5301
5388
|
const rows = this._values.map((value, index) => {
|
|
5389
|
+
const entries = !useSelect || choices.some(([option]) => option === value) ? choices : [[value, value], ...choices];
|
|
5302
5390
|
const control = useSelect ? h("select", {
|
|
5303
5391
|
class: "zx-multi-value-editor__value",
|
|
5304
5392
|
ariaLabel: `Value ${index + 1}`,
|
|
5305
5393
|
dataset: { index }
|
|
5306
|
-
},
|
|
5394
|
+
}, entries.map(([optionValue, label]) => h("option", {
|
|
5307
5395
|
value: optionValue,
|
|
5308
5396
|
selected: optionValue === value
|
|
5309
5397
|
}, label))) : h("input", {
|
|
@@ -5316,40 +5404,52 @@ var MultiValueEditor = class extends Component {
|
|
|
5316
5404
|
return h(
|
|
5317
5405
|
"div",
|
|
5318
5406
|
{ class: "zx-multi-value-editor__row", dataset: { row: index } },
|
|
5407
|
+
drag ? h("button", {
|
|
5408
|
+
class: "zx-multi-value-editor__handle",
|
|
5409
|
+
type: "button",
|
|
5410
|
+
ariaLabel: `Reorder value ${index + 1}. Use the arrow keys to move it.`,
|
|
5411
|
+
dataset: { action: "grab", index }
|
|
5412
|
+
}, icon("drag", { size: 12 })) : null,
|
|
5319
5413
|
control,
|
|
5320
5414
|
h(
|
|
5321
5415
|
"div",
|
|
5322
5416
|
{ class: "zx-multi-value-editor__controls" },
|
|
5323
|
-
h("button", {
|
|
5417
|
+
buttons ? h("button", {
|
|
5324
5418
|
class: "zx-btn",
|
|
5325
5419
|
type: "button",
|
|
5326
|
-
disabled
|
|
5420
|
+
// `aria-disabled` rather than `disabled`: the buttons re-render on every move, and a
|
|
5421
|
+
// control that disables itself under the reader's finger drops focus to the document.
|
|
5422
|
+
ariaDisabled: String(index === 0),
|
|
5327
5423
|
ariaLabel: `Move value ${index + 1} up`,
|
|
5328
5424
|
dataset: { action: "up", index }
|
|
5329
|
-
}, "
|
|
5330
|
-
h("button", {
|
|
5425
|
+
}, icon("chevron-up", { size: 12 })) : null,
|
|
5426
|
+
buttons ? h("button", {
|
|
5331
5427
|
class: "zx-btn",
|
|
5332
5428
|
type: "button",
|
|
5333
|
-
|
|
5429
|
+
ariaDisabled: String(index === last),
|
|
5334
5430
|
ariaLabel: `Move value ${index + 1} down`,
|
|
5335
5431
|
dataset: { action: "down", index }
|
|
5336
|
-
}, "
|
|
5432
|
+
}, icon("chevron-down", { size: 12 })) : null,
|
|
5337
5433
|
h("button", {
|
|
5338
5434
|
class: "zx-btn",
|
|
5339
5435
|
type: "button",
|
|
5340
5436
|
ariaLabel: `Remove value ${index + 1}`,
|
|
5341
5437
|
dataset: { action: "remove", index }
|
|
5342
|
-
}, "
|
|
5438
|
+
}, icon("x", { size: 12 }))
|
|
5343
5439
|
)
|
|
5344
5440
|
);
|
|
5345
5441
|
});
|
|
5346
5442
|
this._rows.replaceChildren(...rows);
|
|
5347
|
-
if (focusIndex >= 0)
|
|
5443
|
+
if (focusIndex >= 0) {
|
|
5444
|
+
const row = rows[Math.min(focusIndex, rows.length - 1)];
|
|
5445
|
+
row?.querySelector(PARTS[focusPart] ?? PARTS.value)?.focus();
|
|
5446
|
+
}
|
|
5348
5447
|
}
|
|
5349
5448
|
/** @param {MouseEvent} event @returns {void} */
|
|
5350
5449
|
_handleClick(event) {
|
|
5351
5450
|
const button = event.target instanceof Element ? event.target.closest("[data-action]") : null;
|
|
5352
5451
|
if (!button || !this.el.contains(button)) return;
|
|
5452
|
+
if (button.getAttribute("aria-disabled") === "true") return;
|
|
5353
5453
|
const action = button.dataset.action;
|
|
5354
5454
|
const index = Number(button.dataset.index);
|
|
5355
5455
|
if (action === "add") {
|
|
@@ -5362,28 +5462,156 @@ var MultiValueEditor = class extends Component {
|
|
|
5362
5462
|
this._renderRows(Math.min(index, this._values.length - 1));
|
|
5363
5463
|
this._emitChange();
|
|
5364
5464
|
} else if ((action === "up" || action === "down") && Number.isInteger(index)) {
|
|
5365
|
-
|
|
5366
|
-
if (target < 0 || target >= this._values.length) return;
|
|
5367
|
-
[this._values[index], this._values[target]] = [this._values[target], this._values[index]];
|
|
5368
|
-
this._renderRows(target);
|
|
5369
|
-
this._emitChange();
|
|
5465
|
+
this._move(index, index + (action === "up" ? -1 : 1), action);
|
|
5370
5466
|
}
|
|
5371
5467
|
}
|
|
5372
5468
|
/** @param {Event} event @returns {void} */
|
|
5373
5469
|
_handleChange(event) {
|
|
5374
5470
|
const control = event.target instanceof Element ? event.target.closest("[data-index]") : null;
|
|
5375
|
-
if (!control) return;
|
|
5471
|
+
if (!control || !("value" in control)) return;
|
|
5376
5472
|
const index = Number(control.dataset.index);
|
|
5377
5473
|
if (!Number.isInteger(index) || index < 0 || index >= this._values.length) return;
|
|
5378
5474
|
this._values[index] = control.value;
|
|
5379
5475
|
this._emitChange();
|
|
5380
5476
|
}
|
|
5477
|
+
/**
|
|
5478
|
+
* The handle is the keyboard equivalent of dragging it, so `reorder: 'drag'` is still operable
|
|
5479
|
+
* without a pointer.
|
|
5480
|
+
* @param {KeyboardEvent} event
|
|
5481
|
+
* @returns {void}
|
|
5482
|
+
*/
|
|
5483
|
+
_handleKeydown(event) {
|
|
5484
|
+
const handle = event.target instanceof Element ? event.target.closest(".zx-multi-value-editor__handle") : null;
|
|
5485
|
+
if (!handle) return;
|
|
5486
|
+
const index = Number(handle.dataset.index);
|
|
5487
|
+
const target = event.key === "ArrowUp" ? index - 1 : event.key === "ArrowDown" ? index + 1 : event.key === "Home" ? 0 : event.key === "End" ? this._values.length - 1 : -1;
|
|
5488
|
+
if (target < 0 || target >= this._values.length) return;
|
|
5489
|
+
event.preventDefault();
|
|
5490
|
+
this._move(index, target, "handle");
|
|
5491
|
+
}
|
|
5492
|
+
/**
|
|
5493
|
+
* Arms the row for dragging. Only the handle does this, so text selection inside a row's input
|
|
5494
|
+
* keeps working and a stray drag cannot reorder anything.
|
|
5495
|
+
* @param {PointerEvent} event
|
|
5496
|
+
* @returns {void}
|
|
5497
|
+
*/
|
|
5498
|
+
_handlePointerDown(event) {
|
|
5499
|
+
const handle = event.target instanceof Element ? event.target.closest(".zx-multi-value-editor__handle") : null;
|
|
5500
|
+
this._disarm();
|
|
5501
|
+
if (!handle || handle.disabled) return;
|
|
5502
|
+
const row = (
|
|
5503
|
+
/** @type {HTMLElement|null} */
|
|
5504
|
+
handle.closest(".zx-multi-value-editor__row")
|
|
5505
|
+
);
|
|
5506
|
+
if (!row) return;
|
|
5507
|
+
row.draggable = true;
|
|
5508
|
+
this._armed = row;
|
|
5509
|
+
}
|
|
5510
|
+
/** @returns {void} */
|
|
5511
|
+
_disarm() {
|
|
5512
|
+
if (this._armed) this._armed.draggable = false;
|
|
5513
|
+
this._armed = null;
|
|
5514
|
+
}
|
|
5515
|
+
/** @param {DragEvent} event @returns {void} */
|
|
5516
|
+
_handleDragStart(event) {
|
|
5517
|
+
const row = event.target instanceof Element ? event.target.closest(".zx-multi-value-editor__row") : null;
|
|
5518
|
+
if (!row || row !== this._armed) {
|
|
5519
|
+
event.preventDefault();
|
|
5520
|
+
return;
|
|
5521
|
+
}
|
|
5522
|
+
this._dragIndex = Number(row.dataset.row);
|
|
5523
|
+
row.dataset.dragging = "true";
|
|
5524
|
+
this.el.setAttribute("data-state", "dragging");
|
|
5525
|
+
event.dataTransfer?.setData("text/plain", String(this._dragIndex));
|
|
5526
|
+
if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
|
|
5527
|
+
}
|
|
5528
|
+
/** @param {DragEvent} event @returns {void} */
|
|
5529
|
+
_handleDragOver(event) {
|
|
5530
|
+
if (this._dragIndex < 0) return;
|
|
5531
|
+
event.preventDefault();
|
|
5532
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
|
|
5533
|
+
this._showDrop(this._dropTarget(event.clientY));
|
|
5534
|
+
}
|
|
5535
|
+
/** @param {DragEvent} event @returns {void} */
|
|
5536
|
+
_handleDragLeave(event) {
|
|
5537
|
+
const to = event.relatedTarget;
|
|
5538
|
+
if (to instanceof Node && this._rows.contains(to)) return;
|
|
5539
|
+
this._clearDrop();
|
|
5540
|
+
}
|
|
5541
|
+
/** @param {DragEvent} event @returns {void} */
|
|
5542
|
+
_handleDrop(event) {
|
|
5543
|
+
if (this._dragIndex < 0) return;
|
|
5544
|
+
event.preventDefault();
|
|
5545
|
+
const from = this._dragIndex;
|
|
5546
|
+
const slot = this._dropTarget(event.clientY);
|
|
5547
|
+
this._finishDrag();
|
|
5548
|
+
this._move(from, slot > from ? slot - 1 : slot, "handle");
|
|
5549
|
+
}
|
|
5550
|
+
/** @returns {void} */
|
|
5551
|
+
_finishDrag() {
|
|
5552
|
+
this._dragIndex = -1;
|
|
5553
|
+
this._disarm();
|
|
5554
|
+
this._clearDrop();
|
|
5555
|
+
for (const row of this._rows.children) delete /** @type {HTMLElement} */
|
|
5556
|
+
row.dataset.dragging;
|
|
5557
|
+
if (this._initialState === null) this.el.removeAttribute("data-state");
|
|
5558
|
+
else this.el.setAttribute("data-state", this._initialState);
|
|
5559
|
+
}
|
|
5560
|
+
/**
|
|
5561
|
+
* Insertion slot (0…length) for a pointer at `clientY`.
|
|
5562
|
+
* @param {number} clientY Pointer position.
|
|
5563
|
+
* @returns {number}
|
|
5564
|
+
*/
|
|
5565
|
+
_dropTarget(clientY) {
|
|
5566
|
+
const rows = [...this._rows.children];
|
|
5567
|
+
for (const [index, row] of rows.entries()) {
|
|
5568
|
+
const box = row.getBoundingClientRect();
|
|
5569
|
+
if (clientY < box.top + box.height / 2) return index;
|
|
5570
|
+
}
|
|
5571
|
+
return rows.length;
|
|
5572
|
+
}
|
|
5573
|
+
/** @param {number} slot Insertion slot to mark. @returns {void} */
|
|
5574
|
+
_showDrop(slot) {
|
|
5575
|
+
const rows = [...this._rows.children];
|
|
5576
|
+
const inert = slot === this._dragIndex || slot === this._dragIndex + 1;
|
|
5577
|
+
for (const [index, row] of rows.entries()) {
|
|
5578
|
+
const cell = (
|
|
5579
|
+
/** @type {HTMLElement} */
|
|
5580
|
+
row
|
|
5581
|
+
);
|
|
5582
|
+
if (!inert && index === slot) cell.dataset.drop = "before";
|
|
5583
|
+
else if (!inert && slot === rows.length && index === rows.length - 1) cell.dataset.drop = "after";
|
|
5584
|
+
else delete cell.dataset.drop;
|
|
5585
|
+
}
|
|
5586
|
+
}
|
|
5587
|
+
/** @returns {void} */
|
|
5588
|
+
_clearDrop() {
|
|
5589
|
+
for (const row of this._rows.children) delete /** @type {HTMLElement} */
|
|
5590
|
+
row.dataset.drop;
|
|
5591
|
+
}
|
|
5592
|
+
/**
|
|
5593
|
+
* @param {number} from Index to move.
|
|
5594
|
+
* @param {number} to Destination index.
|
|
5595
|
+
* @param {keyof PARTS} part Control of the moved row that takes focus.
|
|
5596
|
+
* @returns {boolean} Whether anything moved.
|
|
5597
|
+
*/
|
|
5598
|
+
_move(from, to, part) {
|
|
5599
|
+
const size = this._values.length;
|
|
5600
|
+
if (from === to || from < 0 || to < 0 || from >= size || to >= size) return false;
|
|
5601
|
+
const [value] = this._values.splice(from, 1);
|
|
5602
|
+
this._values.splice(to, 0, value);
|
|
5603
|
+
this._renderRows(to, part);
|
|
5604
|
+
this._emitChange();
|
|
5605
|
+
return true;
|
|
5606
|
+
}
|
|
5381
5607
|
/** @returns {void} */
|
|
5382
5608
|
_emitChange() {
|
|
5383
5609
|
this.emit("change", { values: this.getValues() });
|
|
5384
5610
|
}
|
|
5385
5611
|
/** @returns {void} */
|
|
5386
5612
|
destroy() {
|
|
5613
|
+
if (this._initialState === null) this.el?.removeAttribute("data-state");
|
|
5614
|
+
else this.el?.setAttribute("data-state", this._initialState);
|
|
5387
5615
|
for (const node of this._ownedNodes ?? []) node.remove();
|
|
5388
5616
|
super.destroy();
|
|
5389
5617
|
}
|
|
@@ -6311,6 +6539,7 @@ var Select = class _Select extends Component {
|
|
|
6311
6539
|
/** @type {SelectOptions} */
|
|
6312
6540
|
static defaults = {
|
|
6313
6541
|
items: [],
|
|
6542
|
+
fixedItems: [],
|
|
6314
6543
|
valueKey: "ID",
|
|
6315
6544
|
labelKey: "name",
|
|
6316
6545
|
renderItem: null,
|
|
@@ -6330,6 +6559,8 @@ var Select = class _Select extends Component {
|
|
|
6330
6559
|
render() {
|
|
6331
6560
|
this._items = [];
|
|
6332
6561
|
this._visibleItems = [];
|
|
6562
|
+
this._fixedItems = [];
|
|
6563
|
+
this._visibleFixed = [];
|
|
6333
6564
|
this._selected = null;
|
|
6334
6565
|
this._optionEntries = [];
|
|
6335
6566
|
this._activeIndex = -1;
|
|
@@ -6413,6 +6644,8 @@ var Select = class _Select extends Component {
|
|
|
6413
6644
|
root.replaceChildren(control, panel);
|
|
6414
6645
|
this._items = Array.isArray(this.options.items) ? this.options.items.slice() : [];
|
|
6415
6646
|
this._visibleItems = this._items.slice();
|
|
6647
|
+
this._fixedItems = Array.isArray(this.options.fixedItems) ? this.options.fixedItems.slice() : [];
|
|
6648
|
+
this._visibleFixed = this._fixedItems.slice();
|
|
6416
6649
|
this._disabled = Boolean(this.options.disabled);
|
|
6417
6650
|
this._handleTypeahead = typeahead(
|
|
6418
6651
|
() => this._optionEntries.filter((entry) => !entry.clear).map((entry) => entry.element),
|
|
@@ -6489,7 +6722,22 @@ var Select = class _Select extends Component {
|
|
|
6489
6722
|
const selectedValue = this.value;
|
|
6490
6723
|
this._items = Array.isArray(items) ? items.slice() : [];
|
|
6491
6724
|
this._visibleItems = this._items.slice();
|
|
6492
|
-
this._selected = selectedValue == null ? null : this.
|
|
6725
|
+
this._selected = selectedValue == null ? null : this._findItem(selectedValue);
|
|
6726
|
+
this._syncInputValue();
|
|
6727
|
+
this._renderOptions();
|
|
6728
|
+
this._position?.update();
|
|
6729
|
+
return this;
|
|
6730
|
+
}
|
|
6731
|
+
/**
|
|
6732
|
+
* Replaces the pinned choices and preserves selection by ID when possible.
|
|
6733
|
+
* @param {SelectItem[]} items New fixed items.
|
|
6734
|
+
* @returns {this}
|
|
6735
|
+
*/
|
|
6736
|
+
setFixedItems(items) {
|
|
6737
|
+
const selectedValue = this.value;
|
|
6738
|
+
this._fixedItems = Array.isArray(items) ? items.slice() : [];
|
|
6739
|
+
this._visibleFixed = this._fixedItems.slice();
|
|
6740
|
+
this._selected = selectedValue == null ? null : this._findItem(selectedValue);
|
|
6493
6741
|
this._syncInputValue();
|
|
6494
6742
|
this._renderOptions();
|
|
6495
6743
|
this._position?.update();
|
|
@@ -6543,6 +6791,7 @@ var Select = class _Select extends Component {
|
|
|
6543
6791
|
this.refs.input.removeAttribute("aria-activedescendant");
|
|
6544
6792
|
this._activeIndex = -1;
|
|
6545
6793
|
this._visibleItems = this._items.slice();
|
|
6794
|
+
this._visibleFixed = this._fixedItems.slice();
|
|
6546
6795
|
this._syncInputValue();
|
|
6547
6796
|
this.emit("close", {});
|
|
6548
6797
|
return this;
|
|
@@ -6598,6 +6847,38 @@ var Select = class _Select extends Component {
|
|
|
6598
6847
|
select.set(requestedValue, { silent: true });
|
|
6599
6848
|
return select;
|
|
6600
6849
|
}
|
|
6850
|
+
/**
|
|
6851
|
+
* Creates the record-permission picker: the fixed Private and Public choices above the groups a
|
|
6852
|
+
* record can be shared with. The value is the tri-state ZeyOS stores — `false` for private,
|
|
6853
|
+
* `true` for public, or a group ID — so the control binds straight to the record field.
|
|
6854
|
+
*
|
|
6855
|
+
* @param {Element|string|null} target Component target.
|
|
6856
|
+
* @param {SelectOptions & {groups?: SelectItem[]|((query: string) => Promise<SelectItem[]>|SelectItem[])}} [options={}]
|
|
6857
|
+
* Select overrides other than the fixed choices; `groups` holds the shareable groups, either as
|
|
6858
|
+
* an array (filtered locally) or as a loader function (queried remotely).
|
|
6859
|
+
* @returns {Select}
|
|
6860
|
+
*/
|
|
6861
|
+
static permission(target, options = {}) {
|
|
6862
|
+
const { groups = [], value = true, valueKey = "ID", labelKey = "name", ...rest } = options;
|
|
6863
|
+
const loader = typeof groups === "function" ? groups : null;
|
|
6864
|
+
const shareable = loader ? [] : groups;
|
|
6865
|
+
const choices = [{ ID: false, name: "Private" }, { ID: true, name: "Public" }];
|
|
6866
|
+
const select = new _Select(target, {
|
|
6867
|
+
filter: loader ?? "local",
|
|
6868
|
+
...rest,
|
|
6869
|
+
value: null,
|
|
6870
|
+
items: shareable,
|
|
6871
|
+
fixedItems: [],
|
|
6872
|
+
// The two choices carry their own ID and label, so a caller's group readers stay untouched.
|
|
6873
|
+
valueKey: (item) => choices.includes(item) ? item.ID : readItem(item, valueKey),
|
|
6874
|
+
labelKey: (item) => choices.includes(item) ? item.name : String(readItem(item, labelKey) ?? "")
|
|
6875
|
+
});
|
|
6876
|
+
choices[0].name = select._message("permission.private", "Private");
|
|
6877
|
+
choices[1].name = select._message("permission.public", "Public");
|
|
6878
|
+
select.setFixedItems(choices);
|
|
6879
|
+
select.set(value, { silent: true });
|
|
6880
|
+
return select;
|
|
6881
|
+
}
|
|
6601
6882
|
/** Cleans up the panel, pending async work, and enhanced target contents. @returns {void} */
|
|
6602
6883
|
destroy() {
|
|
6603
6884
|
this._cancelQuery();
|
|
@@ -6668,8 +6949,8 @@ var Select = class _Select extends Component {
|
|
|
6668
6949
|
);
|
|
6669
6950
|
if (!this._open) this.open();
|
|
6670
6951
|
if (this.options.filter === "local") {
|
|
6671
|
-
|
|
6672
|
-
this._visibleItems = matchItems(this._items, query,
|
|
6952
|
+
this._visibleFixed = this._matchFixed(query);
|
|
6953
|
+
this._visibleItems = matchItems(this._items, query, this._searchKeys());
|
|
6673
6954
|
this._renderOptions();
|
|
6674
6955
|
this._setActive(this._optionEntries.length > 0 ? 0 : -1);
|
|
6675
6956
|
this._position?.update();
|
|
@@ -6724,6 +7005,10 @@ var Select = class _Select extends Component {
|
|
|
6724
7005
|
if (this.options.clearable) {
|
|
6725
7006
|
this._appendOption(null, true, this.options.placeholder || this._message("select.none", "No selection"));
|
|
6726
7007
|
}
|
|
7008
|
+
for (const item of this._visibleFixed) this._appendOption(item, false);
|
|
7009
|
+
if (this._visibleFixed.length > 0 && this._visibleItems.length > 0 && !this.options.groupKey) {
|
|
7010
|
+
list.append(h("div", { class: "zx-select__separator", role: "presentation" }));
|
|
7011
|
+
}
|
|
6727
7012
|
for (const item of this._visibleItems) {
|
|
6728
7013
|
if (this.options.groupKey) {
|
|
6729
7014
|
const group = this._read(item, this.options.groupKey);
|
|
@@ -6737,7 +7022,7 @@ var Select = class _Select extends Component {
|
|
|
6737
7022
|
}
|
|
6738
7023
|
this._appendOption(item, false);
|
|
6739
7024
|
}
|
|
6740
|
-
if (this._visibleItems.length === 0 && !this._loading) {
|
|
7025
|
+
if (this._visibleItems.length === 0 && this._visibleFixed.length === 0 && !this._loading) {
|
|
6741
7026
|
list.append(h("div", {
|
|
6742
7027
|
class: "zx-select__empty",
|
|
6743
7028
|
role: "status"
|
|
@@ -6777,6 +7062,7 @@ var Select = class _Select extends Component {
|
|
|
6777
7062
|
if (this._queryTimer !== null) clearTimeout(this._queryTimer);
|
|
6778
7063
|
this._requestSequence += 1;
|
|
6779
7064
|
const sequence = this._requestSequence;
|
|
7065
|
+
this._visibleFixed = this._matchFixed(query);
|
|
6780
7066
|
if (query.length < Math.max(0, Number(this.options.minQuery) || 0)) {
|
|
6781
7067
|
this._setLoading(false);
|
|
6782
7068
|
this._visibleItems = [];
|
|
@@ -6861,13 +7147,14 @@ var Select = class _Select extends Component {
|
|
|
6861
7147
|
}
|
|
6862
7148
|
/** @param {unknown} candidate @returns {SelectItem|null} */
|
|
6863
7149
|
_findItem(candidate) {
|
|
6864
|
-
const
|
|
7150
|
+
const pool = this._fixedItems.length === 0 ? this._items : [...this._fixedItems, ...this._items];
|
|
7151
|
+
const direct = pool.find((item) => item === candidate);
|
|
6865
7152
|
if (direct !== void 0) return direct;
|
|
6866
7153
|
const value = candidate !== null && typeof candidate === "object" ? this._itemValue(
|
|
6867
7154
|
/** @type {SelectItem} */
|
|
6868
7155
|
candidate
|
|
6869
7156
|
) : candidate;
|
|
6870
|
-
return
|
|
7157
|
+
return pool.find((item) => Object.is(this._itemValue(item), value)) ?? null;
|
|
6871
7158
|
}
|
|
6872
7159
|
/** @param {SelectItem|null} item @returns {unknown} */
|
|
6873
7160
|
_itemValue(item) {
|
|
@@ -6880,9 +7167,20 @@ var Select = class _Select extends Component {
|
|
|
6880
7167
|
}
|
|
6881
7168
|
/** @param {SelectItem|null} item @param {SelectValueReader|SelectLabelReader} reader @returns {unknown} */
|
|
6882
7169
|
_read(item, reader) {
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
7170
|
+
return readItem(item, reader);
|
|
7171
|
+
}
|
|
7172
|
+
/** @returns {Array<SelectValueReader|SelectLabelReader>} */
|
|
7173
|
+
_searchKeys() {
|
|
7174
|
+
return this.options.searchKeys ?? [this.options.labelKey];
|
|
7175
|
+
}
|
|
7176
|
+
/**
|
|
7177
|
+
* Narrows the pinned choices. They are always matched here and never handed to a filter
|
|
7178
|
+
* function: a fixed choice belongs to the control, not to whatever the remote source returns.
|
|
7179
|
+
* @param {string} query Search text.
|
|
7180
|
+
* @returns {SelectItem[]}
|
|
7181
|
+
*/
|
|
7182
|
+
_matchFixed(query) {
|
|
7183
|
+
return matchItems(this._fixedItems, query, this._searchKeys());
|
|
6886
7184
|
}
|
|
6887
7185
|
/** @returns {number} */
|
|
6888
7186
|
_selectedEntryIndex() {
|
|
@@ -6905,6 +7203,11 @@ var Select = class _Select extends Component {
|
|
|
6905
7203
|
return message === key ? fallback : message;
|
|
6906
7204
|
}
|
|
6907
7205
|
};
|
|
7206
|
+
function readItem(item, reader) {
|
|
7207
|
+
if (typeof reader === "function") return reader(item);
|
|
7208
|
+
if (item === null || typeof item !== "object") return item;
|
|
7209
|
+
return item[reader];
|
|
7210
|
+
}
|
|
6908
7211
|
function isPrintable(event) {
|
|
6909
7212
|
return event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
6910
7213
|
}
|
|
@@ -8821,6 +9124,82 @@ function onBreakpoint(handler, options = {}) {
|
|
|
8821
9124
|
};
|
|
8822
9125
|
}
|
|
8823
9126
|
|
|
9127
|
+
// src/core/format.js
|
|
9128
|
+
var MIN_DIGITS = 0;
|
|
9129
|
+
var MAX_DIGITS = 20;
|
|
9130
|
+
var DEFAULT_MAX_DIGITS = 3;
|
|
9131
|
+
var numberFormatters = /* @__PURE__ */ new Map();
|
|
9132
|
+
function formatNumber(value, options = {}) {
|
|
9133
|
+
const number = toFiniteNumber(value);
|
|
9134
|
+
if (number === null) return "";
|
|
9135
|
+
const { locale = getLanguage(), decimals, minDecimals, maxDecimals, group = true } = options;
|
|
9136
|
+
return format(locale, digitOptions({ decimals, minDecimals, maxDecimals, group }), number);
|
|
9137
|
+
}
|
|
9138
|
+
function formatCurrency(value, currency, options = {}) {
|
|
9139
|
+
const number = toFiniteNumber(value);
|
|
9140
|
+
if (number === null) return "";
|
|
9141
|
+
const { locale = getLanguage(), decimals } = options;
|
|
9142
|
+
const code = currencyCode(currency);
|
|
9143
|
+
if (code === null) return formatNumber(number, { locale, decimals });
|
|
9144
|
+
const digits = digitOptions({ decimals, group: true });
|
|
9145
|
+
return format(locale, { ...digits, style: "currency", currency: code }, number);
|
|
9146
|
+
}
|
|
9147
|
+
function formatPercent2(value, options = {}) {
|
|
9148
|
+
const number = toFiniteNumber(value);
|
|
9149
|
+
if (number === null) return "";
|
|
9150
|
+
const { locale = getLanguage(), decimals = 0 } = options;
|
|
9151
|
+
return format(locale, { ...digitOptions({ decimals, group: true }), style: "percent" }, number);
|
|
9152
|
+
}
|
|
9153
|
+
function format(locale, options, value) {
|
|
9154
|
+
const key = `${locale}\0${JSON.stringify(options)}`;
|
|
9155
|
+
let formatter = numberFormatters.get(key);
|
|
9156
|
+
if (formatter === void 0) {
|
|
9157
|
+
formatter = createNumberFormat(locale, options);
|
|
9158
|
+
numberFormatters.set(key, formatter);
|
|
9159
|
+
}
|
|
9160
|
+
return formatter.format(value);
|
|
9161
|
+
}
|
|
9162
|
+
function createNumberFormat(locale, options) {
|
|
9163
|
+
try {
|
|
9164
|
+
return new Intl.NumberFormat(locale, options);
|
|
9165
|
+
} catch {
|
|
9166
|
+
try {
|
|
9167
|
+
return new Intl.NumberFormat(void 0, options);
|
|
9168
|
+
} catch {
|
|
9169
|
+
return new Intl.NumberFormat();
|
|
9170
|
+
}
|
|
9171
|
+
}
|
|
9172
|
+
}
|
|
9173
|
+
function digitOptions({ decimals, minDecimals, maxDecimals, group = true }) {
|
|
9174
|
+
const result = { useGrouping: group !== false };
|
|
9175
|
+
const min = digitCount(minDecimals ?? decimals);
|
|
9176
|
+
const max = digitCount(maxDecimals ?? decimals);
|
|
9177
|
+
if (min !== null) result.minimumFractionDigits = min;
|
|
9178
|
+
if (max !== null) {
|
|
9179
|
+
result.maximumFractionDigits = max;
|
|
9180
|
+
if (min !== null && min > max) result.minimumFractionDigits = max;
|
|
9181
|
+
} else if (min !== null) {
|
|
9182
|
+
result.maximumFractionDigits = Math.max(min, DEFAULT_MAX_DIGITS);
|
|
9183
|
+
}
|
|
9184
|
+
return result;
|
|
9185
|
+
}
|
|
9186
|
+
function digitCount(value) {
|
|
9187
|
+
if (value == null) return null;
|
|
9188
|
+
const number = Number(value);
|
|
9189
|
+
if (!Number.isFinite(number)) return null;
|
|
9190
|
+
return Math.min(MAX_DIGITS, Math.max(MIN_DIGITS, Math.round(number)));
|
|
9191
|
+
}
|
|
9192
|
+
function toFiniteNumber(value) {
|
|
9193
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
9194
|
+
const number = typeof value === "number" ? value : Number(value);
|
|
9195
|
+
return Number.isFinite(number) ? number : null;
|
|
9196
|
+
}
|
|
9197
|
+
function currencyCode(value) {
|
|
9198
|
+
if (typeof value !== "string") return null;
|
|
9199
|
+
const code = value.trim().toUpperCase();
|
|
9200
|
+
return /^[A-Z]{3}$/.test(code) ? code : null;
|
|
9201
|
+
}
|
|
9202
|
+
|
|
8824
9203
|
// src/components/table/sort.js
|
|
8825
9204
|
function compareStrings(left, right) {
|
|
8826
9205
|
return String(left).localeCompare(String(right));
|
|
@@ -8876,7 +9255,8 @@ var Table = class extends Component {
|
|
|
8876
9255
|
emptyText: null,
|
|
8877
9256
|
rowClass: null,
|
|
8878
9257
|
zebra: true,
|
|
8879
|
-
editMode: false
|
|
9258
|
+
editMode: false,
|
|
9259
|
+
hierarchy: false
|
|
8880
9260
|
};
|
|
8881
9261
|
/**
|
|
8882
9262
|
* Creates a table around a target or creates a new root when target is null.
|
|
@@ -8913,8 +9293,17 @@ var Table = class extends Component {
|
|
|
8913
9293
|
this._rovingKey = null;
|
|
8914
9294
|
this._rovingCell = null;
|
|
8915
9295
|
this._rovingMatched = false;
|
|
9296
|
+
this._hierarchy = normalizeHierarchy(this.options.hierarchy, this._columns);
|
|
9297
|
+
this._expanded = initialExpanded(this._hierarchy?.expanded);
|
|
8916
9298
|
if (this._sort && this.options.sortMode === "local") this._sortData();
|
|
9299
|
+
if (this._hierarchy?.expanded === true) {
|
|
9300
|
+
for (const id of hierarchyBranchIds(this._data, this.options.rowId, this._hierarchy.parentId)) {
|
|
9301
|
+
this._expanded.add(id);
|
|
9302
|
+
}
|
|
9303
|
+
}
|
|
9304
|
+
this._reconcileExpanded();
|
|
8917
9305
|
this._table = h("table", { class: "zx-table__table" });
|
|
9306
|
+
if (this._hierarchy) this._table.setAttribute("role", "treegrid");
|
|
8918
9307
|
this._colgroup = h("colgroup", { class: "zx-table__columns" });
|
|
8919
9308
|
this._thead = h("thead", { class: "zx-table__head" });
|
|
8920
9309
|
this._tbody = h("tbody", { class: "zx-table__body" });
|
|
@@ -8983,6 +9372,7 @@ var Table = class extends Component {
|
|
|
8983
9372
|
assertRows(rows);
|
|
8984
9373
|
this._data = [...rows];
|
|
8985
9374
|
if (this._sort && this.options.sortMode === "local") this._sortData();
|
|
9375
|
+
this._reconcileExpanded();
|
|
8986
9376
|
const selectionChanged = this._pruneSelection();
|
|
8987
9377
|
this._renderBody();
|
|
8988
9378
|
if (selectionChanged) this._emitSelectionChange();
|
|
@@ -9000,6 +9390,7 @@ var Table = class extends Component {
|
|
|
9000
9390
|
assertRows(rows);
|
|
9001
9391
|
this._data.push(...rows);
|
|
9002
9392
|
if (this._sort && this.options.sortMode === "local") this._sortData();
|
|
9393
|
+
this._reconcileExpanded();
|
|
9003
9394
|
this._renderBody();
|
|
9004
9395
|
this._emitDataChange();
|
|
9005
9396
|
return this;
|
|
@@ -9019,6 +9410,7 @@ var Table = class extends Component {
|
|
|
9019
9410
|
this._data[index] = row;
|
|
9020
9411
|
if (wasSelected) this._selected.add(this._idFor(row));
|
|
9021
9412
|
if (this._sort && this.options.sortMode === "local") this._sortData();
|
|
9413
|
+
this._reconcileExpanded();
|
|
9022
9414
|
this._renderBody();
|
|
9023
9415
|
if (wasSelected && !Object.is(id, this._idFor(row))) this._emitSelectionChange();
|
|
9024
9416
|
this._emitDataChange();
|
|
@@ -9036,6 +9428,7 @@ var Table = class extends Component {
|
|
|
9036
9428
|
const index = this._data.findIndex((candidate) => Object.is(this._idFor(candidate), id));
|
|
9037
9429
|
if (index < 0) return this;
|
|
9038
9430
|
this._data.splice(index, 1);
|
|
9431
|
+
this._reconcileExpanded();
|
|
9039
9432
|
const selectionChanged = this._selected.delete(id);
|
|
9040
9433
|
if (Object.is(this._selectionAnchorId, id)) this._selectionAnchorId = null;
|
|
9041
9434
|
this._renderBody();
|
|
@@ -9051,7 +9444,10 @@ var Table = class extends Component {
|
|
|
9051
9444
|
getRow(id) {
|
|
9052
9445
|
return this._data.find((row) => Object.is(this._idFor(row), id)) ?? null;
|
|
9053
9446
|
}
|
|
9054
|
-
/**
|
|
9447
|
+
/**
|
|
9448
|
+
* Returns a shallow copy of the current rows in display order.
|
|
9449
|
+
* @returns {TableRow[]} Current table rows.
|
|
9450
|
+
*/
|
|
9055
9451
|
getData() {
|
|
9056
9452
|
return [...this._data];
|
|
9057
9453
|
}
|
|
@@ -9065,6 +9461,7 @@ var Table = class extends Component {
|
|
|
9065
9461
|
empty() {
|
|
9066
9462
|
const selectionChanged = this._selected.size > 0;
|
|
9067
9463
|
this._data = [];
|
|
9464
|
+
this._expanded.clear();
|
|
9068
9465
|
this._selected.clear();
|
|
9069
9466
|
this._selectionAnchorId = null;
|
|
9070
9467
|
this._renderBody();
|
|
@@ -9099,7 +9496,10 @@ var Table = class extends Component {
|
|
|
9099
9496
|
if (!options.silent) this.emit("sort", { id, dir: direction });
|
|
9100
9497
|
return this;
|
|
9101
9498
|
}
|
|
9102
|
-
/**
|
|
9499
|
+
/**
|
|
9500
|
+
* Returns selected rows in current display order.
|
|
9501
|
+
* @returns {TableRow[]} Selected rows.
|
|
9502
|
+
*/
|
|
9103
9503
|
getSelection() {
|
|
9104
9504
|
return this._data.filter((row) => this._selected.has(this._idFor(row)));
|
|
9105
9505
|
}
|
|
@@ -9138,6 +9538,75 @@ var Table = class extends Component {
|
|
|
9138
9538
|
this._emitSelectionChange();
|
|
9139
9539
|
return this;
|
|
9140
9540
|
}
|
|
9541
|
+
/**
|
|
9542
|
+
* Toggles one hierarchy branch. A leaf or an id absent from the table is a no-op.
|
|
9543
|
+
* @param {unknown} id Row id.
|
|
9544
|
+
* @param {{silent?: boolean}} [options={}] Event behavior.
|
|
9545
|
+
* @returns {this}
|
|
9546
|
+
* @fires Table#rowtoggle
|
|
9547
|
+
*/
|
|
9548
|
+
toggleRow(id, options = {}) {
|
|
9549
|
+
return this._expanded.has(id) ? this.collapseRow(id, options) : this.expandRow(id, options);
|
|
9550
|
+
}
|
|
9551
|
+
/**
|
|
9552
|
+
* Expands one hierarchy branch.
|
|
9553
|
+
* @param {unknown} id Row id.
|
|
9554
|
+
* @param {{silent?: boolean}} [options={}] Event behavior.
|
|
9555
|
+
* @returns {this}
|
|
9556
|
+
* @fires Table#rowtoggle
|
|
9557
|
+
*/
|
|
9558
|
+
expandRow(id, options = {}) {
|
|
9559
|
+
return this._setRowExpanded(id, true, options);
|
|
9560
|
+
}
|
|
9561
|
+
/**
|
|
9562
|
+
* Collapses one hierarchy branch without discarding its descendants or their expansion state.
|
|
9563
|
+
* @param {unknown} id Row id.
|
|
9564
|
+
* @param {{silent?: boolean}} [options={}] Event behavior.
|
|
9565
|
+
* @returns {this}
|
|
9566
|
+
* @fires Table#rowtoggle
|
|
9567
|
+
*/
|
|
9568
|
+
collapseRow(id, options = {}) {
|
|
9569
|
+
return this._setRowExpanded(id, false, options);
|
|
9570
|
+
}
|
|
9571
|
+
/**
|
|
9572
|
+
* Expands every row that currently has children.
|
|
9573
|
+
* @param {{silent?: boolean}} [options={}] Event behavior. When not silent, one `rowtoggle`
|
|
9574
|
+
* event is emitted per branch whose state changed.
|
|
9575
|
+
* @returns {this}
|
|
9576
|
+
*/
|
|
9577
|
+
expandAll(options = {}) {
|
|
9578
|
+
if (!this._hierarchy) return this;
|
|
9579
|
+
const branchIds = hierarchyBranchIds(this._data, this.options.rowId, this._hierarchy.parentId);
|
|
9580
|
+
const changed = branchIds.filter((id) => !this._expanded.has(id));
|
|
9581
|
+
for (const id of changed) this._expanded.add(id);
|
|
9582
|
+
if (changed.length) this._renderBody();
|
|
9583
|
+
if (!options.silent) {
|
|
9584
|
+
for (const id of changed) this._emitRowToggle(id, true);
|
|
9585
|
+
}
|
|
9586
|
+
return this;
|
|
9587
|
+
}
|
|
9588
|
+
/**
|
|
9589
|
+
* Collapses every hierarchy branch.
|
|
9590
|
+
* @param {{silent?: boolean}} [options={}] Event behavior.
|
|
9591
|
+
* @returns {this}
|
|
9592
|
+
*/
|
|
9593
|
+
collapseAll(options = {}) {
|
|
9594
|
+
if (!this._hierarchy || this._expanded.size === 0) return this;
|
|
9595
|
+
const changed = [...this._expanded];
|
|
9596
|
+
this._expanded.clear();
|
|
9597
|
+
this._renderBody();
|
|
9598
|
+
if (!options.silent) {
|
|
9599
|
+
for (const id of changed) this._emitRowToggle(id, false);
|
|
9600
|
+
}
|
|
9601
|
+
return this;
|
|
9602
|
+
}
|
|
9603
|
+
/**
|
|
9604
|
+
* Returns expanded branch ids in current flat-data order.
|
|
9605
|
+
* @returns {unknown[]} Expanded branch ids.
|
|
9606
|
+
*/
|
|
9607
|
+
getExpanded() {
|
|
9608
|
+
return this._data.map((row) => this._idFor(row)).filter((id) => this._expanded.has(id));
|
|
9609
|
+
}
|
|
9141
9610
|
/**
|
|
9142
9611
|
* Opens the editor for a cell. No-op unless the table runs with `editMode` and the column
|
|
9143
9612
|
* resolves to an editable type for that row. In `'row'` mode every editable cell of the row is
|
|
@@ -9245,11 +9714,17 @@ var Table = class extends Component {
|
|
|
9245
9714
|
this._abortEdit({ focus: true });
|
|
9246
9715
|
return this;
|
|
9247
9716
|
}
|
|
9248
|
-
/**
|
|
9717
|
+
/**
|
|
9718
|
+
* Reports whether a cell or row is currently open for editing.
|
|
9719
|
+
* @returns {boolean} Whether an edit is open.
|
|
9720
|
+
*/
|
|
9249
9721
|
isEditing() {
|
|
9250
9722
|
return this._edit !== null;
|
|
9251
9723
|
}
|
|
9252
|
-
/**
|
|
9724
|
+
/**
|
|
9725
|
+
* Returns the row id and focused column id for the open edit.
|
|
9726
|
+
* @returns {{id: unknown, columnId: string}|null} Editing location, or null.
|
|
9727
|
+
*/
|
|
9253
9728
|
getEditing() {
|
|
9254
9729
|
return this._edit ? { id: this._edit.id, columnId: this._edit.columnId } : null;
|
|
9255
9730
|
}
|
|
@@ -9258,7 +9733,31 @@ var Table = class extends Component {
|
|
|
9258
9733
|
* @returns {TableRow[]}
|
|
9259
9734
|
*/
|
|
9260
9735
|
_visibleData() {
|
|
9261
|
-
return this.
|
|
9736
|
+
return this._visibleEntries().map((entry) => entry.row);
|
|
9737
|
+
}
|
|
9738
|
+
/** @returns {TableHierarchyEntry[]} Rows visible after hierarchy expansion, before growing. */
|
|
9739
|
+
_projectedData() {
|
|
9740
|
+
if (!this._hierarchy) {
|
|
9741
|
+
return this._data.map((row, index) => ({
|
|
9742
|
+
row,
|
|
9743
|
+
id: this._idFor(row),
|
|
9744
|
+
index,
|
|
9745
|
+
depth: 0,
|
|
9746
|
+
hasChildren: false,
|
|
9747
|
+
parentId: null
|
|
9748
|
+
}));
|
|
9749
|
+
}
|
|
9750
|
+
return projectHierarchyRows(
|
|
9751
|
+
this._data,
|
|
9752
|
+
this.options.rowId,
|
|
9753
|
+
this._hierarchy.parentId,
|
|
9754
|
+
this._expanded
|
|
9755
|
+
);
|
|
9756
|
+
}
|
|
9757
|
+
/** @returns {TableHierarchyEntry[]} Rows that currently have DOM entries. */
|
|
9758
|
+
_visibleEntries() {
|
|
9759
|
+
const projected = this._projectedData();
|
|
9760
|
+
return this._rendered === Infinity ? projected : projected.slice(0, this._rendered);
|
|
9262
9761
|
}
|
|
9263
9762
|
/**
|
|
9264
9763
|
* Renders the next batch of rows.
|
|
@@ -9270,10 +9769,11 @@ var Table = class extends Component {
|
|
|
9270
9769
|
if (!this._growStep) return this;
|
|
9271
9770
|
const step = Number(count) > 0 ? Math.trunc(Number(count)) : this._growStep;
|
|
9272
9771
|
const before = this._rendered;
|
|
9273
|
-
|
|
9772
|
+
const total = this._projectedData().length;
|
|
9773
|
+
this._rendered = Math.min(total, this._rendered + step);
|
|
9274
9774
|
if (this._rendered === before) return this;
|
|
9275
9775
|
this._renderBody();
|
|
9276
|
-
this.emit("grow", { rendered: this._rendered, total
|
|
9776
|
+
this.emit("grow", { rendered: this._rendered, total });
|
|
9277
9777
|
return this;
|
|
9278
9778
|
}
|
|
9279
9779
|
/**
|
|
@@ -9282,14 +9782,14 @@ var Table = class extends Component {
|
|
|
9282
9782
|
* @fires Table#grow
|
|
9283
9783
|
*/
|
|
9284
9784
|
showAll() {
|
|
9285
|
-
return this.growBy(this.
|
|
9785
|
+
return this.growBy(this._projectedData().length);
|
|
9286
9786
|
}
|
|
9287
9787
|
/**
|
|
9288
9788
|
* How many rows are currently rendered.
|
|
9289
9789
|
* @returns {number}
|
|
9290
9790
|
*/
|
|
9291
9791
|
getRenderedCount() {
|
|
9292
|
-
return Math.min(this._rendered, this.
|
|
9792
|
+
return Math.min(this._rendered, this._projectedData().length);
|
|
9293
9793
|
}
|
|
9294
9794
|
/**
|
|
9295
9795
|
* Keeps the "show more" control in step with what is rendered.
|
|
@@ -9301,7 +9801,7 @@ var Table = class extends Component {
|
|
|
9301
9801
|
*/
|
|
9302
9802
|
_syncGrowing() {
|
|
9303
9803
|
if (!this._growStep) return;
|
|
9304
|
-
const remaining = this.
|
|
9804
|
+
const remaining = this._projectedData().length - this.getRenderedCount();
|
|
9305
9805
|
if (remaining <= 0) {
|
|
9306
9806
|
this._more?.remove();
|
|
9307
9807
|
this._more = null;
|
|
@@ -9372,10 +9872,11 @@ var Table = class extends Component {
|
|
|
9372
9872
|
* @returns {void}
|
|
9373
9873
|
*/
|
|
9374
9874
|
_applyStackedRoles(stacked) {
|
|
9375
|
-
const parts = [[this._table, "table"], [this._thead, "rowgroup"], [this._tbody, "rowgroup"]];
|
|
9875
|
+
const parts = [[this._table, this._hierarchy ? "treegrid" : "table"], [this._thead, "rowgroup"], [this._tbody, "rowgroup"]];
|
|
9376
9876
|
for (const [element, role] of parts) {
|
|
9377
9877
|
if (!element) continue;
|
|
9378
9878
|
if (stacked) element.setAttribute("role", role);
|
|
9879
|
+
else if (element === this._table && this._hierarchy) element.setAttribute("role", "treegrid");
|
|
9379
9880
|
else element.removeAttribute("role");
|
|
9380
9881
|
}
|
|
9381
9882
|
for (const [selector, role] of [["tr", "row"], ["td", "cell"], ["th", "columnheader"]]) {
|
|
@@ -9406,9 +9907,9 @@ var Table = class extends Component {
|
|
|
9406
9907
|
}
|
|
9407
9908
|
/** @returns {void} */
|
|
9408
9909
|
_renderColumns() {
|
|
9409
|
-
const
|
|
9910
|
+
const fragment2 = document.createDocumentFragment();
|
|
9410
9911
|
if (this.options.selectable === "multi") {
|
|
9411
|
-
|
|
9912
|
+
fragment2.append(h("col", { class: "zx-table__selection-column" }));
|
|
9412
9913
|
}
|
|
9413
9914
|
const fractions = this._columns.map((column) => parseFraction(column.width));
|
|
9414
9915
|
const total = fractions.reduce((sum, value) => sum + value, 0);
|
|
@@ -9417,9 +9918,9 @@ var Table = class extends Component {
|
|
|
9417
9918
|
const col = h("col");
|
|
9418
9919
|
if (fractions[index] > 0) col.style.width = `${fractions[index] / total * 100}%`;
|
|
9419
9920
|
else if (column.width && column.width !== "auto") col.style.width = String(column.width);
|
|
9420
|
-
|
|
9921
|
+
fragment2.append(col);
|
|
9421
9922
|
});
|
|
9422
|
-
this._colgroup.replaceChildren(
|
|
9923
|
+
this._colgroup.replaceChildren(fragment2);
|
|
9423
9924
|
}
|
|
9424
9925
|
/** @returns {void} */
|
|
9425
9926
|
_renderHeader() {
|
|
@@ -9452,7 +9953,8 @@ var Table = class extends Component {
|
|
|
9452
9953
|
this._headers = /* @__PURE__ */ new Map();
|
|
9453
9954
|
for (const column of this._columns) {
|
|
9454
9955
|
const th = h("th", { scope: "col" });
|
|
9455
|
-
|
|
9956
|
+
const align = column.align ?? (isNumericColumn(column) ? "end" : null);
|
|
9957
|
+
if (align) th.dataset.align = align;
|
|
9456
9958
|
if (column.headerTitle) th.title = column.headerTitle;
|
|
9457
9959
|
if (column.sortable) {
|
|
9458
9960
|
const button = h("button", {
|
|
@@ -9476,13 +9978,15 @@ var Table = class extends Component {
|
|
|
9476
9978
|
/** @returns {void} @fires Table#editcancel */
|
|
9477
9979
|
_renderBody() {
|
|
9478
9980
|
this._abortEdit({ focus: false });
|
|
9479
|
-
const
|
|
9981
|
+
const fragment2 = document.createDocumentFragment();
|
|
9480
9982
|
this._rowMeta = /* @__PURE__ */ new WeakMap();
|
|
9481
9983
|
this._rowElements = /* @__PURE__ */ new Map();
|
|
9482
9984
|
this._rovingCell = null;
|
|
9483
9985
|
this._rovingMatched = false;
|
|
9986
|
+
const projected = this._projectedData();
|
|
9987
|
+
this._hierarchyMeta = new Map(projected.map((entry) => [entry.id, entry]));
|
|
9484
9988
|
if (this._data.length === 0) {
|
|
9485
|
-
|
|
9989
|
+
fragment2.append(h(
|
|
9486
9990
|
"tr",
|
|
9487
9991
|
{ class: "zx-table__empty-row" },
|
|
9488
9992
|
h("td", {
|
|
@@ -9491,10 +9995,12 @@ var Table = class extends Component {
|
|
|
9491
9995
|
}, this._emptyContent())
|
|
9492
9996
|
));
|
|
9493
9997
|
} else {
|
|
9494
|
-
this.
|
|
9998
|
+
this._visibleEntries().forEach((entry, visibleIndex) => {
|
|
9999
|
+
fragment2.append(this._createRow(entry.row, entry.index, { ...entry, visibleIndex }));
|
|
10000
|
+
});
|
|
9495
10001
|
}
|
|
9496
|
-
this._tbody.replaceChildren(
|
|
9497
|
-
if (this._growStep) this._table.setAttribute("aria-rowcount", String(
|
|
10002
|
+
this._tbody.replaceChildren(fragment2);
|
|
10003
|
+
if (this._growStep) this._table.setAttribute("aria-rowcount", String(projected.length + 1));
|
|
9498
10004
|
this._syncSelectAll();
|
|
9499
10005
|
this._syncGrowing();
|
|
9500
10006
|
if (this._stacked) this._applyStackedRoles(true);
|
|
@@ -9513,18 +10019,27 @@ var Table = class extends Component {
|
|
|
9513
10019
|
if (empty == null) return this.msg("table.empty");
|
|
9514
10020
|
return typeof empty === "function" ? empty() : empty;
|
|
9515
10021
|
}
|
|
9516
|
-
/**
|
|
9517
|
-
|
|
10022
|
+
/**
|
|
10023
|
+
* @param {TableRow} row
|
|
10024
|
+
* @param {number} index
|
|
10025
|
+
* @param {TableHierarchyEntry & {visibleIndex: number}} tree
|
|
10026
|
+
* @returns {HTMLTableRowElement}
|
|
10027
|
+
*/
|
|
10028
|
+
_createRow(row, index, tree) {
|
|
9518
10029
|
const id = this._idFor(row);
|
|
9519
10030
|
const tr = (
|
|
9520
10031
|
/** @type {HTMLTableRowElement} */
|
|
9521
10032
|
h("tr", { dataset: { row: "" } })
|
|
9522
10033
|
);
|
|
9523
|
-
if (this._growStep) tr.setAttribute("aria-rowindex", String(
|
|
10034
|
+
if (this._growStep) tr.setAttribute("aria-rowindex", String(tree.visibleIndex + 2));
|
|
9524
10035
|
const rowClass = typeof this.options.rowClass === "function" ? this.options.rowClass(row) : "";
|
|
9525
10036
|
if (rowClass) tr.classList.add(...String(rowClass).split(/\s+/).filter(Boolean));
|
|
9526
10037
|
if (this.options.selectable !== false) tr.setAttribute("aria-selected", String(this._selected.has(id)));
|
|
9527
|
-
this.
|
|
10038
|
+
if (this._hierarchy) {
|
|
10039
|
+
tr.setAttribute("aria-level", String(tree.depth + 1));
|
|
10040
|
+
if (tree.hasChildren) tr.setAttribute("aria-expanded", String(this._expanded.has(id)));
|
|
10041
|
+
}
|
|
10042
|
+
this._rowMeta.set(tr, { row, id, index, visibleIndex: tree.visibleIndex });
|
|
9528
10043
|
const elements = this._rowElements.get(id) ?? [];
|
|
9529
10044
|
elements.push(tr);
|
|
9530
10045
|
this._rowElements.set(id, elements);
|
|
@@ -9542,7 +10057,8 @@ var Table = class extends Component {
|
|
|
9542
10057
|
/** @type {HTMLTableCellElement} */
|
|
9543
10058
|
h("td")
|
|
9544
10059
|
);
|
|
9545
|
-
|
|
10060
|
+
const align = column.align ?? (isNumericColumn(column) ? "end" : null);
|
|
10061
|
+
if (align) cell.dataset.align = align;
|
|
9546
10062
|
cell.dataset.label = column.label;
|
|
9547
10063
|
if (column.popin === false) cell.dataset.popin = "false";
|
|
9548
10064
|
if (this._editMode !== false) {
|
|
@@ -9553,7 +10069,7 @@ var Table = class extends Component {
|
|
|
9553
10069
|
this._offerRovingCell(cell, id, column.id);
|
|
9554
10070
|
}
|
|
9555
10071
|
}
|
|
9556
|
-
this._renderCellContent(cell, column, row, index);
|
|
10072
|
+
this._renderCellContent(cell, column, row, index, tree);
|
|
9557
10073
|
tr.append(cell);
|
|
9558
10074
|
});
|
|
9559
10075
|
return tr;
|
|
@@ -9564,13 +10080,33 @@ var Table = class extends Component {
|
|
|
9564
10080
|
* @param {TableColumn} column Column definition.
|
|
9565
10081
|
* @param {TableRow} row Row data.
|
|
9566
10082
|
* @param {number} index Row index.
|
|
10083
|
+
* @param {TableHierarchyEntry} [tree] Hierarchy metadata for the row.
|
|
9567
10084
|
* @returns {void}
|
|
9568
10085
|
*/
|
|
9569
|
-
_renderCellContent(cell, column, row, index) {
|
|
9570
|
-
const value =
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
else
|
|
10086
|
+
_renderCellContent(cell, column, row, index, tree = this._hierarchyMeta?.get(this._idFor(row))) {
|
|
10087
|
+
const value = formatTableCell(column, row, index);
|
|
10088
|
+
const content = h("span", { class: "zx-table__cell-content" });
|
|
10089
|
+
if (value && typeof value === "object" && typeof value.nodeType === "number") content.append(value);
|
|
10090
|
+
else if (value != null) content.append(document.createTextNode(String(value)));
|
|
10091
|
+
if (!this._hierarchy || column.id !== this._hierarchy.column || !tree) {
|
|
10092
|
+
cell.replaceChildren(...content.childNodes);
|
|
10093
|
+
return;
|
|
10094
|
+
}
|
|
10095
|
+
const branch = h("span", { class: "zx-table__tree-cell" });
|
|
10096
|
+
branch.style.setProperty("--zx-table-depth", String(tree.depth));
|
|
10097
|
+
if (tree.hasChildren) {
|
|
10098
|
+
const expanded = this._expanded.has(tree.id);
|
|
10099
|
+
branch.append(h("button", {
|
|
10100
|
+
class: "zx-table__tree-toggle",
|
|
10101
|
+
type: "button",
|
|
10102
|
+
ariaExpanded: String(expanded),
|
|
10103
|
+
ariaLabel: `${expanded ? "Collapse" : "Expand"} row ${index + 1}`
|
|
10104
|
+
}, icon(expanded ? "chevron-down" : "chevron-right", { size: 14 })));
|
|
10105
|
+
} else {
|
|
10106
|
+
branch.append(h("span", { class: "zx-table__tree-spacer", ariaHidden: "true" }));
|
|
10107
|
+
}
|
|
10108
|
+
branch.append(content);
|
|
10109
|
+
cell.replaceChildren(branch);
|
|
9574
10110
|
}
|
|
9575
10111
|
/** @param {MouseEvent} event @returns {void} */
|
|
9576
10112
|
_handleBodyClick(event) {
|
|
@@ -9583,7 +10119,15 @@ var Table = class extends Component {
|
|
|
9583
10119
|
if (!rowElement || !this._tbody.contains(rowElement)) return;
|
|
9584
10120
|
const meta = this._rowMeta.get(rowElement);
|
|
9585
10121
|
if (!meta) return;
|
|
9586
|
-
if (this._edit)
|
|
10122
|
+
if (this._edit) {
|
|
10123
|
+
this.commitEdit();
|
|
10124
|
+
if (this._edit) return;
|
|
10125
|
+
}
|
|
10126
|
+
const disclosure = target.closest(".zx-table__tree-toggle");
|
|
10127
|
+
if (disclosure) {
|
|
10128
|
+
this.toggleRow(meta.id);
|
|
10129
|
+
return;
|
|
10130
|
+
}
|
|
9587
10131
|
const checkbox = target.closest(".zx-table__row-checkbox");
|
|
9588
10132
|
if (checkbox && this.options.selectable === "multi") {
|
|
9589
10133
|
this._toggleMultiSelection(
|
|
@@ -9679,7 +10223,15 @@ var Table = class extends Component {
|
|
|
9679
10223
|
return entry;
|
|
9680
10224
|
}
|
|
9681
10225
|
if (type === "number") {
|
|
9682
|
-
const
|
|
10226
|
+
const locale = resolveColumnOption(column.locale, row);
|
|
10227
|
+
const decimals = resolveColumnDecimals(column, row);
|
|
10228
|
+
const suffix = column.type === "currency" ? resolveColumnOption(column.currency, row) : column.type === "unit" ? resolveColumnOption(column.unit, row) : null;
|
|
10229
|
+
const inferred = {
|
|
10230
|
+
...locale != null && !Object.prototype.hasOwnProperty.call(props, "locale") ? { locale } : {},
|
|
10231
|
+
...decimals != null && !Object.prototype.hasOwnProperty.call(props, "precision") ? { precision: decimals } : {},
|
|
10232
|
+
...suffix != null && !Object.prototype.hasOwnProperty.call(props, "unit") ? { unit: String(suffix) } : {}
|
|
10233
|
+
};
|
|
10234
|
+
const field = new NumberField(null, { ...inferred, ...props, value: value ?? null });
|
|
9683
10235
|
host.append(field.el);
|
|
9684
10236
|
entry.component = field;
|
|
9685
10237
|
entry.control = field.getInput();
|
|
@@ -9910,9 +10462,12 @@ var Table = class extends Component {
|
|
|
9910
10462
|
_nextEditableCell(rowIndex, columnId, direction) {
|
|
9911
10463
|
const columnCount = this._columns.length;
|
|
9912
10464
|
if (rowIndex < 0 || columnCount === 0) return null;
|
|
9913
|
-
|
|
10465
|
+
const visible = this._visibleData();
|
|
10466
|
+
const startingId = this._idFor(this._data[rowIndex]);
|
|
10467
|
+
let row = visible.findIndex((candidate) => Object.is(this._idFor(candidate), startingId));
|
|
10468
|
+
if (row < 0) return null;
|
|
9914
10469
|
let column = this._columns.findIndex((candidate) => candidate.id === columnId);
|
|
9915
|
-
const limit =
|
|
10470
|
+
const limit = visible.length * columnCount + columnCount;
|
|
9916
10471
|
for (let step = 0; step < limit; step += 1) {
|
|
9917
10472
|
column += direction;
|
|
9918
10473
|
if (column >= columnCount) {
|
|
@@ -9922,11 +10477,15 @@ var Table = class extends Component {
|
|
|
9922
10477
|
row -= 1;
|
|
9923
10478
|
column = columnCount - 1;
|
|
9924
10479
|
}
|
|
9925
|
-
if (row < 0 || row >=
|
|
9926
|
-
const candidateRow =
|
|
10480
|
+
if (row < 0 || row >= visible.length) return null;
|
|
10481
|
+
const candidateRow = visible[row];
|
|
9927
10482
|
const candidateColumn = this._columns[column];
|
|
9928
10483
|
if (resolveEditable(candidateColumn, candidateRow) !== false) {
|
|
9929
|
-
return {
|
|
10484
|
+
return {
|
|
10485
|
+
id: this._idFor(candidateRow),
|
|
10486
|
+
columnId: candidateColumn.id,
|
|
10487
|
+
index: this._data.indexOf(candidateRow)
|
|
10488
|
+
};
|
|
9930
10489
|
}
|
|
9931
10490
|
}
|
|
9932
10491
|
return null;
|
|
@@ -9993,14 +10552,16 @@ var Table = class extends Component {
|
|
|
9993
10552
|
const message = this.msg(key);
|
|
9994
10553
|
return message === key ? fallback : message;
|
|
9995
10554
|
}
|
|
9996
|
-
/** @param {{row: TableRow, id: unknown, index: number}} meta @param {boolean} checked @param {boolean} shiftKey @returns {void} */
|
|
10555
|
+
/** @param {{row: TableRow, id: unknown, index: number, visibleIndex?: number}} meta @param {boolean} checked @param {boolean} shiftKey @returns {void} */
|
|
9997
10556
|
_toggleMultiSelection(meta, checked, shiftKey) {
|
|
9998
|
-
const
|
|
10557
|
+
const visible = this._visibleData();
|
|
10558
|
+
const anchorIndex = visible.findIndex((row) => Object.is(this._idFor(row), this._selectionAnchorId));
|
|
9999
10559
|
if (shiftKey && anchorIndex >= 0) {
|
|
10000
|
-
const
|
|
10001
|
-
const
|
|
10560
|
+
const currentIndex = meta.visibleIndex ?? visible.findIndex((row) => Object.is(this._idFor(row), meta.id));
|
|
10561
|
+
const start = Math.min(anchorIndex, currentIndex);
|
|
10562
|
+
const end = Math.max(anchorIndex, currentIndex);
|
|
10002
10563
|
for (let index = start; index <= end; index += 1) {
|
|
10003
|
-
const id = this._idFor(
|
|
10564
|
+
const id = this._idFor(visible[index]);
|
|
10004
10565
|
if (checked) this._selected.add(id);
|
|
10005
10566
|
else this._selected.delete(id);
|
|
10006
10567
|
}
|
|
@@ -10051,7 +10612,34 @@ var Table = class extends Component {
|
|
|
10051
10612
|
const column = this._columns.find((candidate) => candidate.id === this._sort.id);
|
|
10052
10613
|
if (!column) return;
|
|
10053
10614
|
const getValue = typeof column.sortValue === "function" ? column.sortValue : (row) => row?.[column.id];
|
|
10054
|
-
this._data = sortRows(this._data, getValue, this._sort.dir);
|
|
10615
|
+
this._data = this._hierarchy ? sortHierarchyRows(this._data, this.options.rowId, this._hierarchy.parentId, getValue, this._sort.dir) : sortRows(this._data, getValue, this._sort.dir);
|
|
10616
|
+
}
|
|
10617
|
+
/** @param {unknown} id @param {boolean} expanded @param {{silent?: boolean}} options @returns {this} */
|
|
10618
|
+
_setRowExpanded(id, expanded, options) {
|
|
10619
|
+
if (!this._hierarchy) return this;
|
|
10620
|
+
const branchIds = hierarchyBranchIds(this._data, this.options.rowId, this._hierarchy.parentId);
|
|
10621
|
+
if (!branchIds.some((candidate) => Object.is(candidate, id))) return this;
|
|
10622
|
+
if (this._expanded.has(id) === expanded) return this;
|
|
10623
|
+
if (expanded) this._expanded.add(id);
|
|
10624
|
+
else this._expanded.delete(id);
|
|
10625
|
+
this._renderBody();
|
|
10626
|
+
if (!options.silent) this._emitRowToggle(id, expanded);
|
|
10627
|
+
return this;
|
|
10628
|
+
}
|
|
10629
|
+
/** @param {unknown} id @param {boolean} expanded @returns {void} */
|
|
10630
|
+
_emitRowToggle(id, expanded) {
|
|
10631
|
+
const row = this.getRow(id);
|
|
10632
|
+
if (row) this.emit("rowtoggle", { row, id, expanded });
|
|
10633
|
+
}
|
|
10634
|
+
/** Keeps the reader's current expansion state limited to branches still present in the data. */
|
|
10635
|
+
_reconcileExpanded() {
|
|
10636
|
+
if (!this._hierarchy) {
|
|
10637
|
+
this._expanded.clear();
|
|
10638
|
+
return;
|
|
10639
|
+
}
|
|
10640
|
+
const branches = hierarchyBranchIds(this._data, this.options.rowId, this._hierarchy.parentId);
|
|
10641
|
+
const valid = new Set(branches);
|
|
10642
|
+
for (const id of this._expanded) if (!valid.has(id)) this._expanded.delete(id);
|
|
10055
10643
|
}
|
|
10056
10644
|
/** @returns {boolean} Whether selection changed. */
|
|
10057
10645
|
_pruneSelection() {
|
|
@@ -10075,6 +10663,65 @@ var Table = class extends Component {
|
|
|
10075
10663
|
this.emit("datachange", { rows: this.getData() });
|
|
10076
10664
|
}
|
|
10077
10665
|
};
|
|
10666
|
+
function formatTableCell(column, row, index = 0) {
|
|
10667
|
+
if (typeof column?.render === "function") return column.render(row, index);
|
|
10668
|
+
const value = row?.[column?.id];
|
|
10669
|
+
const type = column?.type ?? "text";
|
|
10670
|
+
if (type === "text") return value;
|
|
10671
|
+
const locale = resolveColumnOption(column.locale, row);
|
|
10672
|
+
const decimals = resolveColumnDecimals(column, row);
|
|
10673
|
+
const options = {
|
|
10674
|
+
...locale != null ? { locale: String(locale) } : {},
|
|
10675
|
+
...decimals != null ? { decimals } : {}
|
|
10676
|
+
};
|
|
10677
|
+
if (type === "number") return formatNumber(value, options);
|
|
10678
|
+
if (type === "currency") {
|
|
10679
|
+
const currency = resolveColumnOption(column.currency, row);
|
|
10680
|
+
return formatCurrency(value, currency == null ? null : String(currency), options);
|
|
10681
|
+
}
|
|
10682
|
+
if (type === "percent") return formatPercent2(value, options);
|
|
10683
|
+
if (type === "unit") {
|
|
10684
|
+
const number = formatNumber(value, options);
|
|
10685
|
+
const unit = resolveColumnOption(column.unit, row);
|
|
10686
|
+
return number && unit != null && String(unit) !== "" ? `${number}\xA0${unit}` : number;
|
|
10687
|
+
}
|
|
10688
|
+
return value;
|
|
10689
|
+
}
|
|
10690
|
+
function projectHierarchyRows(rows, rowId, parentId, expanded = []) {
|
|
10691
|
+
const forest = hierarchyForest(rows, rowId, parentId);
|
|
10692
|
+
const open = expanded instanceof Set ? expanded : new Set(expanded);
|
|
10693
|
+
const result = [];
|
|
10694
|
+
const stack = forest.roots.slice().reverse().map((node) => ({ node, depth: 0 }));
|
|
10695
|
+
while (stack.length) {
|
|
10696
|
+
const { node, depth } = stack.pop();
|
|
10697
|
+
result.push({
|
|
10698
|
+
row: node.row,
|
|
10699
|
+
id: node.id,
|
|
10700
|
+
index: node.index,
|
|
10701
|
+
depth,
|
|
10702
|
+
hasChildren: node.children.length > 0,
|
|
10703
|
+
parentId: node.parent?.id ?? null
|
|
10704
|
+
});
|
|
10705
|
+
if (!open.has(node.id)) continue;
|
|
10706
|
+
for (let index = node.children.length - 1; index >= 0; index -= 1) {
|
|
10707
|
+
stack.push({ node: node.children[index], depth: depth + 1 });
|
|
10708
|
+
}
|
|
10709
|
+
}
|
|
10710
|
+
return result;
|
|
10711
|
+
}
|
|
10712
|
+
function sortHierarchyRows(rows, rowId, parentId, getValue, direction) {
|
|
10713
|
+
const forest = hierarchyForest(rows, rowId, parentId);
|
|
10714
|
+
const sorted = (nodes) => sortRows(nodes, (node) => getValue(node.row), direction);
|
|
10715
|
+
const result = [];
|
|
10716
|
+
const stack = sorted(forest.roots).reverse();
|
|
10717
|
+
while (stack.length) {
|
|
10718
|
+
const node = stack.pop();
|
|
10719
|
+
result.push(node.row);
|
|
10720
|
+
const children = sorted(node.children);
|
|
10721
|
+
for (let index = children.length - 1; index >= 0; index -= 1) stack.push(children[index]);
|
|
10722
|
+
}
|
|
10723
|
+
return result;
|
|
10724
|
+
}
|
|
10078
10725
|
function resolveEditable(column, row) {
|
|
10079
10726
|
if (!column || typeof column !== "object") return false;
|
|
10080
10727
|
if (typeof column.editor === "function") {
|
|
@@ -10082,7 +10729,7 @@ function resolveEditable(column, row) {
|
|
|
10082
10729
|
return "custom";
|
|
10083
10730
|
}
|
|
10084
10731
|
const declared = typeof column.editable === "function" ? column.editable(row) : column.editable;
|
|
10085
|
-
if (declared === true) return "text";
|
|
10732
|
+
if (declared === true) return isNumericColumn(column) ? "number" : "text";
|
|
10086
10733
|
if (typeof declared === "string" && EDITOR_TYPES.has(declared)) return (
|
|
10087
10734
|
/** @type {TableEditorType} */
|
|
10088
10735
|
declared
|
|
@@ -10131,6 +10778,71 @@ function firstValidationError(values, columns, row, fallbackMessage = "Invalid v
|
|
|
10131
10778
|
}
|
|
10132
10779
|
return null;
|
|
10133
10780
|
}
|
|
10781
|
+
function resolveColumnOption(option, row) {
|
|
10782
|
+
return typeof option === "function" ? option(row) : option;
|
|
10783
|
+
}
|
|
10784
|
+
function resolveColumnDecimals(column, row) {
|
|
10785
|
+
const value = resolveColumnOption(column?.decimals, row);
|
|
10786
|
+
if (value == null) return null;
|
|
10787
|
+
const number = Number(value);
|
|
10788
|
+
return Number.isFinite(number) ? Math.max(0, Math.trunc(number)) : null;
|
|
10789
|
+
}
|
|
10790
|
+
function isNumericColumn(column) {
|
|
10791
|
+
return ["number", "currency", "percent", "unit"].includes(column?.type);
|
|
10792
|
+
}
|
|
10793
|
+
function hierarchyForest(rows, rowId, parentId) {
|
|
10794
|
+
const nodes = (Array.isArray(rows) ? rows : []).map((row, index) => ({
|
|
10795
|
+
row,
|
|
10796
|
+
id: readRowValue(rowId, row),
|
|
10797
|
+
index,
|
|
10798
|
+
parent: null,
|
|
10799
|
+
children: []
|
|
10800
|
+
}));
|
|
10801
|
+
const byId = /* @__PURE__ */ new Map();
|
|
10802
|
+
for (const node of nodes) if (!byId.has(node.id)) byId.set(node.id, node);
|
|
10803
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
10804
|
+
for (const node of nodes) {
|
|
10805
|
+
const parent = byId.get(readRowValue(parentId, node.row)) ?? null;
|
|
10806
|
+
parentOf.set(node, parent === node ? null : parent);
|
|
10807
|
+
}
|
|
10808
|
+
for (const node of nodes) {
|
|
10809
|
+
const seen = /* @__PURE__ */ new Set([node]);
|
|
10810
|
+
let cursor = parentOf.get(node) ?? null;
|
|
10811
|
+
while (cursor) {
|
|
10812
|
+
if (seen.has(cursor)) {
|
|
10813
|
+
parentOf.set(cursor, null);
|
|
10814
|
+
break;
|
|
10815
|
+
}
|
|
10816
|
+
seen.add(cursor);
|
|
10817
|
+
cursor = parentOf.get(cursor) ?? null;
|
|
10818
|
+
}
|
|
10819
|
+
}
|
|
10820
|
+
const roots = [];
|
|
10821
|
+
for (const node of nodes) {
|
|
10822
|
+
node.parent = parentOf.get(node) ?? null;
|
|
10823
|
+
if (node.parent) node.parent.children.push(node);
|
|
10824
|
+
else roots.push(node);
|
|
10825
|
+
}
|
|
10826
|
+
return { roots, nodes };
|
|
10827
|
+
}
|
|
10828
|
+
function hierarchyBranchIds(rows, rowId, parentId) {
|
|
10829
|
+
return hierarchyForest(rows, rowId, parentId).nodes.filter((node) => node.children.length > 0).map((node) => node.id);
|
|
10830
|
+
}
|
|
10831
|
+
function readRowValue(accessor, row) {
|
|
10832
|
+
return typeof accessor === "function" ? accessor(row) : row?.[accessor];
|
|
10833
|
+
}
|
|
10834
|
+
function normalizeHierarchy(option, columns) {
|
|
10835
|
+
if (!option || typeof option !== "object") return null;
|
|
10836
|
+
const parentId = option.parentId;
|
|
10837
|
+
if (typeof parentId !== "string" && typeof parentId !== "function") return null;
|
|
10838
|
+
const requested = typeof option.column === "string" ? option.column : null;
|
|
10839
|
+
const column = columns.some((candidate) => candidate.id === requested) ? requested : columns[0]?.id;
|
|
10840
|
+
if (!column) return null;
|
|
10841
|
+
return { ...option, parentId, column };
|
|
10842
|
+
}
|
|
10843
|
+
function initialExpanded(option) {
|
|
10844
|
+
return new Set(Array.isArray(option) ? option : []);
|
|
10845
|
+
}
|
|
10134
10846
|
function normalizeEditMode(mode) {
|
|
10135
10847
|
if (mode === "cell" || mode === "row") return mode;
|
|
10136
10848
|
return mode === true ? "cell" : false;
|
|
@@ -11698,7 +12410,7 @@ function buildZeyosTableConfig(client, resource, opts = {}) {
|
|
|
11698
12410
|
const monetary = isMonetaryField(meta);
|
|
11699
12411
|
const column = fieldToZxColumn(monetary ? { ...meta, format: "money" } : meta);
|
|
11700
12412
|
if (monetary) {
|
|
11701
|
-
column.render = (row) =>
|
|
12413
|
+
column.render = (row) => formatCurrency2(row?.[meta.id], row?.currency);
|
|
11702
12414
|
if (schemaHasField(client, resource, "currency")) projection.currency = "currency";
|
|
11703
12415
|
}
|
|
11704
12416
|
return column;
|
|
@@ -11823,10 +12535,10 @@ function inferLabelKey2(client, resource) {
|
|
|
11823
12535
|
return names[0] ?? "ID";
|
|
11824
12536
|
}
|
|
11825
12537
|
function isMonetaryField(meta) {
|
|
11826
|
-
const
|
|
11827
|
-
return /money|currency|price/.test(
|
|
12538
|
+
const format2 = String(meta.format ?? "").toLowerCase();
|
|
12539
|
+
return /money|currency|price/.test(format2) || /(?:amount|price)$/.test(String(meta.id).toLowerCase());
|
|
11828
12540
|
}
|
|
11829
|
-
function
|
|
12541
|
+
function formatCurrency2(value, currency) {
|
|
11830
12542
|
if (value == null || value === "") return "";
|
|
11831
12543
|
const number = Number(value);
|
|
11832
12544
|
if (!Number.isFinite(number)) return "";
|