@zeyos/zx 2.2.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +110 -0
- package/README.md +28 -2
- package/dist/types/components/multi-value-editor/multi-value-editor.d.ts +67 -8
- package/dist/types/components/permission/permission.d.ts +3 -0
- package/dist/types/components/select/select.d.ts +36 -1
- package/dist/types/components/tabbox/tabbox.d.ts +12 -3
- package/dist/zx-compat.esm.js +302 -34
- package/dist/zx-compat.esm.min.js +1 -1
- package/dist/zx-compat.global.js +302 -34
- package/dist/zx-compat.global.min.js +3 -3
- package/dist/zx-zeyos.esm.js +280 -27
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +159 -33
- package/dist/zx.esm.js +302 -34
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +302 -34
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +420 -17
- package/docs/llms.md +54 -12
- package/package.json +1 -1
- package/src/components/date-picker/date-range-picker.css +7 -0
- package/src/components/fieldset/fieldset.css +7 -0
- package/src/components/form/form.css +7 -0
- package/src/components/multi-value-editor/multi-value-editor.css +99 -15
- package/src/components/multi-value-editor/multi-value-editor.js +235 -18
- package/src/components/navigation-bar/navigation-bar.css +7 -0
- package/src/components/permission/permission.js +3 -0
- package/src/components/search/search.css +10 -2
- package/src/components/select/select.css +6 -0
- package/src/components/select/select.js +99 -11
- package/src/components/stepper/stepper.css +12 -2
- package/src/components/tabbox/tabbox.css +13 -3
- package/src/components/tabbox/tabbox.js +34 -10
- package/styles/tokens/dark.css +13 -10
- package/styles/tokens/global.css +12 -0
- package/styles/tokens/semantic.css +9 -5
- package/styles/tokens/themes.css +72 -0
- package/styles/zx.css +1 -0
package/dist/zx-zeyos.esm.js
CHANGED
|
@@ -5250,18 +5250,34 @@ function registerFieldUploadAdapter() {
|
|
|
5250
5250
|
}
|
|
5251
5251
|
|
|
5252
5252
|
// src/components/multi-value-editor/multi-value-editor.js
|
|
5253
|
+
var REORDER = Object.freeze({
|
|
5254
|
+
both: { drag: true, buttons: true },
|
|
5255
|
+
buttons: { drag: false, buttons: true },
|
|
5256
|
+
drag: { drag: true, buttons: false },
|
|
5257
|
+
none: { drag: false, buttons: false }
|
|
5258
|
+
});
|
|
5259
|
+
var PARTS = Object.freeze({
|
|
5260
|
+
value: ".zx-multi-value-editor__value",
|
|
5261
|
+
handle: ".zx-multi-value-editor__handle",
|
|
5262
|
+
up: '[data-action="up"]',
|
|
5263
|
+
down: '[data-action="down"]'
|
|
5264
|
+
});
|
|
5253
5265
|
var MultiValueEditor = class extends Component {
|
|
5254
5266
|
static cssName = "multi-value-editor";
|
|
5255
5267
|
/** @type {MultiValueEditorOptions} */
|
|
5256
|
-
static defaults = { values: [], options: null, addLabel: "Add value" };
|
|
5268
|
+
static defaults = { values: [], options: null, addLabel: "Add value", reorder: "both" };
|
|
5257
5269
|
/** @returns {HTMLElement} */
|
|
5258
5270
|
render() {
|
|
5259
5271
|
const root = (
|
|
5260
5272
|
/** @type {HTMLElement} */
|
|
5261
5273
|
this.el ?? h("div")
|
|
5262
5274
|
);
|
|
5275
|
+
this.el = root;
|
|
5263
5276
|
this._values = Array.isArray(this.options.values) ? this.options.values.map(String) : [];
|
|
5264
5277
|
this._ownedNodes = [];
|
|
5278
|
+
this._initialState = root.getAttribute("data-state");
|
|
5279
|
+
this._dragIndex = -1;
|
|
5280
|
+
this._armed = null;
|
|
5265
5281
|
this._rows = h("div", { class: "zx-multi-value-editor__rows" });
|
|
5266
5282
|
this._add = h("button", {
|
|
5267
5283
|
class: "zx-btn zx-multi-value-editor__add",
|
|
@@ -5272,6 +5288,17 @@ var MultiValueEditor = class extends Component {
|
|
|
5272
5288
|
root.append(this._rows, this._add);
|
|
5273
5289
|
this.listen(root, "click", (event) => this._handleClick(event));
|
|
5274
5290
|
this.listen(this._rows, "change", (event) => this._handleChange(event));
|
|
5291
|
+
this.listen(this._rows, "keydown", (event) => this._handleKeydown(event));
|
|
5292
|
+
if (this._reorder().drag) {
|
|
5293
|
+
this.listen(this._rows, "pointerdown", (event) => this._handlePointerDown(event));
|
|
5294
|
+
this.listen(this._rows, "dragstart", (event) => this._handleDragStart(event));
|
|
5295
|
+
this.listen(this._rows, "dragover", (event) => this._handleDragOver(event));
|
|
5296
|
+
this.listen(this._rows, "dragleave", (event) => this._handleDragLeave(event));
|
|
5297
|
+
this.listen(this._rows, "drop", (event) => this._handleDrop(event));
|
|
5298
|
+
this.listen(this._rows, "dragend", () => this._finishDrag());
|
|
5299
|
+
this.listen(document, "pointerup", () => this._disarm());
|
|
5300
|
+
this.listen(document, "pointercancel", () => this._disarm());
|
|
5301
|
+
}
|
|
5275
5302
|
this._renderRows();
|
|
5276
5303
|
return root;
|
|
5277
5304
|
}
|
|
@@ -5294,16 +5321,27 @@ var MultiValueEditor = class extends Component {
|
|
|
5294
5321
|
if (!silent) this.emit("change", { values: this.getValues() });
|
|
5295
5322
|
return this;
|
|
5296
5323
|
}
|
|
5297
|
-
/** @
|
|
5298
|
-
|
|
5324
|
+
/** @returns {{drag: boolean, buttons: boolean}} */
|
|
5325
|
+
_reorder() {
|
|
5326
|
+
return REORDER[this.options.reorder] ?? REORDER.both;
|
|
5327
|
+
}
|
|
5328
|
+
/**
|
|
5329
|
+
* @param {number} [focusIndex=-1] Row to focus after the render, or -1 for none.
|
|
5330
|
+
* @param {keyof PARTS} [focusPart='value'] Which control of that row takes focus.
|
|
5331
|
+
* @returns {void}
|
|
5332
|
+
*/
|
|
5333
|
+
_renderRows(focusIndex = -1, focusPart = "value") {
|
|
5299
5334
|
const choices = normalizeOptions2(this.options.options);
|
|
5300
5335
|
const useSelect = this.options.options !== null;
|
|
5336
|
+
const { drag, buttons } = this._reorder();
|
|
5337
|
+
const last = this._values.length - 1;
|
|
5301
5338
|
const rows = this._values.map((value, index) => {
|
|
5339
|
+
const entries = !useSelect || choices.some(([option]) => option === value) ? choices : [[value, value], ...choices];
|
|
5302
5340
|
const control = useSelect ? h("select", {
|
|
5303
5341
|
class: "zx-multi-value-editor__value",
|
|
5304
5342
|
ariaLabel: `Value ${index + 1}`,
|
|
5305
5343
|
dataset: { index }
|
|
5306
|
-
},
|
|
5344
|
+
}, entries.map(([optionValue, label]) => h("option", {
|
|
5307
5345
|
value: optionValue,
|
|
5308
5346
|
selected: optionValue === value
|
|
5309
5347
|
}, label))) : h("input", {
|
|
@@ -5316,40 +5354,52 @@ var MultiValueEditor = class extends Component {
|
|
|
5316
5354
|
return h(
|
|
5317
5355
|
"div",
|
|
5318
5356
|
{ class: "zx-multi-value-editor__row", dataset: { row: index } },
|
|
5357
|
+
drag ? h("button", {
|
|
5358
|
+
class: "zx-multi-value-editor__handle",
|
|
5359
|
+
type: "button",
|
|
5360
|
+
ariaLabel: `Reorder value ${index + 1}. Use the arrow keys to move it.`,
|
|
5361
|
+
dataset: { action: "grab", index }
|
|
5362
|
+
}, icon("drag", { size: 12 })) : null,
|
|
5319
5363
|
control,
|
|
5320
5364
|
h(
|
|
5321
5365
|
"div",
|
|
5322
5366
|
{ class: "zx-multi-value-editor__controls" },
|
|
5323
|
-
h("button", {
|
|
5367
|
+
buttons ? h("button", {
|
|
5324
5368
|
class: "zx-btn",
|
|
5325
5369
|
type: "button",
|
|
5326
|
-
disabled
|
|
5370
|
+
// `aria-disabled` rather than `disabled`: the buttons re-render on every move, and a
|
|
5371
|
+
// control that disables itself under the reader's finger drops focus to the document.
|
|
5372
|
+
ariaDisabled: String(index === 0),
|
|
5327
5373
|
ariaLabel: `Move value ${index + 1} up`,
|
|
5328
5374
|
dataset: { action: "up", index }
|
|
5329
|
-
}, "
|
|
5330
|
-
h("button", {
|
|
5375
|
+
}, icon("chevron-up", { size: 12 })) : null,
|
|
5376
|
+
buttons ? h("button", {
|
|
5331
5377
|
class: "zx-btn",
|
|
5332
5378
|
type: "button",
|
|
5333
|
-
|
|
5379
|
+
ariaDisabled: String(index === last),
|
|
5334
5380
|
ariaLabel: `Move value ${index + 1} down`,
|
|
5335
5381
|
dataset: { action: "down", index }
|
|
5336
|
-
}, "
|
|
5382
|
+
}, icon("chevron-down", { size: 12 })) : null,
|
|
5337
5383
|
h("button", {
|
|
5338
5384
|
class: "zx-btn",
|
|
5339
5385
|
type: "button",
|
|
5340
5386
|
ariaLabel: `Remove value ${index + 1}`,
|
|
5341
5387
|
dataset: { action: "remove", index }
|
|
5342
|
-
}, "
|
|
5388
|
+
}, icon("x", { size: 12 }))
|
|
5343
5389
|
)
|
|
5344
5390
|
);
|
|
5345
5391
|
});
|
|
5346
5392
|
this._rows.replaceChildren(...rows);
|
|
5347
|
-
if (focusIndex >= 0)
|
|
5393
|
+
if (focusIndex >= 0) {
|
|
5394
|
+
const row = rows[Math.min(focusIndex, rows.length - 1)];
|
|
5395
|
+
row?.querySelector(PARTS[focusPart] ?? PARTS.value)?.focus();
|
|
5396
|
+
}
|
|
5348
5397
|
}
|
|
5349
5398
|
/** @param {MouseEvent} event @returns {void} */
|
|
5350
5399
|
_handleClick(event) {
|
|
5351
5400
|
const button = event.target instanceof Element ? event.target.closest("[data-action]") : null;
|
|
5352
5401
|
if (!button || !this.el.contains(button)) return;
|
|
5402
|
+
if (button.getAttribute("aria-disabled") === "true") return;
|
|
5353
5403
|
const action = button.dataset.action;
|
|
5354
5404
|
const index = Number(button.dataset.index);
|
|
5355
5405
|
if (action === "add") {
|
|
@@ -5362,28 +5412,156 @@ var MultiValueEditor = class extends Component {
|
|
|
5362
5412
|
this._renderRows(Math.min(index, this._values.length - 1));
|
|
5363
5413
|
this._emitChange();
|
|
5364
5414
|
} else if ((action === "up" || action === "down") && Number.isInteger(index)) {
|
|
5365
|
-
|
|
5366
|
-
if (target < 0 || target >= this._values.length) return;
|
|
5367
|
-
[this._values[index], this._values[target]] = [this._values[target], this._values[index]];
|
|
5368
|
-
this._renderRows(target);
|
|
5369
|
-
this._emitChange();
|
|
5415
|
+
this._move(index, index + (action === "up" ? -1 : 1), action);
|
|
5370
5416
|
}
|
|
5371
5417
|
}
|
|
5372
5418
|
/** @param {Event} event @returns {void} */
|
|
5373
5419
|
_handleChange(event) {
|
|
5374
5420
|
const control = event.target instanceof Element ? event.target.closest("[data-index]") : null;
|
|
5375
|
-
if (!control) return;
|
|
5421
|
+
if (!control || !("value" in control)) return;
|
|
5376
5422
|
const index = Number(control.dataset.index);
|
|
5377
5423
|
if (!Number.isInteger(index) || index < 0 || index >= this._values.length) return;
|
|
5378
5424
|
this._values[index] = control.value;
|
|
5379
5425
|
this._emitChange();
|
|
5380
5426
|
}
|
|
5427
|
+
/**
|
|
5428
|
+
* The handle is the keyboard equivalent of dragging it, so `reorder: 'drag'` is still operable
|
|
5429
|
+
* without a pointer.
|
|
5430
|
+
* @param {KeyboardEvent} event
|
|
5431
|
+
* @returns {void}
|
|
5432
|
+
*/
|
|
5433
|
+
_handleKeydown(event) {
|
|
5434
|
+
const handle = event.target instanceof Element ? event.target.closest(".zx-multi-value-editor__handle") : null;
|
|
5435
|
+
if (!handle) return;
|
|
5436
|
+
const index = Number(handle.dataset.index);
|
|
5437
|
+
const target = event.key === "ArrowUp" ? index - 1 : event.key === "ArrowDown" ? index + 1 : event.key === "Home" ? 0 : event.key === "End" ? this._values.length - 1 : -1;
|
|
5438
|
+
if (target < 0 || target >= this._values.length) return;
|
|
5439
|
+
event.preventDefault();
|
|
5440
|
+
this._move(index, target, "handle");
|
|
5441
|
+
}
|
|
5442
|
+
/**
|
|
5443
|
+
* Arms the row for dragging. Only the handle does this, so text selection inside a row's input
|
|
5444
|
+
* keeps working and a stray drag cannot reorder anything.
|
|
5445
|
+
* @param {PointerEvent} event
|
|
5446
|
+
* @returns {void}
|
|
5447
|
+
*/
|
|
5448
|
+
_handlePointerDown(event) {
|
|
5449
|
+
const handle = event.target instanceof Element ? event.target.closest(".zx-multi-value-editor__handle") : null;
|
|
5450
|
+
this._disarm();
|
|
5451
|
+
if (!handle || handle.disabled) return;
|
|
5452
|
+
const row = (
|
|
5453
|
+
/** @type {HTMLElement|null} */
|
|
5454
|
+
handle.closest(".zx-multi-value-editor__row")
|
|
5455
|
+
);
|
|
5456
|
+
if (!row) return;
|
|
5457
|
+
row.draggable = true;
|
|
5458
|
+
this._armed = row;
|
|
5459
|
+
}
|
|
5460
|
+
/** @returns {void} */
|
|
5461
|
+
_disarm() {
|
|
5462
|
+
if (this._armed) this._armed.draggable = false;
|
|
5463
|
+
this._armed = null;
|
|
5464
|
+
}
|
|
5465
|
+
/** @param {DragEvent} event @returns {void} */
|
|
5466
|
+
_handleDragStart(event) {
|
|
5467
|
+
const row = event.target instanceof Element ? event.target.closest(".zx-multi-value-editor__row") : null;
|
|
5468
|
+
if (!row || row !== this._armed) {
|
|
5469
|
+
event.preventDefault();
|
|
5470
|
+
return;
|
|
5471
|
+
}
|
|
5472
|
+
this._dragIndex = Number(row.dataset.row);
|
|
5473
|
+
row.dataset.dragging = "true";
|
|
5474
|
+
this.el.setAttribute("data-state", "dragging");
|
|
5475
|
+
event.dataTransfer?.setData("text/plain", String(this._dragIndex));
|
|
5476
|
+
if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
|
|
5477
|
+
}
|
|
5478
|
+
/** @param {DragEvent} event @returns {void} */
|
|
5479
|
+
_handleDragOver(event) {
|
|
5480
|
+
if (this._dragIndex < 0) return;
|
|
5481
|
+
event.preventDefault();
|
|
5482
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
|
|
5483
|
+
this._showDrop(this._dropTarget(event.clientY));
|
|
5484
|
+
}
|
|
5485
|
+
/** @param {DragEvent} event @returns {void} */
|
|
5486
|
+
_handleDragLeave(event) {
|
|
5487
|
+
const to = event.relatedTarget;
|
|
5488
|
+
if (to instanceof Node && this._rows.contains(to)) return;
|
|
5489
|
+
this._clearDrop();
|
|
5490
|
+
}
|
|
5491
|
+
/** @param {DragEvent} event @returns {void} */
|
|
5492
|
+
_handleDrop(event) {
|
|
5493
|
+
if (this._dragIndex < 0) return;
|
|
5494
|
+
event.preventDefault();
|
|
5495
|
+
const from = this._dragIndex;
|
|
5496
|
+
const slot = this._dropTarget(event.clientY);
|
|
5497
|
+
this._finishDrag();
|
|
5498
|
+
this._move(from, slot > from ? slot - 1 : slot, "handle");
|
|
5499
|
+
}
|
|
5500
|
+
/** @returns {void} */
|
|
5501
|
+
_finishDrag() {
|
|
5502
|
+
this._dragIndex = -1;
|
|
5503
|
+
this._disarm();
|
|
5504
|
+
this._clearDrop();
|
|
5505
|
+
for (const row of this._rows.children) delete /** @type {HTMLElement} */
|
|
5506
|
+
row.dataset.dragging;
|
|
5507
|
+
if (this._initialState === null) this.el.removeAttribute("data-state");
|
|
5508
|
+
else this.el.setAttribute("data-state", this._initialState);
|
|
5509
|
+
}
|
|
5510
|
+
/**
|
|
5511
|
+
* Insertion slot (0…length) for a pointer at `clientY`.
|
|
5512
|
+
* @param {number} clientY Pointer position.
|
|
5513
|
+
* @returns {number}
|
|
5514
|
+
*/
|
|
5515
|
+
_dropTarget(clientY) {
|
|
5516
|
+
const rows = [...this._rows.children];
|
|
5517
|
+
for (const [index, row] of rows.entries()) {
|
|
5518
|
+
const box = row.getBoundingClientRect();
|
|
5519
|
+
if (clientY < box.top + box.height / 2) return index;
|
|
5520
|
+
}
|
|
5521
|
+
return rows.length;
|
|
5522
|
+
}
|
|
5523
|
+
/** @param {number} slot Insertion slot to mark. @returns {void} */
|
|
5524
|
+
_showDrop(slot) {
|
|
5525
|
+
const rows = [...this._rows.children];
|
|
5526
|
+
const inert = slot === this._dragIndex || slot === this._dragIndex + 1;
|
|
5527
|
+
for (const [index, row] of rows.entries()) {
|
|
5528
|
+
const cell = (
|
|
5529
|
+
/** @type {HTMLElement} */
|
|
5530
|
+
row
|
|
5531
|
+
);
|
|
5532
|
+
if (!inert && index === slot) cell.dataset.drop = "before";
|
|
5533
|
+
else if (!inert && slot === rows.length && index === rows.length - 1) cell.dataset.drop = "after";
|
|
5534
|
+
else delete cell.dataset.drop;
|
|
5535
|
+
}
|
|
5536
|
+
}
|
|
5537
|
+
/** @returns {void} */
|
|
5538
|
+
_clearDrop() {
|
|
5539
|
+
for (const row of this._rows.children) delete /** @type {HTMLElement} */
|
|
5540
|
+
row.dataset.drop;
|
|
5541
|
+
}
|
|
5542
|
+
/**
|
|
5543
|
+
* @param {number} from Index to move.
|
|
5544
|
+
* @param {number} to Destination index.
|
|
5545
|
+
* @param {keyof PARTS} part Control of the moved row that takes focus.
|
|
5546
|
+
* @returns {boolean} Whether anything moved.
|
|
5547
|
+
*/
|
|
5548
|
+
_move(from, to, part) {
|
|
5549
|
+
const size = this._values.length;
|
|
5550
|
+
if (from === to || from < 0 || to < 0 || from >= size || to >= size) return false;
|
|
5551
|
+
const [value] = this._values.splice(from, 1);
|
|
5552
|
+
this._values.splice(to, 0, value);
|
|
5553
|
+
this._renderRows(to, part);
|
|
5554
|
+
this._emitChange();
|
|
5555
|
+
return true;
|
|
5556
|
+
}
|
|
5381
5557
|
/** @returns {void} */
|
|
5382
5558
|
_emitChange() {
|
|
5383
5559
|
this.emit("change", { values: this.getValues() });
|
|
5384
5560
|
}
|
|
5385
5561
|
/** @returns {void} */
|
|
5386
5562
|
destroy() {
|
|
5563
|
+
if (this._initialState === null) this.el?.removeAttribute("data-state");
|
|
5564
|
+
else this.el?.setAttribute("data-state", this._initialState);
|
|
5387
5565
|
for (const node of this._ownedNodes ?? []) node.remove();
|
|
5388
5566
|
super.destroy();
|
|
5389
5567
|
}
|
|
@@ -6311,6 +6489,7 @@ var Select = class _Select extends Component {
|
|
|
6311
6489
|
/** @type {SelectOptions} */
|
|
6312
6490
|
static defaults = {
|
|
6313
6491
|
items: [],
|
|
6492
|
+
fixedItems: [],
|
|
6314
6493
|
valueKey: "ID",
|
|
6315
6494
|
labelKey: "name",
|
|
6316
6495
|
renderItem: null,
|
|
@@ -6330,6 +6509,8 @@ var Select = class _Select extends Component {
|
|
|
6330
6509
|
render() {
|
|
6331
6510
|
this._items = [];
|
|
6332
6511
|
this._visibleItems = [];
|
|
6512
|
+
this._fixedItems = [];
|
|
6513
|
+
this._visibleFixed = [];
|
|
6333
6514
|
this._selected = null;
|
|
6334
6515
|
this._optionEntries = [];
|
|
6335
6516
|
this._activeIndex = -1;
|
|
@@ -6413,6 +6594,8 @@ var Select = class _Select extends Component {
|
|
|
6413
6594
|
root.replaceChildren(control, panel);
|
|
6414
6595
|
this._items = Array.isArray(this.options.items) ? this.options.items.slice() : [];
|
|
6415
6596
|
this._visibleItems = this._items.slice();
|
|
6597
|
+
this._fixedItems = Array.isArray(this.options.fixedItems) ? this.options.fixedItems.slice() : [];
|
|
6598
|
+
this._visibleFixed = this._fixedItems.slice();
|
|
6416
6599
|
this._disabled = Boolean(this.options.disabled);
|
|
6417
6600
|
this._handleTypeahead = typeahead(
|
|
6418
6601
|
() => this._optionEntries.filter((entry) => !entry.clear).map((entry) => entry.element),
|
|
@@ -6489,7 +6672,22 @@ var Select = class _Select extends Component {
|
|
|
6489
6672
|
const selectedValue = this.value;
|
|
6490
6673
|
this._items = Array.isArray(items) ? items.slice() : [];
|
|
6491
6674
|
this._visibleItems = this._items.slice();
|
|
6492
|
-
this._selected = selectedValue == null ? null : this.
|
|
6675
|
+
this._selected = selectedValue == null ? null : this._findItem(selectedValue);
|
|
6676
|
+
this._syncInputValue();
|
|
6677
|
+
this._renderOptions();
|
|
6678
|
+
this._position?.update();
|
|
6679
|
+
return this;
|
|
6680
|
+
}
|
|
6681
|
+
/**
|
|
6682
|
+
* Replaces the pinned choices and preserves selection by ID when possible.
|
|
6683
|
+
* @param {SelectItem[]} items New fixed items.
|
|
6684
|
+
* @returns {this}
|
|
6685
|
+
*/
|
|
6686
|
+
setFixedItems(items) {
|
|
6687
|
+
const selectedValue = this.value;
|
|
6688
|
+
this._fixedItems = Array.isArray(items) ? items.slice() : [];
|
|
6689
|
+
this._visibleFixed = this._fixedItems.slice();
|
|
6690
|
+
this._selected = selectedValue == null ? null : this._findItem(selectedValue);
|
|
6493
6691
|
this._syncInputValue();
|
|
6494
6692
|
this._renderOptions();
|
|
6495
6693
|
this._position?.update();
|
|
@@ -6543,6 +6741,7 @@ var Select = class _Select extends Component {
|
|
|
6543
6741
|
this.refs.input.removeAttribute("aria-activedescendant");
|
|
6544
6742
|
this._activeIndex = -1;
|
|
6545
6743
|
this._visibleItems = this._items.slice();
|
|
6744
|
+
this._visibleFixed = this._fixedItems.slice();
|
|
6546
6745
|
this._syncInputValue();
|
|
6547
6746
|
this.emit("close", {});
|
|
6548
6747
|
return this;
|
|
@@ -6598,6 +6797,38 @@ var Select = class _Select extends Component {
|
|
|
6598
6797
|
select.set(requestedValue, { silent: true });
|
|
6599
6798
|
return select;
|
|
6600
6799
|
}
|
|
6800
|
+
/**
|
|
6801
|
+
* Creates the record-permission picker: the fixed Private and Public choices above the groups a
|
|
6802
|
+
* record can be shared with. The value is the tri-state ZeyOS stores — `false` for private,
|
|
6803
|
+
* `true` for public, or a group ID — so the control binds straight to the record field.
|
|
6804
|
+
*
|
|
6805
|
+
* @param {Element|string|null} target Component target.
|
|
6806
|
+
* @param {SelectOptions & {groups?: SelectItem[]|((query: string) => Promise<SelectItem[]>|SelectItem[])}} [options={}]
|
|
6807
|
+
* Select overrides other than the fixed choices; `groups` holds the shareable groups, either as
|
|
6808
|
+
* an array (filtered locally) or as a loader function (queried remotely).
|
|
6809
|
+
* @returns {Select}
|
|
6810
|
+
*/
|
|
6811
|
+
static permission(target, options = {}) {
|
|
6812
|
+
const { groups = [], value = true, valueKey = "ID", labelKey = "name", ...rest } = options;
|
|
6813
|
+
const loader = typeof groups === "function" ? groups : null;
|
|
6814
|
+
const shareable = loader ? [] : groups;
|
|
6815
|
+
const choices = [{ ID: false, name: "Private" }, { ID: true, name: "Public" }];
|
|
6816
|
+
const select = new _Select(target, {
|
|
6817
|
+
filter: loader ?? "local",
|
|
6818
|
+
...rest,
|
|
6819
|
+
value: null,
|
|
6820
|
+
items: shareable,
|
|
6821
|
+
fixedItems: [],
|
|
6822
|
+
// The two choices carry their own ID and label, so a caller's group readers stay untouched.
|
|
6823
|
+
valueKey: (item) => choices.includes(item) ? item.ID : readItem(item, valueKey),
|
|
6824
|
+
labelKey: (item) => choices.includes(item) ? item.name : String(readItem(item, labelKey) ?? "")
|
|
6825
|
+
});
|
|
6826
|
+
choices[0].name = select._message("permission.private", "Private");
|
|
6827
|
+
choices[1].name = select._message("permission.public", "Public");
|
|
6828
|
+
select.setFixedItems(choices);
|
|
6829
|
+
select.set(value, { silent: true });
|
|
6830
|
+
return select;
|
|
6831
|
+
}
|
|
6601
6832
|
/** Cleans up the panel, pending async work, and enhanced target contents. @returns {void} */
|
|
6602
6833
|
destroy() {
|
|
6603
6834
|
this._cancelQuery();
|
|
@@ -6668,8 +6899,8 @@ var Select = class _Select extends Component {
|
|
|
6668
6899
|
);
|
|
6669
6900
|
if (!this._open) this.open();
|
|
6670
6901
|
if (this.options.filter === "local") {
|
|
6671
|
-
|
|
6672
|
-
this._visibleItems = matchItems(this._items, query,
|
|
6902
|
+
this._visibleFixed = this._matchFixed(query);
|
|
6903
|
+
this._visibleItems = matchItems(this._items, query, this._searchKeys());
|
|
6673
6904
|
this._renderOptions();
|
|
6674
6905
|
this._setActive(this._optionEntries.length > 0 ? 0 : -1);
|
|
6675
6906
|
this._position?.update();
|
|
@@ -6724,6 +6955,10 @@ var Select = class _Select extends Component {
|
|
|
6724
6955
|
if (this.options.clearable) {
|
|
6725
6956
|
this._appendOption(null, true, this.options.placeholder || this._message("select.none", "No selection"));
|
|
6726
6957
|
}
|
|
6958
|
+
for (const item of this._visibleFixed) this._appendOption(item, false);
|
|
6959
|
+
if (this._visibleFixed.length > 0 && this._visibleItems.length > 0 && !this.options.groupKey) {
|
|
6960
|
+
list.append(h("div", { class: "zx-select__separator", role: "presentation" }));
|
|
6961
|
+
}
|
|
6727
6962
|
for (const item of this._visibleItems) {
|
|
6728
6963
|
if (this.options.groupKey) {
|
|
6729
6964
|
const group = this._read(item, this.options.groupKey);
|
|
@@ -6737,7 +6972,7 @@ var Select = class _Select extends Component {
|
|
|
6737
6972
|
}
|
|
6738
6973
|
this._appendOption(item, false);
|
|
6739
6974
|
}
|
|
6740
|
-
if (this._visibleItems.length === 0 && !this._loading) {
|
|
6975
|
+
if (this._visibleItems.length === 0 && this._visibleFixed.length === 0 && !this._loading) {
|
|
6741
6976
|
list.append(h("div", {
|
|
6742
6977
|
class: "zx-select__empty",
|
|
6743
6978
|
role: "status"
|
|
@@ -6777,6 +7012,7 @@ var Select = class _Select extends Component {
|
|
|
6777
7012
|
if (this._queryTimer !== null) clearTimeout(this._queryTimer);
|
|
6778
7013
|
this._requestSequence += 1;
|
|
6779
7014
|
const sequence = this._requestSequence;
|
|
7015
|
+
this._visibleFixed = this._matchFixed(query);
|
|
6780
7016
|
if (query.length < Math.max(0, Number(this.options.minQuery) || 0)) {
|
|
6781
7017
|
this._setLoading(false);
|
|
6782
7018
|
this._visibleItems = [];
|
|
@@ -6861,13 +7097,14 @@ var Select = class _Select extends Component {
|
|
|
6861
7097
|
}
|
|
6862
7098
|
/** @param {unknown} candidate @returns {SelectItem|null} */
|
|
6863
7099
|
_findItem(candidate) {
|
|
6864
|
-
const
|
|
7100
|
+
const pool = this._fixedItems.length === 0 ? this._items : [...this._fixedItems, ...this._items];
|
|
7101
|
+
const direct = pool.find((item) => item === candidate);
|
|
6865
7102
|
if (direct !== void 0) return direct;
|
|
6866
7103
|
const value = candidate !== null && typeof candidate === "object" ? this._itemValue(
|
|
6867
7104
|
/** @type {SelectItem} */
|
|
6868
7105
|
candidate
|
|
6869
7106
|
) : candidate;
|
|
6870
|
-
return
|
|
7107
|
+
return pool.find((item) => Object.is(this._itemValue(item), value)) ?? null;
|
|
6871
7108
|
}
|
|
6872
7109
|
/** @param {SelectItem|null} item @returns {unknown} */
|
|
6873
7110
|
_itemValue(item) {
|
|
@@ -6880,9 +7117,20 @@ var Select = class _Select extends Component {
|
|
|
6880
7117
|
}
|
|
6881
7118
|
/** @param {SelectItem|null} item @param {SelectValueReader|SelectLabelReader} reader @returns {unknown} */
|
|
6882
7119
|
_read(item, reader) {
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
7120
|
+
return readItem(item, reader);
|
|
7121
|
+
}
|
|
7122
|
+
/** @returns {Array<SelectValueReader|SelectLabelReader>} */
|
|
7123
|
+
_searchKeys() {
|
|
7124
|
+
return this.options.searchKeys ?? [this.options.labelKey];
|
|
7125
|
+
}
|
|
7126
|
+
/**
|
|
7127
|
+
* Narrows the pinned choices. They are always matched here and never handed to a filter
|
|
7128
|
+
* function: a fixed choice belongs to the control, not to whatever the remote source returns.
|
|
7129
|
+
* @param {string} query Search text.
|
|
7130
|
+
* @returns {SelectItem[]}
|
|
7131
|
+
*/
|
|
7132
|
+
_matchFixed(query) {
|
|
7133
|
+
return matchItems(this._fixedItems, query, this._searchKeys());
|
|
6886
7134
|
}
|
|
6887
7135
|
/** @returns {number} */
|
|
6888
7136
|
_selectedEntryIndex() {
|
|
@@ -6905,6 +7153,11 @@ var Select = class _Select extends Component {
|
|
|
6905
7153
|
return message === key ? fallback : message;
|
|
6906
7154
|
}
|
|
6907
7155
|
};
|
|
7156
|
+
function readItem(item, reader) {
|
|
7157
|
+
if (typeof reader === "function") return reader(item);
|
|
7158
|
+
if (item === null || typeof item !== "object") return item;
|
|
7159
|
+
return item[reader];
|
|
7160
|
+
}
|
|
6908
7161
|
function isPrintable(event) {
|
|
6909
7162
|
return event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
6910
7163
|
}
|