@zeyos/zx 4.0.0 → 4.1.1
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 +61 -0
- package/DESIGN-SYSTEM.md +59 -3
- package/dist/types/components/app-icon/app-icon.d.ts +88 -0
- package/dist/types/components/filter/filter-model.d.ts +87 -0
- package/dist/types/components/filter/filter.d.ts +331 -0
- package/dist/types/components/select/select.d.ts +46 -4
- package/dist/types/components/table/table.d.ts +103 -0
- package/dist/types/components/tag-picker/tag-picker.d.ts +16 -0
- package/dist/types/core/dom.d.ts +1 -1
- package/dist/types/core/util.d.ts +9 -0
- package/dist/types/index.d.ts +4 -1
- package/dist/types/zeyos/icons.d.ts +21 -4
- package/dist/types/zeyos/index.d.ts +1 -1
- package/dist/types/zeyos/select.d.ts +2 -0
- package/dist/zx-zeyos.esm.js +703 -75
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +564 -9
- package/dist/zx.esm.js +1569 -115
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +1569 -115
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +379 -0
- package/docs/llms.md +97 -18
- package/docs/llms.txt +11 -9
- package/package.json +1 -1
- package/src/components/app-icon/app-icon.css +189 -0
- package/src/components/app-icon/app-icon.js +137 -0
- package/src/components/button/button.css +34 -2
- package/src/components/card/card.css +25 -3
- package/src/components/filter/filter-model.js +226 -0
- package/src/components/filter/filter.css +147 -0
- package/src/components/filter/filter.js +554 -0
- package/src/components/grid/grid.css +1 -1
- package/src/components/grid/grid.js +4 -1
- package/src/components/message/message.css +40 -3
- package/src/components/select/select.css +17 -0
- package/src/components/select/select.js +105 -24
- package/src/components/table/table.css +85 -0
- package/src/components/table/table.js +418 -24
- package/src/components/tag-picker/tag-picker.css +19 -0
- package/src/components/tag-picker/tag-picker.js +42 -5
- package/src/core/dom.js +1 -1
- package/src/core/util.js +20 -0
- package/src/index.js +7 -1
- package/src/zeyos/icons.js +37 -24
- package/src/zeyos/index.js +1 -1
- package/src/zeyos/select.js +6 -5
- package/styles/tokens/semantic.css +17 -0
- package/styles/tokens/themes.css +2 -2
- package/styles/zx.css +2 -0
package/dist/zx.global.js
CHANGED
|
@@ -21,6 +21,7 @@ var zx = (() => {
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AccountMenu: () => AccountMenu,
|
|
24
|
+
AppIcon: () => AppIcon,
|
|
24
25
|
AppSidebar: () => AppSidebar,
|
|
25
26
|
Avatar: () => Avatar,
|
|
26
27
|
Breadcrumb: () => Breadcrumb,
|
|
@@ -44,6 +45,7 @@ var zx = (() => {
|
|
|
44
45
|
Field: () => Field,
|
|
45
46
|
FieldUpload: () => FieldUpload,
|
|
46
47
|
Fieldset: () => Fieldset,
|
|
48
|
+
Filter: () => Filter,
|
|
47
49
|
Finder: () => Finder,
|
|
48
50
|
Form: () => Form,
|
|
49
51
|
Grid: () => Grid,
|
|
@@ -84,6 +86,7 @@ var zx = (() => {
|
|
|
84
86
|
__: () => __,
|
|
85
87
|
addDays: () => addDays,
|
|
86
88
|
addMonths: () => addMonths,
|
|
89
|
+
appIcon: () => appIcon,
|
|
87
90
|
appendCompact: () => appendCompact,
|
|
88
91
|
appendCompactChild: () => appendCompactChild,
|
|
89
92
|
appendContent: () => appendContent,
|
|
@@ -103,6 +106,7 @@ var zx = (() => {
|
|
|
103
106
|
clampRange: () => clampRange,
|
|
104
107
|
clampSize: () => clampSize,
|
|
105
108
|
cloneChartValue: () => cloneChartValue,
|
|
109
|
+
cloneFilterAst: () => cloneFilterAst,
|
|
106
110
|
compactElement: () => compactElement,
|
|
107
111
|
configureIcons: () => configureIcons,
|
|
108
112
|
copyButton: () => copyButton,
|
|
@@ -113,12 +117,16 @@ var zx = (() => {
|
|
|
113
117
|
defineElements: () => defineElements,
|
|
114
118
|
describe: () => describe,
|
|
115
119
|
downloadBlob: () => downloadBlob,
|
|
120
|
+
emptyFilterAst: () => emptyFilterAst,
|
|
116
121
|
emptyState: () => emptyState,
|
|
117
122
|
escapeRegExp: () => escapeRegExp,
|
|
118
123
|
faFamilies: () => faFamilies,
|
|
119
124
|
faIconClasses: () => faIconClasses,
|
|
120
125
|
faNames: () => faNames,
|
|
121
126
|
faStyles: () => faStyles,
|
|
127
|
+
filterCondition: () => filterCondition,
|
|
128
|
+
filterGroup: () => filterGroup,
|
|
129
|
+
filterOperators: () => filterOperators,
|
|
122
130
|
fireCompactEvent: () => fireCompactEvent,
|
|
123
131
|
focusTrap: () => focusTrap,
|
|
124
132
|
formatCurrency: () => formatCurrency,
|
|
@@ -158,6 +166,7 @@ var zx = (() => {
|
|
|
158
166
|
paginationRange: () => paginationRange,
|
|
159
167
|
parseCompactTag: () => parseCompactTag,
|
|
160
168
|
parseDate: () => parseDate,
|
|
169
|
+
parseFilterAst: () => parseFilterAst,
|
|
161
170
|
parseIconSpec: () => parseIconSpec,
|
|
162
171
|
parseNumber: () => parseNumber,
|
|
163
172
|
parseRangeText: () => parseRangeText,
|
|
@@ -169,6 +178,7 @@ var zx = (() => {
|
|
|
169
178
|
rankLauncherItems: () => rankLauncherItems,
|
|
170
179
|
registerFieldAdapters: () => registerFieldAdapters,
|
|
171
180
|
registerIcons: () => registerIcons,
|
|
181
|
+
reorderTableRows: () => reorderTableRows,
|
|
172
182
|
resolveElement: () => resolveElement,
|
|
173
183
|
resolveSize: () => resolveSize,
|
|
174
184
|
rovingTabindex: () => rovingTabindex,
|
|
@@ -185,6 +195,7 @@ var zx = (() => {
|
|
|
185
195
|
stack: () => stack,
|
|
186
196
|
stepPrecision: () => stepPrecision,
|
|
187
197
|
storage: () => storage,
|
|
198
|
+
stringifyFilterAst: () => stringifyFilterAst,
|
|
188
199
|
throttle: () => throttle,
|
|
189
200
|
toArray: () => toArray,
|
|
190
201
|
toCsv: () => toCsv,
|
|
@@ -196,6 +207,7 @@ var zx = (() => {
|
|
|
196
207
|
uniqueBy: () => uniqueBy,
|
|
197
208
|
useBuiltinIcons: () => useBuiltinIcons,
|
|
198
209
|
useFontAwesome: () => useFontAwesome,
|
|
210
|
+
validateFilterAst: () => validateFilterAst,
|
|
199
211
|
zeyosService: () => zeyosService
|
|
200
212
|
});
|
|
201
213
|
|
|
@@ -274,6 +286,20 @@ var zx = (() => {
|
|
|
274
286
|
return Boolean(value && typeof value === "object" && /** @type {{nodeType?: unknown}} */
|
|
275
287
|
value.nodeType === 1);
|
|
276
288
|
}
|
|
289
|
+
function isCssColor(value) {
|
|
290
|
+
if (typeof value !== "string") return false;
|
|
291
|
+
const text4 = value.trim();
|
|
292
|
+
if (!text4 || text4.length > 256 || /[;{}\u0000-\u001f\u007f]/.test(text4) || /(?:url|image|image-set|cross-fade|element|var)\s*\(/i.test(text4)) return false;
|
|
293
|
+
const supports = globalThis.CSS?.supports;
|
|
294
|
+
if (typeof supports === "function") {
|
|
295
|
+
try {
|
|
296
|
+
return supports.call(globalThis.CSS, "color", text4);
|
|
297
|
+
} catch {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return /^(?:#[\da-f]{3,8}|[a-z]+|(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color|color-mix)\([^;{}]+\))$/i.test(text4);
|
|
302
|
+
}
|
|
277
303
|
function clamp(n, min, max) {
|
|
278
304
|
return Math.min(max, Math.max(min, n));
|
|
279
305
|
}
|
|
@@ -911,9 +937,9 @@ var zx = (() => {
|
|
|
911
937
|
let rendered;
|
|
912
938
|
try {
|
|
913
939
|
rendered = h.scope(this.refs, () => this.render());
|
|
914
|
-
} catch (
|
|
940
|
+
} catch (error2) {
|
|
915
941
|
this.#abort.abort();
|
|
916
|
-
throw
|
|
942
|
+
throw error2;
|
|
917
943
|
}
|
|
918
944
|
if (this.#created) {
|
|
919
945
|
const root = isElement(rendered) ? rendered : this.el;
|
|
@@ -1204,8 +1230,8 @@ var zx = (() => {
|
|
|
1204
1230
|
/** @type {Readonly<FieldOptions>} */
|
|
1205
1231
|
this.options
|
|
1206
1232
|
));
|
|
1207
|
-
} catch (
|
|
1208
|
-
const message =
|
|
1233
|
+
} catch (error2) {
|
|
1234
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
1209
1235
|
console.warn(`[zx.Field] Adapter "${type}" failed: ${message}`);
|
|
1210
1236
|
this.adapter = errorAdapter(message, this.controlId);
|
|
1211
1237
|
}
|
|
@@ -5188,8 +5214,8 @@ var zx = (() => {
|
|
|
5188
5214
|
return response;
|
|
5189
5215
|
} catch (caught) {
|
|
5190
5216
|
if (runId !== this._runId) return null;
|
|
5191
|
-
const
|
|
5192
|
-
if (
|
|
5217
|
+
const error2 = caught instanceof Error ? caught : new Error(String(caught));
|
|
5218
|
+
if (error2.name === "AbortError") {
|
|
5193
5219
|
this._finishAbort();
|
|
5194
5220
|
return null;
|
|
5195
5221
|
}
|
|
@@ -5198,8 +5224,8 @@ var zx = (() => {
|
|
|
5198
5224
|
this._transportController = null;
|
|
5199
5225
|
this._abort.hidden = true;
|
|
5200
5226
|
this._setState("error");
|
|
5201
|
-
this._status.textContent =
|
|
5202
|
-
this.emit("error", { error });
|
|
5227
|
+
this._status.textContent = error2.message;
|
|
5228
|
+
this.emit("error", { error: error2 });
|
|
5203
5229
|
return null;
|
|
5204
5230
|
}
|
|
5205
5231
|
}
|
|
@@ -5347,10 +5373,10 @@ var zx = (() => {
|
|
|
5347
5373
|
this.emit("abort", {});
|
|
5348
5374
|
}
|
|
5349
5375
|
/** @param {Error} error @returns {void} */
|
|
5350
|
-
_selectionError(
|
|
5376
|
+
_selectionError(error2) {
|
|
5351
5377
|
this._setState("error");
|
|
5352
|
-
this._status.textContent =
|
|
5353
|
-
this.emit("error", { error });
|
|
5378
|
+
this._status.textContent = error2.message;
|
|
5379
|
+
this.emit("error", { error: error2 });
|
|
5354
5380
|
}
|
|
5355
5381
|
/** @returns {void} */
|
|
5356
5382
|
_renderPreview() {
|
|
@@ -5433,9 +5459,9 @@ var zx = (() => {
|
|
|
5433
5459
|
}
|
|
5434
5460
|
function abortError() {
|
|
5435
5461
|
if (typeof DOMException === "function") return new DOMException("Upload aborted", "AbortError");
|
|
5436
|
-
const
|
|
5437
|
-
|
|
5438
|
-
return
|
|
5462
|
+
const error2 = new Error("Upload aborted");
|
|
5463
|
+
error2.name = "AbortError";
|
|
5464
|
+
return error2;
|
|
5439
5465
|
}
|
|
5440
5466
|
|
|
5441
5467
|
// src/components/field-upload/field-adapter.js
|
|
@@ -6561,8 +6587,8 @@ var zx = (() => {
|
|
|
6561
6587
|
const shown = this._preview ?? this._value;
|
|
6562
6588
|
const symbols = this.refs.symbols.querySelectorAll(".zx-rating__symbol");
|
|
6563
6589
|
symbols.forEach((symbol, index) => {
|
|
6564
|
-
const
|
|
6565
|
-
symbol.style.setProperty("--zx-rating-fill", `${
|
|
6590
|
+
const filled2 = Math.min(1, Math.max(0, shown - index));
|
|
6591
|
+
symbol.style.setProperty("--zx-rating-fill", `${filled2 * 100}%`);
|
|
6566
6592
|
symbol.toggleAttribute("data-preview", this._preview !== null);
|
|
6567
6593
|
});
|
|
6568
6594
|
this.el.dataset.value = String(shown);
|
|
@@ -6848,6 +6874,7 @@ var zx = (() => {
|
|
|
6848
6874
|
static defaults = {
|
|
6849
6875
|
items: [],
|
|
6850
6876
|
fixedItems: [],
|
|
6877
|
+
fixedLabel: null,
|
|
6851
6878
|
valueKey: "ID",
|
|
6852
6879
|
labelKey: "name",
|
|
6853
6880
|
renderItem: null,
|
|
@@ -6885,8 +6912,7 @@ var zx = (() => {
|
|
|
6885
6912
|
this._position = null;
|
|
6886
6913
|
this._createdRoot = false;
|
|
6887
6914
|
this._root = null;
|
|
6888
|
-
this.
|
|
6889
|
-
this._originalAttributes = /* @__PURE__ */ new Map();
|
|
6915
|
+
this._snapshot = null;
|
|
6890
6916
|
this._createdRoot = this.el === null;
|
|
6891
6917
|
const root = (
|
|
6892
6918
|
/** @type {HTMLElement} */
|
|
@@ -6894,10 +6920,7 @@ var zx = (() => {
|
|
|
6894
6920
|
);
|
|
6895
6921
|
this.el = root;
|
|
6896
6922
|
this._root = root;
|
|
6897
|
-
|
|
6898
|
-
this._rememberAttribute(root, "data-state");
|
|
6899
|
-
this._rememberAttribute(root, "data-loading");
|
|
6900
|
-
this._rememberAttribute(root, "aria-disabled");
|
|
6923
|
+
this._snapshot = this._createdRoot ? null : snapshotTarget(root);
|
|
6901
6924
|
const listId = uid("zx-select-list");
|
|
6902
6925
|
const input = h("input", {
|
|
6903
6926
|
ref: "input",
|
|
@@ -7200,6 +7223,66 @@ var zx = (() => {
|
|
|
7200
7223
|
select.set(value, { silent: true });
|
|
7201
7224
|
return select;
|
|
7202
7225
|
}
|
|
7226
|
+
/**
|
|
7227
|
+
* Creates a rich entity picker with optional Recent choices and a create command.
|
|
7228
|
+
* @param {Element|string|null} target Component target.
|
|
7229
|
+
* @param {SelectOptions & EntitySelectOptions} [options={}] Entity-preset options.
|
|
7230
|
+
* @returns {Select}
|
|
7231
|
+
*/
|
|
7232
|
+
static entity(target, options = {}) {
|
|
7233
|
+
const {
|
|
7234
|
+
recent = [],
|
|
7235
|
+
recentLabel = "Recent",
|
|
7236
|
+
create = false,
|
|
7237
|
+
iconKey = "icon",
|
|
7238
|
+
colorKey = "color",
|
|
7239
|
+
subtitleKey = "description",
|
|
7240
|
+
renderIcon = null,
|
|
7241
|
+
fixedItems = [],
|
|
7242
|
+
actions: actions2 = [],
|
|
7243
|
+
renderItem = null,
|
|
7244
|
+
renderValueAdornment = null,
|
|
7245
|
+
...rest
|
|
7246
|
+
} = options;
|
|
7247
|
+
const visual = (item, selected = false) => {
|
|
7248
|
+
if (typeof renderIcon === "function") return renderIcon(item, { selected });
|
|
7249
|
+
const value = readSelectItem(item, iconKey);
|
|
7250
|
+
if (value && typeof value === "object" && typeof value.nodeType === "number") return value.cloneNode(true);
|
|
7251
|
+
if (typeof value !== "string" || !value) return null;
|
|
7252
|
+
const color = readSelectItem(item, colorKey);
|
|
7253
|
+
return h("span", {
|
|
7254
|
+
class: "zx-select__entity-glyph",
|
|
7255
|
+
style: isCssColor(color) ? { "--zx-select-entity-color": String(color) } : null,
|
|
7256
|
+
ariaHidden: "true"
|
|
7257
|
+
}, icon(value, { size: 14 }));
|
|
7258
|
+
};
|
|
7259
|
+
const createAction = create ? normalizeEntityCreateAction(create) : null;
|
|
7260
|
+
const select = new _Select(target, {
|
|
7261
|
+
...rest,
|
|
7262
|
+
fixedItems: [...Array.isArray(recent) ? recent : [], ...Array.isArray(fixedItems) ? fixedItems : []],
|
|
7263
|
+
fixedLabel: Array.isArray(recent) && recent.length ? recentLabel : rest.fixedLabel ?? null,
|
|
7264
|
+
actions: [...Array.isArray(actions2) ? actions2 : [], ...createAction ? [createAction] : []],
|
|
7265
|
+
renderItem: renderItem ?? ((item) => {
|
|
7266
|
+
const decoration = visual(item, false);
|
|
7267
|
+
const subtitle = readSelectItem(item, subtitleKey);
|
|
7268
|
+
const copy = h(
|
|
7269
|
+
"span",
|
|
7270
|
+
{ class: "zx-select__entity-copy" },
|
|
7271
|
+
h("span", { class: "zx-select__entity-label" }, String(readSelectItem(item, rest.labelKey ?? "name") ?? "")),
|
|
7272
|
+
subtitle == null || subtitle === "" ? null : h("span", { class: "zx-select__entity-subtitle" }, String(subtitle))
|
|
7273
|
+
);
|
|
7274
|
+
return h(
|
|
7275
|
+
"span",
|
|
7276
|
+
{ class: "zx-select__entity" },
|
|
7277
|
+
decoration ? h("span", { class: "zx-select__entity-icon", ariaHidden: "true" }, decoration) : null,
|
|
7278
|
+
copy
|
|
7279
|
+
);
|
|
7280
|
+
}),
|
|
7281
|
+
renderValueAdornment: renderValueAdornment ?? ((item) => visual(item, true))
|
|
7282
|
+
});
|
|
7283
|
+
select.el.dataset.preset = "entity";
|
|
7284
|
+
return select;
|
|
7285
|
+
}
|
|
7203
7286
|
/**
|
|
7204
7287
|
* Creates the record-permission picker: the fixed Private and Public choices above the groups a
|
|
7205
7288
|
* record can be shared with. The value is the tri-state ZeyOS stores — `false` for private,
|
|
@@ -7250,13 +7333,7 @@ var zx = (() => {
|
|
|
7250
7333
|
/** @type {HTMLElement} */
|
|
7251
7334
|
this.el
|
|
7252
7335
|
);
|
|
7253
|
-
if (!this._createdRoot && root)
|
|
7254
|
-
root.replaceChildren(...this._originalChildren);
|
|
7255
|
-
for (const [name, value] of this._originalAttributes) {
|
|
7256
|
-
if (value === null) root.removeAttribute(name);
|
|
7257
|
-
else root.setAttribute(name, value);
|
|
7258
|
-
}
|
|
7259
|
-
}
|
|
7336
|
+
if (!this._createdRoot && root) restoreTarget(root, this._snapshot);
|
|
7260
7337
|
super.destroy();
|
|
7261
7338
|
}
|
|
7262
7339
|
/** @param {KeyboardEvent} event @returns {void} */
|
|
@@ -7393,14 +7470,19 @@ var zx = (() => {
|
|
|
7393
7470
|
list.replaceChildren();
|
|
7394
7471
|
this._optionEntries = [];
|
|
7395
7472
|
let lastGroup = /* @__PURE__ */ Symbol("initial-group");
|
|
7473
|
+
const fixed = this._uniqueItems(this._visibleFixed);
|
|
7474
|
+
const items = this._uniqueItems(this._visibleItems, fixed);
|
|
7396
7475
|
if (this.options.clearable) {
|
|
7397
7476
|
this._appendOption(null, true, this.options.noneLabel ?? this._message("select.none", "No selection"));
|
|
7398
7477
|
}
|
|
7399
|
-
|
|
7400
|
-
|
|
7478
|
+
if (fixed.length > 0 && this.options.fixedLabel) {
|
|
7479
|
+
list.append(h("div", { class: "zx-select__group", role: "presentation" }, String(this.options.fixedLabel)));
|
|
7480
|
+
}
|
|
7481
|
+
for (const item of fixed) this._appendOption(item, false);
|
|
7482
|
+
if (fixed.length > 0 && items.length > 0 && !this.options.groupKey) {
|
|
7401
7483
|
list.append(h("div", { class: "zx-select__separator", role: "presentation" }));
|
|
7402
7484
|
}
|
|
7403
|
-
for (const item of
|
|
7485
|
+
for (const item of items) {
|
|
7404
7486
|
if (this.options.groupKey) {
|
|
7405
7487
|
const group = this._read(item, this.options.groupKey);
|
|
7406
7488
|
if (!Object.is(group, lastGroup)) {
|
|
@@ -7414,7 +7496,7 @@ var zx = (() => {
|
|
|
7414
7496
|
this._appendOption(item, false);
|
|
7415
7497
|
}
|
|
7416
7498
|
if (this._actions.length > 0) {
|
|
7417
|
-
if (
|
|
7499
|
+
if (fixed.length > 0 || items.length > 0 || this.options.clearable) {
|
|
7418
7500
|
list.append(h("div", { class: "zx-select__separator", role: "presentation" }));
|
|
7419
7501
|
}
|
|
7420
7502
|
let actionGroup = /* @__PURE__ */ Symbol("initial-action-group");
|
|
@@ -7429,7 +7511,7 @@ var zx = (() => {
|
|
|
7429
7511
|
this._appendAction(action);
|
|
7430
7512
|
}
|
|
7431
7513
|
}
|
|
7432
|
-
if (
|
|
7514
|
+
if (items.length === 0 && fixed.length === 0 && this._actions.length === 0 && !this._loading) {
|
|
7433
7515
|
list.append(h("div", {
|
|
7434
7516
|
class: "zx-select__empty",
|
|
7435
7517
|
role: "status"
|
|
@@ -7630,6 +7712,16 @@ var zx = (() => {
|
|
|
7630
7712
|
_matchFixed(query) {
|
|
7631
7713
|
return matchItems(this._fixedItems, query, this._searchKeys());
|
|
7632
7714
|
}
|
|
7715
|
+
/** De-duplicates choices by public value while preserving caller order. @param {SelectItem[]} items @param {SelectItem[]} [excluded=[]] @returns {SelectItem[]} */
|
|
7716
|
+
_uniqueItems(items, excluded = []) {
|
|
7717
|
+
const values = excluded.map((item) => this._itemValue(item));
|
|
7718
|
+
return items.filter((item) => {
|
|
7719
|
+
const value = this._itemValue(item);
|
|
7720
|
+
if (values.some((candidate) => Object.is(candidate, value))) return false;
|
|
7721
|
+
values.push(value);
|
|
7722
|
+
return true;
|
|
7723
|
+
});
|
|
7724
|
+
}
|
|
7633
7725
|
/** @returns {number} */
|
|
7634
7726
|
_selectedEntryIndex() {
|
|
7635
7727
|
if (this._selected === null) return this.options.clearable ? 0 : -1;
|
|
@@ -7641,16 +7733,28 @@ var zx = (() => {
|
|
|
7641
7733
|
_itemEntryIndex(itemIndex) {
|
|
7642
7734
|
return itemIndex + (this.options.clearable ? 1 : 0);
|
|
7643
7735
|
}
|
|
7644
|
-
/** @param {string} name @returns {void} */
|
|
7645
|
-
_rememberAttribute(element, name) {
|
|
7646
|
-
this._originalAttributes.set(name, element.getAttribute(name));
|
|
7647
|
-
}
|
|
7648
7736
|
/** @param {string} key @param {string} fallback @returns {string} */
|
|
7649
7737
|
_message(key, fallback) {
|
|
7650
7738
|
const message = this.msg(key);
|
|
7651
7739
|
return message === key ? fallback : message;
|
|
7652
7740
|
}
|
|
7653
7741
|
};
|
|
7742
|
+
function readSelectItem(item, reader) {
|
|
7743
|
+
if (typeof reader === "function") return reader(item);
|
|
7744
|
+
if (item !== null && typeof item === "object" && typeof reader === "string") return item[reader];
|
|
7745
|
+
return null;
|
|
7746
|
+
}
|
|
7747
|
+
function normalizeEntityCreateAction(value) {
|
|
7748
|
+
const descriptor = value === true ? {} : value;
|
|
7749
|
+
return {
|
|
7750
|
+
id: descriptor.id ?? "create",
|
|
7751
|
+
label: descriptor.label ?? "Create new\u2026",
|
|
7752
|
+
description: descriptor.description,
|
|
7753
|
+
group: descriptor.group ?? "New",
|
|
7754
|
+
icon: descriptor.icon ?? "plus",
|
|
7755
|
+
invoke: descriptor.invoke
|
|
7756
|
+
};
|
|
7757
|
+
}
|
|
7654
7758
|
function normalizeRenderedContent(rendered) {
|
|
7655
7759
|
return rendered && typeof rendered === "object" && "nodeType" in rendered ? (
|
|
7656
7760
|
/** @type {Node} */
|
|
@@ -8127,6 +8231,8 @@ var zx = (() => {
|
|
|
8127
8231
|
values: [],
|
|
8128
8232
|
valueKey: "ID",
|
|
8129
8233
|
labelKey: "name",
|
|
8234
|
+
iconKey: "icon",
|
|
8235
|
+
colorKey: "color",
|
|
8130
8236
|
searchKeys: null,
|
|
8131
8237
|
filter: "local",
|
|
8132
8238
|
minQuery: 0,
|
|
@@ -8467,12 +8573,13 @@ var zx = (() => {
|
|
|
8467
8573
|
_renderTags() {
|
|
8468
8574
|
const tags = this._selected.map((item) => {
|
|
8469
8575
|
const label = this._labelOf(item);
|
|
8470
|
-
const
|
|
8471
|
-
const
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8576
|
+
const color = this._colorOf(item);
|
|
8577
|
+
const content = this.options.renderTag ? this.options.renderTag(item) : [this._iconOf(item), h("span", { class: "zx-tag-picker__tag-label" }, label)];
|
|
8578
|
+
const tag = h("span", {
|
|
8579
|
+
class: "zx-tag-picker__tag",
|
|
8580
|
+
dataset: { colored: String(Boolean(color)) },
|
|
8581
|
+
style: color ? { "--zx-tag-color": color } : null
|
|
8582
|
+
}, content);
|
|
8476
8583
|
if (!this._readonly && !this._disabled) {
|
|
8477
8584
|
tag.append(h("button", {
|
|
8478
8585
|
class: "zx-icon-btn zx-tag-picker__remove",
|
|
@@ -8510,7 +8617,7 @@ var zx = (() => {
|
|
|
8510
8617
|
h(
|
|
8511
8618
|
"span",
|
|
8512
8619
|
{ class: "zx-tag-picker__option-label" },
|
|
8513
|
-
this.options.renderItem ? this.options.renderItem(item) : this._labelOf(item)
|
|
8620
|
+
this.options.renderItem ? this.options.renderItem(item) : [this._iconOf(item), h("span", {}, this._labelOf(item))]
|
|
8514
8621
|
)
|
|
8515
8622
|
);
|
|
8516
8623
|
if (!selected && this.isFull()) option.setAttribute("aria-disabled", "true");
|
|
@@ -8735,10 +8842,10 @@ var zx = (() => {
|
|
|
8735
8842
|
if (sequence !== this._requestSequence) return;
|
|
8736
8843
|
this._items = Array.isArray(result) ? result.slice() : [];
|
|
8737
8844
|
this._visible = this._items.slice();
|
|
8738
|
-
} catch (
|
|
8845
|
+
} catch (error2) {
|
|
8739
8846
|
if (sequence !== this._requestSequence) return;
|
|
8740
8847
|
this._visible = [];
|
|
8741
|
-
this.emit("error", { error });
|
|
8848
|
+
this.emit("error", { error: error2 });
|
|
8742
8849
|
} finally {
|
|
8743
8850
|
if (sequence === this._requestSequence) {
|
|
8744
8851
|
this._setLoading(false);
|
|
@@ -8831,6 +8938,34 @@ var zx = (() => {
|
|
|
8831
8938
|
if (item !== null && typeof item === "object") return String(item[key] ?? "");
|
|
8832
8939
|
return String(item ?? "");
|
|
8833
8940
|
}
|
|
8941
|
+
/** @param {unknown} item @returns {Node|null} */
|
|
8942
|
+
_iconOf(item) {
|
|
8943
|
+
const value = this._readItem(item, this.options.iconKey);
|
|
8944
|
+
const visual = value && typeof value === "object" && typeof value.nodeType === "number" ? (
|
|
8945
|
+
/** @type {Node} */
|
|
8946
|
+
value.cloneNode(true)
|
|
8947
|
+
) : typeof value === "string" && value.trim() ? icon(value, { size: 12 }) : null;
|
|
8948
|
+
if (!visual) return null;
|
|
8949
|
+
return h("span", {
|
|
8950
|
+
class: "zx-tag-picker__visual",
|
|
8951
|
+
style: this._colorOf(item) ? { "--zx-tag-color": this._colorOf(item) } : null,
|
|
8952
|
+
ariaHidden: "true"
|
|
8953
|
+
}, visual);
|
|
8954
|
+
}
|
|
8955
|
+
/** @param {unknown} item @returns {string|null} */
|
|
8956
|
+
_colorOf(item) {
|
|
8957
|
+
const value = this._readItem(item, this.options.colorKey);
|
|
8958
|
+
return isCssColor(value) ? value : null;
|
|
8959
|
+
}
|
|
8960
|
+
/** @param {unknown} item @param {unknown} reader @returns {unknown} */
|
|
8961
|
+
_readItem(item, reader) {
|
|
8962
|
+
if (typeof reader === "function") return reader(
|
|
8963
|
+
/** @type {TagItem} */
|
|
8964
|
+
item
|
|
8965
|
+
);
|
|
8966
|
+
if (item !== null && typeof item === "object" && typeof reader === "string") return item[reader];
|
|
8967
|
+
return null;
|
|
8968
|
+
}
|
|
8834
8969
|
/** @returns {void} */
|
|
8835
8970
|
_emitChange() {
|
|
8836
8971
|
this.emit("change", { values: this.getValues(), items: this.getItems() });
|
|
@@ -9715,38 +9850,38 @@ var zx = (() => {
|
|
|
9715
9850
|
try {
|
|
9716
9851
|
json2 = JSON.parse(text4);
|
|
9717
9852
|
} catch (cause) {
|
|
9718
|
-
const
|
|
9853
|
+
const error2 = (
|
|
9719
9854
|
/** @type {Error & {response?: Response}} */
|
|
9720
9855
|
new Error("Invalid JSON response", { cause })
|
|
9721
9856
|
);
|
|
9722
|
-
|
|
9723
|
-
throw
|
|
9857
|
+
error2.response = response;
|
|
9858
|
+
throw error2;
|
|
9724
9859
|
}
|
|
9725
9860
|
}
|
|
9726
9861
|
if (!response.ok) {
|
|
9727
9862
|
try {
|
|
9728
9863
|
parseResult(json2);
|
|
9729
|
-
} catch (
|
|
9730
|
-
|
|
9731
|
-
throw
|
|
9864
|
+
} catch (error3) {
|
|
9865
|
+
error3.response = response;
|
|
9866
|
+
throw error3;
|
|
9732
9867
|
}
|
|
9733
|
-
const
|
|
9868
|
+
const error2 = (
|
|
9734
9869
|
/** @type {Error & {response?: Response}} */
|
|
9735
9870
|
new Error(`HTTP ${response.status} ${response.statusText}`.trim())
|
|
9736
9871
|
);
|
|
9737
|
-
|
|
9738
|
-
throw
|
|
9872
|
+
error2.response = response;
|
|
9873
|
+
throw error2;
|
|
9739
9874
|
}
|
|
9740
9875
|
return parseResult(json2);
|
|
9741
9876
|
} catch (caught) {
|
|
9742
|
-
const
|
|
9877
|
+
const error2 = caught instanceof Error ? caught : new Error(String(caught));
|
|
9743
9878
|
if (this.onError) {
|
|
9744
9879
|
try {
|
|
9745
|
-
this.onError(
|
|
9880
|
+
this.onError(error2, { path, options });
|
|
9746
9881
|
} catch {
|
|
9747
9882
|
}
|
|
9748
9883
|
}
|
|
9749
|
-
throw
|
|
9884
|
+
throw error2;
|
|
9750
9885
|
} finally {
|
|
9751
9886
|
if (timeoutId !== null) clearTimeout(timeoutId);
|
|
9752
9887
|
}
|
|
@@ -10463,9 +10598,9 @@ var zx = (() => {
|
|
|
10463
10598
|
this.el.returnValue = "";
|
|
10464
10599
|
try {
|
|
10465
10600
|
this._show();
|
|
10466
|
-
} catch (
|
|
10601
|
+
} catch (error2) {
|
|
10467
10602
|
delete this.el.dataset.zxOverlayOrder;
|
|
10468
|
-
throw
|
|
10603
|
+
throw error2;
|
|
10469
10604
|
}
|
|
10470
10605
|
this.el.dataset.state = "open";
|
|
10471
10606
|
this.emit("open");
|
|
@@ -10948,9 +11083,9 @@ var zx = (() => {
|
|
|
10948
11083
|
}
|
|
10949
11084
|
try {
|
|
10950
11085
|
super(normalizedTarget, options);
|
|
10951
|
-
} catch (
|
|
11086
|
+
} catch (error2) {
|
|
10952
11087
|
if (adoption) restoreAdoption(adoption);
|
|
10953
|
-
throw
|
|
11088
|
+
throw error2;
|
|
10954
11089
|
}
|
|
10955
11090
|
this._adoption = adoption;
|
|
10956
11091
|
}
|
|
@@ -11678,7 +11813,11 @@ var zx = (() => {
|
|
|
11678
11813
|
rowClass: null,
|
|
11679
11814
|
zebra: true,
|
|
11680
11815
|
editMode: false,
|
|
11681
|
-
|
|
11816
|
+
editTrigger: "double",
|
|
11817
|
+
hierarchy: false,
|
|
11818
|
+
rowReorder: false,
|
|
11819
|
+
columnVisibility: false,
|
|
11820
|
+
hiddenColumns: []
|
|
11682
11821
|
};
|
|
11683
11822
|
/**
|
|
11684
11823
|
* Creates a table around a target or creates a new root when target is null.
|
|
@@ -11707,6 +11846,7 @@ var zx = (() => {
|
|
|
11707
11846
|
this._rowElements = /* @__PURE__ */ new Map();
|
|
11708
11847
|
this._restored = false;
|
|
11709
11848
|
this._editMode = normalizeEditMode(this.options.editMode);
|
|
11849
|
+
this._editTrigger = this.options.editTrigger === "single" ? "single" : "double";
|
|
11710
11850
|
this._edit = null;
|
|
11711
11851
|
this._editMessage = null;
|
|
11712
11852
|
this._stacked = false;
|
|
@@ -11718,6 +11858,10 @@ var zx = (() => {
|
|
|
11718
11858
|
this._rovingCell = null;
|
|
11719
11859
|
this._rovingMatched = false;
|
|
11720
11860
|
this._hierarchy = normalizeHierarchy(this.options.hierarchy, this._columns);
|
|
11861
|
+
this._hiddenColumns = new Set((Array.isArray(this.options.hiddenColumns) ? this.options.hiddenColumns : []).map(String).filter((id) => this._columns.some((column) => column.id === id)));
|
|
11862
|
+
if (this._hiddenColumns.size >= this._columns.length && this._columns[0]) this._hiddenColumns.delete(this._columns[0].id);
|
|
11863
|
+
this._dragId = null;
|
|
11864
|
+
this._grabbedId = null;
|
|
11721
11865
|
this._expanded = initialExpanded(this._hierarchy?.expanded);
|
|
11722
11866
|
if (this._sort && this.options.sortMode === "local") this._sortData();
|
|
11723
11867
|
if (this._hierarchy?.expanded === true) {
|
|
@@ -11739,11 +11883,17 @@ var zx = (() => {
|
|
|
11739
11883
|
zebra: String(Boolean(this.options.zebra))
|
|
11740
11884
|
}
|
|
11741
11885
|
}, this._table);
|
|
11886
|
+
this._rowMoveMessage = this.options.rowReorder ? h("div", {
|
|
11887
|
+
class: "zx-table__reorder-status",
|
|
11888
|
+
role: "status",
|
|
11889
|
+
ariaLive: "polite"
|
|
11890
|
+
}) : null;
|
|
11742
11891
|
if (this.options.height != null) {
|
|
11743
11892
|
this._scroll.style.blockSize = typeof this.options.height === "number" ? `${this.options.height}px` : String(this.options.height);
|
|
11744
11893
|
}
|
|
11894
|
+
this._columnControls = this.options.columnVisibility ? this._createColumnControls() : null;
|
|
11745
11895
|
if (this._editMode === false) {
|
|
11746
|
-
root.replaceChildren(this._scroll);
|
|
11896
|
+
root.replaceChildren(...[this._columnControls, this._scroll, this._rowMoveMessage].filter(Boolean));
|
|
11747
11897
|
} else {
|
|
11748
11898
|
root.dataset.editMode = this._editMode;
|
|
11749
11899
|
this._editMessage = h("div", {
|
|
@@ -11752,7 +11902,7 @@ var zx = (() => {
|
|
|
11752
11902
|
role: "status",
|
|
11753
11903
|
ariaLive: "polite"
|
|
11754
11904
|
});
|
|
11755
|
-
root.replaceChildren(this._scroll, this._editMessage);
|
|
11905
|
+
root.replaceChildren(...[this._columnControls, this._scroll, this._editMessage, this._rowMoveMessage].filter(Boolean));
|
|
11756
11906
|
}
|
|
11757
11907
|
this._renderColumns();
|
|
11758
11908
|
this._renderHeader();
|
|
@@ -11760,6 +11910,34 @@ var zx = (() => {
|
|
|
11760
11910
|
this._watchWidth();
|
|
11761
11911
|
this.listen(this._tbody, "click", (event) => this._handleBodyClick(event));
|
|
11762
11912
|
this.listen(this._tbody, "dblclick", (event) => this._handleBodyDoubleClick(event));
|
|
11913
|
+
if (this.options.rowReorder) {
|
|
11914
|
+
this.listen(this._tbody, "dragstart", (event) => this._handleRowDragStart(
|
|
11915
|
+
/** @type {DragEvent} */
|
|
11916
|
+
event
|
|
11917
|
+
));
|
|
11918
|
+
this.listen(this._tbody, "dragover", (event) => this._handleRowDragOver(
|
|
11919
|
+
/** @type {DragEvent} */
|
|
11920
|
+
event
|
|
11921
|
+
));
|
|
11922
|
+
this.listen(this._tbody, "drop", (event) => this._handleRowDrop(
|
|
11923
|
+
/** @type {DragEvent} */
|
|
11924
|
+
event
|
|
11925
|
+
));
|
|
11926
|
+
this.listen(this._tbody, "dragend", () => this._clearRowDrag());
|
|
11927
|
+
this.listen(this._tbody, "keydown", (event) => this._handleRowHandleKeydown(
|
|
11928
|
+
/** @type {KeyboardEvent} */
|
|
11929
|
+
event
|
|
11930
|
+
));
|
|
11931
|
+
}
|
|
11932
|
+
if (this._columnControls) {
|
|
11933
|
+
this.listen(this._columnControls, "change", (event) => {
|
|
11934
|
+
const input = (
|
|
11935
|
+
/** @type {HTMLInputElement|null} */
|
|
11936
|
+
event.target
|
|
11937
|
+
);
|
|
11938
|
+
if (input?.classList.contains("zx-table__column-toggle")) this.setColumnVisible(input.value, input.checked);
|
|
11939
|
+
});
|
|
11940
|
+
}
|
|
11763
11941
|
if (this._editMode !== false) {
|
|
11764
11942
|
this.listen(this._tbody, "keydown", (event) => this._handleEditKeydown(
|
|
11765
11943
|
/** @type {KeyboardEvent} */
|
|
@@ -11875,6 +12053,81 @@ var zx = (() => {
|
|
|
11875
12053
|
getData() {
|
|
11876
12054
|
return [...this._data];
|
|
11877
12055
|
}
|
|
12056
|
+
/** Returns visible column IDs in caller order. @returns {string[]} */
|
|
12057
|
+
getVisibleColumns() {
|
|
12058
|
+
return this._visibleColumns().map((column) => column.id);
|
|
12059
|
+
}
|
|
12060
|
+
/** Returns hidden column IDs in caller order. @returns {string[]} */
|
|
12061
|
+
getHiddenColumns() {
|
|
12062
|
+
return this._columns.filter((column) => this._hiddenColumns.has(column.id)).map((column) => column.id);
|
|
12063
|
+
}
|
|
12064
|
+
/**
|
|
12065
|
+
* Shows or hides one column. The final visible column cannot be hidden.
|
|
12066
|
+
* @param {string} id Column ID.
|
|
12067
|
+
* @param {boolean} [visible=true] Desired visibility.
|
|
12068
|
+
* @param {{silent?:boolean}} [options={}] Event behavior.
|
|
12069
|
+
* @returns {this}
|
|
12070
|
+
*/
|
|
12071
|
+
setColumnVisible(id, visible = true, options = {}) {
|
|
12072
|
+
const column = this._columns.find((candidate) => candidate.id === id);
|
|
12073
|
+
if (!column) throw new RangeError(`Unknown table column: ${id}`);
|
|
12074
|
+
const active = (
|
|
12075
|
+
/** @type {HTMLElement|null} */
|
|
12076
|
+
document.activeElement
|
|
12077
|
+
);
|
|
12078
|
+
const restoreFocus = active?.classList.contains("zx-table__column-toggle") && this._columnList?.contains(active) ? (
|
|
12079
|
+
/** @type {HTMLInputElement} */
|
|
12080
|
+
active.value
|
|
12081
|
+
) : null;
|
|
12082
|
+
if (!visible && !this._hiddenColumns.has(id) && this._visibleColumns().length <= 1) {
|
|
12083
|
+
this._renderColumnControls(restoreFocus);
|
|
12084
|
+
return this;
|
|
12085
|
+
}
|
|
12086
|
+
const changed = visible ? this._hiddenColumns.delete(id) : !this._hiddenColumns.has(id);
|
|
12087
|
+
if (!visible && changed) this._hiddenColumns.add(id);
|
|
12088
|
+
if (!changed) return this;
|
|
12089
|
+
this._renderColumns();
|
|
12090
|
+
this._renderHeader();
|
|
12091
|
+
this._renderBody();
|
|
12092
|
+
this._renderColumnControls(restoreFocus);
|
|
12093
|
+
if (!options.silent) this.emit("columnvisibilitychange", {
|
|
12094
|
+
visible: this.getVisibleColumns(),
|
|
12095
|
+
hidden: this.getHiddenColumns()
|
|
12096
|
+
});
|
|
12097
|
+
return this;
|
|
12098
|
+
}
|
|
12099
|
+
/** Toggles one column. @param {string} id @param {{silent?:boolean}} [options={}] @returns {this} */
|
|
12100
|
+
toggleColumn(id, options = {}) {
|
|
12101
|
+
return this.setColumnVisible(id, this._hiddenColumns.has(id), options);
|
|
12102
|
+
}
|
|
12103
|
+
/**
|
|
12104
|
+
* Reorders one row relative to another and emits a cancelable `rowmove` before committing.
|
|
12105
|
+
* Hierarchical rows may only move among siblings; an active local sort disables manual order.
|
|
12106
|
+
* @param {unknown} id Moving row ID.
|
|
12107
|
+
* @param {unknown} targetId Target row ID.
|
|
12108
|
+
* @param {'before'|'after'} [position='before'] Target edge.
|
|
12109
|
+
* @returns {this}
|
|
12110
|
+
*/
|
|
12111
|
+
moveRow(id, targetId, position2 = "before") {
|
|
12112
|
+
if (!this.options.rowReorder || this._sort && this.options.sortMode === "local") return this;
|
|
12113
|
+
const row2 = this.getRow(id);
|
|
12114
|
+
const target = this.getRow(targetId);
|
|
12115
|
+
if (!row2 || !target || Object.is(id, targetId)) return this;
|
|
12116
|
+
const next = reorderTableRows(this._data, this.options.rowId, id, targetId, position2, this._hierarchy?.parentId);
|
|
12117
|
+
if (!next) return this;
|
|
12118
|
+
const event = this.emit("rowmove", { row: row2, id, target, targetId, position: position2 });
|
|
12119
|
+
if (event.defaultPrevented) return this;
|
|
12120
|
+
this._data = next;
|
|
12121
|
+
this._renderBody();
|
|
12122
|
+
if (this._rowMoveMessage) {
|
|
12123
|
+
const siblings = this._siblingIds(id);
|
|
12124
|
+
const index = siblings.findIndex((candidate) => Object.is(candidate, id));
|
|
12125
|
+
this._rowMoveMessage.textContent = `Moved row to position ${index + 1} of ${siblings.length}.`;
|
|
12126
|
+
}
|
|
12127
|
+
this._emitDataChange();
|
|
12128
|
+
queueMicrotask(() => this._rowHandle(id)?.focus());
|
|
12129
|
+
return this;
|
|
12130
|
+
}
|
|
11878
12131
|
/**
|
|
11879
12132
|
* Removes all rows.
|
|
11880
12133
|
* @returns {this}
|
|
@@ -12043,14 +12296,17 @@ var zx = (() => {
|
|
|
12043
12296
|
*/
|
|
12044
12297
|
startEdit(rowId, columnId) {
|
|
12045
12298
|
if (this._editMode === false) return this;
|
|
12046
|
-
const
|
|
12047
|
-
if (!
|
|
12299
|
+
const requested = this._columns.find((candidate) => candidate.id === columnId);
|
|
12300
|
+
if (!requested) return this;
|
|
12048
12301
|
const current = this._data.find((candidate) => Object.is(this._idFor(candidate), rowId));
|
|
12049
|
-
if (current === void 0
|
|
12302
|
+
if (current === void 0) return this;
|
|
12303
|
+
const column = this._editMode === "row" && this._hiddenColumns.has(requested.id) ? this._visibleColumns().find((candidate) => resolveEditable(candidate, current) !== false) ?? null : requested;
|
|
12304
|
+
if (!column || resolveEditable(column, current) === false) return this;
|
|
12305
|
+
const activeColumnId = column.id;
|
|
12050
12306
|
if (this._edit) {
|
|
12051
|
-
if (Object.is(this._edit.id, rowId) && this._edit.cells.has(
|
|
12052
|
-
this._edit.columnId =
|
|
12053
|
-
this._focusEditor(
|
|
12307
|
+
if (Object.is(this._edit.id, rowId) && this._edit.cells.has(activeColumnId)) {
|
|
12308
|
+
this._edit.columnId = activeColumnId;
|
|
12309
|
+
this._focusEditor(activeColumnId);
|
|
12054
12310
|
return this;
|
|
12055
12311
|
}
|
|
12056
12312
|
this.commitEdit();
|
|
@@ -12061,7 +12317,7 @@ var zx = (() => {
|
|
|
12061
12317
|
const row2 = this._data[index];
|
|
12062
12318
|
const tr = this._rowElements.get(rowId)?.[0];
|
|
12063
12319
|
if (!tr) return this;
|
|
12064
|
-
const targets = this._editMode === "row" ? this.
|
|
12320
|
+
const targets = this._editMode === "row" ? this._visibleColumns().filter((candidate) => resolveEditable(candidate, row2) !== false) : [column];
|
|
12065
12321
|
const cells = /* @__PURE__ */ new Map();
|
|
12066
12322
|
for (const target of targets) {
|
|
12067
12323
|
const td = cellIn(tr, target.id);
|
|
@@ -12070,10 +12326,10 @@ var zx = (() => {
|
|
|
12070
12326
|
if (entry) cells.set(target.id, entry);
|
|
12071
12327
|
}
|
|
12072
12328
|
if (cells.size === 0) return this;
|
|
12073
|
-
this._edit = { id: rowId, columnId, row: row2, cells, tr };
|
|
12329
|
+
this._edit = { id: rowId, columnId: activeColumnId, row: row2, cells, tr };
|
|
12074
12330
|
tr.dataset.editing = "true";
|
|
12075
|
-
this.emit("editstart", { row: row2, id: rowId, column, columnId, value: row2?.[
|
|
12076
|
-
this._focusEditor(
|
|
12331
|
+
this.emit("editstart", { row: row2, id: rowId, column, columnId: activeColumnId, value: row2?.[activeColumnId] });
|
|
12332
|
+
this._focusEditor(activeColumnId);
|
|
12077
12333
|
return this;
|
|
12078
12334
|
}
|
|
12079
12335
|
/**
|
|
@@ -12328,16 +12584,197 @@ var zx = (() => {
|
|
|
12328
12584
|
}
|
|
12329
12585
|
super.destroy();
|
|
12330
12586
|
}
|
|
12587
|
+
/** @returns {TableColumn[]} */
|
|
12588
|
+
_visibleColumns() {
|
|
12589
|
+
return this._columns.filter((column) => !this._hiddenColumns.has(column.id));
|
|
12590
|
+
}
|
|
12591
|
+
/** @returns {HTMLElement} */
|
|
12592
|
+
_createColumnControls() {
|
|
12593
|
+
const list = h("div", { class: "zx-table__column-list" });
|
|
12594
|
+
this._columnList = list;
|
|
12595
|
+
const details = h(
|
|
12596
|
+
"details",
|
|
12597
|
+
{ class: "zx-table__column-controls" },
|
|
12598
|
+
h("summary", { class: "zx-button zx-button--sm" }, icon("fields", { size: 13 }), "Columns"),
|
|
12599
|
+
list
|
|
12600
|
+
);
|
|
12601
|
+
this._renderColumnControls();
|
|
12602
|
+
return details;
|
|
12603
|
+
}
|
|
12604
|
+
/** @param {string|null} [focusColumn=null] Column checkbox that keeps focus. @returns {void} */
|
|
12605
|
+
_renderColumnControls(focusColumn = null) {
|
|
12606
|
+
if (!this._columnList) return;
|
|
12607
|
+
const visible = this._visibleColumns();
|
|
12608
|
+
const current = [...this._columnList.querySelectorAll(".zx-table__column-toggle")];
|
|
12609
|
+
const reusable = current.length === this._columns.length && current.every((control, index) => (
|
|
12610
|
+
/** @type {HTMLInputElement} */
|
|
12611
|
+
control.value === this._columns[index].id
|
|
12612
|
+
));
|
|
12613
|
+
if (reusable) {
|
|
12614
|
+
current.forEach((control, index) => {
|
|
12615
|
+
const input = (
|
|
12616
|
+
/** @type {HTMLInputElement} */
|
|
12617
|
+
control
|
|
12618
|
+
);
|
|
12619
|
+
const checked = !this._hiddenColumns.has(this._columns[index].id);
|
|
12620
|
+
input.checked = checked;
|
|
12621
|
+
input.disabled = checked && visible.length === 1;
|
|
12622
|
+
});
|
|
12623
|
+
return;
|
|
12624
|
+
}
|
|
12625
|
+
this._columnList.replaceChildren(...this._columns.map((column) => {
|
|
12626
|
+
const checked = !this._hiddenColumns.has(column.id);
|
|
12627
|
+
return h(
|
|
12628
|
+
"label",
|
|
12629
|
+
{ class: "zx-table__column-option" },
|
|
12630
|
+
h("input", {
|
|
12631
|
+
class: "zx-table__column-toggle",
|
|
12632
|
+
type: "checkbox",
|
|
12633
|
+
value: column.id,
|
|
12634
|
+
checked,
|
|
12635
|
+
disabled: checked && visible.length === 1
|
|
12636
|
+
}),
|
|
12637
|
+
h("span", {}, column.label)
|
|
12638
|
+
);
|
|
12639
|
+
}));
|
|
12640
|
+
if (focusColumn !== null) {
|
|
12641
|
+
const control = [...this._columnList.querySelectorAll(".zx-table__column-toggle")].find((candidate) => (
|
|
12642
|
+
/** @type {HTMLInputElement} */
|
|
12643
|
+
candidate.value === focusColumn
|
|
12644
|
+
));
|
|
12645
|
+
queueMicrotask(() => {
|
|
12646
|
+
if (control?.isConnected) control.focus();
|
|
12647
|
+
});
|
|
12648
|
+
}
|
|
12649
|
+
}
|
|
12650
|
+
/** @param {DragEvent} event @returns {void} */
|
|
12651
|
+
_handleRowDragStart(event) {
|
|
12652
|
+
const handle = (
|
|
12653
|
+
/** @type {Element|null} */
|
|
12654
|
+
event.target?.closest?.(".zx-table__row-handle")
|
|
12655
|
+
);
|
|
12656
|
+
const rowElement = handle?.closest("tr[data-row]");
|
|
12657
|
+
const meta = rowElement ? this._rowMeta.get(rowElement) : null;
|
|
12658
|
+
if (!handle || !meta || this._sort && this.options.sortMode === "local") {
|
|
12659
|
+
event.preventDefault();
|
|
12660
|
+
return;
|
|
12661
|
+
}
|
|
12662
|
+
this._dragId = meta.id;
|
|
12663
|
+
rowElement.dataset.dragging = "true";
|
|
12664
|
+
event.dataTransfer?.setData("text/plain", "zx-table-row");
|
|
12665
|
+
if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
|
|
12666
|
+
}
|
|
12667
|
+
/** @param {DragEvent} event @returns {void} */
|
|
12668
|
+
_handleRowDragOver(event) {
|
|
12669
|
+
if (this._dragId == null) return;
|
|
12670
|
+
const rowElement = (
|
|
12671
|
+
/** @type {Element|null} */
|
|
12672
|
+
event.target?.closest?.("tr[data-row]")
|
|
12673
|
+
);
|
|
12674
|
+
const meta = rowElement ? this._rowMeta.get(rowElement) : null;
|
|
12675
|
+
if (!meta || Object.is(meta.id, this._dragId) || !this._canMoveTogether(this._dragId, meta.id)) return;
|
|
12676
|
+
event.preventDefault();
|
|
12677
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
|
|
12678
|
+
this._clearDropMarkers();
|
|
12679
|
+
const rectangle = rowElement.getBoundingClientRect();
|
|
12680
|
+
rowElement.dataset.drop = event.clientY >= rectangle.top + rectangle.height / 2 ? "after" : "before";
|
|
12681
|
+
}
|
|
12682
|
+
/** @param {DragEvent} event @returns {void} */
|
|
12683
|
+
_handleRowDrop(event) {
|
|
12684
|
+
if (this._dragId == null) return;
|
|
12685
|
+
const rowElement = (
|
|
12686
|
+
/** @type {Element|null} */
|
|
12687
|
+
event.target?.closest?.("tr[data-row]")
|
|
12688
|
+
);
|
|
12689
|
+
const meta = rowElement ? this._rowMeta.get(rowElement) : null;
|
|
12690
|
+
const position2 = rowElement?.dataset.drop === "after" ? "after" : "before";
|
|
12691
|
+
if (meta && this._canMoveTogether(this._dragId, meta.id)) {
|
|
12692
|
+
event.preventDefault();
|
|
12693
|
+
this.moveRow(this._dragId, meta.id, position2);
|
|
12694
|
+
}
|
|
12695
|
+
this._clearRowDrag();
|
|
12696
|
+
}
|
|
12697
|
+
/** @param {KeyboardEvent} event @returns {void} */
|
|
12698
|
+
_handleRowHandleKeydown(event) {
|
|
12699
|
+
const handle = (
|
|
12700
|
+
/** @type {Element|null} */
|
|
12701
|
+
event.target?.closest?.(".zx-table__row-handle")
|
|
12702
|
+
);
|
|
12703
|
+
const rowElement = handle?.closest("tr[data-row]");
|
|
12704
|
+
const meta = rowElement ? this._rowMeta.get(rowElement) : null;
|
|
12705
|
+
if (!handle || !meta) return;
|
|
12706
|
+
if (event.key === " " || event.key === "Enter") {
|
|
12707
|
+
event.preventDefault();
|
|
12708
|
+
this._grabbedId = Object.is(this._grabbedId, meta.id) ? null : meta.id;
|
|
12709
|
+
handle.setAttribute("aria-pressed", String(this._grabbedId != null));
|
|
12710
|
+
const status = this._rowMoveMessage ?? this._editMessage;
|
|
12711
|
+
if (status) status.textContent = this._grabbedId == null ? "Row dropped" : "Row grabbed. Use the arrow keys to move it.";
|
|
12712
|
+
return;
|
|
12713
|
+
}
|
|
12714
|
+
if (event.key === "Escape" && this._grabbedId != null) {
|
|
12715
|
+
event.preventDefault();
|
|
12716
|
+
this._grabbedId = null;
|
|
12717
|
+
handle.setAttribute("aria-pressed", "false");
|
|
12718
|
+
return;
|
|
12719
|
+
}
|
|
12720
|
+
if (!Object.is(this._grabbedId, meta.id) || !["ArrowUp", "ArrowDown"].includes(event.key)) return;
|
|
12721
|
+
event.preventDefault();
|
|
12722
|
+
const siblings = this._siblingIds(meta.id);
|
|
12723
|
+
const index = siblings.findIndex((id) => Object.is(id, meta.id));
|
|
12724
|
+
const targetId = siblings[index + (event.key === "ArrowUp" ? -1 : 1)];
|
|
12725
|
+
if (targetId !== void 0) this.moveRow(meta.id, targetId, event.key === "ArrowUp" ? "before" : "after");
|
|
12726
|
+
}
|
|
12727
|
+
/** @param {unknown} id @returns {unknown[]} */
|
|
12728
|
+
_siblingIds(id) {
|
|
12729
|
+
const row2 = this.getRow(id);
|
|
12730
|
+
if (!row2) return [];
|
|
12731
|
+
const parent = this._hierarchy ? readRowValue(this._hierarchy.parentId, row2) : null;
|
|
12732
|
+
return this._data.filter((candidate) => !this._hierarchy || sameHierarchyParent(readRowValue(this._hierarchy.parentId, candidate), parent)).map((candidate) => this._idFor(candidate));
|
|
12733
|
+
}
|
|
12734
|
+
/** @param {unknown} left @param {unknown} right @returns {boolean} */
|
|
12735
|
+
_canMoveTogether(left, right) {
|
|
12736
|
+
if (!this._hierarchy) return true;
|
|
12737
|
+
const a = this.getRow(left);
|
|
12738
|
+
const b = this.getRow(right);
|
|
12739
|
+
return Boolean(a && b && sameHierarchyParent(
|
|
12740
|
+
readRowValue(this._hierarchy.parentId, a),
|
|
12741
|
+
readRowValue(this._hierarchy.parentId, b)
|
|
12742
|
+
));
|
|
12743
|
+
}
|
|
12744
|
+
/** @param {unknown} id @returns {HTMLElement|null} */
|
|
12745
|
+
_rowHandle(id) {
|
|
12746
|
+
return (
|
|
12747
|
+
/** @type {HTMLElement|null} */
|
|
12748
|
+
this._rowElements.get(id)?.[0]?.querySelector(".zx-table__row-handle") ?? null
|
|
12749
|
+
);
|
|
12750
|
+
}
|
|
12751
|
+
/** @returns {void} */
|
|
12752
|
+
_clearDropMarkers() {
|
|
12753
|
+
for (const row2 of this._tbody.querySelectorAll("tr[data-drop]")) delete row2.dataset.drop;
|
|
12754
|
+
}
|
|
12755
|
+
/** @returns {void} */
|
|
12756
|
+
_clearRowDrag() {
|
|
12757
|
+
for (const row2 of this._tbody.querySelectorAll("tr[data-dragging]")) delete row2.dataset.dragging;
|
|
12758
|
+
this._clearDropMarkers();
|
|
12759
|
+
this._dragId = null;
|
|
12760
|
+
}
|
|
12761
|
+
/** @returns {string|null} */
|
|
12762
|
+
_hierarchyColumn() {
|
|
12763
|
+
if (!this._hierarchy) return null;
|
|
12764
|
+
return this._visibleColumns().some((column) => column.id === this._hierarchy.column) ? this._hierarchy.column : this._visibleColumns()[0]?.id ?? null;
|
|
12765
|
+
}
|
|
12331
12766
|
/** @returns {void} */
|
|
12332
12767
|
_renderColumns() {
|
|
12333
12768
|
const fragment2 = document.createDocumentFragment();
|
|
12769
|
+
if (this.options.rowReorder) fragment2.append(h("col", { class: "zx-table__reorder-column" }));
|
|
12334
12770
|
if (this.options.selectable === "multi") {
|
|
12335
12771
|
fragment2.append(h("col", { class: "zx-table__selection-column" }));
|
|
12336
12772
|
}
|
|
12337
|
-
const
|
|
12773
|
+
const columns = this._visibleColumns();
|
|
12774
|
+
const fractions = columns.map((column) => parseFraction(column.width));
|
|
12338
12775
|
const total = fractions.reduce((sum, value) => sum + value, 0);
|
|
12339
12776
|
this._table.dataset.layout = total > 0 ? "fixed" : "auto";
|
|
12340
|
-
|
|
12777
|
+
columns.forEach((column, index) => {
|
|
12341
12778
|
const col = h("col");
|
|
12342
12779
|
if (fractions[index] > 0) col.style.width = `${fractions[index] / total * 100}%`;
|
|
12343
12780
|
else if (column.width && column.width !== "auto") col.style.width = String(column.width);
|
|
@@ -12349,6 +12786,11 @@ var zx = (() => {
|
|
|
12349
12786
|
_renderHeader() {
|
|
12350
12787
|
const row2 = h("tr", { class: "zx-table__header-row" });
|
|
12351
12788
|
if (this._growStep) row2.setAttribute("aria-rowindex", "1");
|
|
12789
|
+
if (this.options.rowReorder) row2.append(h("th", {
|
|
12790
|
+
class: "zx-table__reorder-header",
|
|
12791
|
+
scope: "col",
|
|
12792
|
+
ariaLabel: "Row order"
|
|
12793
|
+
}));
|
|
12352
12794
|
if (this.options.selectable === "multi") {
|
|
12353
12795
|
this._selectAll = h("input", {
|
|
12354
12796
|
class: "zx-table__checkbox zx-table__select-all",
|
|
@@ -12374,7 +12816,7 @@ var zx = (() => {
|
|
|
12374
12816
|
this._selectAll = null;
|
|
12375
12817
|
}
|
|
12376
12818
|
this._headers = /* @__PURE__ */ new Map();
|
|
12377
|
-
for (const column of this.
|
|
12819
|
+
for (const column of this._visibleColumns()) {
|
|
12378
12820
|
const th = h("th", { scope: "col" });
|
|
12379
12821
|
const align = column.align ?? (isNumericColumn(column) ? "end" : null);
|
|
12380
12822
|
if (align) th.dataset.align = align;
|
|
@@ -12414,7 +12856,7 @@ var zx = (() => {
|
|
|
12414
12856
|
{ class: "zx-table__empty-row" },
|
|
12415
12857
|
h("td", {
|
|
12416
12858
|
class: "zx-table__empty",
|
|
12417
|
-
colspan: this.
|
|
12859
|
+
colspan: this._visibleColumns().length + (this.options.selectable === "multi" ? 1 : 0) + (this.options.rowReorder ? 1 : 0)
|
|
12418
12860
|
}, this._emptyContent())
|
|
12419
12861
|
));
|
|
12420
12862
|
} else {
|
|
@@ -12423,6 +12865,7 @@ var zx = (() => {
|
|
|
12423
12865
|
});
|
|
12424
12866
|
}
|
|
12425
12867
|
this._tbody.replaceChildren(fragment2);
|
|
12868
|
+
this._table.setAttribute("aria-colcount", String(this._visibleColumns().length + (this.options.selectable === "multi" ? 1 : 0) + (this.options.rowReorder ? 1 : 0)));
|
|
12426
12869
|
if (this._growStep) this._table.setAttribute("aria-rowcount", String(projected.length + 1));
|
|
12427
12870
|
this._syncSelectAll();
|
|
12428
12871
|
this._syncGrowing();
|
|
@@ -12466,6 +12909,19 @@ var zx = (() => {
|
|
|
12466
12909
|
const elements = this._rowElements.get(id) ?? [];
|
|
12467
12910
|
elements.push(tr);
|
|
12468
12911
|
this._rowElements.set(id, elements);
|
|
12912
|
+
if (this.options.rowReorder) {
|
|
12913
|
+
const handle = h("button", {
|
|
12914
|
+
class: "zx-table__row-handle",
|
|
12915
|
+
type: "button",
|
|
12916
|
+
draggable: true,
|
|
12917
|
+
dataset: { rowHandle: "" },
|
|
12918
|
+
ariaLabel: `Move row ${index + 1}`,
|
|
12919
|
+
ariaPressed: String(Object.is(this._grabbedId, id)),
|
|
12920
|
+
disabled: Boolean(this._sort && this.options.sortMode === "local"),
|
|
12921
|
+
title: this._sort && this.options.sortMode === "local" ? "Clear sorting to reorder rows" : null
|
|
12922
|
+
}, icon("drag", { size: 14 }));
|
|
12923
|
+
tr.append(h("td", { class: "zx-table__reorder-cell" }, handle));
|
|
12924
|
+
}
|
|
12469
12925
|
if (this.options.selectable === "multi") {
|
|
12470
12926
|
const checkbox = h("input", {
|
|
12471
12927
|
class: "zx-table__checkbox zx-table__row-checkbox",
|
|
@@ -12475,7 +12931,7 @@ var zx = (() => {
|
|
|
12475
12931
|
});
|
|
12476
12932
|
tr.append(h("td", { class: "zx-table__selection-cell" }, checkbox));
|
|
12477
12933
|
}
|
|
12478
|
-
this.
|
|
12934
|
+
this._visibleColumns().forEach((column) => {
|
|
12479
12935
|
const cell = (
|
|
12480
12936
|
/** @type {HTMLTableCellElement} */
|
|
12481
12937
|
h("td")
|
|
@@ -12511,7 +12967,7 @@ var zx = (() => {
|
|
|
12511
12967
|
const content = h("span", { class: "zx-table__cell-content" });
|
|
12512
12968
|
if (value && typeof value === "object" && typeof value.nodeType === "number") content.append(value);
|
|
12513
12969
|
else if (value != null) content.append(document.createTextNode(String(value)));
|
|
12514
|
-
if (!this._hierarchy || column.id !== this.
|
|
12970
|
+
if (!this._hierarchy || column.id !== this._hierarchyColumn() || !tree) {
|
|
12515
12971
|
cell.replaceChildren(...content.childNodes);
|
|
12516
12972
|
return;
|
|
12517
12973
|
}
|
|
@@ -12542,6 +12998,7 @@ var zx = (() => {
|
|
|
12542
12998
|
if (!rowElement || !this._tbody.contains(rowElement)) return;
|
|
12543
12999
|
const meta = this._rowMeta.get(rowElement);
|
|
12544
13000
|
if (!meta) return;
|
|
13001
|
+
if (target.closest(".zx-table__row-handle")) return;
|
|
12545
13002
|
if (this._edit) {
|
|
12546
13003
|
this.commitEdit();
|
|
12547
13004
|
if (this._edit) return;
|
|
@@ -12561,6 +13018,14 @@ var zx = (() => {
|
|
|
12561
13018
|
);
|
|
12562
13019
|
return;
|
|
12563
13020
|
}
|
|
13021
|
+
if (isInteractiveCellContent(target)) return;
|
|
13022
|
+
if (this._editMode !== false && this._editTrigger === "single") {
|
|
13023
|
+
const cell = target?.closest('td[data-editable="true"]');
|
|
13024
|
+
if (cell && this._tbody.contains(cell)) {
|
|
13025
|
+
this.startEdit(meta.id, String(cell.dataset.column));
|
|
13026
|
+
return;
|
|
13027
|
+
}
|
|
13028
|
+
}
|
|
12564
13029
|
this.emit("rowclick", { ...meta, event });
|
|
12565
13030
|
if (this.options.selectable === "single" && !this._selected.has(meta.id)) {
|
|
12566
13031
|
this._selected = /* @__PURE__ */ new Set([meta.id]);
|
|
@@ -12581,7 +13046,8 @@ var zx = (() => {
|
|
|
12581
13046
|
if (!rowElement || !this._tbody.contains(rowElement)) return;
|
|
12582
13047
|
const meta = this._rowMeta.get(rowElement);
|
|
12583
13048
|
if (!meta) return;
|
|
12584
|
-
if (
|
|
13049
|
+
if (isInteractiveCellContent(target)) return;
|
|
13050
|
+
if (this._editMode !== false && this._editTrigger === "double") {
|
|
12585
13051
|
const cell = target?.closest('td[data-editable="true"]');
|
|
12586
13052
|
if (cell && this._tbody.contains(cell)) {
|
|
12587
13053
|
this.startEdit(meta.id, String(cell.dataset.column));
|
|
@@ -12883,13 +13349,14 @@ var zx = (() => {
|
|
|
12883
13349
|
* @returns {{id: unknown, columnId: string, index: number}|null} Next cell, or null at the end.
|
|
12884
13350
|
*/
|
|
12885
13351
|
_nextEditableCell(rowIndex, columnId, direction) {
|
|
12886
|
-
const
|
|
13352
|
+
const columns = this._visibleColumns();
|
|
13353
|
+
const columnCount = columns.length;
|
|
12887
13354
|
if (rowIndex < 0 || columnCount === 0) return null;
|
|
12888
13355
|
const visible = this._visibleData();
|
|
12889
13356
|
const startingId = this._idFor(this._data[rowIndex]);
|
|
12890
13357
|
let row2 = visible.findIndex((candidate) => Object.is(this._idFor(candidate), startingId));
|
|
12891
13358
|
if (row2 < 0) return null;
|
|
12892
|
-
let column =
|
|
13359
|
+
let column = columns.findIndex((candidate) => candidate.id === columnId);
|
|
12893
13360
|
const limit = visible.length * columnCount + columnCount;
|
|
12894
13361
|
for (let step = 0; step < limit; step += 1) {
|
|
12895
13362
|
column += direction;
|
|
@@ -12902,7 +13369,7 @@ var zx = (() => {
|
|
|
12902
13369
|
}
|
|
12903
13370
|
if (row2 < 0 || row2 >= visible.length) return null;
|
|
12904
13371
|
const candidateRow = visible[row2];
|
|
12905
|
-
const candidateColumn =
|
|
13372
|
+
const candidateColumn = columns[column];
|
|
12906
13373
|
if (resolveEditable(candidateColumn, candidateRow) !== false) {
|
|
12907
13374
|
return {
|
|
12908
13375
|
id: this._idFor(candidateRow),
|
|
@@ -13145,6 +13612,53 @@ var zx = (() => {
|
|
|
13145
13612
|
}
|
|
13146
13613
|
return result;
|
|
13147
13614
|
}
|
|
13615
|
+
function reorderTableRows(rows, rowId, id, targetId, position2 = "before", parentId = null) {
|
|
13616
|
+
if (!Array.isArray(rows) || Object.is(id, targetId)) return null;
|
|
13617
|
+
const from = rows.findIndex((row2) => Object.is(readRowValue(rowId, row2), id));
|
|
13618
|
+
const target = rows.findIndex((row2) => Object.is(readRowValue(rowId, row2), targetId));
|
|
13619
|
+
if (from < 0 || target < 0) return null;
|
|
13620
|
+
if (parentId && !sameHierarchyParent(
|
|
13621
|
+
readRowValue(parentId, rows[from]),
|
|
13622
|
+
readRowValue(parentId, rows[target])
|
|
13623
|
+
)) return null;
|
|
13624
|
+
if (parentId) {
|
|
13625
|
+
const forest = hierarchyForest(rows, rowId, parentId);
|
|
13626
|
+
const movingNode = forest.nodes[from];
|
|
13627
|
+
const targetNode = forest.nodes[target];
|
|
13628
|
+
if (!movingNode || !targetNode || movingNode.parent !== targetNode.parent) return null;
|
|
13629
|
+
const branchNodes = (root) => {
|
|
13630
|
+
const result = [];
|
|
13631
|
+
const stack2 = [root];
|
|
13632
|
+
while (stack2.length) {
|
|
13633
|
+
const node = (
|
|
13634
|
+
/** @type {TableHierarchyNode} */
|
|
13635
|
+
stack2.pop()
|
|
13636
|
+
);
|
|
13637
|
+
result.push(node);
|
|
13638
|
+
for (let index = node.children.length - 1; index >= 0; index -= 1) stack2.push(node.children[index]);
|
|
13639
|
+
}
|
|
13640
|
+
return result;
|
|
13641
|
+
};
|
|
13642
|
+
const moving2 = new Set(branchNodes(movingNode).map((node) => node.index));
|
|
13643
|
+
const targetBranch = new Set(branchNodes(targetNode).map((node) => node.index));
|
|
13644
|
+
const block = rows.filter((_row, index) => moving2.has(index));
|
|
13645
|
+
const remaining = rows.filter((_row, index) => !moving2.has(index));
|
|
13646
|
+
const remainingSourceIndexes = rows.map((_row, index) => index).filter((index) => !moving2.has(index));
|
|
13647
|
+
let insertion = remainingSourceIndexes.findIndex((index) => index === target);
|
|
13648
|
+
if (position2 === "after") {
|
|
13649
|
+
const targetPositions = remainingSourceIndexes.map((sourceIndex, index) => targetBranch.has(sourceIndex) ? index : -1).filter((index) => index >= 0);
|
|
13650
|
+
insertion = Math.max(...targetPositions) + 1;
|
|
13651
|
+
}
|
|
13652
|
+
if (insertion < 0) return null;
|
|
13653
|
+
remaining.splice(insertion, 0, ...block);
|
|
13654
|
+
return remaining;
|
|
13655
|
+
}
|
|
13656
|
+
const next = [...rows];
|
|
13657
|
+
const [moving] = next.splice(from, 1);
|
|
13658
|
+
const adjustedTarget = next.findIndex((row2) => Object.is(readRowValue(rowId, row2), targetId));
|
|
13659
|
+
next.splice(adjustedTarget + (position2 === "after" ? 1 : 0), 0, moving);
|
|
13660
|
+
return next;
|
|
13661
|
+
}
|
|
13148
13662
|
function resolveEditable(column, row2) {
|
|
13149
13663
|
if (!column || typeof column !== "object") return false;
|
|
13150
13664
|
if (typeof column.editor === "function") {
|
|
@@ -13225,7 +13739,8 @@ var zx = (() => {
|
|
|
13225
13739
|
for (const node of nodes) if (!byId.has(node.id)) byId.set(node.id, node);
|
|
13226
13740
|
const parentOf = /* @__PURE__ */ new Map();
|
|
13227
13741
|
for (const node of nodes) {
|
|
13228
|
-
const
|
|
13742
|
+
const parentValue = readRowValue(parentId, node.row);
|
|
13743
|
+
const parent = parentValue == null ? null : byId.get(parentValue) ?? null;
|
|
13229
13744
|
parentOf.set(node, parent === node ? null : parent);
|
|
13230
13745
|
}
|
|
13231
13746
|
for (const node of nodes) {
|
|
@@ -13254,6 +13769,9 @@ var zx = (() => {
|
|
|
13254
13769
|
function readRowValue(accessor, row2) {
|
|
13255
13770
|
return typeof accessor === "function" ? accessor(row2) : row2?.[accessor];
|
|
13256
13771
|
}
|
|
13772
|
+
function sameHierarchyParent(left, right) {
|
|
13773
|
+
return left == null && right == null || Object.is(left, right);
|
|
13774
|
+
}
|
|
13257
13775
|
function normalizeHierarchy(option, columns) {
|
|
13258
13776
|
if (!option || typeof option !== "object") return null;
|
|
13259
13777
|
const parentId = option.parentId;
|
|
@@ -13273,6 +13791,23 @@ var zx = (() => {
|
|
|
13273
13791
|
function isInsideEditor(target) {
|
|
13274
13792
|
return Boolean(target?.closest?.(".zx-table__editor"));
|
|
13275
13793
|
}
|
|
13794
|
+
function isInteractiveCellContent(target) {
|
|
13795
|
+
const cell = target?.closest?.("td[data-column]");
|
|
13796
|
+
const interactive = target?.closest?.([
|
|
13797
|
+
"a[href]",
|
|
13798
|
+
"button",
|
|
13799
|
+
"input",
|
|
13800
|
+
"select",
|
|
13801
|
+
"textarea",
|
|
13802
|
+
"summary",
|
|
13803
|
+
'[contenteditable="true"]',
|
|
13804
|
+
'[role="button"]',
|
|
13805
|
+
'[role="link"]',
|
|
13806
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
13807
|
+
"[data-zx-table-no-edit]"
|
|
13808
|
+
].join(","));
|
|
13809
|
+
return Boolean(cell && interactive && interactive !== cell && cell.contains(interactive));
|
|
13810
|
+
}
|
|
13276
13811
|
function deferToEditor(target) {
|
|
13277
13812
|
if (target.closest("[popover]")) return true;
|
|
13278
13813
|
return target.getAttribute?.("aria-expanded") === "true";
|
|
@@ -15761,11 +16296,11 @@ var zx = (() => {
|
|
|
15761
16296
|
this.setFooter(this.options.footer);
|
|
15762
16297
|
this.setLink(this.options.link);
|
|
15763
16298
|
return root;
|
|
15764
|
-
} catch (
|
|
16299
|
+
} catch (error2) {
|
|
15765
16300
|
this._actionAbort?.abort();
|
|
15766
16301
|
this._linkAbort?.abort();
|
|
15767
16302
|
if (!created) restoreTarget(root, this._original);
|
|
15768
|
-
throw
|
|
16303
|
+
throw error2;
|
|
15769
16304
|
}
|
|
15770
16305
|
}
|
|
15771
16306
|
/** Replaces the card title. @param {string} title Next title. @returns {this} */
|
|
@@ -15809,9 +16344,9 @@ var zx = (() => {
|
|
|
15809
16344
|
if (onclick) control.addEventListener("click", onclick, { signal: nextAbort.signal });
|
|
15810
16345
|
return control;
|
|
15811
16346
|
});
|
|
15812
|
-
} catch (
|
|
16347
|
+
} catch (error2) {
|
|
15813
16348
|
nextAbort.abort();
|
|
15814
|
-
throw
|
|
16349
|
+
throw error2;
|
|
15815
16350
|
}
|
|
15816
16351
|
this._actionAbort?.abort();
|
|
15817
16352
|
this._actionAbort = nextAbort;
|
|
@@ -19252,6 +19787,107 @@ var zx = (() => {
|
|
|
19252
19787
|
return value.toLocaleString();
|
|
19253
19788
|
}
|
|
19254
19789
|
|
|
19790
|
+
// src/components/app-icon/app-icon.js
|
|
19791
|
+
var AppIcon = class extends Component {
|
|
19792
|
+
static cssName = "app-icon";
|
|
19793
|
+
/** @type {Readonly<AppIconOptions>} */
|
|
19794
|
+
static defaults = {
|
|
19795
|
+
icon: null,
|
|
19796
|
+
color: null,
|
|
19797
|
+
size: 36,
|
|
19798
|
+
iconSize: "52%",
|
|
19799
|
+
label: null,
|
|
19800
|
+
badge: null,
|
|
19801
|
+
selected: false,
|
|
19802
|
+
glass: "subtle",
|
|
19803
|
+
class: []
|
|
19804
|
+
};
|
|
19805
|
+
/** @returns {HTMLElement} */
|
|
19806
|
+
render() {
|
|
19807
|
+
this._createdRoot = this.el === null;
|
|
19808
|
+
const root = (
|
|
19809
|
+
/** @type {HTMLElement} */
|
|
19810
|
+
this.el ?? h("span")
|
|
19811
|
+
);
|
|
19812
|
+
this.el = root;
|
|
19813
|
+
this._snapshot = this._createdRoot ? null : snapshotTarget(root);
|
|
19814
|
+
this._state = { ...this.options };
|
|
19815
|
+
this._appliedClasses = [];
|
|
19816
|
+
this._sync();
|
|
19817
|
+
return root;
|
|
19818
|
+
}
|
|
19819
|
+
/** Updates identity or presentation without replacing the root. @param {Partial<AppIconOptions>} values @returns {this} */
|
|
19820
|
+
set(values = {}) {
|
|
19821
|
+
if (!values || typeof values !== "object") return this;
|
|
19822
|
+
Object.assign(this._state, values);
|
|
19823
|
+
this._sync();
|
|
19824
|
+
return this;
|
|
19825
|
+
}
|
|
19826
|
+
/** Restores an enhanced target. @returns {void} */
|
|
19827
|
+
destroy() {
|
|
19828
|
+
const root = this.el;
|
|
19829
|
+
const snapshot10 = this._snapshot;
|
|
19830
|
+
super.destroy();
|
|
19831
|
+
if (!this._createdRoot && root) restoreTarget(root, snapshot10);
|
|
19832
|
+
}
|
|
19833
|
+
/** @returns {void} */
|
|
19834
|
+
_sync() {
|
|
19835
|
+
const state = this._state;
|
|
19836
|
+
this.el.classList.remove(...this._appliedClasses);
|
|
19837
|
+
this._appliedClasses = [state.class].flat().flatMap((value) => String(value ?? "").split(/\s+/)).filter(Boolean);
|
|
19838
|
+
this.el.classList.add(...this._appliedClasses);
|
|
19839
|
+
const size2 = cssLength2(state.size, "36px");
|
|
19840
|
+
const glyphSize = cssLength2(state.iconSize, "52%");
|
|
19841
|
+
this.el.style.setProperty("--zx-app-icon-size", size2);
|
|
19842
|
+
this.el.style.setProperty("--zx-app-icon-glyph-size", glyphSize);
|
|
19843
|
+
if (isCssColor(state.color)) this.el.style.setProperty("--zx-app-icon-color", String(state.color));
|
|
19844
|
+
else this.el.style.removeProperty("--zx-app-icon-color");
|
|
19845
|
+
this.el.dataset.glass = normalizeGlass(state.glass);
|
|
19846
|
+
this.el.toggleAttribute("data-selected", Boolean(state.selected));
|
|
19847
|
+
const label = state.label == null ? "" : String(state.label).trim();
|
|
19848
|
+
if (label) {
|
|
19849
|
+
this.el.setAttribute("role", "img");
|
|
19850
|
+
this.el.setAttribute("aria-label", label);
|
|
19851
|
+
this.el.removeAttribute("aria-hidden");
|
|
19852
|
+
} else {
|
|
19853
|
+
this.el.removeAttribute("role");
|
|
19854
|
+
this.el.removeAttribute("aria-label");
|
|
19855
|
+
this.el.setAttribute("aria-hidden", "true");
|
|
19856
|
+
}
|
|
19857
|
+
const visual = resolveVisual(state.icon);
|
|
19858
|
+
const badge2 = state.badge == null || state.badge === "" ? null : h("span", {
|
|
19859
|
+
class: "zx-app-icon__badge",
|
|
19860
|
+
ariaHidden: "true"
|
|
19861
|
+
}, String(state.badge));
|
|
19862
|
+
const children = [
|
|
19863
|
+
h("span", { class: "zx-app-icon__shine", ariaHidden: "true" }),
|
|
19864
|
+
h("span", { class: "zx-app-icon__glyph", ariaHidden: "true" }, visual)
|
|
19865
|
+
];
|
|
19866
|
+
if (badge2) children.push(badge2);
|
|
19867
|
+
this.el.replaceChildren(...children);
|
|
19868
|
+
}
|
|
19869
|
+
};
|
|
19870
|
+
function appIcon(options = {}) {
|
|
19871
|
+
return new AppIcon(null, options).toElement();
|
|
19872
|
+
}
|
|
19873
|
+
function resolveVisual(value) {
|
|
19874
|
+
const resolved = typeof value === "function" ? value() : value;
|
|
19875
|
+
if (typeof resolved === "string") return icon(resolved, { size: "1em" });
|
|
19876
|
+
return resolved && typeof resolved === "object" && typeof resolved.nodeType === "number" ? (
|
|
19877
|
+
/** @type {Node} */
|
|
19878
|
+
resolved
|
|
19879
|
+
) : null;
|
|
19880
|
+
}
|
|
19881
|
+
function cssLength2(value, fallback) {
|
|
19882
|
+
if (typeof value === "number" && Number.isFinite(value)) return `${Math.max(1, value)}px`;
|
|
19883
|
+
const text4 = String(value ?? "").trim();
|
|
19884
|
+
return text4 && !/[;{}]/.test(text4) ? text4 : fallback;
|
|
19885
|
+
}
|
|
19886
|
+
function normalizeGlass(value) {
|
|
19887
|
+
if (value === false || value === "none") return "none";
|
|
19888
|
+
return value === "strong" ? "strong" : "subtle";
|
|
19889
|
+
}
|
|
19890
|
+
|
|
19255
19891
|
// src/components/launcher/launcher.js
|
|
19256
19892
|
var Launcher = class extends Component {
|
|
19257
19893
|
static cssName = "launcher";
|
|
@@ -19521,9 +20157,9 @@ var zx = (() => {
|
|
|
19521
20157
|
itemOrder: item.itemOrder ?? (source.order === "source" ? itemOrder : void 0)
|
|
19522
20158
|
})));
|
|
19523
20159
|
this._renderResults();
|
|
19524
|
-
} catch (
|
|
20160
|
+
} catch (error2) {
|
|
19525
20161
|
if (request.signal.aborted || sequence !== this._requestSequence) return;
|
|
19526
|
-
this.emit("error", { source, error });
|
|
20162
|
+
this.emit("error", { source, error: error2 });
|
|
19527
20163
|
}
|
|
19528
20164
|
}));
|
|
19529
20165
|
if (request.signal.aborted || sequence !== this._requestSequence) return;
|
|
@@ -20293,7 +20929,7 @@ var zx = (() => {
|
|
|
20293
20929
|
this.#content = this.options.content;
|
|
20294
20930
|
this.#disabled = Boolean(this.options.disabled);
|
|
20295
20931
|
this.#describedBy = resolved.getAttribute("aria-describedby");
|
|
20296
|
-
this.el.style.setProperty("--zx-tooltip-max-inline-size",
|
|
20932
|
+
this.el.style.setProperty("--zx-tooltip-max-inline-size", cssLength3(this.options.maxWidth));
|
|
20297
20933
|
const trigger = TRIGGERS.has(this.options.trigger) ? this.options.trigger : "both";
|
|
20298
20934
|
if (trigger === "manual") return;
|
|
20299
20935
|
if (trigger === "hover" || trigger === "both") {
|
|
@@ -20551,7 +21187,7 @@ var zx = (() => {
|
|
|
20551
21187
|
if (!tokens.includes(token)) tokens.push(token);
|
|
20552
21188
|
return tokens.join(" ");
|
|
20553
21189
|
}
|
|
20554
|
-
function
|
|
21190
|
+
function cssLength3(value) {
|
|
20555
21191
|
return typeof value === "number" && Number.isFinite(value) ? `${value}px` : String(value);
|
|
20556
21192
|
}
|
|
20557
21193
|
function isFocusVisible(element) {
|
|
@@ -23052,7 +23688,7 @@ var zx = (() => {
|
|
|
23052
23688
|
* @param {GridOptions & import('../table/table.js').TableOptions} [options={}] Grid/Table options.
|
|
23053
23689
|
*/
|
|
23054
23690
|
constructor(target = null, options = {}) {
|
|
23055
|
-
super(target, options);
|
|
23691
|
+
super(target, { editTrigger: "single", ...options });
|
|
23056
23692
|
}
|
|
23057
23693
|
/**
|
|
23058
23694
|
* Creates an editable hierarchical billing-line grid.
|
|
@@ -23091,6 +23727,9 @@ var zx = (() => {
|
|
|
23091
23727
|
return {
|
|
23092
23728
|
rowId: fields.id,
|
|
23093
23729
|
editMode: "cell",
|
|
23730
|
+
editTrigger: "single",
|
|
23731
|
+
rowReorder: true,
|
|
23732
|
+
columnVisibility: true,
|
|
23094
23733
|
hierarchy: { parentId: fields.parent, column: fields.item, expanded: true },
|
|
23095
23734
|
responsive: "sm",
|
|
23096
23735
|
...tableOptions,
|
|
@@ -23322,8 +23961,8 @@ var zx = (() => {
|
|
|
23322
23961
|
return this;
|
|
23323
23962
|
}
|
|
23324
23963
|
/** Shows an error and destroys the current engine. @param {unknown} error @returns {this} */
|
|
23325
|
-
setError(
|
|
23326
|
-
this._error =
|
|
23964
|
+
setError(error2) {
|
|
23965
|
+
this._error = error2 ?? new Error(String(this.options.errorText));
|
|
23327
23966
|
this._hasError = true;
|
|
23328
23967
|
this._loading = false;
|
|
23329
23968
|
this._destroyHandle();
|
|
@@ -23342,8 +23981,8 @@ var zx = (() => {
|
|
|
23342
23981
|
resize() {
|
|
23343
23982
|
try {
|
|
23344
23983
|
this._handle?.resize?.();
|
|
23345
|
-
} catch (
|
|
23346
|
-
this.setError(
|
|
23984
|
+
} catch (error2) {
|
|
23985
|
+
this.setError(error2);
|
|
23347
23986
|
}
|
|
23348
23987
|
return this;
|
|
23349
23988
|
}
|
|
@@ -23396,8 +24035,8 @@ var zx = (() => {
|
|
|
23396
24035
|
this.emit("ready", { instance: this.getInstance() });
|
|
23397
24036
|
}
|
|
23398
24037
|
if (emitUpdate) this.emit("update", { data: this._data, type: this._type });
|
|
23399
|
-
} catch (
|
|
23400
|
-
this.setError(
|
|
24038
|
+
} catch (error2) {
|
|
24039
|
+
this.setError(error2);
|
|
23401
24040
|
}
|
|
23402
24041
|
}
|
|
23403
24042
|
/** @returns {ChartSpec} */
|
|
@@ -23639,8 +24278,8 @@ var zx = (() => {
|
|
|
23639
24278
|
borderColor: borderColor || void 0
|
|
23640
24279
|
};
|
|
23641
24280
|
}
|
|
23642
|
-
function errorMessage(
|
|
23643
|
-
const message =
|
|
24281
|
+
function errorMessage(error2, fallback) {
|
|
24282
|
+
const message = error2 && typeof error2 === "object" && "message" in error2 ? String(error2.message) : "";
|
|
23644
24283
|
return message.trim() ? message : String(fallback);
|
|
23645
24284
|
}
|
|
23646
24285
|
|
|
@@ -24279,8 +24918,8 @@ var zx = (() => {
|
|
|
24279
24918
|
node[this._childrenKey] = Array.isArray(children) ? children : [];
|
|
24280
24919
|
if (node[this._childrenKey].length === 0) node.hasChildren = false;
|
|
24281
24920
|
return true;
|
|
24282
|
-
} catch (
|
|
24283
|
-
this.emit("error", { error, node, id });
|
|
24921
|
+
} catch (error2) {
|
|
24922
|
+
this.emit("error", { error: error2, node, id });
|
|
24284
24923
|
return false;
|
|
24285
24924
|
} finally {
|
|
24286
24925
|
this._loading.delete(id);
|
|
@@ -24768,9 +25407,9 @@ var zx = (() => {
|
|
|
24768
25407
|
const children = await this.options.load(node);
|
|
24769
25408
|
node[this._childrenKey] = Array.isArray(children) ? children : [];
|
|
24770
25409
|
if (node[this._childrenKey].length === 0) node.hasChildren = false;
|
|
24771
|
-
} catch (
|
|
25410
|
+
} catch (error2) {
|
|
24772
25411
|
node[this._childrenKey] = [];
|
|
24773
|
-
this.emit("error", { error, node, id });
|
|
25412
|
+
this.emit("error", { error: error2, node, id });
|
|
24774
25413
|
} finally {
|
|
24775
25414
|
this._loading.delete(id);
|
|
24776
25415
|
}
|
|
@@ -25200,6 +25839,821 @@ var zx = (() => {
|
|
|
25200
25839
|
}
|
|
25201
25840
|
}
|
|
25202
25841
|
|
|
25842
|
+
// src/components/filter/filter-model.js
|
|
25843
|
+
var filterOperators = Object.freeze([
|
|
25844
|
+
op("contains", "contains", "single", ["text"]),
|
|
25845
|
+
op("notContains", "does not contain", "single", ["text"]),
|
|
25846
|
+
op("eq", "is", "single", ["text", "number", "money", "boolean", "enum", "status", "priority", "country", "currency", "unit", "entity", "date", "datetime"]),
|
|
25847
|
+
op("neq", "is not", "single", ["text", "number", "money", "boolean", "enum", "status", "priority", "country", "currency", "unit", "entity", "date", "datetime"]),
|
|
25848
|
+
op("startsWith", "starts with", "single", ["text"]),
|
|
25849
|
+
op("endsWith", "ends with", "single", ["text"]),
|
|
25850
|
+
op("lt", "is less than", "single", ["number", "money"]),
|
|
25851
|
+
op("lte", "is at most", "single", ["number", "money"]),
|
|
25852
|
+
op("gt", "is greater than", "single", ["number", "money"]),
|
|
25853
|
+
op("gte", "is at least", "single", ["number", "money"]),
|
|
25854
|
+
op("on", "is on", "single", ["date", "datetime"]),
|
|
25855
|
+
op("notOn", "is not on", "single", ["date", "datetime"]),
|
|
25856
|
+
op("before", "is before", "single", ["date", "datetime"]),
|
|
25857
|
+
op("onOrBefore", "is on or before", "single", ["date", "datetime"]),
|
|
25858
|
+
op("after", "is after", "single", ["date", "datetime"]),
|
|
25859
|
+
op("onOrAfter", "is on or after", "single", ["date", "datetime"]),
|
|
25860
|
+
op("between", "is between", "pair", ["number", "money", "date", "datetime"]),
|
|
25861
|
+
op("notBetween", "is not between", "pair", ["number", "money", "date", "datetime"]),
|
|
25862
|
+
op("withinLast", "is within the last", "pair", ["date", "datetime"]),
|
|
25863
|
+
op("withinNext", "is within the next", "pair", ["date", "datetime"]),
|
|
25864
|
+
op("anyOf", "is any of", "many", ["enum", "status", "priority", "country", "currency", "unit", "entity"]),
|
|
25865
|
+
op("noneOf", "is none of", "many", ["enum", "status", "priority", "country", "currency", "unit", "entity"]),
|
|
25866
|
+
op("containsAny", "contains any", "many", ["tags"]),
|
|
25867
|
+
op("containsAll", "contains all", "many", ["tags"]),
|
|
25868
|
+
op("containsNone", "contains none", "many", ["tags"]),
|
|
25869
|
+
op("isEmpty", "is empty", "none", ["text", "number", "money", "date", "datetime", "boolean", "enum", "status", "priority", "country", "currency", "unit", "entity", "tags"]),
|
|
25870
|
+
op("isNotEmpty", "is not empty", "none", ["text", "number", "money", "date", "datetime", "boolean", "enum", "status", "priority", "country", "currency", "unit", "entity", "tags"])
|
|
25871
|
+
]);
|
|
25872
|
+
function emptyFilterAst() {
|
|
25873
|
+
return { version: 1, root: { kind: "group", id: uid("filter-root"), logic: "and", children: [] } };
|
|
25874
|
+
}
|
|
25875
|
+
function filterCondition(initial = {}) {
|
|
25876
|
+
return {
|
|
25877
|
+
kind: "condition",
|
|
25878
|
+
id: String(initial.id || uid("filter-condition")),
|
|
25879
|
+
field: String(initial.field ?? ""),
|
|
25880
|
+
operator: String(initial.operator ?? ""),
|
|
25881
|
+
value: cloneFilterValue(initial.value ?? null)
|
|
25882
|
+
};
|
|
25883
|
+
}
|
|
25884
|
+
function cloneFilterValue(value) {
|
|
25885
|
+
return cloneJson(value);
|
|
25886
|
+
}
|
|
25887
|
+
function filterGroup(logic = "and", children = []) {
|
|
25888
|
+
return { kind: "group", id: uid("filter-group"), logic, children };
|
|
25889
|
+
}
|
|
25890
|
+
function parseFilterAst(value, limits = {}) {
|
|
25891
|
+
const maxDepth = finiteLimit(limits.maxDepth, 3);
|
|
25892
|
+
const maxConditions = finiteLimit(limits.maxConditions, 50);
|
|
25893
|
+
if (!plainObject(value) || value.version !== 1 || !plainObject(value.root)) {
|
|
25894
|
+
throw new TypeError("Filter AST must have version 1 and a root group");
|
|
25895
|
+
}
|
|
25896
|
+
const ids = /* @__PURE__ */ new Set();
|
|
25897
|
+
let conditions = 0;
|
|
25898
|
+
const visit = (raw2, depth, root2 = false) => {
|
|
25899
|
+
if (!plainObject(raw2)) throw new TypeError("Filter nodes must be plain objects");
|
|
25900
|
+
const id = String(raw2.id ?? "").trim();
|
|
25901
|
+
if (!id || ids.has(id)) throw new TypeError("Filter node IDs must be non-empty and unique");
|
|
25902
|
+
ids.add(id);
|
|
25903
|
+
if (raw2.kind === "group") {
|
|
25904
|
+
if (depth > maxDepth) throw new RangeError("Filter group depth exceeds maxDepth");
|
|
25905
|
+
if (raw2.logic !== "and" && raw2.logic !== "or") throw new TypeError("Filter group logic must be and or or");
|
|
25906
|
+
if (!Array.isArray(raw2.children)) throw new TypeError("Filter group children must be an array");
|
|
25907
|
+
return {
|
|
25908
|
+
kind: "group",
|
|
25909
|
+
id,
|
|
25910
|
+
logic: raw2.logic,
|
|
25911
|
+
children: raw2.children.map((child) => visit(child, depth + 1, false))
|
|
25912
|
+
};
|
|
25913
|
+
}
|
|
25914
|
+
if (root2 || raw2.kind !== "condition") throw new TypeError("Filter root must be a group");
|
|
25915
|
+
conditions += 1;
|
|
25916
|
+
if (conditions > maxConditions) throw new RangeError("Filter condition count exceeds maxConditions");
|
|
25917
|
+
return {
|
|
25918
|
+
kind: "condition",
|
|
25919
|
+
id,
|
|
25920
|
+
field: String(raw2.field ?? ""),
|
|
25921
|
+
operator: String(raw2.operator ?? ""),
|
|
25922
|
+
value: cloneJson(raw2.value)
|
|
25923
|
+
};
|
|
25924
|
+
};
|
|
25925
|
+
const root = visit(value.root, 0, true);
|
|
25926
|
+
if (root.kind !== "group") throw new TypeError("Filter root must be a group");
|
|
25927
|
+
return (
|
|
25928
|
+
/** @type {FilterAst} */
|
|
25929
|
+
{ version: 1, root }
|
|
25930
|
+
);
|
|
25931
|
+
}
|
|
25932
|
+
function cloneFilterAst(value) {
|
|
25933
|
+
return parseFilterAst(value, { maxDepth: Infinity, maxConditions: Infinity });
|
|
25934
|
+
}
|
|
25935
|
+
function stringifyFilterAst(value) {
|
|
25936
|
+
return JSON.stringify(cloneFilterAst(value));
|
|
25937
|
+
}
|
|
25938
|
+
function validateFilterAst(ast, fields, operators = filterOperators) {
|
|
25939
|
+
const errors = [];
|
|
25940
|
+
const fieldMap = new Map((Array.isArray(fields) ? fields : []).map((field) => [String(field.id), field]));
|
|
25941
|
+
const operatorMap = new Map((Array.isArray(operators) ? operators : []).map((operator) => [String(operator.id), operator]));
|
|
25942
|
+
const visit = (node, root = false) => {
|
|
25943
|
+
if (node.kind === "group") {
|
|
25944
|
+
if (!root && node.children.length === 0) errors.push(error(node.id, "empty-group", "Nested groups need at least one condition"));
|
|
25945
|
+
node.children.forEach((child) => visit(child));
|
|
25946
|
+
return;
|
|
25947
|
+
}
|
|
25948
|
+
const field = fieldMap.get(node.field);
|
|
25949
|
+
if (!field) errors.push(error(node.id, "unknown-field", node.field ? `Unavailable field: ${node.field}` : "Choose a field"));
|
|
25950
|
+
const operator = operatorMap.get(node.operator);
|
|
25951
|
+
if (!operator) {
|
|
25952
|
+
errors.push(error(node.id, "unknown-operator", node.operator ? `Unavailable operator: ${node.operator}` : "Choose an operator"));
|
|
25953
|
+
return;
|
|
25954
|
+
}
|
|
25955
|
+
if (field) {
|
|
25956
|
+
const allowed = Array.isArray(field.operators) && field.operators.length ? field.operators : operators.filter((candidate) => candidate.types.includes(field.type)).map((candidate) => candidate.id);
|
|
25957
|
+
if (!allowed.includes(operator.id) || !operator.types.includes(field.type)) {
|
|
25958
|
+
errors.push(error(node.id, "operator-type", `${operator.label} is not available for ${field.label}`));
|
|
25959
|
+
}
|
|
25960
|
+
}
|
|
25961
|
+
if (!validArity(operator.arity, node.value)) {
|
|
25962
|
+
errors.push(error(node.id, "value", operator.arity === "pair" ? "Enter both values" : operator.arity === "many" ? "Choose at least one value" : operator.arity === "none" ? "This operator does not accept a value" : "Enter a value"));
|
|
25963
|
+
}
|
|
25964
|
+
};
|
|
25965
|
+
visit(ast.root, true);
|
|
25966
|
+
return { valid: errors.length === 0, errors };
|
|
25967
|
+
}
|
|
25968
|
+
function op(id, label, arity, types) {
|
|
25969
|
+
return Object.freeze({ id, label, arity, types: Object.freeze(types) });
|
|
25970
|
+
}
|
|
25971
|
+
function error(nodeId, code, message) {
|
|
25972
|
+
return { nodeId, code, message };
|
|
25973
|
+
}
|
|
25974
|
+
function validArity(arity, value) {
|
|
25975
|
+
if (arity === "none") return value === null;
|
|
25976
|
+
if (arity === "pair") return Array.isArray(value) && value.length === 2 && value.every(filled);
|
|
25977
|
+
if (arity === "many") return Array.isArray(value) && value.length > 0 && value.every(filled);
|
|
25978
|
+
return filled(value);
|
|
25979
|
+
}
|
|
25980
|
+
function filled(value) {
|
|
25981
|
+
return value !== null && value !== void 0 && !(typeof value === "string" && value.trim() === "");
|
|
25982
|
+
}
|
|
25983
|
+
function cloneJson(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
25984
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
25985
|
+
if (typeof value === "number") {
|
|
25986
|
+
if (!Number.isFinite(value)) throw new TypeError("Filter values cannot contain non-finite numbers");
|
|
25987
|
+
return value;
|
|
25988
|
+
}
|
|
25989
|
+
if (typeof value !== "object" || value instanceof Date || typeof value.nodeType === "number") {
|
|
25990
|
+
throw new TypeError("Filter values must be JSON-safe");
|
|
25991
|
+
}
|
|
25992
|
+
if (seen.has(value)) throw new TypeError("Filter values cannot contain cycles");
|
|
25993
|
+
seen.add(value);
|
|
25994
|
+
if (Array.isArray(value)) {
|
|
25995
|
+
const result2 = value.map((item) => cloneJson(item, seen));
|
|
25996
|
+
seen.delete(value);
|
|
25997
|
+
return result2;
|
|
25998
|
+
}
|
|
25999
|
+
if (!plainObject(value)) throw new TypeError("Filter values must be plain JSON objects");
|
|
26000
|
+
const result = {};
|
|
26001
|
+
for (const [key, item] of Object.entries(value)) {
|
|
26002
|
+
Object.defineProperty(result, key, {
|
|
26003
|
+
value: cloneJson(item, seen),
|
|
26004
|
+
enumerable: true,
|
|
26005
|
+
configurable: true,
|
|
26006
|
+
writable: true
|
|
26007
|
+
});
|
|
26008
|
+
}
|
|
26009
|
+
seen.delete(value);
|
|
26010
|
+
return result;
|
|
26011
|
+
}
|
|
26012
|
+
function plainObject(value) {
|
|
26013
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
26014
|
+
const prototype = Object.getPrototypeOf(value);
|
|
26015
|
+
return prototype === Object.prototype || prototype === null;
|
|
26016
|
+
}
|
|
26017
|
+
function finiteLimit(value, fallback) {
|
|
26018
|
+
if (value === Infinity) return Infinity;
|
|
26019
|
+
const number2 = Number(value);
|
|
26020
|
+
return Number.isFinite(number2) && number2 >= 0 ? Math.floor(number2) : fallback;
|
|
26021
|
+
}
|
|
26022
|
+
|
|
26023
|
+
// src/components/filter/filter.js
|
|
26024
|
+
var Filter = class extends Component {
|
|
26025
|
+
static cssName = "filter";
|
|
26026
|
+
/** @type {Readonly<FilterOptions>} */
|
|
26027
|
+
static defaults = {
|
|
26028
|
+
fields: [],
|
|
26029
|
+
value: null,
|
|
26030
|
+
operators: [],
|
|
26031
|
+
allowGroups: true,
|
|
26032
|
+
maxDepth: 3,
|
|
26033
|
+
maxConditions: 50,
|
|
26034
|
+
autoApply: false,
|
|
26035
|
+
readonly: false,
|
|
26036
|
+
disabled: false,
|
|
26037
|
+
applyLabel: "Apply filters",
|
|
26038
|
+
clearLabel: "Clear"
|
|
26039
|
+
};
|
|
26040
|
+
/** @returns {HTMLElement} */
|
|
26041
|
+
render() {
|
|
26042
|
+
this._createdRoot = this.el === null;
|
|
26043
|
+
const root = (
|
|
26044
|
+
/** @type {HTMLElement} */
|
|
26045
|
+
this.el ?? h("div")
|
|
26046
|
+
);
|
|
26047
|
+
this.el = root;
|
|
26048
|
+
this._snapshot = this._createdRoot ? null : snapshotTarget(root);
|
|
26049
|
+
this._fields = (Array.isArray(this.options.fields) ? this.options.fields : []).map((field) => ({ ...field }));
|
|
26050
|
+
this._operators = mergeOperators(filterOperators, this.options.operators);
|
|
26051
|
+
this._readonly = Boolean(this.options.readonly);
|
|
26052
|
+
this._disabled = Boolean(this.options.disabled);
|
|
26053
|
+
this._async = /* @__PURE__ */ new Map();
|
|
26054
|
+
this._customEditors = [];
|
|
26055
|
+
this._value = this.options.value == null ? emptyFilterAst() : parseFilterAst(this.options.value, this._limits());
|
|
26056
|
+
this.listen(root, "change", (event) => this._handleChange(event));
|
|
26057
|
+
this.listen(root, "input", (event) => this._handleInput(event));
|
|
26058
|
+
this.listen(root, "click", (event) => this._handleClick(event));
|
|
26059
|
+
this._render();
|
|
26060
|
+
return root;
|
|
26061
|
+
}
|
|
26062
|
+
/** Returns a defensive AST copy. @returns {FilterAst} */
|
|
26063
|
+
getValue() {
|
|
26064
|
+
return cloneFilterAst(this._value);
|
|
26065
|
+
}
|
|
26066
|
+
/** Atomically replaces the AST. @param {unknown} value @param {{silent?:boolean}} [options={}] @returns {this} */
|
|
26067
|
+
setValue(value, { silent = false } = {}) {
|
|
26068
|
+
const parsed = parseFilterAst(value, this._limits());
|
|
26069
|
+
this._abortAll();
|
|
26070
|
+
this._value = parsed;
|
|
26071
|
+
this._render();
|
|
26072
|
+
if (!silent) this._changed("set", this._value.root.id);
|
|
26073
|
+
return this;
|
|
26074
|
+
}
|
|
26075
|
+
/** Adds a condition to a group. @param {string|null} [parentId=null] @param {Record<string,unknown>} [initial={}] @returns {string|null} */
|
|
26076
|
+
addCondition(parentId = null, initial = {}) {
|
|
26077
|
+
if (!this._canMutate() || conditionCount(this._value.root) >= Number(this.options.maxConditions)) return null;
|
|
26078
|
+
const parent = findGroup(this._value.root, parentId ?? this._value.root.id);
|
|
26079
|
+
if (!parent) return null;
|
|
26080
|
+
const first = this._fields[0];
|
|
26081
|
+
const field = String(initial.field ?? first?.id ?? "");
|
|
26082
|
+
const definition = this._field(field);
|
|
26083
|
+
const node = filterCondition({
|
|
26084
|
+
...initial,
|
|
26085
|
+
field,
|
|
26086
|
+
operator: String(initial.operator ?? this._defaultOperator(definition) ?? "")
|
|
26087
|
+
});
|
|
26088
|
+
parent.children.push(node);
|
|
26089
|
+
this._render();
|
|
26090
|
+
this._changed("add", node.id);
|
|
26091
|
+
this.focus(node.id);
|
|
26092
|
+
return node.id;
|
|
26093
|
+
}
|
|
26094
|
+
/** Adds a nested group. @param {string|null} [parentId=null] @param {'and'|'or'} [logic='and'] @returns {string|null} */
|
|
26095
|
+
addGroup(parentId = null, logic = "and") {
|
|
26096
|
+
if (!this._canMutate() || !this.options.allowGroups) return null;
|
|
26097
|
+
const parent = findGroup(this._value.root, parentId ?? this._value.root.id);
|
|
26098
|
+
if (!parent || groupDepth(this._value.root, parent.id) >= Number(this.options.maxDepth)) return null;
|
|
26099
|
+
const group = filterGroup(logic === "or" ? "or" : "and");
|
|
26100
|
+
parent.children.push(group);
|
|
26101
|
+
this._render();
|
|
26102
|
+
this._changed("add", group.id);
|
|
26103
|
+
this.focus(group.id);
|
|
26104
|
+
return group.id;
|
|
26105
|
+
}
|
|
26106
|
+
/** Updates a node by stable ID. @param {string} id @param {Record<string,unknown>} patch @returns {this} */
|
|
26107
|
+
update(id, patch = {}) {
|
|
26108
|
+
if (!this._canMutate()) return this;
|
|
26109
|
+
const node = findNode2(this._value.root, id);
|
|
26110
|
+
if (!node) return this;
|
|
26111
|
+
if (node.kind === "group") {
|
|
26112
|
+
if (patch.logic === "and" || patch.logic === "or") node.logic = patch.logic;
|
|
26113
|
+
} else {
|
|
26114
|
+
if (Object.hasOwn(patch, "field")) node.field = String(patch.field ?? "");
|
|
26115
|
+
if (Object.hasOwn(patch, "operator")) node.operator = String(patch.operator ?? "");
|
|
26116
|
+
if (Object.hasOwn(patch, "value")) node.value = parseValueJson(patch.value);
|
|
26117
|
+
}
|
|
26118
|
+
this._abort(id);
|
|
26119
|
+
this._render();
|
|
26120
|
+
this._changed("update", id);
|
|
26121
|
+
return this;
|
|
26122
|
+
}
|
|
26123
|
+
/** Removes a non-root node. @param {string} id @returns {this} */
|
|
26124
|
+
remove(id) {
|
|
26125
|
+
if (!this._canMutate() || id === this._value.root.id) return this;
|
|
26126
|
+
const parent = findParent(this._value.root, id);
|
|
26127
|
+
if (!parent) return this;
|
|
26128
|
+
const index = parent.children.findIndex((child) => child.id === id);
|
|
26129
|
+
const focusId = parent.children[index + 1]?.id ?? parent.children[index - 1]?.id ?? parent.id;
|
|
26130
|
+
const removed = removeNode(this._value.root, id);
|
|
26131
|
+
if (!removed) return this;
|
|
26132
|
+
this._abortTree(removed);
|
|
26133
|
+
this._render();
|
|
26134
|
+
const label = removed.kind === "condition" ? this._field(removed.field)?.label ?? "Filter" : "Filter group";
|
|
26135
|
+
this._announce(`${label} removed`);
|
|
26136
|
+
this._changed("remove", id);
|
|
26137
|
+
this.focus(focusId);
|
|
26138
|
+
return this;
|
|
26139
|
+
}
|
|
26140
|
+
/** Moves a node into a group while preserving its identity. @param {string} id @param {string} parentId @param {number} index @returns {this} */
|
|
26141
|
+
move(id, parentId, index) {
|
|
26142
|
+
const node = findNode2(this._value.root, id);
|
|
26143
|
+
const parent = findGroup(this._value.root, parentId);
|
|
26144
|
+
const previous = findParent(this._value.root, id);
|
|
26145
|
+
if (!this._canMutate() || id === this._value.root.id || !node || !parent || !previous || containsNode(node, parentId)) return this;
|
|
26146
|
+
const previousIndex = previous.children.findIndex((child) => child.id === id);
|
|
26147
|
+
const removed = previous.children.splice(previousIndex, 1)[0];
|
|
26148
|
+
parent.children.splice(Math.max(0, Math.min(parent.children.length, Number(index) || 0)), 0, removed);
|
|
26149
|
+
try {
|
|
26150
|
+
parseFilterAst(this._value, this._limits());
|
|
26151
|
+
} catch {
|
|
26152
|
+
parent.children.splice(parent.children.indexOf(removed), 1);
|
|
26153
|
+
previous.children.splice(previousIndex, 0, removed);
|
|
26154
|
+
return this;
|
|
26155
|
+
}
|
|
26156
|
+
this._render();
|
|
26157
|
+
this._changed("move", id);
|
|
26158
|
+
return this;
|
|
26159
|
+
}
|
|
26160
|
+
/** Returns semantic validity and errors. @returns {{valid:boolean,errors:Array<Record<string,string>>}} */
|
|
26161
|
+
validate() {
|
|
26162
|
+
return validateFilterAst(this._value, this._fields, this._operators);
|
|
26163
|
+
}
|
|
26164
|
+
/** Applies only a valid AST. @returns {FilterAst|null} */
|
|
26165
|
+
apply() {
|
|
26166
|
+
if (this._disabled) return null;
|
|
26167
|
+
const result = this.validate();
|
|
26168
|
+
if (!result.valid) {
|
|
26169
|
+
this.emit("invalid", { errors: result.errors });
|
|
26170
|
+
this.focus(result.errors[0]?.nodeId);
|
|
26171
|
+
return null;
|
|
26172
|
+
}
|
|
26173
|
+
const value = this.getValue();
|
|
26174
|
+
this.emit("apply", { value });
|
|
26175
|
+
return value;
|
|
26176
|
+
}
|
|
26177
|
+
/** Clears all conditions. @param {{silent?:boolean}} [options={}] @returns {this} */
|
|
26178
|
+
clear({ silent = false } = {}) {
|
|
26179
|
+
if (!this._canMutate()) return this;
|
|
26180
|
+
this._abortAll();
|
|
26181
|
+
this._value.root.children = [];
|
|
26182
|
+
this._render();
|
|
26183
|
+
if (!silent) this._changed("clear", this._value.root.id);
|
|
26184
|
+
this.refs.add?.focus();
|
|
26185
|
+
return this;
|
|
26186
|
+
}
|
|
26187
|
+
/** Focuses a condition's field or the Add button. @param {string|null} [id=null] @returns {this} */
|
|
26188
|
+
focus(id = null) {
|
|
26189
|
+
const selector = id ? `[data-node-id="${cssEscape(id)}"] :is([data-filter-field], [data-filter-logic], [data-filter-action="add"])` : '[data-filter-action="add"]';
|
|
26190
|
+
queueMicrotask(() => (
|
|
26191
|
+
/** @type {HTMLElement|null} */
|
|
26192
|
+
this.el.querySelector(selector)?.focus()
|
|
26193
|
+
));
|
|
26194
|
+
return this;
|
|
26195
|
+
}
|
|
26196
|
+
/** @param {boolean} readonly @returns {this} */
|
|
26197
|
+
setReadonly(readonly) {
|
|
26198
|
+
this._readonly = Boolean(readonly);
|
|
26199
|
+
this._render();
|
|
26200
|
+
return this;
|
|
26201
|
+
}
|
|
26202
|
+
/** @returns {this} */
|
|
26203
|
+
enable() {
|
|
26204
|
+
this._disabled = false;
|
|
26205
|
+
this._render();
|
|
26206
|
+
return this;
|
|
26207
|
+
}
|
|
26208
|
+
/** @returns {this} */
|
|
26209
|
+
disable() {
|
|
26210
|
+
this._disabled = true;
|
|
26211
|
+
this._abortAll();
|
|
26212
|
+
this._render();
|
|
26213
|
+
return this;
|
|
26214
|
+
}
|
|
26215
|
+
/** @returns {void} */
|
|
26216
|
+
destroy() {
|
|
26217
|
+
this._abortAll();
|
|
26218
|
+
this._destroyEditors();
|
|
26219
|
+
const root = this.el;
|
|
26220
|
+
const snapshot10 = this._snapshot;
|
|
26221
|
+
super.destroy();
|
|
26222
|
+
if (!this._createdRoot && root) restoreTarget(root, snapshot10);
|
|
26223
|
+
}
|
|
26224
|
+
/** @returns {void} */
|
|
26225
|
+
_render() {
|
|
26226
|
+
this._destroyEditors();
|
|
26227
|
+
this._renderVersion = (this._renderVersion ?? 0) + 1;
|
|
26228
|
+
const status = h("div", { ref: "status", class: "zx-filter__status", role: "status", ariaLive: "polite" });
|
|
26229
|
+
const tree = this._renderGroup(this._value.root, 0, true);
|
|
26230
|
+
const actions2 = h(
|
|
26231
|
+
"div",
|
|
26232
|
+
{ class: "zx-filter__actions" },
|
|
26233
|
+
actionButton("add", this._value.root.id, "plus", "Add filter", { ref: "add", disabled: this._blocked() }),
|
|
26234
|
+
this.options.allowGroups ? actionButton("add-group", this._value.root.id, "folder", "Add group", { disabled: this._blocked() }) : null,
|
|
26235
|
+
actionButton("clear", "", "x", String(this.options.clearLabel), { disabled: this._blocked() }),
|
|
26236
|
+
actionButton("apply", "", "check", String(this.options.applyLabel), { class: "zx-button--primary", disabled: this._disabled })
|
|
26237
|
+
);
|
|
26238
|
+
this.el.replaceChildren(status, tree, actions2);
|
|
26239
|
+
this.refs.status = status;
|
|
26240
|
+
this.refs.add = /** @type {HTMLElement} */
|
|
26241
|
+
this.el.querySelector('[data-filter-action="add"]');
|
|
26242
|
+
this.el.toggleAttribute("data-readonly", this._readonly);
|
|
26243
|
+
this.el.toggleAttribute("data-disabled", this._disabled);
|
|
26244
|
+
const result = this.validate();
|
|
26245
|
+
this.el.dataset.valid = String(result.valid);
|
|
26246
|
+
for (const error2 of result.errors) this.el.querySelector(`[data-node-id="${cssEscape(error2.nodeId)}"]`)?.setAttribute("data-invalid", "");
|
|
26247
|
+
}
|
|
26248
|
+
/** @param {any} group @param {number} depth @param {boolean} root @returns {HTMLElement} */
|
|
26249
|
+
_renderGroup(group, depth, root) {
|
|
26250
|
+
const children = h(
|
|
26251
|
+
"ol",
|
|
26252
|
+
{ class: "zx-filter__children" },
|
|
26253
|
+
group.children.map((node) => node.kind === "group" ? this._renderGroup(node, depth + 1, false) : this._renderCondition(node))
|
|
26254
|
+
);
|
|
26255
|
+
const logic = h(
|
|
26256
|
+
"select",
|
|
26257
|
+
{
|
|
26258
|
+
class: "zx-filter__logic",
|
|
26259
|
+
dataset: { filterLogic: "", nodeId: group.id },
|
|
26260
|
+
ariaLabel: root ? "Root filter logic" : "Group filter logic",
|
|
26261
|
+
disabled: this._blocked()
|
|
26262
|
+
},
|
|
26263
|
+
h("option", { value: "and", selected: group.logic === "and" }, "Match all"),
|
|
26264
|
+
h("option", { value: "or", selected: group.logic === "or" }, "Match any")
|
|
26265
|
+
);
|
|
26266
|
+
const legend = h("legend", { class: "zx-filter__legend" }, logic, h("span", {}, root ? "conditions" : "in this group"));
|
|
26267
|
+
const controls = h(
|
|
26268
|
+
"div",
|
|
26269
|
+
{ class: "zx-filter__group-actions" },
|
|
26270
|
+
actionButton("add", group.id, "plus", "Add filter", { disabled: this._blocked() }),
|
|
26271
|
+
this.options.allowGroups && depth < Number(this.options.maxDepth) ? actionButton("add-group", group.id, "folder", "Add subgroup", { disabled: this._blocked() }) : null,
|
|
26272
|
+
root ? null : actionButton("remove", group.id, "trash", "Remove group", { disabled: this._blocked() })
|
|
26273
|
+
);
|
|
26274
|
+
return h(root ? "fieldset" : "li", {
|
|
26275
|
+
class: "zx-filter__group",
|
|
26276
|
+
dataset: { nodeId: group.id, depth: String(depth) }
|
|
26277
|
+
}, root ? legend : h("fieldset", {}, legend, children, controls), root ? children : null, root ? controls : null);
|
|
26278
|
+
}
|
|
26279
|
+
/** @param {any} node @returns {HTMLElement} */
|
|
26280
|
+
_renderCondition(node) {
|
|
26281
|
+
const field = this._field(node.field);
|
|
26282
|
+
const operators = this._operatorsFor(field);
|
|
26283
|
+
const operator = this._operator(node.operator);
|
|
26284
|
+
const fieldSelect = h(
|
|
26285
|
+
"select",
|
|
26286
|
+
{
|
|
26287
|
+
class: "zx-filter__field",
|
|
26288
|
+
dataset: { filterField: "", nodeId: node.id },
|
|
26289
|
+
ariaLabel: "Filter field",
|
|
26290
|
+
disabled: this._blocked()
|
|
26291
|
+
},
|
|
26292
|
+
h("option", { value: "", selected: !node.field }, "Choose a field\u2026"),
|
|
26293
|
+
this._fields.map((candidate) => h("option", { value: candidate.id, selected: candidate.id === node.field }, candidate.label)),
|
|
26294
|
+
node.field && !field ? h("option", { value: node.field, selected: true }, `Unavailable: ${node.field}`) : null
|
|
26295
|
+
);
|
|
26296
|
+
const operatorSelect = h(
|
|
26297
|
+
"select",
|
|
26298
|
+
{
|
|
26299
|
+
class: "zx-filter__operator",
|
|
26300
|
+
dataset: { filterOperator: "", nodeId: node.id },
|
|
26301
|
+
ariaLabel: "Filter operator",
|
|
26302
|
+
disabled: this._blocked()
|
|
26303
|
+
},
|
|
26304
|
+
h("option", { value: "", selected: !node.operator }, "Choose an operator\u2026"),
|
|
26305
|
+
operators.map((candidate) => h("option", { value: candidate.id, selected: candidate.id === node.operator }, candidate.label)),
|
|
26306
|
+
node.operator && !operators.some((candidate) => candidate.id === node.operator) ? h("option", { value: node.operator, selected: true }, `Unavailable: ${node.operator}`) : null
|
|
26307
|
+
);
|
|
26308
|
+
return h(
|
|
26309
|
+
"li",
|
|
26310
|
+
{ class: "zx-filter__condition", dataset: { nodeId: node.id } },
|
|
26311
|
+
fieldSelect,
|
|
26312
|
+
operatorSelect,
|
|
26313
|
+
this._renderValue(node, field, operator),
|
|
26314
|
+
actionButton("remove", node.id, "trash", `Remove ${field?.label ?? "filter"}`, { disabled: this._blocked() })
|
|
26315
|
+
);
|
|
26316
|
+
}
|
|
26317
|
+
/** @param {any} node @param {FilterField|null} field @param {any} operator @returns {HTMLElement} */
|
|
26318
|
+
_renderValue(node, field, operator) {
|
|
26319
|
+
const host = h("div", { class: "zx-filter__value" });
|
|
26320
|
+
if (!field || !operator) return host;
|
|
26321
|
+
if (operator.arity === "none") return h("div", { class: "zx-filter__value zx-filter__value--empty" }, "No value");
|
|
26322
|
+
if (field.type === "custom" && typeof field.editor === "function") {
|
|
26323
|
+
const renderVersion = this._renderVersion;
|
|
26324
|
+
queueMicrotask(() => {
|
|
26325
|
+
if (renderVersion !== this._renderVersion || !this.el.contains(host)) return;
|
|
26326
|
+
const handle = field.editor(host, {
|
|
26327
|
+
value: node.value,
|
|
26328
|
+
field,
|
|
26329
|
+
conditionId: node.id,
|
|
26330
|
+
setValue: (value) => this._setNodeValue(node.id, parseValueJson(value))
|
|
26331
|
+
});
|
|
26332
|
+
if (handle && typeof handle.destroy === "function") this._customEditors.push(handle);
|
|
26333
|
+
});
|
|
26334
|
+
return host;
|
|
26335
|
+
}
|
|
26336
|
+
const values = operator.arity === "pair" ? Array.isArray(node.value) ? node.value : [null, null] : [node.value];
|
|
26337
|
+
if (operator.arity === "many") {
|
|
26338
|
+
const control = choiceSelect(field, node, values[0], true, this._blocked());
|
|
26339
|
+
if (control) return h("div", { class: "zx-filter__value" }, control);
|
|
26340
|
+
return h("div", { class: "zx-filter__value" }, valueInput(field, node.id, Array.isArray(node.value) ? node.value.join(", ") : "", 0, this._blocked(), true));
|
|
26341
|
+
}
|
|
26342
|
+
const controls = values.map((value, index) => choiceSelect(field, node, value, false, this._blocked(), index) ?? valueInput(field, node.id, value, index, this._blocked()));
|
|
26343
|
+
return h("div", { class: "zx-filter__value", dataset: { arity: operator.arity } }, controls);
|
|
26344
|
+
}
|
|
26345
|
+
/** @param {Event} event @returns {void} */
|
|
26346
|
+
_handleChange(event) {
|
|
26347
|
+
if (!this._canMutate()) return;
|
|
26348
|
+
const target = (
|
|
26349
|
+
/** @type {HTMLInputElement|HTMLSelectElement|null} */
|
|
26350
|
+
event.target
|
|
26351
|
+
);
|
|
26352
|
+
const id = target?.dataset.nodeId;
|
|
26353
|
+
if (!target || !id) return;
|
|
26354
|
+
if (target.dataset.filterField !== void 0) {
|
|
26355
|
+
const node = findNode2(this._value.root, id);
|
|
26356
|
+
if (node?.kind !== "condition") return;
|
|
26357
|
+
node.field = target.value;
|
|
26358
|
+
const field = this._field(node.field);
|
|
26359
|
+
node.operator = this._defaultOperator(field) ?? "";
|
|
26360
|
+
node.value = null;
|
|
26361
|
+
this._abort(id);
|
|
26362
|
+
this._render();
|
|
26363
|
+
this._changed("update", id);
|
|
26364
|
+
this.focus(id);
|
|
26365
|
+
} else if (target.dataset.filterOperator !== void 0) {
|
|
26366
|
+
const node = findNode2(this._value.root, id);
|
|
26367
|
+
if (node?.kind !== "condition") return;
|
|
26368
|
+
node.operator = target.value;
|
|
26369
|
+
node.value = null;
|
|
26370
|
+
this._abort(id);
|
|
26371
|
+
this._render();
|
|
26372
|
+
this._changed("update", id);
|
|
26373
|
+
queueMicrotask(() => (
|
|
26374
|
+
/** @type {HTMLElement|null} */
|
|
26375
|
+
this.el.querySelector(`[data-node-id="${cssEscape(id)}"] [data-filter-value]`)?.focus()
|
|
26376
|
+
));
|
|
26377
|
+
} else if (target.dataset.filterLogic !== void 0) {
|
|
26378
|
+
const node = findNode2(this._value.root, id);
|
|
26379
|
+
if (node?.kind === "group") node.logic = target.value === "or" ? "or" : "and";
|
|
26380
|
+
this._changed("update", id);
|
|
26381
|
+
} else if (target.dataset.filterValue !== void 0) this._valueInput(target);
|
|
26382
|
+
}
|
|
26383
|
+
/** @param {Event} event @returns {void} */
|
|
26384
|
+
_handleInput(event) {
|
|
26385
|
+
if (!this._canMutate()) return;
|
|
26386
|
+
const target = (
|
|
26387
|
+
/** @type {HTMLInputElement|null} */
|
|
26388
|
+
event.target
|
|
26389
|
+
);
|
|
26390
|
+
if (!target || target.dataset.filterValue === void 0) return;
|
|
26391
|
+
this._valueInput(target);
|
|
26392
|
+
const id = target.dataset.nodeId;
|
|
26393
|
+
const node = findNode2(this._value.root, id);
|
|
26394
|
+
const field = node?.kind === "condition" ? this._field(node.field) : null;
|
|
26395
|
+
if (field?.loadChoices) this._scheduleChoices(node.id, field, target.value);
|
|
26396
|
+
}
|
|
26397
|
+
/** @param {HTMLInputElement|HTMLSelectElement} target @returns {void} */
|
|
26398
|
+
_valueInput(target) {
|
|
26399
|
+
const node = findNode2(this._value.root, target.dataset.nodeId);
|
|
26400
|
+
if (node?.kind !== "condition") return;
|
|
26401
|
+
const operator = this._operator(node.operator);
|
|
26402
|
+
const field = this._field(node.field);
|
|
26403
|
+
if (!operator || !field) return;
|
|
26404
|
+
let value;
|
|
26405
|
+
if (target instanceof HTMLSelectElement && target.multiple) {
|
|
26406
|
+
value = Array.from(target.selectedOptions, (option) => parseScalar(option.value, field.type));
|
|
26407
|
+
} else if (operator.arity === "many") {
|
|
26408
|
+
value = target.value.split(",").map((item) => item.trim()).filter(Boolean).map((item) => parseScalar(item, field.type));
|
|
26409
|
+
} else if (operator.arity === "pair") {
|
|
26410
|
+
const pair = Array.isArray(node.value) ? [...node.value] : [null, null];
|
|
26411
|
+
pair[Number(target.dataset.valueIndex) || 0] = parseScalar(target.value, field.type);
|
|
26412
|
+
value = pair;
|
|
26413
|
+
} else value = parseScalar(target.value, field.type);
|
|
26414
|
+
this._setNodeValue(
|
|
26415
|
+
node.id,
|
|
26416
|
+
/** @type {any} */
|
|
26417
|
+
value
|
|
26418
|
+
);
|
|
26419
|
+
}
|
|
26420
|
+
/** @param {MouseEvent} event @returns {void} */
|
|
26421
|
+
_handleClick(event) {
|
|
26422
|
+
const button2 = (
|
|
26423
|
+
/** @type {HTMLElement|null} */
|
|
26424
|
+
event.target?.closest?.("[data-filter-action]")
|
|
26425
|
+
);
|
|
26426
|
+
if (!button2 || !this.el.contains(button2)) return;
|
|
26427
|
+
const action = button2.dataset.filterAction;
|
|
26428
|
+
const id = button2.dataset.nodeId || null;
|
|
26429
|
+
if (action === "add") this.addCondition(id);
|
|
26430
|
+
else if (action === "add-group") this.addGroup(id);
|
|
26431
|
+
else if (action === "remove" && id) this.remove(id);
|
|
26432
|
+
else if (action === "clear") this.clear();
|
|
26433
|
+
else if (action === "apply") this.apply();
|
|
26434
|
+
}
|
|
26435
|
+
/** @param {string} id @param {any} value @returns {void} */
|
|
26436
|
+
_setNodeValue(id, value) {
|
|
26437
|
+
const node = findNode2(this._value.root, id);
|
|
26438
|
+
if (node?.kind !== "condition") return;
|
|
26439
|
+
node.value = value;
|
|
26440
|
+
const result = this.validate();
|
|
26441
|
+
this.el.dataset.valid = String(result.valid);
|
|
26442
|
+
const row2 = this.el.querySelector(`[data-node-id="${cssEscape(id)}"]`);
|
|
26443
|
+
row2?.toggleAttribute("data-invalid", result.errors.some((candidate) => candidate.nodeId === id));
|
|
26444
|
+
this._changed("update", id);
|
|
26445
|
+
}
|
|
26446
|
+
/** @param {string} reason @param {string} nodeId @returns {void} */
|
|
26447
|
+
_changed(reason, nodeId) {
|
|
26448
|
+
const result = this.validate();
|
|
26449
|
+
const value = this.getValue();
|
|
26450
|
+
this.emit("change", { value, valid: result.valid, errors: result.errors, reason, nodeId });
|
|
26451
|
+
if (this.options.autoApply && result.valid) this.emit("apply", { value });
|
|
26452
|
+
}
|
|
26453
|
+
/** @param {string} id @param {FilterField} field @param {string} query @returns {void} */
|
|
26454
|
+
_scheduleChoices(id, field, query) {
|
|
26455
|
+
this._abort(id);
|
|
26456
|
+
if (query.length < Number(field.minQuery ?? 0)) return;
|
|
26457
|
+
const controller = new AbortController();
|
|
26458
|
+
this._setAsyncBusy(id, true);
|
|
26459
|
+
const entry = { controller, timer: setTimeout(async () => {
|
|
26460
|
+
this.emit("query", { conditionId: id, fieldId: field.id, query });
|
|
26461
|
+
try {
|
|
26462
|
+
const choices = await field.loadChoices({ query, signal: controller.signal, conditionId: id, field });
|
|
26463
|
+
if (controller.signal.aborted || this._async.get(id)?.controller !== controller) return;
|
|
26464
|
+
const input = this.el.querySelector(`[data-node-id="${cssEscape(id)}"] [data-filter-value]`);
|
|
26465
|
+
const listId = input?.getAttribute("list");
|
|
26466
|
+
const list = listId ? this.el.querySelector(`#${cssEscape(listId)}`) : null;
|
|
26467
|
+
if (list) list.replaceChildren(...(Array.isArray(choices) ? choices : []).map((choice) => h("option", { value: choice.value }, choice.label)));
|
|
26468
|
+
this.emit("loaded", { conditionId: id, fieldId: field.id, query, choices });
|
|
26469
|
+
} catch (error2) {
|
|
26470
|
+
if (!controller.signal.aborted) this.emit("error", { conditionId: id, fieldId: field.id, query, error: error2 instanceof Error ? error2 : new Error(String(error2)) });
|
|
26471
|
+
} finally {
|
|
26472
|
+
if (this._async.get(id)?.controller === controller) {
|
|
26473
|
+
this._async.delete(id);
|
|
26474
|
+
this._setAsyncBusy(id, false);
|
|
26475
|
+
}
|
|
26476
|
+
}
|
|
26477
|
+
}, Math.max(0, Number(field.debounce ?? 200))) };
|
|
26478
|
+
this._async.set(id, entry);
|
|
26479
|
+
}
|
|
26480
|
+
/** @param {string} id @returns {void} */
|
|
26481
|
+
_abort(id) {
|
|
26482
|
+
const entry = this._async.get(id);
|
|
26483
|
+
if (entry) {
|
|
26484
|
+
clearTimeout(entry.timer);
|
|
26485
|
+
entry.controller.abort();
|
|
26486
|
+
this._async.delete(id);
|
|
26487
|
+
this._setAsyncBusy(id, false);
|
|
26488
|
+
}
|
|
26489
|
+
}
|
|
26490
|
+
/** @param {string} id @param {boolean} busy @returns {void} */
|
|
26491
|
+
_setAsyncBusy(id, busy) {
|
|
26492
|
+
const input = this.el.querySelector(`[data-node-id="${cssEscape(id)}"] [data-filter-value]`);
|
|
26493
|
+
if (busy) input?.setAttribute("aria-busy", "true");
|
|
26494
|
+
else input?.removeAttribute("aria-busy");
|
|
26495
|
+
}
|
|
26496
|
+
/** @param {any} node @returns {void} */
|
|
26497
|
+
_abortTree(node) {
|
|
26498
|
+
if (node.kind === "condition") this._abort(node.id);
|
|
26499
|
+
else node.children.forEach((child) => this._abortTree(child));
|
|
26500
|
+
}
|
|
26501
|
+
/** @returns {void} */
|
|
26502
|
+
_abortAll() {
|
|
26503
|
+
for (const id of [...this._async.keys()]) this._abort(id);
|
|
26504
|
+
}
|
|
26505
|
+
/** @returns {void} */
|
|
26506
|
+
_destroyEditors() {
|
|
26507
|
+
for (const editor of this._customEditors ?? []) editor.destroy?.();
|
|
26508
|
+
this._customEditors = [];
|
|
26509
|
+
}
|
|
26510
|
+
/** @returns {boolean} */
|
|
26511
|
+
_blocked() {
|
|
26512
|
+
return this._readonly || this._disabled;
|
|
26513
|
+
}
|
|
26514
|
+
/** @returns {boolean} */
|
|
26515
|
+
_canMutate() {
|
|
26516
|
+
return !this._blocked();
|
|
26517
|
+
}
|
|
26518
|
+
/** @returns {{maxDepth:number,maxConditions:number}} */
|
|
26519
|
+
_limits() {
|
|
26520
|
+
return { maxDepth: Number(this.options.maxDepth), maxConditions: Number(this.options.maxConditions) };
|
|
26521
|
+
}
|
|
26522
|
+
/** @param {string} id @returns {FilterField|null} */
|
|
26523
|
+
_field(id) {
|
|
26524
|
+
return this._fields.find((field) => field.id === id) ?? null;
|
|
26525
|
+
}
|
|
26526
|
+
/** @param {string} id @returns {any} */
|
|
26527
|
+
_operator(id) {
|
|
26528
|
+
return this._operators.find((operator) => operator.id === id) ?? null;
|
|
26529
|
+
}
|
|
26530
|
+
/** @param {FilterField|null} field @returns {any[]} */
|
|
26531
|
+
_operatorsFor(field) {
|
|
26532
|
+
if (!field) return [];
|
|
26533
|
+
const ids = field.operators?.length ? field.operators : null;
|
|
26534
|
+
return this._operators.filter((operator) => ids ? ids.includes(operator.id) : operator.types.includes(field.type));
|
|
26535
|
+
}
|
|
26536
|
+
/** @param {FilterField|null} field @returns {string|null} */
|
|
26537
|
+
_defaultOperator(field) {
|
|
26538
|
+
return field?.defaultOperator && this._operatorsFor(field).some((operator) => operator.id === field.defaultOperator) ? field.defaultOperator : this._operatorsFor(field)[0]?.id ?? null;
|
|
26539
|
+
}
|
|
26540
|
+
/** @param {string} text @returns {void} */
|
|
26541
|
+
_announce(text4) {
|
|
26542
|
+
if (this.refs.status) this.refs.status.textContent = text4;
|
|
26543
|
+
}
|
|
26544
|
+
};
|
|
26545
|
+
function actionButton(action, id, iconName, label, props = {}) {
|
|
26546
|
+
return h("button", { type: "button", class: ["zx-button", "zx-button--sm", props.class].filter(Boolean), ...props, dataset: { filterAction: action, nodeId: id } }, icon(iconName, { size: 13 }), h("span", {}, label));
|
|
26547
|
+
}
|
|
26548
|
+
function choiceSelect(field, node, value, multiple, disabled, index = 0) {
|
|
26549
|
+
const choices = Array.isArray(field.choices) && field.choices.length ? field.choices : field.type === "boolean" ? [{ value: true, label: "True" }, { value: false, label: "False" }] : null;
|
|
26550
|
+
if (!choices) return null;
|
|
26551
|
+
const values = new Set(Array.isArray(value) ? value.map(String) : [String(value ?? "")]);
|
|
26552
|
+
return (
|
|
26553
|
+
/** @type {HTMLSelectElement} */
|
|
26554
|
+
h(
|
|
26555
|
+
"select",
|
|
26556
|
+
{
|
|
26557
|
+
dataset: { filterValue: "", nodeId: node.id, valueIndex: String(index) },
|
|
26558
|
+
ariaLabel: `${field.label} value`,
|
|
26559
|
+
multiple,
|
|
26560
|
+
disabled
|
|
26561
|
+
},
|
|
26562
|
+
!multiple ? h("option", { value: "", selected: !values.has(String(value)) || value == null }, "Choose\u2026") : null,
|
|
26563
|
+
choices.map((choice) => h("option", { value: choice.value, selected: values.has(String(choice.value)) }, choice.label))
|
|
26564
|
+
)
|
|
26565
|
+
);
|
|
26566
|
+
}
|
|
26567
|
+
function valueInput(field, id, value, index, disabled, many = false) {
|
|
26568
|
+
const type = many ? "text" : ["number", "money"].includes(field.type) ? "number" : field.type === "date" ? "date" : field.type === "datetime" ? "datetime-local" : "text";
|
|
26569
|
+
const listId = field.loadChoices ? `zx-filter-choices-${id}-${index}` : null;
|
|
26570
|
+
const input = (
|
|
26571
|
+
/** @type {HTMLInputElement} */
|
|
26572
|
+
h("input", {
|
|
26573
|
+
type,
|
|
26574
|
+
value: value ?? "",
|
|
26575
|
+
dataset: { filterValue: "", nodeId: id, valueIndex: String(index) },
|
|
26576
|
+
ariaLabel: `${field.label} value${index ? ` ${index + 1}` : ""}`,
|
|
26577
|
+
disabled,
|
|
26578
|
+
placeholder: many ? "Comma-separated values" : "",
|
|
26579
|
+
list: listId
|
|
26580
|
+
})
|
|
26581
|
+
);
|
|
26582
|
+
return h(
|
|
26583
|
+
"span",
|
|
26584
|
+
{ class: "zx-filter__value-control" },
|
|
26585
|
+
input,
|
|
26586
|
+
listId ? h("datalist", { id: listId }) : null
|
|
26587
|
+
);
|
|
26588
|
+
}
|
|
26589
|
+
function parseScalar(value, type) {
|
|
26590
|
+
if (value === "") return null;
|
|
26591
|
+
if (["number", "money"].includes(type)) {
|
|
26592
|
+
const number2 = Number(value);
|
|
26593
|
+
return Number.isFinite(number2) ? number2 : null;
|
|
26594
|
+
}
|
|
26595
|
+
if (type === "boolean") return value === "true";
|
|
26596
|
+
return value;
|
|
26597
|
+
}
|
|
26598
|
+
function parseValueJson(value) {
|
|
26599
|
+
return cloneFilterValue(value === void 0 ? null : value);
|
|
26600
|
+
}
|
|
26601
|
+
function findNode2(root, id) {
|
|
26602
|
+
if (!root || root.id === id) return root ?? null;
|
|
26603
|
+
if (root.kind === "group") for (const child of root.children) {
|
|
26604
|
+
const match = findNode2(child, id);
|
|
26605
|
+
if (match) return match;
|
|
26606
|
+
}
|
|
26607
|
+
return null;
|
|
26608
|
+
}
|
|
26609
|
+
function findGroup(root, id) {
|
|
26610
|
+
const node = findNode2(root, id);
|
|
26611
|
+
return node?.kind === "group" ? node : null;
|
|
26612
|
+
}
|
|
26613
|
+
function findParent(root, id) {
|
|
26614
|
+
if (root?.kind !== "group") return null;
|
|
26615
|
+
if (root.children.some((child) => child.id === id)) return root;
|
|
26616
|
+
for (const child of root.children) {
|
|
26617
|
+
const parent = findParent(child, id);
|
|
26618
|
+
if (parent) return parent;
|
|
26619
|
+
}
|
|
26620
|
+
return null;
|
|
26621
|
+
}
|
|
26622
|
+
function removeNode(root, id) {
|
|
26623
|
+
if (root.kind !== "group") return null;
|
|
26624
|
+
const index = root.children.findIndex((child) => child.id === id);
|
|
26625
|
+
if (index >= 0) return root.children.splice(index, 1)[0];
|
|
26626
|
+
for (const child of root.children) {
|
|
26627
|
+
const found = removeNode(child, id);
|
|
26628
|
+
if (found) return found;
|
|
26629
|
+
}
|
|
26630
|
+
return null;
|
|
26631
|
+
}
|
|
26632
|
+
function containsNode(node, id) {
|
|
26633
|
+
return Boolean(node && findNode2(node, id));
|
|
26634
|
+
}
|
|
26635
|
+
function conditionCount(node) {
|
|
26636
|
+
return node.kind === "condition" ? 1 : node.children.reduce((sum, child) => sum + conditionCount(child), 0);
|
|
26637
|
+
}
|
|
26638
|
+
function groupDepth(root, id, depth = 0) {
|
|
26639
|
+
if (root.id === id) return depth;
|
|
26640
|
+
if (root.kind === "group") for (const child of root.children) {
|
|
26641
|
+
if (child.kind === "group") {
|
|
26642
|
+
const found = groupDepth(child, id, depth + 1);
|
|
26643
|
+
if (found >= 0) return found;
|
|
26644
|
+
}
|
|
26645
|
+
}
|
|
26646
|
+
return -1;
|
|
26647
|
+
}
|
|
26648
|
+
function mergeOperators(base, extra) {
|
|
26649
|
+
const map = new Map(base.map((item) => [item.id, { ...item, types: [...item.types] }]));
|
|
26650
|
+
for (const item of Array.isArray(extra) ? extra : []) if (item?.id) map.set(String(item.id), { ...item, id: String(item.id), types: Array.isArray(item.types) ? [...item.types] : [] });
|
|
26651
|
+
return [...map.values()];
|
|
26652
|
+
}
|
|
26653
|
+
function cssEscape(value) {
|
|
26654
|
+
return globalThis.CSS?.escape ? CSS.escape(value) : String(value).replaceAll(/[^a-zA-Z0-9_-]/g, "\\$&");
|
|
26655
|
+
}
|
|
26656
|
+
|
|
25203
26657
|
// src/components/fieldset/fieldset.js
|
|
25204
26658
|
var Fieldset = class extends Component {
|
|
25205
26659
|
static cssName = "fieldset";
|
|
@@ -26367,8 +27821,8 @@ var zx = (() => {
|
|
|
26367
27821
|
this._setBusy(true);
|
|
26368
27822
|
try {
|
|
26369
27823
|
message = await item.validate(answer, this.getAnswers());
|
|
26370
|
-
} catch (
|
|
26371
|
-
message =
|
|
27824
|
+
} catch (error2) {
|
|
27825
|
+
message = error2 instanceof Error ? error2.message : String(error2);
|
|
26372
27826
|
} finally {
|
|
26373
27827
|
if (!this._destroyed) this._setBusy(false);
|
|
26374
27828
|
}
|
|
@@ -26506,10 +27960,10 @@ var zx = (() => {
|
|
|
26506
27960
|
}
|
|
26507
27961
|
/** @param {NormalizedItem} item @returns {void} */
|
|
26508
27962
|
_paintError(item) {
|
|
26509
|
-
const
|
|
26510
|
-
if (
|
|
26511
|
-
|
|
26512
|
-
|
|
27963
|
+
const error2 = this._itemHost.querySelector(".zx-questionnaire__error");
|
|
27964
|
+
if (error2 instanceof HTMLElement) {
|
|
27965
|
+
error2.textContent = this._error;
|
|
27966
|
+
error2.hidden = !this._error;
|
|
26513
27967
|
}
|
|
26514
27968
|
if (this._error) this._itemHost.setAttribute("aria-invalid", "true");
|
|
26515
27969
|
else this._itemHost.removeAttribute("aria-invalid");
|
|
@@ -26517,7 +27971,7 @@ var zx = (() => {
|
|
|
26517
27971
|
if (field) {
|
|
26518
27972
|
if (this._error) field.setHighlight(this._error, "danger");
|
|
26519
27973
|
else field.clearHighlight();
|
|
26520
|
-
if (
|
|
27974
|
+
if (error2 instanceof HTMLElement) error2.hidden = true;
|
|
26521
27975
|
}
|
|
26522
27976
|
}
|
|
26523
27977
|
/** @returns {void} */
|