@zeyos/zx 3.0.0 → 4.1.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 +95 -0
- package/DESIGN-SYSTEM.md +87 -71
- package/README.md +16 -37
- package/dist/types/components/app-icon/app-icon.d.ts +88 -0
- package/dist/types/components/app-sidebar/app-sidebar.d.ts +106 -32
- package/dist/types/components/card/card.d.ts +128 -0
- package/dist/types/components/chart/chart.d.ts +1 -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/launcher/launcher.d.ts +65 -6
- package/dist/types/components/select/select.d.ts +97 -4
- package/dist/types/components/table/table.d.ts +107 -3
- package/dist/types/components/tag-picker/tag-picker.d.ts +16 -0
- package/dist/types/core/dom.d.ts +9 -1
- package/dist/types/core/util.d.ts +9 -0
- package/dist/types/index.d.ts +6 -2
- package/dist/types/{components/app-rail → internal}/app-rail.d.ts +34 -19
- package/dist/types/zeyos/icons.d.ts +24 -2
- package/dist/types/zeyos/index.d.ts +3 -2
- package/dist/types/zeyos/launcher.d.ts +147 -0
- package/dist/types/zeyos/select.d.ts +41 -0
- package/dist/zx-zeyos.esm.js +2018 -113
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +815 -16
- package/dist/zx.esm.js +2661 -338
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +2661 -338
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +602 -110
- package/docs/llms.md +181 -123
- package/docs/llms.txt +24 -21
- package/package.json +8 -17
- package/src/components/app-icon/app-icon.css +105 -0
- package/src/components/app-icon/app-icon.js +134 -0
- package/src/components/app-sidebar/app-sidebar.css +12 -0
- package/src/components/app-sidebar/app-sidebar.js +310 -110
- package/src/components/card/card.css +149 -0
- package/src/components/card/card.js +285 -0
- package/src/components/chart/chart.js +8 -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 +14 -4
- package/src/components/launcher/launcher.css +113 -3
- package/src/components/launcher/launcher.js +234 -26
- package/src/components/message/message.css +36 -3
- package/src/components/select/field-adapter.js +9 -4
- package/src/components/select/select.css +161 -10
- package/src/components/select/select.js +361 -48
- package/src/components/table/table.css +85 -0
- package/src/components/table/table.js +426 -31
- 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 +21 -1
- package/src/core/util.js +20 -0
- package/src/index.js +9 -2
- package/src/{components/app-rail → internal}/app-rail.js +80 -30
- package/src/zeyos/icons.js +38 -20
- package/src/zeyos/index.js +8 -2
- package/src/zeyos/launcher.js +338 -0
- package/src/zeyos/modules.js +1 -0
- package/src/zeyos/select.js +116 -1
- package/styles/tokens/global.css +1 -1
- package/styles/tokens/modules.css +1 -0
- package/styles/tokens/semantic.css +9 -0
- package/styles/tokens/themes.css +2 -2
- package/styles/zx.css +3 -0
- package/MIGRATION.md +0 -749
- package/dist/types/compat/base.d.ts +0 -156
- package/dist/types/compat/globals.d.ts +0 -20
- package/dist/types/compat/index.d.ts +0 -128
- package/dist/types/compat/map/basic.d.ts +0 -175
- package/dist/types/compat/map/data.d.ts +0 -103
- package/dist/types/compat/map/date-time.d.ts +0 -80
- package/dist/types/compat/map/factory.d.ts +0 -9
- package/dist/types/compat/map/form.d.ts +0 -98
- package/dist/types/compat/map/helpers.d.ts +0 -12
- package/dist/types/compat/map/http.d.ts +0 -36
- package/dist/types/compat/map/layout.d.ts +0 -106
- package/dist/types/compat/map/options.d.ts +0 -80
- package/dist/types/compat/map/select.d.ts +0 -95
- package/dist/types/compat/map/stubs.d.ts +0 -32
- package/dist/types/compat/map/utilities.d.ts +0 -37
- package/dist/types/compat-entry.d.ts +0 -5
- package/dist/zx-compat.esm.js +0 -17848
- package/dist/zx-compat.esm.min.js +0 -1
- package/dist/zx-compat.global.js +0 -29008
- package/dist/zx-compat.global.min.js +0 -3
- package/src/compat/base.js +0 -291
- package/src/compat/globals.js +0 -167
- package/src/compat/index.js +0 -84
- package/src/compat/map/basic.js +0 -464
- package/src/compat/map/data.js +0 -254
- package/src/compat/map/date-time.js +0 -177
- package/src/compat/map/factory.js +0 -45
- package/src/compat/map/form.js +0 -330
- package/src/compat/map/helpers.js +0 -46
- package/src/compat/map/http.js +0 -117
- package/src/compat/map/layout.js +0 -256
- package/src/compat/map/options.js +0 -267
- package/src/compat/map/select.js +0 -198
- package/src/compat/map/stubs.js +0 -26
- package/src/compat/map/utilities.js +0 -113
- package/src/compat-entry.js +0 -11
package/dist/zx-zeyos.esm.js
CHANGED
|
@@ -500,6 +500,20 @@ function isElement(value) {
|
|
|
500
500
|
return Boolean(value && typeof value === "object" && /** @type {{nodeType?: unknown}} */
|
|
501
501
|
value.nodeType === 1);
|
|
502
502
|
}
|
|
503
|
+
function isCssColor(value) {
|
|
504
|
+
if (typeof value !== "string") return false;
|
|
505
|
+
const text3 = value.trim();
|
|
506
|
+
if (!text3 || text3.length > 256 || /[;{}\u0000-\u001f\u007f]/.test(text3) || /(?:url|image|image-set|cross-fade|element|var)\s*\(/i.test(text3)) return false;
|
|
507
|
+
const supports = globalThis.CSS?.supports;
|
|
508
|
+
if (typeof supports === "function") {
|
|
509
|
+
try {
|
|
510
|
+
return supports.call(globalThis.CSS, "color", text3);
|
|
511
|
+
} catch {
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return /^(?:#[\da-f]{3,8}|[a-z]+|(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color|color-mix)\([^;{}]+\))$/i.test(text3);
|
|
516
|
+
}
|
|
503
517
|
function clamp(n, min, max) {
|
|
504
518
|
return Math.min(max, Math.max(min, n));
|
|
505
519
|
}
|
|
@@ -524,6 +538,18 @@ function cloneValue(value) {
|
|
|
524
538
|
|
|
525
539
|
// src/core/dom.js
|
|
526
540
|
var activeRefs = null;
|
|
541
|
+
function safeHref(value) {
|
|
542
|
+
if (typeof value !== "string") return null;
|
|
543
|
+
const href = value.trim();
|
|
544
|
+
if (!href) return null;
|
|
545
|
+
try {
|
|
546
|
+
const protocol = new URL(href, globalThis.location?.href ?? "https://zx.invalid/").protocol;
|
|
547
|
+
if (["javascript:", "data:", "vbscript:"].includes(protocol.toLowerCase())) return null;
|
|
548
|
+
} catch {
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
return href;
|
|
552
|
+
}
|
|
527
553
|
function h(tag, props, ...children) {
|
|
528
554
|
const element = document.createElement(tag);
|
|
529
555
|
let properties = props;
|
|
@@ -6533,17 +6559,33 @@ var PRIORITY_KEYS = [
|
|
|
6533
6559
|
"priority.high",
|
|
6534
6560
|
"priority.highest"
|
|
6535
6561
|
];
|
|
6536
|
-
var PRIORITY_FALLBACKS = ["Lowest", "Low", "
|
|
6562
|
+
var PRIORITY_FALLBACKS = ["Lowest", "Low", "Medium", "High", "Highest"];
|
|
6563
|
+
var DEFAULT_STATUSES = Object.freeze([
|
|
6564
|
+
["backlog", "status.backlog", "Backlog", "dotted", "muted"],
|
|
6565
|
+
["todo", "status.todo", "Todo", "ring", "neutral"],
|
|
6566
|
+
["in-progress", "status.inProgress", "In Progress", "progress", "warning"],
|
|
6567
|
+
["in-review", "status.inReview", "In Review", "review", "info"],
|
|
6568
|
+
["ready", "status.ready", "Ready to Merge", "ready", "accent"],
|
|
6569
|
+
["waiting", "status.waiting", "Waiting for feedback", "waiting", "warning"],
|
|
6570
|
+
["done", "status.done", "Done", "done", "info"],
|
|
6571
|
+
["canceled", "status.canceled", "Canceled", "canceled", "muted"],
|
|
6572
|
+
["duplicate", "status.duplicate", "Duplicate", "duplicate", "muted"]
|
|
6573
|
+
]);
|
|
6537
6574
|
var Select = class _Select extends Component {
|
|
6538
6575
|
static cssName = "select";
|
|
6539
6576
|
/** @type {SelectOptions} */
|
|
6540
6577
|
static defaults = {
|
|
6541
6578
|
items: [],
|
|
6542
6579
|
fixedItems: [],
|
|
6580
|
+
fixedLabel: null,
|
|
6543
6581
|
valueKey: "ID",
|
|
6544
6582
|
labelKey: "name",
|
|
6545
6583
|
renderItem: null,
|
|
6546
6584
|
renderValue: null,
|
|
6585
|
+
renderValueAdornment: null,
|
|
6586
|
+
renderNone: null,
|
|
6587
|
+
noneLabel: null,
|
|
6588
|
+
actions: [],
|
|
6547
6589
|
value: null,
|
|
6548
6590
|
disabled: false,
|
|
6549
6591
|
placeholder: "",
|
|
@@ -6561,6 +6603,7 @@ var Select = class _Select extends Component {
|
|
|
6561
6603
|
this._visibleItems = [];
|
|
6562
6604
|
this._fixedItems = [];
|
|
6563
6605
|
this._visibleFixed = [];
|
|
6606
|
+
this._actions = [];
|
|
6564
6607
|
this._selected = null;
|
|
6565
6608
|
this._optionEntries = [];
|
|
6566
6609
|
this._activeIndex = -1;
|
|
@@ -6572,8 +6615,7 @@ var Select = class _Select extends Component {
|
|
|
6572
6615
|
this._position = null;
|
|
6573
6616
|
this._createdRoot = false;
|
|
6574
6617
|
this._root = null;
|
|
6575
|
-
this.
|
|
6576
|
-
this._originalAttributes = /* @__PURE__ */ new Map();
|
|
6618
|
+
this._snapshot = null;
|
|
6577
6619
|
this._createdRoot = this.el === null;
|
|
6578
6620
|
const root = (
|
|
6579
6621
|
/** @type {HTMLElement} */
|
|
@@ -6581,10 +6623,7 @@ var Select = class _Select extends Component {
|
|
|
6581
6623
|
);
|
|
6582
6624
|
this.el = root;
|
|
6583
6625
|
this._root = root;
|
|
6584
|
-
|
|
6585
|
-
this._rememberAttribute(root, "data-state");
|
|
6586
|
-
this._rememberAttribute(root, "data-loading");
|
|
6587
|
-
this._rememberAttribute(root, "aria-disabled");
|
|
6626
|
+
this._snapshot = this._createdRoot ? null : snapshotTarget(root);
|
|
6588
6627
|
const listId = uid("zx-select-list");
|
|
6589
6628
|
const input = h("input", {
|
|
6590
6629
|
ref: "input",
|
|
@@ -6599,6 +6638,12 @@ var Select = class _Select extends Component {
|
|
|
6599
6638
|
placeholder: String(this.options.placeholder ?? ""),
|
|
6600
6639
|
readOnly: !this.options.filter
|
|
6601
6640
|
});
|
|
6641
|
+
const valueAdornment = h("span", {
|
|
6642
|
+
ref: "valueAdornment",
|
|
6643
|
+
class: "zx-select__value-adornment",
|
|
6644
|
+
ariaHidden: "true",
|
|
6645
|
+
hidden: true
|
|
6646
|
+
});
|
|
6602
6647
|
const clear = h("button", {
|
|
6603
6648
|
ref: "clear",
|
|
6604
6649
|
class: "zx-icon-btn zx-select__clear",
|
|
@@ -6624,6 +6669,7 @@ var Select = class _Select extends Component {
|
|
|
6624
6669
|
const control = h(
|
|
6625
6670
|
"div",
|
|
6626
6671
|
{ ref: "control", class: "zx-select__control" },
|
|
6672
|
+
valueAdornment,
|
|
6627
6673
|
input,
|
|
6628
6674
|
clear,
|
|
6629
6675
|
loading,
|
|
@@ -6646,9 +6692,10 @@ var Select = class _Select extends Component {
|
|
|
6646
6692
|
this._visibleItems = this._items.slice();
|
|
6647
6693
|
this._fixedItems = Array.isArray(this.options.fixedItems) ? this.options.fixedItems.slice() : [];
|
|
6648
6694
|
this._visibleFixed = this._fixedItems.slice();
|
|
6695
|
+
this._actions = normalizeSelectActions(this.options.actions);
|
|
6649
6696
|
this._disabled = Boolean(this.options.disabled);
|
|
6650
6697
|
this._handleTypeahead = typeahead(
|
|
6651
|
-
() => this._optionEntries.filter((entry) => !entry.clear).map((entry) => entry.element),
|
|
6698
|
+
() => this._optionEntries.filter((entry) => !entry.clear && !entry.action).map((entry) => entry.element),
|
|
6652
6699
|
(_option, index) => {
|
|
6653
6700
|
if (!this._open) this.open();
|
|
6654
6701
|
this._setActive(this._itemEntryIndex(index));
|
|
@@ -6743,6 +6790,13 @@ var Select = class _Select extends Component {
|
|
|
6743
6790
|
this._position?.update();
|
|
6744
6791
|
return this;
|
|
6745
6792
|
}
|
|
6793
|
+
/** Replaces command rows without changing the selected value. @param {SelectAction[]} actions @returns {this} */
|
|
6794
|
+
setActions(actions) {
|
|
6795
|
+
this._actions = normalizeSelectActions(actions);
|
|
6796
|
+
this._renderOptions();
|
|
6797
|
+
this._position?.update();
|
|
6798
|
+
return this;
|
|
6799
|
+
}
|
|
6746
6800
|
/**
|
|
6747
6801
|
* Restores the configured initial value.
|
|
6748
6802
|
* @param {{silent?: boolean}} [options={}] Reset behavior.
|
|
@@ -6827,24 +6881,109 @@ var Select = class _Select extends Component {
|
|
|
6827
6881
|
value: null,
|
|
6828
6882
|
items: [],
|
|
6829
6883
|
renderItem(item) {
|
|
6884
|
+
return priorityContent(item, true);
|
|
6885
|
+
},
|
|
6886
|
+
renderValueAdornment(item) {
|
|
6887
|
+
return priorityIndicator(item.level);
|
|
6888
|
+
}
|
|
6889
|
+
});
|
|
6890
|
+
select.setItems(PRIORITY_KEYS.map((key, level) => {
|
|
6891
|
+
let name = level === 2 ? select.msg("priority.medium") : key;
|
|
6892
|
+
if (name === "priority.medium") name = select._message(key, PRIORITY_FALLBACKS[level]);
|
|
6893
|
+
else if (level !== 2) name = select._message(key, PRIORITY_FALLBACKS[level]);
|
|
6894
|
+
return { ID: level, name, level };
|
|
6895
|
+
}));
|
|
6896
|
+
select.set(requestedValue, { silent: true });
|
|
6897
|
+
return select;
|
|
6898
|
+
}
|
|
6899
|
+
/**
|
|
6900
|
+
* Creates a status picker with shape- and color-distinct workflow indicators.
|
|
6901
|
+
* @param {Element|string|null} target Component target.
|
|
6902
|
+
* @param {SelectOptions & {items?: Array<Record<string, any>>}} [options={}] Select overrides and status items.
|
|
6903
|
+
* @returns {Select}
|
|
6904
|
+
*/
|
|
6905
|
+
static status(target, options = {}) {
|
|
6906
|
+
const { items = null, value = null, ...rest } = options;
|
|
6907
|
+
const select = new _Select(target, {
|
|
6908
|
+
...rest,
|
|
6909
|
+
value: null,
|
|
6910
|
+
items: [],
|
|
6911
|
+
renderItem(item) {
|
|
6912
|
+
return statusContent(item);
|
|
6913
|
+
},
|
|
6914
|
+
renderValueAdornment(item) {
|
|
6915
|
+
return statusIndicator(item);
|
|
6916
|
+
}
|
|
6917
|
+
});
|
|
6918
|
+
const statuses = Array.isArray(items) ? items.map(normalizeStatusItem).filter(Boolean) : DEFAULT_STATUSES.map(([ID, key, fallback, indicator, tone], index) => ({
|
|
6919
|
+
ID,
|
|
6920
|
+
name: select._message(key, fallback),
|
|
6921
|
+
indicator,
|
|
6922
|
+
tone,
|
|
6923
|
+
shortcut: String(index + 1)
|
|
6924
|
+
}));
|
|
6925
|
+
select.setItems(statuses);
|
|
6926
|
+
select.set(value, { silent: true });
|
|
6927
|
+
return select;
|
|
6928
|
+
}
|
|
6929
|
+
/**
|
|
6930
|
+
* Creates a rich entity picker with optional Recent choices and a create command.
|
|
6931
|
+
* @param {Element|string|null} target Component target.
|
|
6932
|
+
* @param {SelectOptions & EntitySelectOptions} [options={}] Entity-preset options.
|
|
6933
|
+
* @returns {Select}
|
|
6934
|
+
*/
|
|
6935
|
+
static entity(target, options = {}) {
|
|
6936
|
+
const {
|
|
6937
|
+
recent = [],
|
|
6938
|
+
recentLabel = "Recent",
|
|
6939
|
+
create = false,
|
|
6940
|
+
iconKey = "icon",
|
|
6941
|
+
colorKey = "color",
|
|
6942
|
+
subtitleKey = "description",
|
|
6943
|
+
renderIcon = null,
|
|
6944
|
+
fixedItems = [],
|
|
6945
|
+
actions = [],
|
|
6946
|
+
renderItem = null,
|
|
6947
|
+
renderValueAdornment = null,
|
|
6948
|
+
...rest
|
|
6949
|
+
} = options;
|
|
6950
|
+
const visual = (item, selected = false) => {
|
|
6951
|
+
if (typeof renderIcon === "function") return renderIcon(item, { selected });
|
|
6952
|
+
const value = readSelectItem(item, iconKey);
|
|
6953
|
+
if (value && typeof value === "object" && typeof value.nodeType === "number") return value.cloneNode(true);
|
|
6954
|
+
if (typeof value !== "string" || !value) return null;
|
|
6955
|
+
const color = readSelectItem(item, colorKey);
|
|
6956
|
+
return h("span", {
|
|
6957
|
+
class: "zx-select__entity-glyph",
|
|
6958
|
+
style: isCssColor(color) ? { "--zx-select-entity-color": String(color) } : null,
|
|
6959
|
+
ariaHidden: "true"
|
|
6960
|
+
}, icon(value, { size: 14 }));
|
|
6961
|
+
};
|
|
6962
|
+
const createAction = create ? normalizeEntityCreateAction(create) : null;
|
|
6963
|
+
const select = new _Select(target, {
|
|
6964
|
+
...rest,
|
|
6965
|
+
fixedItems: [...Array.isArray(recent) ? recent : [], ...Array.isArray(fixedItems) ? fixedItems : []],
|
|
6966
|
+
fixedLabel: Array.isArray(recent) && recent.length ? recentLabel : rest.fixedLabel ?? null,
|
|
6967
|
+
actions: [...Array.isArray(actions) ? actions : [], ...createAction ? [createAction] : []],
|
|
6968
|
+
renderItem: renderItem ?? ((item) => {
|
|
6969
|
+
const decoration = visual(item, false);
|
|
6970
|
+
const subtitle = readSelectItem(item, subtitleKey);
|
|
6971
|
+
const copy = h(
|
|
6972
|
+
"span",
|
|
6973
|
+
{ class: "zx-select__entity-copy" },
|
|
6974
|
+
h("span", { class: "zx-select__entity-label" }, String(readSelectItem(item, rest.labelKey ?? "name") ?? "")),
|
|
6975
|
+
subtitle == null || subtitle === "" ? null : h("span", { class: "zx-select__entity-subtitle" }, String(subtitle))
|
|
6976
|
+
);
|
|
6830
6977
|
return h(
|
|
6831
6978
|
"span",
|
|
6832
|
-
{ class: "zx-
|
|
6833
|
-
h("span", {
|
|
6834
|
-
|
|
6835
|
-
dataset: { level: item.level },
|
|
6836
|
-
ariaHidden: "true"
|
|
6837
|
-
}),
|
|
6838
|
-
h("span", {}, String(item.name))
|
|
6979
|
+
{ class: "zx-select__entity" },
|
|
6980
|
+
decoration ? h("span", { class: "zx-select__entity-icon", ariaHidden: "true" }, decoration) : null,
|
|
6981
|
+
copy
|
|
6839
6982
|
);
|
|
6840
|
-
}
|
|
6983
|
+
}),
|
|
6984
|
+
renderValueAdornment: renderValueAdornment ?? ((item) => visual(item, true))
|
|
6841
6985
|
});
|
|
6842
|
-
select.
|
|
6843
|
-
ID: level,
|
|
6844
|
-
name: select._message(key, PRIORITY_FALLBACKS[level]),
|
|
6845
|
-
level
|
|
6846
|
-
})));
|
|
6847
|
-
select.set(requestedValue, { silent: true });
|
|
6986
|
+
select.el.dataset.preset = "entity";
|
|
6848
6987
|
return select;
|
|
6849
6988
|
}
|
|
6850
6989
|
/**
|
|
@@ -6859,12 +6998,21 @@ var Select = class _Select extends Component {
|
|
|
6859
6998
|
* @returns {Select}
|
|
6860
6999
|
*/
|
|
6861
7000
|
static permission(target, options = {}) {
|
|
6862
|
-
const {
|
|
7001
|
+
const {
|
|
7002
|
+
groups = [],
|
|
7003
|
+
value = true,
|
|
7004
|
+
valueKey = "ID",
|
|
7005
|
+
labelKey = "name",
|
|
7006
|
+
filter = "local",
|
|
7007
|
+
...rest
|
|
7008
|
+
} = options;
|
|
6863
7009
|
const loader = typeof groups === "function" ? groups : null;
|
|
6864
7010
|
const shareable = loader ? [] : groups;
|
|
6865
7011
|
const choices = [{ ID: false, name: "Private" }, { ID: true, name: "Public" }];
|
|
6866
7012
|
const select = new _Select(target, {
|
|
6867
|
-
|
|
7013
|
+
// An async group loader is the data source and cannot be disconnected by an incidental
|
|
7014
|
+
// `filter` override. Array-backed groups still honor local/disabled filtering.
|
|
7015
|
+
filter: loader ?? filter,
|
|
6868
7016
|
...rest,
|
|
6869
7017
|
value: null,
|
|
6870
7018
|
items: shareable,
|
|
@@ -6888,18 +7036,24 @@ var Select = class _Select extends Component {
|
|
|
6888
7036
|
/** @type {HTMLElement} */
|
|
6889
7037
|
this.el
|
|
6890
7038
|
);
|
|
6891
|
-
if (!this._createdRoot && root)
|
|
6892
|
-
root.replaceChildren(...this._originalChildren);
|
|
6893
|
-
for (const [name, value] of this._originalAttributes) {
|
|
6894
|
-
if (value === null) root.removeAttribute(name);
|
|
6895
|
-
else root.setAttribute(name, value);
|
|
6896
|
-
}
|
|
6897
|
-
}
|
|
7039
|
+
if (!this._createdRoot && root) restoreTarget(root, this._snapshot);
|
|
6898
7040
|
super.destroy();
|
|
6899
7041
|
}
|
|
6900
7042
|
/** @param {KeyboardEvent} event @returns {void} */
|
|
6901
7043
|
_onKeydown(event) {
|
|
6902
7044
|
if (this._disabled) return;
|
|
7045
|
+
if (this._open && isPrintable(event)) {
|
|
7046
|
+
const shortcut = this._optionEntries.findIndex((entry) => !entry.clear && !entry.action && String(
|
|
7047
|
+
/** @type {Record<string, any>|null} */
|
|
7048
|
+
entry.item?.shortcut ?? ""
|
|
7049
|
+
) === event.key);
|
|
7050
|
+
if (shortcut >= 0) {
|
|
7051
|
+
event.preventDefault();
|
|
7052
|
+
this._setActive(shortcut);
|
|
7053
|
+
this._chooseEntry(this._optionEntries[shortcut]);
|
|
7054
|
+
return;
|
|
7055
|
+
}
|
|
7056
|
+
}
|
|
6903
7057
|
if (event.key === "ArrowDown") {
|
|
6904
7058
|
event.preventDefault();
|
|
6905
7059
|
if (!this._open) this.open();
|
|
@@ -6947,6 +7101,7 @@ var Select = class _Select extends Component {
|
|
|
6947
7101
|
/** @type {HTMLInputElement} */
|
|
6948
7102
|
this.refs.input.value
|
|
6949
7103
|
);
|
|
7104
|
+
this._syncValueAdornment();
|
|
6950
7105
|
if (!this._open) this.open();
|
|
6951
7106
|
if (this.options.filter === "local") {
|
|
6952
7107
|
this._visibleFixed = this._matchFixed(query);
|
|
@@ -6970,6 +7125,22 @@ var Select = class _Select extends Component {
|
|
|
6970
7125
|
/** @param {SelectOptionEntry|undefined} entry @returns {void} */
|
|
6971
7126
|
_chooseEntry(entry) {
|
|
6972
7127
|
if (!entry) return;
|
|
7128
|
+
if (entry.action) {
|
|
7129
|
+
const detail = {
|
|
7130
|
+
id: entry.action.id,
|
|
7131
|
+
action: entry.action,
|
|
7132
|
+
query: (
|
|
7133
|
+
/** @type {HTMLInputElement} */
|
|
7134
|
+
this.refs.input.value
|
|
7135
|
+
),
|
|
7136
|
+
select: this
|
|
7137
|
+
};
|
|
7138
|
+
const action = this.emit("action", detail);
|
|
7139
|
+
if (!action.defaultPrevented) entry.action.invoke?.(detail);
|
|
7140
|
+
this.close();
|
|
7141
|
+
this.focus();
|
|
7142
|
+
return;
|
|
7143
|
+
}
|
|
6973
7144
|
this.set(entry.clear ? null : entry.item);
|
|
6974
7145
|
this.close();
|
|
6975
7146
|
this.focus();
|
|
@@ -7002,14 +7173,19 @@ var Select = class _Select extends Component {
|
|
|
7002
7173
|
list.replaceChildren();
|
|
7003
7174
|
this._optionEntries = [];
|
|
7004
7175
|
let lastGroup = /* @__PURE__ */ Symbol("initial-group");
|
|
7176
|
+
const fixed = this._uniqueItems(this._visibleFixed);
|
|
7177
|
+
const items = this._uniqueItems(this._visibleItems, fixed);
|
|
7005
7178
|
if (this.options.clearable) {
|
|
7006
|
-
this._appendOption(null, true, this.options.
|
|
7179
|
+
this._appendOption(null, true, this.options.noneLabel ?? this._message("select.none", "No selection"));
|
|
7007
7180
|
}
|
|
7008
|
-
|
|
7009
|
-
|
|
7181
|
+
if (fixed.length > 0 && this.options.fixedLabel) {
|
|
7182
|
+
list.append(h("div", { class: "zx-select__group", role: "presentation" }, String(this.options.fixedLabel)));
|
|
7183
|
+
}
|
|
7184
|
+
for (const item of fixed) this._appendOption(item, false);
|
|
7185
|
+
if (fixed.length > 0 && items.length > 0 && !this.options.groupKey) {
|
|
7010
7186
|
list.append(h("div", { class: "zx-select__separator", role: "presentation" }));
|
|
7011
7187
|
}
|
|
7012
|
-
for (const item of
|
|
7188
|
+
for (const item of items) {
|
|
7013
7189
|
if (this.options.groupKey) {
|
|
7014
7190
|
const group = this._read(item, this.options.groupKey);
|
|
7015
7191
|
if (!Object.is(group, lastGroup)) {
|
|
@@ -7022,7 +7198,23 @@ var Select = class _Select extends Component {
|
|
|
7022
7198
|
}
|
|
7023
7199
|
this._appendOption(item, false);
|
|
7024
7200
|
}
|
|
7025
|
-
if (this.
|
|
7201
|
+
if (this._actions.length > 0) {
|
|
7202
|
+
if (fixed.length > 0 || items.length > 0 || this.options.clearable) {
|
|
7203
|
+
list.append(h("div", { class: "zx-select__separator", role: "presentation" }));
|
|
7204
|
+
}
|
|
7205
|
+
let actionGroup = /* @__PURE__ */ Symbol("initial-action-group");
|
|
7206
|
+
for (const action of this._actions) {
|
|
7207
|
+
if (action.group && action.group !== actionGroup) {
|
|
7208
|
+
list.append(h("div", {
|
|
7209
|
+
class: "zx-select__group",
|
|
7210
|
+
role: "presentation"
|
|
7211
|
+
}, action.group));
|
|
7212
|
+
actionGroup = action.group;
|
|
7213
|
+
}
|
|
7214
|
+
this._appendAction(action);
|
|
7215
|
+
}
|
|
7216
|
+
}
|
|
7217
|
+
if (items.length === 0 && fixed.length === 0 && this._actions.length === 0 && !this._loading) {
|
|
7026
7218
|
list.append(h("div", {
|
|
7027
7219
|
class: "zx-select__empty",
|
|
7028
7220
|
role: "status"
|
|
@@ -7034,7 +7226,7 @@ var Select = class _Select extends Component {
|
|
|
7034
7226
|
_appendOption(item, clear, label) {
|
|
7035
7227
|
const selected = clear ? this._selected === null : this._selected !== null && Object.is(this._itemValue(item), this.value);
|
|
7036
7228
|
const index = this._optionEntries.length;
|
|
7037
|
-
const content = clear ? String(label ?? "") : this._renderItem(item);
|
|
7229
|
+
const content = clear && typeof this.options.renderNone === "function" ? normalizeRenderedContent(this.options.renderNone(String(label ?? ""))) : clear ? String(label ?? "") : this._renderItem(item);
|
|
7038
7230
|
const option = h("div", {
|
|
7039
7231
|
class: "zx-select__option",
|
|
7040
7232
|
id: uid("zx-select-option"),
|
|
@@ -7044,16 +7236,37 @@ var Select = class _Select extends Component {
|
|
|
7044
7236
|
}, h("span", { class: "zx-select__option-content" }, content));
|
|
7045
7237
|
if (selected) option.append(icon("check", { size: 15 }));
|
|
7046
7238
|
this.refs.list.append(option);
|
|
7047
|
-
this._optionEntries.push({ item, clear, element: option });
|
|
7239
|
+
this._optionEntries.push({ item, clear, action: null, element: option });
|
|
7240
|
+
}
|
|
7241
|
+
/** @param {SelectAction} action @returns {void} */
|
|
7242
|
+
_appendAction(action) {
|
|
7243
|
+
const index = this._optionEntries.length;
|
|
7244
|
+
const visual = selectActionIcon(action.icon);
|
|
7245
|
+
const option = h("div", {
|
|
7246
|
+
class: "zx-select__option zx-select__action",
|
|
7247
|
+
id: uid("zx-select-option"),
|
|
7248
|
+
role: "option",
|
|
7249
|
+
ariaSelected: "false",
|
|
7250
|
+
dataset: { index, kind: "action" }
|
|
7251
|
+
}, h(
|
|
7252
|
+
"span",
|
|
7253
|
+
{ class: "zx-select__option-content zx-select__action-content" },
|
|
7254
|
+
visual ? h("span", { class: "zx-select__action-icon", ariaHidden: "true" }, visual) : null,
|
|
7255
|
+
h(
|
|
7256
|
+
"span",
|
|
7257
|
+
{ class: "zx-select__action-copy" },
|
|
7258
|
+
h("span", { class: "zx-select__action-label" }, action.label),
|
|
7259
|
+
action.description ? h("span", { class: "zx-select__action-description" }, action.description) : null
|
|
7260
|
+
)
|
|
7261
|
+
));
|
|
7262
|
+
this.refs.list.append(option);
|
|
7263
|
+
this._optionEntries.push({ item: null, clear: false, action, element: option });
|
|
7048
7264
|
}
|
|
7049
7265
|
/** @param {SelectItem|null} item @returns {Node|string} */
|
|
7050
7266
|
_renderItem(item) {
|
|
7051
7267
|
if (typeof this.options.renderItem === "function") {
|
|
7052
7268
|
const rendered = this.options.renderItem(item);
|
|
7053
|
-
return rendered
|
|
7054
|
-
/** @type {Node} */
|
|
7055
|
-
rendered
|
|
7056
|
-
) : String(rendered ?? "");
|
|
7269
|
+
return normalizeRenderedContent(rendered);
|
|
7057
7270
|
}
|
|
7058
7271
|
return this._itemLabel(item);
|
|
7059
7272
|
}
|
|
@@ -7124,9 +7337,29 @@ var Select = class _Select extends Component {
|
|
|
7124
7337
|
this.refs.input
|
|
7125
7338
|
);
|
|
7126
7339
|
input.value = this._selected === null ? "" : typeof this.options.renderValue === "function" ? String(this.options.renderValue(this._selected) ?? "") : this._itemLabel(this._selected);
|
|
7340
|
+
this._syncValueAdornment();
|
|
7127
7341
|
this.refs.clear.hidden = !this.options.clearable || this._selected === null;
|
|
7128
7342
|
}
|
|
7129
7343
|
/** @returns {void} */
|
|
7344
|
+
_syncValueAdornment() {
|
|
7345
|
+
const host = this.refs.valueAdornment;
|
|
7346
|
+
if (!host) return;
|
|
7347
|
+
const selectedLabel = this._selected === null ? "" : typeof this.options.renderValue === "function" ? String(this.options.renderValue(this._selected) ?? "") : this._itemLabel(this._selected);
|
|
7348
|
+
const query = String(
|
|
7349
|
+
/** @type {HTMLInputElement} */
|
|
7350
|
+
this.refs.input?.value ?? ""
|
|
7351
|
+
);
|
|
7352
|
+
if (this._selected === null || typeof this.options.renderValueAdornment !== "function" || Boolean(this.options.filter && query !== selectedLabel)) {
|
|
7353
|
+
host.replaceChildren();
|
|
7354
|
+
host.hidden = true;
|
|
7355
|
+
return;
|
|
7356
|
+
}
|
|
7357
|
+
const rendered = this.options.renderValueAdornment(this._selected);
|
|
7358
|
+
const content = normalizeRenderedContent(rendered);
|
|
7359
|
+
host.replaceChildren(content instanceof Node ? content : document.createTextNode(content));
|
|
7360
|
+
host.hidden = rendered == null || typeof content === "string" && content === "";
|
|
7361
|
+
}
|
|
7362
|
+
/** @returns {void} */
|
|
7130
7363
|
_syncDisabled() {
|
|
7131
7364
|
const input = (
|
|
7132
7365
|
/** @type {HTMLInputElement} */
|
|
@@ -7182,6 +7415,16 @@ var Select = class _Select extends Component {
|
|
|
7182
7415
|
_matchFixed(query) {
|
|
7183
7416
|
return matchItems(this._fixedItems, query, this._searchKeys());
|
|
7184
7417
|
}
|
|
7418
|
+
/** De-duplicates choices by public value while preserving caller order. @param {SelectItem[]} items @param {SelectItem[]} [excluded=[]] @returns {SelectItem[]} */
|
|
7419
|
+
_uniqueItems(items, excluded = []) {
|
|
7420
|
+
const values = excluded.map((item) => this._itemValue(item));
|
|
7421
|
+
return items.filter((item) => {
|
|
7422
|
+
const value = this._itemValue(item);
|
|
7423
|
+
if (values.some((candidate) => Object.is(candidate, value))) return false;
|
|
7424
|
+
values.push(value);
|
|
7425
|
+
return true;
|
|
7426
|
+
});
|
|
7427
|
+
}
|
|
7185
7428
|
/** @returns {number} */
|
|
7186
7429
|
_selectedEntryIndex() {
|
|
7187
7430
|
if (this._selected === null) return this.options.clearable ? 0 : -1;
|
|
@@ -7193,16 +7436,104 @@ var Select = class _Select extends Component {
|
|
|
7193
7436
|
_itemEntryIndex(itemIndex) {
|
|
7194
7437
|
return itemIndex + (this.options.clearable ? 1 : 0);
|
|
7195
7438
|
}
|
|
7196
|
-
/** @param {string} name @returns {void} */
|
|
7197
|
-
_rememberAttribute(element, name) {
|
|
7198
|
-
this._originalAttributes.set(name, element.getAttribute(name));
|
|
7199
|
-
}
|
|
7200
7439
|
/** @param {string} key @param {string} fallback @returns {string} */
|
|
7201
7440
|
_message(key, fallback) {
|
|
7202
7441
|
const message = this.msg(key);
|
|
7203
7442
|
return message === key ? fallback : message;
|
|
7204
7443
|
}
|
|
7205
7444
|
};
|
|
7445
|
+
function readSelectItem(item, reader) {
|
|
7446
|
+
if (typeof reader === "function") return reader(item);
|
|
7447
|
+
if (item !== null && typeof item === "object" && typeof reader === "string") return item[reader];
|
|
7448
|
+
return null;
|
|
7449
|
+
}
|
|
7450
|
+
function normalizeEntityCreateAction(value) {
|
|
7451
|
+
const descriptor = value === true ? {} : value;
|
|
7452
|
+
return {
|
|
7453
|
+
id: descriptor.id ?? "create",
|
|
7454
|
+
label: descriptor.label ?? "Create new\u2026",
|
|
7455
|
+
description: descriptor.description,
|
|
7456
|
+
group: descriptor.group ?? "New",
|
|
7457
|
+
icon: descriptor.icon ?? "plus",
|
|
7458
|
+
invoke: descriptor.invoke
|
|
7459
|
+
};
|
|
7460
|
+
}
|
|
7461
|
+
function normalizeRenderedContent(rendered) {
|
|
7462
|
+
return rendered && typeof rendered === "object" && "nodeType" in rendered ? (
|
|
7463
|
+
/** @type {Node} */
|
|
7464
|
+
rendered
|
|
7465
|
+
) : String(rendered ?? "");
|
|
7466
|
+
}
|
|
7467
|
+
function normalizeSelectActions(actions) {
|
|
7468
|
+
if (!Array.isArray(actions)) return [];
|
|
7469
|
+
return actions.filter((action) => action && typeof action === "object" && action.id != null && action.label != null).map((action) => ({
|
|
7470
|
+
...action,
|
|
7471
|
+
label: String(action.label),
|
|
7472
|
+
description: action.description == null ? void 0 : String(action.description),
|
|
7473
|
+
group: action.group == null ? void 0 : String(action.group)
|
|
7474
|
+
}));
|
|
7475
|
+
}
|
|
7476
|
+
function selectActionIcon(visual) {
|
|
7477
|
+
if (typeof visual === "string") return icon(visual);
|
|
7478
|
+
if (visual instanceof Node) return visual.cloneNode(true);
|
|
7479
|
+
return null;
|
|
7480
|
+
}
|
|
7481
|
+
function priorityContent(item, label) {
|
|
7482
|
+
return h(
|
|
7483
|
+
"span",
|
|
7484
|
+
{ class: "zx-select__priority" },
|
|
7485
|
+
priorityIndicator(item.level),
|
|
7486
|
+
label ? h("span", {}, String(item.name)) : null
|
|
7487
|
+
);
|
|
7488
|
+
}
|
|
7489
|
+
function priorityIndicator(level) {
|
|
7490
|
+
const normalized = Math.max(0, Math.min(4, Math.trunc(Number(level) || 0)));
|
|
7491
|
+
return h("span", {
|
|
7492
|
+
class: "zx-select__priority-icon",
|
|
7493
|
+
dataset: { level: normalized },
|
|
7494
|
+
ariaHidden: "true"
|
|
7495
|
+
}, Array.from({ length: 5 }, (_, index) => h("span", {
|
|
7496
|
+
dataset: { filled: String(index <= normalized) }
|
|
7497
|
+
})));
|
|
7498
|
+
}
|
|
7499
|
+
function normalizeStatusItem(input) {
|
|
7500
|
+
if (!input || typeof input !== "object" || input.ID == null || input.name == null) return null;
|
|
7501
|
+
const indicators = ["dotted", "ring", "progress", "review", "ready", "waiting", "done", "canceled", "duplicate"];
|
|
7502
|
+
const tones = ["muted", "neutral", "warning", "info", "accent", "success", "danger"];
|
|
7503
|
+
return {
|
|
7504
|
+
...input,
|
|
7505
|
+
name: String(input.name),
|
|
7506
|
+
indicator: indicators.includes(String(input.indicator)) ? String(input.indicator) : "ring",
|
|
7507
|
+
tone: tones.includes(String(input.tone)) ? String(input.tone) : "muted",
|
|
7508
|
+
description: input.description == null ? "" : String(input.description),
|
|
7509
|
+
shortcut: input.shortcut == null ? "" : String(input.shortcut)
|
|
7510
|
+
};
|
|
7511
|
+
}
|
|
7512
|
+
function statusContent(item) {
|
|
7513
|
+
return h(
|
|
7514
|
+
"span",
|
|
7515
|
+
{ class: "zx-select__status" },
|
|
7516
|
+
statusIndicator(item),
|
|
7517
|
+
h(
|
|
7518
|
+
"span",
|
|
7519
|
+
{ class: "zx-select__status-copy" },
|
|
7520
|
+
h("span", { class: "zx-select__status-label" }, String(item.name)),
|
|
7521
|
+
item.description ? h("span", { class: "zx-select__status-description" }, item.description) : null
|
|
7522
|
+
),
|
|
7523
|
+
item.shortcut ? h("kbd", { class: "zx-select__status-shortcut" }, item.shortcut) : null
|
|
7524
|
+
);
|
|
7525
|
+
}
|
|
7526
|
+
function statusIndicator(item) {
|
|
7527
|
+
const indicator = String(item.indicator ?? "ring");
|
|
7528
|
+
const marker = h("span", {
|
|
7529
|
+
class: "zx-select__status-indicator",
|
|
7530
|
+
dataset: { indicator, tone: String(item.tone ?? "muted") },
|
|
7531
|
+
ariaHidden: "true"
|
|
7532
|
+
});
|
|
7533
|
+
if (indicator === "done") marker.append(icon("check", { size: 8 }));
|
|
7534
|
+
if (indicator === "canceled") marker.append(icon("x", { size: 7 }));
|
|
7535
|
+
return marker;
|
|
7536
|
+
}
|
|
7206
7537
|
function readItem(item, reader) {
|
|
7207
7538
|
if (typeof reader === "function") return reader(item);
|
|
7208
7539
|
if (item === null || typeof item !== "object") return item;
|
|
@@ -7215,10 +7546,11 @@ function isPrintable(event) {
|
|
|
7215
7546
|
// src/components/select/field-adapter.js
|
|
7216
7547
|
function registerSelectFieldAdapter() {
|
|
7217
7548
|
Field.register("zxselect", (field, options) => {
|
|
7218
|
-
const
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7549
|
+
const props = options.props ?? {};
|
|
7550
|
+
const preset = props.preset;
|
|
7551
|
+
const selectOptions = { ...props, value: options.value };
|
|
7552
|
+
delete selectOptions.preset;
|
|
7553
|
+
const select = field.own(preset === "priority" ? Select.priority(null, selectOptions) : preset === "status" ? Select.status(null, selectOptions) : new Select(null, selectOptions));
|
|
7222
7554
|
field.listen(select.el, "change", (event) => event.stopImmediatePropagation());
|
|
7223
7555
|
select.on("change", (event) => field.emit("change", { value: event.detail.value }));
|
|
7224
7556
|
return {
|
|
@@ -7602,6 +7934,8 @@ var TagPicker = class extends Component {
|
|
|
7602
7934
|
values: [],
|
|
7603
7935
|
valueKey: "ID",
|
|
7604
7936
|
labelKey: "name",
|
|
7937
|
+
iconKey: "icon",
|
|
7938
|
+
colorKey: "color",
|
|
7605
7939
|
searchKeys: null,
|
|
7606
7940
|
filter: "local",
|
|
7607
7941
|
minQuery: 0,
|
|
@@ -7942,12 +8276,13 @@ var TagPicker = class extends Component {
|
|
|
7942
8276
|
_renderTags() {
|
|
7943
8277
|
const tags = this._selected.map((item) => {
|
|
7944
8278
|
const label = this._labelOf(item);
|
|
7945
|
-
const
|
|
7946
|
-
const
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
8279
|
+
const color = this._colorOf(item);
|
|
8280
|
+
const content = this.options.renderTag ? this.options.renderTag(item) : [this._iconOf(item), h("span", { class: "zx-tag-picker__tag-label" }, label)];
|
|
8281
|
+
const tag = h("span", {
|
|
8282
|
+
class: "zx-tag-picker__tag",
|
|
8283
|
+
dataset: { colored: String(Boolean(color)) },
|
|
8284
|
+
style: color ? { "--zx-tag-color": color } : null
|
|
8285
|
+
}, content);
|
|
7951
8286
|
if (!this._readonly && !this._disabled) {
|
|
7952
8287
|
tag.append(h("button", {
|
|
7953
8288
|
class: "zx-icon-btn zx-tag-picker__remove",
|
|
@@ -7985,7 +8320,7 @@ var TagPicker = class extends Component {
|
|
|
7985
8320
|
h(
|
|
7986
8321
|
"span",
|
|
7987
8322
|
{ class: "zx-tag-picker__option-label" },
|
|
7988
|
-
this.options.renderItem ? this.options.renderItem(item) : this._labelOf(item)
|
|
8323
|
+
this.options.renderItem ? this.options.renderItem(item) : [this._iconOf(item), h("span", {}, this._labelOf(item))]
|
|
7989
8324
|
)
|
|
7990
8325
|
);
|
|
7991
8326
|
if (!selected && this.isFull()) option.setAttribute("aria-disabled", "true");
|
|
@@ -8306,6 +8641,34 @@ var TagPicker = class extends Component {
|
|
|
8306
8641
|
if (item !== null && typeof item === "object") return String(item[key] ?? "");
|
|
8307
8642
|
return String(item ?? "");
|
|
8308
8643
|
}
|
|
8644
|
+
/** @param {unknown} item @returns {Node|null} */
|
|
8645
|
+
_iconOf(item) {
|
|
8646
|
+
const value = this._readItem(item, this.options.iconKey);
|
|
8647
|
+
const visual = value && typeof value === "object" && typeof value.nodeType === "number" ? (
|
|
8648
|
+
/** @type {Node} */
|
|
8649
|
+
value.cloneNode(true)
|
|
8650
|
+
) : typeof value === "string" && value.trim() ? icon(value, { size: 12 }) : null;
|
|
8651
|
+
if (!visual) return null;
|
|
8652
|
+
return h("span", {
|
|
8653
|
+
class: "zx-tag-picker__visual",
|
|
8654
|
+
style: this._colorOf(item) ? { "--zx-tag-color": this._colorOf(item) } : null,
|
|
8655
|
+
ariaHidden: "true"
|
|
8656
|
+
}, visual);
|
|
8657
|
+
}
|
|
8658
|
+
/** @param {unknown} item @returns {string|null} */
|
|
8659
|
+
_colorOf(item) {
|
|
8660
|
+
const value = this._readItem(item, this.options.colorKey);
|
|
8661
|
+
return isCssColor(value) ? value : null;
|
|
8662
|
+
}
|
|
8663
|
+
/** @param {unknown} item @param {unknown} reader @returns {unknown} */
|
|
8664
|
+
_readItem(item, reader) {
|
|
8665
|
+
if (typeof reader === "function") return reader(
|
|
8666
|
+
/** @type {TagItem} */
|
|
8667
|
+
item
|
|
8668
|
+
);
|
|
8669
|
+
if (item !== null && typeof item === "object" && typeof reader === "string") return item[reader];
|
|
8670
|
+
return null;
|
|
8671
|
+
}
|
|
8309
8672
|
/** @returns {void} */
|
|
8310
8673
|
_emitChange() {
|
|
8311
8674
|
this.emit("change", { values: this.getValues(), items: this.getItems() });
|
|
@@ -9256,7 +9619,11 @@ var Table = class extends Component {
|
|
|
9256
9619
|
rowClass: null,
|
|
9257
9620
|
zebra: true,
|
|
9258
9621
|
editMode: false,
|
|
9259
|
-
|
|
9622
|
+
editTrigger: "double",
|
|
9623
|
+
hierarchy: false,
|
|
9624
|
+
rowReorder: false,
|
|
9625
|
+
columnVisibility: false,
|
|
9626
|
+
hiddenColumns: []
|
|
9260
9627
|
};
|
|
9261
9628
|
/**
|
|
9262
9629
|
* Creates a table around a target or creates a new root when target is null.
|
|
@@ -9268,12 +9635,14 @@ var Table = class extends Component {
|
|
|
9268
9635
|
}
|
|
9269
9636
|
/** @returns {HTMLElement} Table root. */
|
|
9270
9637
|
render() {
|
|
9638
|
+
const created = this.el === null;
|
|
9271
9639
|
const root = (
|
|
9272
9640
|
/** @type {HTMLElement} */
|
|
9273
9641
|
this.el ?? h("div")
|
|
9274
9642
|
);
|
|
9275
9643
|
this.el = root;
|
|
9276
|
-
this.
|
|
9644
|
+
this._createdRoot = created;
|
|
9645
|
+
this._original = created ? null : snapshotTarget(root);
|
|
9277
9646
|
this._columns = Array.isArray(this.options.columns) ? this.options.columns.map((column) => ({ ...column })) : [];
|
|
9278
9647
|
this._data = Array.isArray(this.options.data) ? [...this.options.data] : [];
|
|
9279
9648
|
this._selected = /* @__PURE__ */ new Set();
|
|
@@ -9283,6 +9652,7 @@ var Table = class extends Component {
|
|
|
9283
9652
|
this._rowElements = /* @__PURE__ */ new Map();
|
|
9284
9653
|
this._restored = false;
|
|
9285
9654
|
this._editMode = normalizeEditMode(this.options.editMode);
|
|
9655
|
+
this._editTrigger = this.options.editTrigger === "single" ? "single" : "double";
|
|
9286
9656
|
this._edit = null;
|
|
9287
9657
|
this._editMessage = null;
|
|
9288
9658
|
this._stacked = false;
|
|
@@ -9294,6 +9664,10 @@ var Table = class extends Component {
|
|
|
9294
9664
|
this._rovingCell = null;
|
|
9295
9665
|
this._rovingMatched = false;
|
|
9296
9666
|
this._hierarchy = normalizeHierarchy(this.options.hierarchy, this._columns);
|
|
9667
|
+
this._hiddenColumns = new Set((Array.isArray(this.options.hiddenColumns) ? this.options.hiddenColumns : []).map(String).filter((id) => this._columns.some((column) => column.id === id)));
|
|
9668
|
+
if (this._hiddenColumns.size >= this._columns.length && this._columns[0]) this._hiddenColumns.delete(this._columns[0].id);
|
|
9669
|
+
this._dragId = null;
|
|
9670
|
+
this._grabbedId = null;
|
|
9297
9671
|
this._expanded = initialExpanded(this._hierarchy?.expanded);
|
|
9298
9672
|
if (this._sort && this.options.sortMode === "local") this._sortData();
|
|
9299
9673
|
if (this._hierarchy?.expanded === true) {
|
|
@@ -9315,11 +9689,17 @@ var Table = class extends Component {
|
|
|
9315
9689
|
zebra: String(Boolean(this.options.zebra))
|
|
9316
9690
|
}
|
|
9317
9691
|
}, this._table);
|
|
9692
|
+
this._rowMoveMessage = this.options.rowReorder ? h("div", {
|
|
9693
|
+
class: "zx-table__reorder-status",
|
|
9694
|
+
role: "status",
|
|
9695
|
+
ariaLive: "polite"
|
|
9696
|
+
}) : null;
|
|
9318
9697
|
if (this.options.height != null) {
|
|
9319
9698
|
this._scroll.style.blockSize = typeof this.options.height === "number" ? `${this.options.height}px` : String(this.options.height);
|
|
9320
9699
|
}
|
|
9700
|
+
this._columnControls = this.options.columnVisibility ? this._createColumnControls() : null;
|
|
9321
9701
|
if (this._editMode === false) {
|
|
9322
|
-
root.replaceChildren(this._scroll);
|
|
9702
|
+
root.replaceChildren(...[this._columnControls, this._scroll, this._rowMoveMessage].filter(Boolean));
|
|
9323
9703
|
} else {
|
|
9324
9704
|
root.dataset.editMode = this._editMode;
|
|
9325
9705
|
this._editMessage = h("div", {
|
|
@@ -9328,7 +9708,7 @@ var Table = class extends Component {
|
|
|
9328
9708
|
role: "status",
|
|
9329
9709
|
ariaLive: "polite"
|
|
9330
9710
|
});
|
|
9331
|
-
root.replaceChildren(this._scroll, this._editMessage);
|
|
9711
|
+
root.replaceChildren(...[this._columnControls, this._scroll, this._editMessage, this._rowMoveMessage].filter(Boolean));
|
|
9332
9712
|
}
|
|
9333
9713
|
this._renderColumns();
|
|
9334
9714
|
this._renderHeader();
|
|
@@ -9336,6 +9716,34 @@ var Table = class extends Component {
|
|
|
9336
9716
|
this._watchWidth();
|
|
9337
9717
|
this.listen(this._tbody, "click", (event) => this._handleBodyClick(event));
|
|
9338
9718
|
this.listen(this._tbody, "dblclick", (event) => this._handleBodyDoubleClick(event));
|
|
9719
|
+
if (this.options.rowReorder) {
|
|
9720
|
+
this.listen(this._tbody, "dragstart", (event) => this._handleRowDragStart(
|
|
9721
|
+
/** @type {DragEvent} */
|
|
9722
|
+
event
|
|
9723
|
+
));
|
|
9724
|
+
this.listen(this._tbody, "dragover", (event) => this._handleRowDragOver(
|
|
9725
|
+
/** @type {DragEvent} */
|
|
9726
|
+
event
|
|
9727
|
+
));
|
|
9728
|
+
this.listen(this._tbody, "drop", (event) => this._handleRowDrop(
|
|
9729
|
+
/** @type {DragEvent} */
|
|
9730
|
+
event
|
|
9731
|
+
));
|
|
9732
|
+
this.listen(this._tbody, "dragend", () => this._clearRowDrag());
|
|
9733
|
+
this.listen(this._tbody, "keydown", (event) => this._handleRowHandleKeydown(
|
|
9734
|
+
/** @type {KeyboardEvent} */
|
|
9735
|
+
event
|
|
9736
|
+
));
|
|
9737
|
+
}
|
|
9738
|
+
if (this._columnControls) {
|
|
9739
|
+
this.listen(this._columnControls, "change", (event) => {
|
|
9740
|
+
const input = (
|
|
9741
|
+
/** @type {HTMLInputElement|null} */
|
|
9742
|
+
event.target
|
|
9743
|
+
);
|
|
9744
|
+
if (input?.classList.contains("zx-table__column-toggle")) this.setColumnVisible(input.value, input.checked);
|
|
9745
|
+
});
|
|
9746
|
+
}
|
|
9339
9747
|
if (this._editMode !== false) {
|
|
9340
9748
|
this.listen(this._tbody, "keydown", (event) => this._handleEditKeydown(
|
|
9341
9749
|
/** @type {KeyboardEvent} */
|
|
@@ -9348,8 +9756,8 @@ var Table = class extends Component {
|
|
|
9348
9756
|
}
|
|
9349
9757
|
/**
|
|
9350
9758
|
* Toggles a loading state. While loading, the body is dimmed, an indeterminate progress bar is
|
|
9351
|
-
* shown, and `aria-busy` is set — a lightweight
|
|
9352
|
-
*
|
|
9759
|
+
* shown, and `aria-busy` is set — a lightweight enterprise-table busy and skeleton pattern.
|
|
9760
|
+
* Call `setLoading(false)` (or `setData`) when done.
|
|
9353
9761
|
* @param {boolean} [loading=true] Whether the table is loading.
|
|
9354
9762
|
* @returns {this}
|
|
9355
9763
|
*/
|
|
@@ -9451,6 +9859,81 @@ var Table = class extends Component {
|
|
|
9451
9859
|
getData() {
|
|
9452
9860
|
return [...this._data];
|
|
9453
9861
|
}
|
|
9862
|
+
/** Returns visible column IDs in caller order. @returns {string[]} */
|
|
9863
|
+
getVisibleColumns() {
|
|
9864
|
+
return this._visibleColumns().map((column) => column.id);
|
|
9865
|
+
}
|
|
9866
|
+
/** Returns hidden column IDs in caller order. @returns {string[]} */
|
|
9867
|
+
getHiddenColumns() {
|
|
9868
|
+
return this._columns.filter((column) => this._hiddenColumns.has(column.id)).map((column) => column.id);
|
|
9869
|
+
}
|
|
9870
|
+
/**
|
|
9871
|
+
* Shows or hides one column. The final visible column cannot be hidden.
|
|
9872
|
+
* @param {string} id Column ID.
|
|
9873
|
+
* @param {boolean} [visible=true] Desired visibility.
|
|
9874
|
+
* @param {{silent?:boolean}} [options={}] Event behavior.
|
|
9875
|
+
* @returns {this}
|
|
9876
|
+
*/
|
|
9877
|
+
setColumnVisible(id, visible = true, options = {}) {
|
|
9878
|
+
const column = this._columns.find((candidate) => candidate.id === id);
|
|
9879
|
+
if (!column) throw new RangeError(`Unknown table column: ${id}`);
|
|
9880
|
+
const active = (
|
|
9881
|
+
/** @type {HTMLElement|null} */
|
|
9882
|
+
document.activeElement
|
|
9883
|
+
);
|
|
9884
|
+
const restoreFocus = active?.classList.contains("zx-table__column-toggle") && this._columnList?.contains(active) ? (
|
|
9885
|
+
/** @type {HTMLInputElement} */
|
|
9886
|
+
active.value
|
|
9887
|
+
) : null;
|
|
9888
|
+
if (!visible && !this._hiddenColumns.has(id) && this._visibleColumns().length <= 1) {
|
|
9889
|
+
this._renderColumnControls(restoreFocus);
|
|
9890
|
+
return this;
|
|
9891
|
+
}
|
|
9892
|
+
const changed = visible ? this._hiddenColumns.delete(id) : !this._hiddenColumns.has(id);
|
|
9893
|
+
if (!visible && changed) this._hiddenColumns.add(id);
|
|
9894
|
+
if (!changed) return this;
|
|
9895
|
+
this._renderColumns();
|
|
9896
|
+
this._renderHeader();
|
|
9897
|
+
this._renderBody();
|
|
9898
|
+
this._renderColumnControls(restoreFocus);
|
|
9899
|
+
if (!options.silent) this.emit("columnvisibilitychange", {
|
|
9900
|
+
visible: this.getVisibleColumns(),
|
|
9901
|
+
hidden: this.getHiddenColumns()
|
|
9902
|
+
});
|
|
9903
|
+
return this;
|
|
9904
|
+
}
|
|
9905
|
+
/** Toggles one column. @param {string} id @param {{silent?:boolean}} [options={}] @returns {this} */
|
|
9906
|
+
toggleColumn(id, options = {}) {
|
|
9907
|
+
return this.setColumnVisible(id, this._hiddenColumns.has(id), options);
|
|
9908
|
+
}
|
|
9909
|
+
/**
|
|
9910
|
+
* Reorders one row relative to another and emits a cancelable `rowmove` before committing.
|
|
9911
|
+
* Hierarchical rows may only move among siblings; an active local sort disables manual order.
|
|
9912
|
+
* @param {unknown} id Moving row ID.
|
|
9913
|
+
* @param {unknown} targetId Target row ID.
|
|
9914
|
+
* @param {'before'|'after'} [position='before'] Target edge.
|
|
9915
|
+
* @returns {this}
|
|
9916
|
+
*/
|
|
9917
|
+
moveRow(id, targetId, position2 = "before") {
|
|
9918
|
+
if (!this.options.rowReorder || this._sort && this.options.sortMode === "local") return this;
|
|
9919
|
+
const row = this.getRow(id);
|
|
9920
|
+
const target = this.getRow(targetId);
|
|
9921
|
+
if (!row || !target || Object.is(id, targetId)) return this;
|
|
9922
|
+
const next = reorderTableRows(this._data, this.options.rowId, id, targetId, position2, this._hierarchy?.parentId);
|
|
9923
|
+
if (!next) return this;
|
|
9924
|
+
const event = this.emit("rowmove", { row, id, target, targetId, position: position2 });
|
|
9925
|
+
if (event.defaultPrevented) return this;
|
|
9926
|
+
this._data = next;
|
|
9927
|
+
this._renderBody();
|
|
9928
|
+
if (this._rowMoveMessage) {
|
|
9929
|
+
const siblings = this._siblingIds(id);
|
|
9930
|
+
const index = siblings.findIndex((candidate) => Object.is(candidate, id));
|
|
9931
|
+
this._rowMoveMessage.textContent = `Moved row to position ${index + 1} of ${siblings.length}.`;
|
|
9932
|
+
}
|
|
9933
|
+
this._emitDataChange();
|
|
9934
|
+
queueMicrotask(() => this._rowHandle(id)?.focus());
|
|
9935
|
+
return this;
|
|
9936
|
+
}
|
|
9454
9937
|
/**
|
|
9455
9938
|
* Removes all rows.
|
|
9456
9939
|
* @returns {this}
|
|
@@ -9619,14 +10102,17 @@ var Table = class extends Component {
|
|
|
9619
10102
|
*/
|
|
9620
10103
|
startEdit(rowId, columnId) {
|
|
9621
10104
|
if (this._editMode === false) return this;
|
|
9622
|
-
const
|
|
9623
|
-
if (!
|
|
10105
|
+
const requested = this._columns.find((candidate) => candidate.id === columnId);
|
|
10106
|
+
if (!requested) return this;
|
|
9624
10107
|
const current = this._data.find((candidate) => Object.is(this._idFor(candidate), rowId));
|
|
9625
|
-
if (current === void 0
|
|
10108
|
+
if (current === void 0) return this;
|
|
10109
|
+
const column = this._editMode === "row" && this._hiddenColumns.has(requested.id) ? this._visibleColumns().find((candidate) => resolveEditable(candidate, current) !== false) ?? null : requested;
|
|
10110
|
+
if (!column || resolveEditable(column, current) === false) return this;
|
|
10111
|
+
const activeColumnId = column.id;
|
|
9626
10112
|
if (this._edit) {
|
|
9627
|
-
if (Object.is(this._edit.id, rowId) && this._edit.cells.has(
|
|
9628
|
-
this._edit.columnId =
|
|
9629
|
-
this._focusEditor(
|
|
10113
|
+
if (Object.is(this._edit.id, rowId) && this._edit.cells.has(activeColumnId)) {
|
|
10114
|
+
this._edit.columnId = activeColumnId;
|
|
10115
|
+
this._focusEditor(activeColumnId);
|
|
9630
10116
|
return this;
|
|
9631
10117
|
}
|
|
9632
10118
|
this.commitEdit();
|
|
@@ -9637,7 +10123,7 @@ var Table = class extends Component {
|
|
|
9637
10123
|
const row = this._data[index];
|
|
9638
10124
|
const tr = this._rowElements.get(rowId)?.[0];
|
|
9639
10125
|
if (!tr) return this;
|
|
9640
|
-
const targets = this._editMode === "row" ? this.
|
|
10126
|
+
const targets = this._editMode === "row" ? this._visibleColumns().filter((candidate) => resolveEditable(candidate, row) !== false) : [column];
|
|
9641
10127
|
const cells = /* @__PURE__ */ new Map();
|
|
9642
10128
|
for (const target of targets) {
|
|
9643
10129
|
const td = cellIn(tr, target.id);
|
|
@@ -9646,10 +10132,10 @@ var Table = class extends Component {
|
|
|
9646
10132
|
if (entry) cells.set(target.id, entry);
|
|
9647
10133
|
}
|
|
9648
10134
|
if (cells.size === 0) return this;
|
|
9649
|
-
this._edit = { id: rowId, columnId, row, cells, tr };
|
|
10135
|
+
this._edit = { id: rowId, columnId: activeColumnId, row, cells, tr };
|
|
9650
10136
|
tr.dataset.editing = "true";
|
|
9651
|
-
this.emit("editstart", { row, id: rowId, column, columnId, value: row?.[
|
|
9652
|
-
this._focusEditor(
|
|
10137
|
+
this.emit("editstart", { row, id: rowId, column, columnId: activeColumnId, value: row?.[activeColumnId] });
|
|
10138
|
+
this._focusEditor(activeColumnId);
|
|
9653
10139
|
return this;
|
|
9654
10140
|
}
|
|
9655
10141
|
/**
|
|
@@ -9898,23 +10384,203 @@ var Table = class extends Component {
|
|
|
9898
10384
|
this._teardownEdit();
|
|
9899
10385
|
this._width?.destroy();
|
|
9900
10386
|
this._width = null;
|
|
9901
|
-
if (this.
|
|
9902
|
-
|
|
9903
|
-
this.el.replaceChildren(...this._originalChildren);
|
|
10387
|
+
if (!this._restored && !this._createdRoot) {
|
|
10388
|
+
restoreTarget(this.el, this._original);
|
|
9904
10389
|
this._restored = true;
|
|
9905
10390
|
}
|
|
9906
10391
|
super.destroy();
|
|
9907
10392
|
}
|
|
10393
|
+
/** @returns {TableColumn[]} */
|
|
10394
|
+
_visibleColumns() {
|
|
10395
|
+
return this._columns.filter((column) => !this._hiddenColumns.has(column.id));
|
|
10396
|
+
}
|
|
10397
|
+
/** @returns {HTMLElement} */
|
|
10398
|
+
_createColumnControls() {
|
|
10399
|
+
const list = h("div", { class: "zx-table__column-list" });
|
|
10400
|
+
this._columnList = list;
|
|
10401
|
+
const details = h(
|
|
10402
|
+
"details",
|
|
10403
|
+
{ class: "zx-table__column-controls" },
|
|
10404
|
+
h("summary", { class: "zx-button zx-button--sm" }, icon("fields", { size: 13 }), "Columns"),
|
|
10405
|
+
list
|
|
10406
|
+
);
|
|
10407
|
+
this._renderColumnControls();
|
|
10408
|
+
return details;
|
|
10409
|
+
}
|
|
10410
|
+
/** @param {string|null} [focusColumn=null] Column checkbox that keeps focus. @returns {void} */
|
|
10411
|
+
_renderColumnControls(focusColumn = null) {
|
|
10412
|
+
if (!this._columnList) return;
|
|
10413
|
+
const visible = this._visibleColumns();
|
|
10414
|
+
const current = [...this._columnList.querySelectorAll(".zx-table__column-toggle")];
|
|
10415
|
+
const reusable = current.length === this._columns.length && current.every((control, index) => (
|
|
10416
|
+
/** @type {HTMLInputElement} */
|
|
10417
|
+
control.value === this._columns[index].id
|
|
10418
|
+
));
|
|
10419
|
+
if (reusable) {
|
|
10420
|
+
current.forEach((control, index) => {
|
|
10421
|
+
const input = (
|
|
10422
|
+
/** @type {HTMLInputElement} */
|
|
10423
|
+
control
|
|
10424
|
+
);
|
|
10425
|
+
const checked = !this._hiddenColumns.has(this._columns[index].id);
|
|
10426
|
+
input.checked = checked;
|
|
10427
|
+
input.disabled = checked && visible.length === 1;
|
|
10428
|
+
});
|
|
10429
|
+
return;
|
|
10430
|
+
}
|
|
10431
|
+
this._columnList.replaceChildren(...this._columns.map((column) => {
|
|
10432
|
+
const checked = !this._hiddenColumns.has(column.id);
|
|
10433
|
+
return h(
|
|
10434
|
+
"label",
|
|
10435
|
+
{ class: "zx-table__column-option" },
|
|
10436
|
+
h("input", {
|
|
10437
|
+
class: "zx-table__column-toggle",
|
|
10438
|
+
type: "checkbox",
|
|
10439
|
+
value: column.id,
|
|
10440
|
+
checked,
|
|
10441
|
+
disabled: checked && visible.length === 1
|
|
10442
|
+
}),
|
|
10443
|
+
h("span", {}, column.label)
|
|
10444
|
+
);
|
|
10445
|
+
}));
|
|
10446
|
+
if (focusColumn !== null) {
|
|
10447
|
+
const control = [...this._columnList.querySelectorAll(".zx-table__column-toggle")].find((candidate) => (
|
|
10448
|
+
/** @type {HTMLInputElement} */
|
|
10449
|
+
candidate.value === focusColumn
|
|
10450
|
+
));
|
|
10451
|
+
queueMicrotask(() => {
|
|
10452
|
+
if (control?.isConnected) control.focus();
|
|
10453
|
+
});
|
|
10454
|
+
}
|
|
10455
|
+
}
|
|
10456
|
+
/** @param {DragEvent} event @returns {void} */
|
|
10457
|
+
_handleRowDragStart(event) {
|
|
10458
|
+
const handle = (
|
|
10459
|
+
/** @type {Element|null} */
|
|
10460
|
+
event.target?.closest?.(".zx-table__row-handle")
|
|
10461
|
+
);
|
|
10462
|
+
const rowElement = handle?.closest("tr[data-row]");
|
|
10463
|
+
const meta = rowElement ? this._rowMeta.get(rowElement) : null;
|
|
10464
|
+
if (!handle || !meta || this._sort && this.options.sortMode === "local") {
|
|
10465
|
+
event.preventDefault();
|
|
10466
|
+
return;
|
|
10467
|
+
}
|
|
10468
|
+
this._dragId = meta.id;
|
|
10469
|
+
rowElement.dataset.dragging = "true";
|
|
10470
|
+
event.dataTransfer?.setData("text/plain", "zx-table-row");
|
|
10471
|
+
if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
|
|
10472
|
+
}
|
|
10473
|
+
/** @param {DragEvent} event @returns {void} */
|
|
10474
|
+
_handleRowDragOver(event) {
|
|
10475
|
+
if (this._dragId == null) return;
|
|
10476
|
+
const rowElement = (
|
|
10477
|
+
/** @type {Element|null} */
|
|
10478
|
+
event.target?.closest?.("tr[data-row]")
|
|
10479
|
+
);
|
|
10480
|
+
const meta = rowElement ? this._rowMeta.get(rowElement) : null;
|
|
10481
|
+
if (!meta || Object.is(meta.id, this._dragId) || !this._canMoveTogether(this._dragId, meta.id)) return;
|
|
10482
|
+
event.preventDefault();
|
|
10483
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
|
|
10484
|
+
this._clearDropMarkers();
|
|
10485
|
+
const rectangle = rowElement.getBoundingClientRect();
|
|
10486
|
+
rowElement.dataset.drop = event.clientY >= rectangle.top + rectangle.height / 2 ? "after" : "before";
|
|
10487
|
+
}
|
|
10488
|
+
/** @param {DragEvent} event @returns {void} */
|
|
10489
|
+
_handleRowDrop(event) {
|
|
10490
|
+
if (this._dragId == null) return;
|
|
10491
|
+
const rowElement = (
|
|
10492
|
+
/** @type {Element|null} */
|
|
10493
|
+
event.target?.closest?.("tr[data-row]")
|
|
10494
|
+
);
|
|
10495
|
+
const meta = rowElement ? this._rowMeta.get(rowElement) : null;
|
|
10496
|
+
const position2 = rowElement?.dataset.drop === "after" ? "after" : "before";
|
|
10497
|
+
if (meta && this._canMoveTogether(this._dragId, meta.id)) {
|
|
10498
|
+
event.preventDefault();
|
|
10499
|
+
this.moveRow(this._dragId, meta.id, position2);
|
|
10500
|
+
}
|
|
10501
|
+
this._clearRowDrag();
|
|
10502
|
+
}
|
|
10503
|
+
/** @param {KeyboardEvent} event @returns {void} */
|
|
10504
|
+
_handleRowHandleKeydown(event) {
|
|
10505
|
+
const handle = (
|
|
10506
|
+
/** @type {Element|null} */
|
|
10507
|
+
event.target?.closest?.(".zx-table__row-handle")
|
|
10508
|
+
);
|
|
10509
|
+
const rowElement = handle?.closest("tr[data-row]");
|
|
10510
|
+
const meta = rowElement ? this._rowMeta.get(rowElement) : null;
|
|
10511
|
+
if (!handle || !meta) return;
|
|
10512
|
+
if (event.key === " " || event.key === "Enter") {
|
|
10513
|
+
event.preventDefault();
|
|
10514
|
+
this._grabbedId = Object.is(this._grabbedId, meta.id) ? null : meta.id;
|
|
10515
|
+
handle.setAttribute("aria-pressed", String(this._grabbedId != null));
|
|
10516
|
+
const status = this._rowMoveMessage ?? this._editMessage;
|
|
10517
|
+
if (status) status.textContent = this._grabbedId == null ? "Row dropped" : "Row grabbed. Use the arrow keys to move it.";
|
|
10518
|
+
return;
|
|
10519
|
+
}
|
|
10520
|
+
if (event.key === "Escape" && this._grabbedId != null) {
|
|
10521
|
+
event.preventDefault();
|
|
10522
|
+
this._grabbedId = null;
|
|
10523
|
+
handle.setAttribute("aria-pressed", "false");
|
|
10524
|
+
return;
|
|
10525
|
+
}
|
|
10526
|
+
if (!Object.is(this._grabbedId, meta.id) || !["ArrowUp", "ArrowDown"].includes(event.key)) return;
|
|
10527
|
+
event.preventDefault();
|
|
10528
|
+
const siblings = this._siblingIds(meta.id);
|
|
10529
|
+
const index = siblings.findIndex((id) => Object.is(id, meta.id));
|
|
10530
|
+
const targetId = siblings[index + (event.key === "ArrowUp" ? -1 : 1)];
|
|
10531
|
+
if (targetId !== void 0) this.moveRow(meta.id, targetId, event.key === "ArrowUp" ? "before" : "after");
|
|
10532
|
+
}
|
|
10533
|
+
/** @param {unknown} id @returns {unknown[]} */
|
|
10534
|
+
_siblingIds(id) {
|
|
10535
|
+
const row = this.getRow(id);
|
|
10536
|
+
if (!row) return [];
|
|
10537
|
+
const parent = this._hierarchy ? readRowValue(this._hierarchy.parentId, row) : null;
|
|
10538
|
+
return this._data.filter((candidate) => !this._hierarchy || sameHierarchyParent(readRowValue(this._hierarchy.parentId, candidate), parent)).map((candidate) => this._idFor(candidate));
|
|
10539
|
+
}
|
|
10540
|
+
/** @param {unknown} left @param {unknown} right @returns {boolean} */
|
|
10541
|
+
_canMoveTogether(left, right) {
|
|
10542
|
+
if (!this._hierarchy) return true;
|
|
10543
|
+
const a = this.getRow(left);
|
|
10544
|
+
const b = this.getRow(right);
|
|
10545
|
+
return Boolean(a && b && sameHierarchyParent(
|
|
10546
|
+
readRowValue(this._hierarchy.parentId, a),
|
|
10547
|
+
readRowValue(this._hierarchy.parentId, b)
|
|
10548
|
+
));
|
|
10549
|
+
}
|
|
10550
|
+
/** @param {unknown} id @returns {HTMLElement|null} */
|
|
10551
|
+
_rowHandle(id) {
|
|
10552
|
+
return (
|
|
10553
|
+
/** @type {HTMLElement|null} */
|
|
10554
|
+
this._rowElements.get(id)?.[0]?.querySelector(".zx-table__row-handle") ?? null
|
|
10555
|
+
);
|
|
10556
|
+
}
|
|
10557
|
+
/** @returns {void} */
|
|
10558
|
+
_clearDropMarkers() {
|
|
10559
|
+
for (const row of this._tbody.querySelectorAll("tr[data-drop]")) delete row.dataset.drop;
|
|
10560
|
+
}
|
|
10561
|
+
/** @returns {void} */
|
|
10562
|
+
_clearRowDrag() {
|
|
10563
|
+
for (const row of this._tbody.querySelectorAll("tr[data-dragging]")) delete row.dataset.dragging;
|
|
10564
|
+
this._clearDropMarkers();
|
|
10565
|
+
this._dragId = null;
|
|
10566
|
+
}
|
|
10567
|
+
/** @returns {string|null} */
|
|
10568
|
+
_hierarchyColumn() {
|
|
10569
|
+
if (!this._hierarchy) return null;
|
|
10570
|
+
return this._visibleColumns().some((column) => column.id === this._hierarchy.column) ? this._hierarchy.column : this._visibleColumns()[0]?.id ?? null;
|
|
10571
|
+
}
|
|
9908
10572
|
/** @returns {void} */
|
|
9909
10573
|
_renderColumns() {
|
|
9910
10574
|
const fragment2 = document.createDocumentFragment();
|
|
10575
|
+
if (this.options.rowReorder) fragment2.append(h("col", { class: "zx-table__reorder-column" }));
|
|
9911
10576
|
if (this.options.selectable === "multi") {
|
|
9912
10577
|
fragment2.append(h("col", { class: "zx-table__selection-column" }));
|
|
9913
10578
|
}
|
|
9914
|
-
const
|
|
10579
|
+
const columns = this._visibleColumns();
|
|
10580
|
+
const fractions = columns.map((column) => parseFraction(column.width));
|
|
9915
10581
|
const total = fractions.reduce((sum, value) => sum + value, 0);
|
|
9916
10582
|
this._table.dataset.layout = total > 0 ? "fixed" : "auto";
|
|
9917
|
-
|
|
10583
|
+
columns.forEach((column, index) => {
|
|
9918
10584
|
const col = h("col");
|
|
9919
10585
|
if (fractions[index] > 0) col.style.width = `${fractions[index] / total * 100}%`;
|
|
9920
10586
|
else if (column.width && column.width !== "auto") col.style.width = String(column.width);
|
|
@@ -9926,6 +10592,11 @@ var Table = class extends Component {
|
|
|
9926
10592
|
_renderHeader() {
|
|
9927
10593
|
const row = h("tr", { class: "zx-table__header-row" });
|
|
9928
10594
|
if (this._growStep) row.setAttribute("aria-rowindex", "1");
|
|
10595
|
+
if (this.options.rowReorder) row.append(h("th", {
|
|
10596
|
+
class: "zx-table__reorder-header",
|
|
10597
|
+
scope: "col",
|
|
10598
|
+
ariaLabel: "Row order"
|
|
10599
|
+
}));
|
|
9929
10600
|
if (this.options.selectable === "multi") {
|
|
9930
10601
|
this._selectAll = h("input", {
|
|
9931
10602
|
class: "zx-table__checkbox zx-table__select-all",
|
|
@@ -9951,7 +10622,7 @@ var Table = class extends Component {
|
|
|
9951
10622
|
this._selectAll = null;
|
|
9952
10623
|
}
|
|
9953
10624
|
this._headers = /* @__PURE__ */ new Map();
|
|
9954
|
-
for (const column of this.
|
|
10625
|
+
for (const column of this._visibleColumns()) {
|
|
9955
10626
|
const th = h("th", { scope: "col" });
|
|
9956
10627
|
const align = column.align ?? (isNumericColumn(column) ? "end" : null);
|
|
9957
10628
|
if (align) th.dataset.align = align;
|
|
@@ -9991,7 +10662,7 @@ var Table = class extends Component {
|
|
|
9991
10662
|
{ class: "zx-table__empty-row" },
|
|
9992
10663
|
h("td", {
|
|
9993
10664
|
class: "zx-table__empty",
|
|
9994
|
-
colspan: this.
|
|
10665
|
+
colspan: this._visibleColumns().length + (this.options.selectable === "multi" ? 1 : 0) + (this.options.rowReorder ? 1 : 0)
|
|
9995
10666
|
}, this._emptyContent())
|
|
9996
10667
|
));
|
|
9997
10668
|
} else {
|
|
@@ -10000,6 +10671,7 @@ var Table = class extends Component {
|
|
|
10000
10671
|
});
|
|
10001
10672
|
}
|
|
10002
10673
|
this._tbody.replaceChildren(fragment2);
|
|
10674
|
+
this._table.setAttribute("aria-colcount", String(this._visibleColumns().length + (this.options.selectable === "multi" ? 1 : 0) + (this.options.rowReorder ? 1 : 0)));
|
|
10003
10675
|
if (this._growStep) this._table.setAttribute("aria-rowcount", String(projected.length + 1));
|
|
10004
10676
|
this._syncSelectAll();
|
|
10005
10677
|
this._syncGrowing();
|
|
@@ -10043,6 +10715,19 @@ var Table = class extends Component {
|
|
|
10043
10715
|
const elements = this._rowElements.get(id) ?? [];
|
|
10044
10716
|
elements.push(tr);
|
|
10045
10717
|
this._rowElements.set(id, elements);
|
|
10718
|
+
if (this.options.rowReorder) {
|
|
10719
|
+
const handle = h("button", {
|
|
10720
|
+
class: "zx-table__row-handle",
|
|
10721
|
+
type: "button",
|
|
10722
|
+
draggable: true,
|
|
10723
|
+
dataset: { rowHandle: "" },
|
|
10724
|
+
ariaLabel: `Move row ${index + 1}`,
|
|
10725
|
+
ariaPressed: String(Object.is(this._grabbedId, id)),
|
|
10726
|
+
disabled: Boolean(this._sort && this.options.sortMode === "local"),
|
|
10727
|
+
title: this._sort && this.options.sortMode === "local" ? "Clear sorting to reorder rows" : null
|
|
10728
|
+
}, icon("drag", { size: 14 }));
|
|
10729
|
+
tr.append(h("td", { class: "zx-table__reorder-cell" }, handle));
|
|
10730
|
+
}
|
|
10046
10731
|
if (this.options.selectable === "multi") {
|
|
10047
10732
|
const checkbox = h("input", {
|
|
10048
10733
|
class: "zx-table__checkbox zx-table__row-checkbox",
|
|
@@ -10052,7 +10737,7 @@ var Table = class extends Component {
|
|
|
10052
10737
|
});
|
|
10053
10738
|
tr.append(h("td", { class: "zx-table__selection-cell" }, checkbox));
|
|
10054
10739
|
}
|
|
10055
|
-
this.
|
|
10740
|
+
this._visibleColumns().forEach((column) => {
|
|
10056
10741
|
const cell = (
|
|
10057
10742
|
/** @type {HTMLTableCellElement} */
|
|
10058
10743
|
h("td")
|
|
@@ -10088,7 +10773,7 @@ var Table = class extends Component {
|
|
|
10088
10773
|
const content = h("span", { class: "zx-table__cell-content" });
|
|
10089
10774
|
if (value && typeof value === "object" && typeof value.nodeType === "number") content.append(value);
|
|
10090
10775
|
else if (value != null) content.append(document.createTextNode(String(value)));
|
|
10091
|
-
if (!this._hierarchy || column.id !== this.
|
|
10776
|
+
if (!this._hierarchy || column.id !== this._hierarchyColumn() || !tree) {
|
|
10092
10777
|
cell.replaceChildren(...content.childNodes);
|
|
10093
10778
|
return;
|
|
10094
10779
|
}
|
|
@@ -10119,6 +10804,7 @@ var Table = class extends Component {
|
|
|
10119
10804
|
if (!rowElement || !this._tbody.contains(rowElement)) return;
|
|
10120
10805
|
const meta = this._rowMeta.get(rowElement);
|
|
10121
10806
|
if (!meta) return;
|
|
10807
|
+
if (target.closest(".zx-table__row-handle")) return;
|
|
10122
10808
|
if (this._edit) {
|
|
10123
10809
|
this.commitEdit();
|
|
10124
10810
|
if (this._edit) return;
|
|
@@ -10138,10 +10824,18 @@ var Table = class extends Component {
|
|
|
10138
10824
|
);
|
|
10139
10825
|
return;
|
|
10140
10826
|
}
|
|
10141
|
-
|
|
10142
|
-
if (this.
|
|
10143
|
-
|
|
10144
|
-
this.
|
|
10827
|
+
if (isInteractiveCellContent(target)) return;
|
|
10828
|
+
if (this._editMode !== false && this._editTrigger === "single") {
|
|
10829
|
+
const cell = target?.closest('td[data-editable="true"]');
|
|
10830
|
+
if (cell && this._tbody.contains(cell)) {
|
|
10831
|
+
this.startEdit(meta.id, String(cell.dataset.column));
|
|
10832
|
+
return;
|
|
10833
|
+
}
|
|
10834
|
+
}
|
|
10835
|
+
this.emit("rowclick", { ...meta, event });
|
|
10836
|
+
if (this.options.selectable === "single" && !this._selected.has(meta.id)) {
|
|
10837
|
+
this._selected = /* @__PURE__ */ new Set([meta.id]);
|
|
10838
|
+
this._selectionAnchorId = meta.id;
|
|
10145
10839
|
this._syncSelection();
|
|
10146
10840
|
this._emitSelectionChange();
|
|
10147
10841
|
}
|
|
@@ -10158,7 +10852,8 @@ var Table = class extends Component {
|
|
|
10158
10852
|
if (!rowElement || !this._tbody.contains(rowElement)) return;
|
|
10159
10853
|
const meta = this._rowMeta.get(rowElement);
|
|
10160
10854
|
if (!meta) return;
|
|
10161
|
-
if (
|
|
10855
|
+
if (isInteractiveCellContent(target)) return;
|
|
10856
|
+
if (this._editMode !== false && this._editTrigger === "double") {
|
|
10162
10857
|
const cell = target?.closest('td[data-editable="true"]');
|
|
10163
10858
|
if (cell && this._tbody.contains(cell)) {
|
|
10164
10859
|
this.startEdit(meta.id, String(cell.dataset.column));
|
|
@@ -10460,13 +11155,14 @@ var Table = class extends Component {
|
|
|
10460
11155
|
* @returns {{id: unknown, columnId: string, index: number}|null} Next cell, or null at the end.
|
|
10461
11156
|
*/
|
|
10462
11157
|
_nextEditableCell(rowIndex, columnId, direction) {
|
|
10463
|
-
const
|
|
11158
|
+
const columns = this._visibleColumns();
|
|
11159
|
+
const columnCount = columns.length;
|
|
10464
11160
|
if (rowIndex < 0 || columnCount === 0) return null;
|
|
10465
11161
|
const visible = this._visibleData();
|
|
10466
11162
|
const startingId = this._idFor(this._data[rowIndex]);
|
|
10467
11163
|
let row = visible.findIndex((candidate) => Object.is(this._idFor(candidate), startingId));
|
|
10468
11164
|
if (row < 0) return null;
|
|
10469
|
-
let column =
|
|
11165
|
+
let column = columns.findIndex((candidate) => candidate.id === columnId);
|
|
10470
11166
|
const limit = visible.length * columnCount + columnCount;
|
|
10471
11167
|
for (let step = 0; step < limit; step += 1) {
|
|
10472
11168
|
column += direction;
|
|
@@ -10479,7 +11175,7 @@ var Table = class extends Component {
|
|
|
10479
11175
|
}
|
|
10480
11176
|
if (row < 0 || row >= visible.length) return null;
|
|
10481
11177
|
const candidateRow = visible[row];
|
|
10482
|
-
const candidateColumn =
|
|
11178
|
+
const candidateColumn = columns[column];
|
|
10483
11179
|
if (resolveEditable(candidateColumn, candidateRow) !== false) {
|
|
10484
11180
|
return {
|
|
10485
11181
|
id: this._idFor(candidateRow),
|
|
@@ -10722,6 +11418,53 @@ function sortHierarchyRows(rows, rowId, parentId, getValue, direction) {
|
|
|
10722
11418
|
}
|
|
10723
11419
|
return result;
|
|
10724
11420
|
}
|
|
11421
|
+
function reorderTableRows(rows, rowId, id, targetId, position2 = "before", parentId = null) {
|
|
11422
|
+
if (!Array.isArray(rows) || Object.is(id, targetId)) return null;
|
|
11423
|
+
const from = rows.findIndex((row) => Object.is(readRowValue(rowId, row), id));
|
|
11424
|
+
const target = rows.findIndex((row) => Object.is(readRowValue(rowId, row), targetId));
|
|
11425
|
+
if (from < 0 || target < 0) return null;
|
|
11426
|
+
if (parentId && !sameHierarchyParent(
|
|
11427
|
+
readRowValue(parentId, rows[from]),
|
|
11428
|
+
readRowValue(parentId, rows[target])
|
|
11429
|
+
)) return null;
|
|
11430
|
+
if (parentId) {
|
|
11431
|
+
const forest = hierarchyForest(rows, rowId, parentId);
|
|
11432
|
+
const movingNode = forest.nodes[from];
|
|
11433
|
+
const targetNode = forest.nodes[target];
|
|
11434
|
+
if (!movingNode || !targetNode || movingNode.parent !== targetNode.parent) return null;
|
|
11435
|
+
const branchNodes = (root) => {
|
|
11436
|
+
const result = [];
|
|
11437
|
+
const stack = [root];
|
|
11438
|
+
while (stack.length) {
|
|
11439
|
+
const node = (
|
|
11440
|
+
/** @type {TableHierarchyNode} */
|
|
11441
|
+
stack.pop()
|
|
11442
|
+
);
|
|
11443
|
+
result.push(node);
|
|
11444
|
+
for (let index = node.children.length - 1; index >= 0; index -= 1) stack.push(node.children[index]);
|
|
11445
|
+
}
|
|
11446
|
+
return result;
|
|
11447
|
+
};
|
|
11448
|
+
const moving2 = new Set(branchNodes(movingNode).map((node) => node.index));
|
|
11449
|
+
const targetBranch = new Set(branchNodes(targetNode).map((node) => node.index));
|
|
11450
|
+
const block = rows.filter((_row, index) => moving2.has(index));
|
|
11451
|
+
const remaining = rows.filter((_row, index) => !moving2.has(index));
|
|
11452
|
+
const remainingSourceIndexes = rows.map((_row, index) => index).filter((index) => !moving2.has(index));
|
|
11453
|
+
let insertion = remainingSourceIndexes.findIndex((index) => index === target);
|
|
11454
|
+
if (position2 === "after") {
|
|
11455
|
+
const targetPositions = remainingSourceIndexes.map((sourceIndex, index) => targetBranch.has(sourceIndex) ? index : -1).filter((index) => index >= 0);
|
|
11456
|
+
insertion = Math.max(...targetPositions) + 1;
|
|
11457
|
+
}
|
|
11458
|
+
if (insertion < 0) return null;
|
|
11459
|
+
remaining.splice(insertion, 0, ...block);
|
|
11460
|
+
return remaining;
|
|
11461
|
+
}
|
|
11462
|
+
const next = [...rows];
|
|
11463
|
+
const [moving] = next.splice(from, 1);
|
|
11464
|
+
const adjustedTarget = next.findIndex((row) => Object.is(readRowValue(rowId, row), targetId));
|
|
11465
|
+
next.splice(adjustedTarget + (position2 === "after" ? 1 : 0), 0, moving);
|
|
11466
|
+
return next;
|
|
11467
|
+
}
|
|
10725
11468
|
function resolveEditable(column, row) {
|
|
10726
11469
|
if (!column || typeof column !== "object") return false;
|
|
10727
11470
|
if (typeof column.editor === "function") {
|
|
@@ -10802,7 +11545,8 @@ function hierarchyForest(rows, rowId, parentId) {
|
|
|
10802
11545
|
for (const node of nodes) if (!byId.has(node.id)) byId.set(node.id, node);
|
|
10803
11546
|
const parentOf = /* @__PURE__ */ new Map();
|
|
10804
11547
|
for (const node of nodes) {
|
|
10805
|
-
const
|
|
11548
|
+
const parentValue = readRowValue(parentId, node.row);
|
|
11549
|
+
const parent = parentValue == null ? null : byId.get(parentValue) ?? null;
|
|
10806
11550
|
parentOf.set(node, parent === node ? null : parent);
|
|
10807
11551
|
}
|
|
10808
11552
|
for (const node of nodes) {
|
|
@@ -10831,6 +11575,9 @@ function hierarchyBranchIds(rows, rowId, parentId) {
|
|
|
10831
11575
|
function readRowValue(accessor, row) {
|
|
10832
11576
|
return typeof accessor === "function" ? accessor(row) : row?.[accessor];
|
|
10833
11577
|
}
|
|
11578
|
+
function sameHierarchyParent(left, right) {
|
|
11579
|
+
return left == null && right == null || Object.is(left, right);
|
|
11580
|
+
}
|
|
10834
11581
|
function normalizeHierarchy(option, columns) {
|
|
10835
11582
|
if (!option || typeof option !== "object") return null;
|
|
10836
11583
|
const parentId = option.parentId;
|
|
@@ -10850,6 +11597,23 @@ function normalizeEditMode(mode) {
|
|
|
10850
11597
|
function isInsideEditor(target) {
|
|
10851
11598
|
return Boolean(target?.closest?.(".zx-table__editor"));
|
|
10852
11599
|
}
|
|
11600
|
+
function isInteractiveCellContent(target) {
|
|
11601
|
+
const cell = target?.closest?.("td[data-column]");
|
|
11602
|
+
const interactive = target?.closest?.([
|
|
11603
|
+
"a[href]",
|
|
11604
|
+
"button",
|
|
11605
|
+
"input",
|
|
11606
|
+
"select",
|
|
11607
|
+
"textarea",
|
|
11608
|
+
"summary",
|
|
11609
|
+
'[contenteditable="true"]',
|
|
11610
|
+
'[role="button"]',
|
|
11611
|
+
'[role="link"]',
|
|
11612
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
11613
|
+
"[data-zx-table-no-edit]"
|
|
11614
|
+
].join(","));
|
|
11615
|
+
return Boolean(cell && interactive && interactive !== cell && cell.contains(interactive));
|
|
11616
|
+
}
|
|
10853
11617
|
function deferToEditor(target) {
|
|
10854
11618
|
if (target.closest("[popover]")) return true;
|
|
10855
11619
|
return target.getAttribute?.("aria-expanded") === "true";
|
|
@@ -10884,6 +11648,730 @@ function growStep(growing) {
|
|
|
10884
11648
|
return Number.isFinite(step) && step > 0 ? step : null;
|
|
10885
11649
|
}
|
|
10886
11650
|
|
|
11651
|
+
// src/components/app-icon/app-icon.js
|
|
11652
|
+
var AppIcon = class extends Component {
|
|
11653
|
+
static cssName = "app-icon";
|
|
11654
|
+
/** @type {Readonly<AppIconOptions>} */
|
|
11655
|
+
static defaults = {
|
|
11656
|
+
icon: null,
|
|
11657
|
+
color: null,
|
|
11658
|
+
size: 36,
|
|
11659
|
+
iconSize: "52%",
|
|
11660
|
+
label: null,
|
|
11661
|
+
badge: null,
|
|
11662
|
+
selected: false,
|
|
11663
|
+
glass: "subtle",
|
|
11664
|
+
class: []
|
|
11665
|
+
};
|
|
11666
|
+
/** @returns {HTMLElement} */
|
|
11667
|
+
render() {
|
|
11668
|
+
this._createdRoot = this.el === null;
|
|
11669
|
+
const root = (
|
|
11670
|
+
/** @type {HTMLElement} */
|
|
11671
|
+
this.el ?? h("span")
|
|
11672
|
+
);
|
|
11673
|
+
this.el = root;
|
|
11674
|
+
this._snapshot = this._createdRoot ? null : snapshotTarget(root);
|
|
11675
|
+
this._state = { ...this.options };
|
|
11676
|
+
this._appliedClasses = [];
|
|
11677
|
+
this._sync();
|
|
11678
|
+
return root;
|
|
11679
|
+
}
|
|
11680
|
+
/** Updates identity or presentation without replacing the root. @param {Partial<AppIconOptions>} values @returns {this} */
|
|
11681
|
+
set(values = {}) {
|
|
11682
|
+
if (!values || typeof values !== "object") return this;
|
|
11683
|
+
Object.assign(this._state, values);
|
|
11684
|
+
this._sync();
|
|
11685
|
+
return this;
|
|
11686
|
+
}
|
|
11687
|
+
/** Restores an enhanced target. @returns {void} */
|
|
11688
|
+
destroy() {
|
|
11689
|
+
const root = this.el;
|
|
11690
|
+
const snapshot3 = this._snapshot;
|
|
11691
|
+
super.destroy();
|
|
11692
|
+
if (!this._createdRoot && root) restoreTarget(root, snapshot3);
|
|
11693
|
+
}
|
|
11694
|
+
/** @returns {void} */
|
|
11695
|
+
_sync() {
|
|
11696
|
+
const state = this._state;
|
|
11697
|
+
this.el.classList.remove(...this._appliedClasses);
|
|
11698
|
+
this._appliedClasses = [state.class].flat().flatMap((value) => String(value ?? "").split(/\s+/)).filter(Boolean);
|
|
11699
|
+
this.el.classList.add(...this._appliedClasses);
|
|
11700
|
+
const size = cssLength(state.size, "36px");
|
|
11701
|
+
const glyphSize = cssLength(state.iconSize, "52%");
|
|
11702
|
+
this.el.style.setProperty("--zx-app-icon-size", size);
|
|
11703
|
+
this.el.style.setProperty("--zx-app-icon-glyph-size", glyphSize);
|
|
11704
|
+
if (isCssColor(state.color)) this.el.style.setProperty("--zx-app-icon-color", String(state.color));
|
|
11705
|
+
else this.el.style.removeProperty("--zx-app-icon-color");
|
|
11706
|
+
this.el.dataset.glass = normalizeGlass(state.glass);
|
|
11707
|
+
this.el.toggleAttribute("data-selected", Boolean(state.selected));
|
|
11708
|
+
const label = state.label == null ? "" : String(state.label).trim();
|
|
11709
|
+
if (label) {
|
|
11710
|
+
this.el.setAttribute("role", "img");
|
|
11711
|
+
this.el.setAttribute("aria-label", label);
|
|
11712
|
+
this.el.removeAttribute("aria-hidden");
|
|
11713
|
+
} else {
|
|
11714
|
+
this.el.removeAttribute("role");
|
|
11715
|
+
this.el.removeAttribute("aria-label");
|
|
11716
|
+
this.el.setAttribute("aria-hidden", "true");
|
|
11717
|
+
}
|
|
11718
|
+
const visual = resolveVisual(state.icon);
|
|
11719
|
+
const badge = state.badge == null || state.badge === "" ? null : h("span", {
|
|
11720
|
+
class: "zx-app-icon__badge",
|
|
11721
|
+
ariaHidden: "true"
|
|
11722
|
+
}, String(state.badge));
|
|
11723
|
+
this.el.replaceChildren(
|
|
11724
|
+
h("span", { class: "zx-app-icon__shine", ariaHidden: "true" }),
|
|
11725
|
+
h("span", { class: "zx-app-icon__glyph", ariaHidden: "true" }, visual),
|
|
11726
|
+
badge
|
|
11727
|
+
);
|
|
11728
|
+
}
|
|
11729
|
+
};
|
|
11730
|
+
function appIcon(options = {}) {
|
|
11731
|
+
return new AppIcon(null, options).toElement();
|
|
11732
|
+
}
|
|
11733
|
+
function resolveVisual(value) {
|
|
11734
|
+
const resolved = typeof value === "function" ? value() : value;
|
|
11735
|
+
if (typeof resolved === "string") return icon(resolved, { size: "1em" });
|
|
11736
|
+
return resolved && typeof resolved === "object" && typeof resolved.nodeType === "number" ? (
|
|
11737
|
+
/** @type {Node} */
|
|
11738
|
+
resolved
|
|
11739
|
+
) : null;
|
|
11740
|
+
}
|
|
11741
|
+
function cssLength(value, fallback) {
|
|
11742
|
+
if (typeof value === "number" && Number.isFinite(value)) return `${Math.max(1, value)}px`;
|
|
11743
|
+
const text3 = String(value ?? "").trim();
|
|
11744
|
+
return text3 && !/[;{}]/.test(text3) ? text3 : fallback;
|
|
11745
|
+
}
|
|
11746
|
+
function normalizeGlass(value) {
|
|
11747
|
+
if (value === false || value === "none") return "none";
|
|
11748
|
+
return value === "strong" ? "strong" : "subtle";
|
|
11749
|
+
}
|
|
11750
|
+
|
|
11751
|
+
// src/components/launcher/launcher.js
|
|
11752
|
+
var Launcher = class extends Component {
|
|
11753
|
+
static cssName = "launcher";
|
|
11754
|
+
/** @type {Readonly<LauncherOptions>} */
|
|
11755
|
+
static defaults = {
|
|
11756
|
+
items: [],
|
|
11757
|
+
sources: [],
|
|
11758
|
+
query: "",
|
|
11759
|
+
debounce: 250,
|
|
11760
|
+
minQuery: 0,
|
|
11761
|
+
maxResults: 100,
|
|
11762
|
+
placeholder: "Search applications and records",
|
|
11763
|
+
label: "Launcher",
|
|
11764
|
+
closeLabel: "Close launcher",
|
|
11765
|
+
resultsLabel: "Launcher results",
|
|
11766
|
+
emptyText: "No results",
|
|
11767
|
+
loadingText: "Searching\u2026",
|
|
11768
|
+
shortcut: "mod+k",
|
|
11769
|
+
hints: { move: "Move", open: "Open", close: "Close" }
|
|
11770
|
+
};
|
|
11771
|
+
/**
|
|
11772
|
+
* Creates an owned launcher dialog or enhances an existing `<dialog>` target.
|
|
11773
|
+
* @param {HTMLDialogElement|string|null} [target=null] Dialog target, selector, or null.
|
|
11774
|
+
* @param {LauncherOptions} [options={}] Launcher options.
|
|
11775
|
+
*/
|
|
11776
|
+
constructor(target = null, options = {}) {
|
|
11777
|
+
super(target, options);
|
|
11778
|
+
}
|
|
11779
|
+
/** @returns {HTMLDialogElement} */
|
|
11780
|
+
render() {
|
|
11781
|
+
if (this.el !== null && !(this.el instanceof HTMLDialogElement)) {
|
|
11782
|
+
throw new TypeError("Launcher target must be a <dialog> element");
|
|
11783
|
+
}
|
|
11784
|
+
this._createdRoot = this.el === null;
|
|
11785
|
+
const dialog = (
|
|
11786
|
+
/** @type {HTMLDialogElement} */
|
|
11787
|
+
this.el ?? h("dialog")
|
|
11788
|
+
);
|
|
11789
|
+
this.el = dialog;
|
|
11790
|
+
this._original = this._createdRoot ? null : {
|
|
11791
|
+
attributes: Array.from(dialog.attributes, (attribute) => [attribute.name, attribute.value]),
|
|
11792
|
+
children: Array.from(dialog.childNodes)
|
|
11793
|
+
};
|
|
11794
|
+
this._items = normalizeItems(this.options.items);
|
|
11795
|
+
this._sources = normalizeSources(this.options.sources);
|
|
11796
|
+
this._sourceItems = /* @__PURE__ */ new Map();
|
|
11797
|
+
this._query = String(this.options.query ?? "");
|
|
11798
|
+
this._renderedQuery = null;
|
|
11799
|
+
this._results = [];
|
|
11800
|
+
this._active = -1;
|
|
11801
|
+
this._request = null;
|
|
11802
|
+
this._requestSequence = 0;
|
|
11803
|
+
this._queryTimer = null;
|
|
11804
|
+
this._busy = false;
|
|
11805
|
+
this._destroyed = false;
|
|
11806
|
+
const listId = uid("zx-launcher-results");
|
|
11807
|
+
const input = h("input", {
|
|
11808
|
+
ref: "input",
|
|
11809
|
+
class: "zx-launcher__input",
|
|
11810
|
+
type: "search",
|
|
11811
|
+
role: "combobox",
|
|
11812
|
+
autocomplete: "off",
|
|
11813
|
+
spellcheck: false,
|
|
11814
|
+
ariaAutocomplete: "list",
|
|
11815
|
+
ariaControls: listId,
|
|
11816
|
+
ariaExpanded: "false",
|
|
11817
|
+
ariaLabel: this.options.label,
|
|
11818
|
+
placeholder: this.options.placeholder,
|
|
11819
|
+
value: this._query
|
|
11820
|
+
});
|
|
11821
|
+
const close = h("button", {
|
|
11822
|
+
ref: "close",
|
|
11823
|
+
class: "zx-icon-btn zx-launcher__close",
|
|
11824
|
+
type: "button",
|
|
11825
|
+
ariaLabel: this.options.closeLabel
|
|
11826
|
+
}, icon("x"));
|
|
11827
|
+
const status = h("div", {
|
|
11828
|
+
ref: "status",
|
|
11829
|
+
class: "zx-launcher__status",
|
|
11830
|
+
role: "status",
|
|
11831
|
+
ariaLive: "polite"
|
|
11832
|
+
});
|
|
11833
|
+
const results = h("div", {
|
|
11834
|
+
ref: "results",
|
|
11835
|
+
class: "zx-launcher__results",
|
|
11836
|
+
id: listId,
|
|
11837
|
+
role: "listbox",
|
|
11838
|
+
ariaLabel: this.options.resultsLabel
|
|
11839
|
+
});
|
|
11840
|
+
const shortcut = this.options.shortcut === "mod+k" ? h("kbd", {
|
|
11841
|
+
class: "zx-launcher__shortcut",
|
|
11842
|
+
ariaHidden: "true"
|
|
11843
|
+
}, launcherShortcutLabel()) : null;
|
|
11844
|
+
const footer = launcherHints(this.options.hints);
|
|
11845
|
+
dialog.setAttribute("aria-label", String(this.options.label));
|
|
11846
|
+
dialog.replaceChildren(
|
|
11847
|
+
h(
|
|
11848
|
+
"div",
|
|
11849
|
+
{ class: "zx-launcher__surface" },
|
|
11850
|
+
h("div", {
|
|
11851
|
+
class: "zx-launcher__search",
|
|
11852
|
+
dataset: { shortcut: shortcut ? "true" : "false" }
|
|
11853
|
+
}, icon("search"), input, shortcut, close),
|
|
11854
|
+
status,
|
|
11855
|
+
results,
|
|
11856
|
+
footer
|
|
11857
|
+
)
|
|
11858
|
+
);
|
|
11859
|
+
dialog.dataset.state = "closed";
|
|
11860
|
+
if (this._createdRoot) document.body.append(dialog);
|
|
11861
|
+
this.listen(input, "input", () => {
|
|
11862
|
+
this._query = /** @type {HTMLInputElement} */
|
|
11863
|
+
input.value;
|
|
11864
|
+
this.emit("query", { query: this._query.trim() });
|
|
11865
|
+
this._refresh();
|
|
11866
|
+
});
|
|
11867
|
+
this.listen(input, "keydown", (event) => this._onKeydown(
|
|
11868
|
+
/** @type {KeyboardEvent} */
|
|
11869
|
+
event
|
|
11870
|
+
));
|
|
11871
|
+
this.listen(close, "click", () => this.close());
|
|
11872
|
+
this.listen(results, "click", (event) => {
|
|
11873
|
+
const option = (
|
|
11874
|
+
/** @type {Element|null} */
|
|
11875
|
+
event.target?.closest?.("[data-launcher-index]")
|
|
11876
|
+
);
|
|
11877
|
+
if (!option || !results.contains(option)) return;
|
|
11878
|
+
this._activate(Number(option.getAttribute("data-launcher-index")), event);
|
|
11879
|
+
});
|
|
11880
|
+
this.listen(results, "pointermove", (event) => {
|
|
11881
|
+
const option = (
|
|
11882
|
+
/** @type {Element|null} */
|
|
11883
|
+
event.target?.closest?.("[data-launcher-index]")
|
|
11884
|
+
);
|
|
11885
|
+
if (!option || !results.contains(option)) return;
|
|
11886
|
+
const index = Number(option.getAttribute("data-launcher-index"));
|
|
11887
|
+
if (!Number.isInteger(index) || this._results[index]?.disabled || index === this._active) return;
|
|
11888
|
+
this._active = index;
|
|
11889
|
+
this._syncActive();
|
|
11890
|
+
});
|
|
11891
|
+
this.listen(dialog, "cancel", (event) => {
|
|
11892
|
+
event.preventDefault();
|
|
11893
|
+
this.close();
|
|
11894
|
+
});
|
|
11895
|
+
this.listen(dialog, "close", () => {
|
|
11896
|
+
dialog.dataset.state = "closed";
|
|
11897
|
+
input.setAttribute("aria-expanded", "false");
|
|
11898
|
+
this._abortRequest();
|
|
11899
|
+
this.emit("close");
|
|
11900
|
+
});
|
|
11901
|
+
this.listen(document, "keydown", (event) => this._onDocumentKeydown(
|
|
11902
|
+
/** @type {KeyboardEvent} */
|
|
11903
|
+
event
|
|
11904
|
+
));
|
|
11905
|
+
this._renderResults();
|
|
11906
|
+
return dialog;
|
|
11907
|
+
}
|
|
11908
|
+
/** Opens the launcher and focuses/selects its query. @returns {this} @fires Launcher#open */
|
|
11909
|
+
open() {
|
|
11910
|
+
if (this.isOpen() || this._destroyed) return this;
|
|
11911
|
+
this.el.showModal();
|
|
11912
|
+
this.el.dataset.state = "open";
|
|
11913
|
+
this.refs.input.setAttribute("aria-expanded", "true");
|
|
11914
|
+
this._refresh();
|
|
11915
|
+
queueMicrotask(() => {
|
|
11916
|
+
if (!this.isOpen()) return;
|
|
11917
|
+
this.refs.input.focus();
|
|
11918
|
+
this.refs.input.select();
|
|
11919
|
+
});
|
|
11920
|
+
this.emit("open");
|
|
11921
|
+
return this;
|
|
11922
|
+
}
|
|
11923
|
+
/** Closes the launcher. @returns {this} */
|
|
11924
|
+
close() {
|
|
11925
|
+
if (this.isOpen()) this.el.close();
|
|
11926
|
+
return this;
|
|
11927
|
+
}
|
|
11928
|
+
/** Toggles the launcher. @returns {this} */
|
|
11929
|
+
toggle() {
|
|
11930
|
+
return this.isOpen() ? this.close() : this.open();
|
|
11931
|
+
}
|
|
11932
|
+
/** Reports whether the launcher is open. @returns {boolean} */
|
|
11933
|
+
isOpen() {
|
|
11934
|
+
return Boolean(this.el.open);
|
|
11935
|
+
}
|
|
11936
|
+
/** Focuses and selects the query field. @returns {this} */
|
|
11937
|
+
focus() {
|
|
11938
|
+
this.refs.input.focus();
|
|
11939
|
+
this.refs.input.select();
|
|
11940
|
+
return this;
|
|
11941
|
+
}
|
|
11942
|
+
/** Replaces the query and refreshes local and remote results. @param {string} query @returns {this} */
|
|
11943
|
+
setQuery(query) {
|
|
11944
|
+
this._query = String(query ?? "");
|
|
11945
|
+
this.refs.input.value = this._query;
|
|
11946
|
+
this.emit("query", { query: this._query.trim() });
|
|
11947
|
+
this._refresh();
|
|
11948
|
+
return this;
|
|
11949
|
+
}
|
|
11950
|
+
/** Returns the untrimmed query. @returns {string} */
|
|
11951
|
+
getQuery() {
|
|
11952
|
+
return this._query;
|
|
11953
|
+
}
|
|
11954
|
+
/** Replaces local catalogue items. @param {LauncherItem[]} items @returns {this} */
|
|
11955
|
+
setItems(items) {
|
|
11956
|
+
this._items = normalizeItems(items);
|
|
11957
|
+
this._renderResults();
|
|
11958
|
+
return this;
|
|
11959
|
+
}
|
|
11960
|
+
/** Marks one local item as the current destination. @param {string|number|null} id Item id. @returns {this} */
|
|
11961
|
+
setCurrent(id) {
|
|
11962
|
+
const key = id == null ? null : String(id);
|
|
11963
|
+
this._items = this._items.map((item) => ({ ...item, current: key !== null && String(item.id) === key }));
|
|
11964
|
+
this._renderResults();
|
|
11965
|
+
return this;
|
|
11966
|
+
}
|
|
11967
|
+
/** Replaces asynchronous sources and aborts work from the old set. @param {LauncherSource[]} sources @returns {this} */
|
|
11968
|
+
setSources(sources) {
|
|
11969
|
+
this._abortRequest();
|
|
11970
|
+
this._sources = normalizeSources(sources);
|
|
11971
|
+
this._sourceItems.clear();
|
|
11972
|
+
this._refresh();
|
|
11973
|
+
return this;
|
|
11974
|
+
}
|
|
11975
|
+
/** Aborts sources and removes the owned dialog. @returns {void} */
|
|
11976
|
+
destroy() {
|
|
11977
|
+
if (this._destroyed) return;
|
|
11978
|
+
this._destroyed = true;
|
|
11979
|
+
const original = this._original;
|
|
11980
|
+
this._abortRequest();
|
|
11981
|
+
if (this.isOpen()) this.el.close();
|
|
11982
|
+
super.destroy();
|
|
11983
|
+
if (!this._createdRoot && original) {
|
|
11984
|
+
for (const attribute of Array.from(this.el.attributes)) this.el.removeAttribute(attribute.name);
|
|
11985
|
+
for (const [name, value] of original.attributes) this.el.setAttribute(name, value);
|
|
11986
|
+
this.el.replaceChildren(...original.children);
|
|
11987
|
+
}
|
|
11988
|
+
}
|
|
11989
|
+
/** @returns {void} */
|
|
11990
|
+
_refresh() {
|
|
11991
|
+
this._sourceItems.clear();
|
|
11992
|
+
this._renderResults();
|
|
11993
|
+
this._abortRequest();
|
|
11994
|
+
if (this._sources.length === 0) return;
|
|
11995
|
+
const delay = Math.max(0, Number(this.options.debounce) || 0);
|
|
11996
|
+
this._queryTimer = setTimeout(() => this._loadSources(), delay);
|
|
11997
|
+
}
|
|
11998
|
+
/** @returns {Promise<void>} */
|
|
11999
|
+
async _loadSources() {
|
|
12000
|
+
this._queryTimer = null;
|
|
12001
|
+
const query = this._query.trim();
|
|
12002
|
+
const sources = this._sources.filter((source) => visibleForQuery(source.when, query) && query.length >= Math.max(Number(this.options.minQuery) || 0, Number(source.minQuery) || 0));
|
|
12003
|
+
if (sources.length === 0) return;
|
|
12004
|
+
const request = new AbortController();
|
|
12005
|
+
const sequence = ++this._requestSequence;
|
|
12006
|
+
this._request = request;
|
|
12007
|
+
this._setBusy(true);
|
|
12008
|
+
await Promise.all(sources.map(async (source) => {
|
|
12009
|
+
try {
|
|
12010
|
+
const loaded = await source.load(query, { signal: request.signal });
|
|
12011
|
+
if (request.signal.aborted || sequence !== this._requestSequence) return;
|
|
12012
|
+
this._sourceItems.set(source.id, normalizeItems(loaded).map((item, itemOrder) => ({
|
|
12013
|
+
...item,
|
|
12014
|
+
_source: source.id,
|
|
12015
|
+
_sourceOrder: source.order === "source",
|
|
12016
|
+
group: item.group ?? source.label ?? "",
|
|
12017
|
+
itemOrder: item.itemOrder ?? (source.order === "source" ? itemOrder : void 0)
|
|
12018
|
+
})));
|
|
12019
|
+
this._renderResults();
|
|
12020
|
+
} catch (error) {
|
|
12021
|
+
if (request.signal.aborted || sequence !== this._requestSequence) return;
|
|
12022
|
+
this.emit("error", { source, error });
|
|
12023
|
+
}
|
|
12024
|
+
}));
|
|
12025
|
+
if (request.signal.aborted || sequence !== this._requestSequence) return;
|
|
12026
|
+
this._request = null;
|
|
12027
|
+
this._setBusy(false);
|
|
12028
|
+
}
|
|
12029
|
+
/** @param {boolean} busy @returns {void} */
|
|
12030
|
+
_setBusy(busy) {
|
|
12031
|
+
this._busy = busy;
|
|
12032
|
+
this.el.setAttribute("aria-busy", String(busy));
|
|
12033
|
+
this._renderStatus();
|
|
12034
|
+
}
|
|
12035
|
+
/** @returns {void} */
|
|
12036
|
+
_abortRequest() {
|
|
12037
|
+
if (this._queryTimer !== null) clearTimeout(this._queryTimer);
|
|
12038
|
+
this._queryTimer = null;
|
|
12039
|
+
this._request?.abort();
|
|
12040
|
+
this._request = null;
|
|
12041
|
+
this._requestSequence += 1;
|
|
12042
|
+
if (this._busy) this._setBusy(false);
|
|
12043
|
+
}
|
|
12044
|
+
/** @returns {void} */
|
|
12045
|
+
_renderResults() {
|
|
12046
|
+
const queryKey = this._query;
|
|
12047
|
+
const activeId = this._renderedQuery === queryKey && this._active >= 0 && this._results[this._active] ? String(this._results[this._active].id) : null;
|
|
12048
|
+
const query = this._query.trim();
|
|
12049
|
+
const local = this._items.filter((item) => visibleForQuery(item.when, query));
|
|
12050
|
+
const rankedLocal = rankLauncherItems(local, query, this.options.maxResults);
|
|
12051
|
+
const remote = this._sources.flatMap((source) => {
|
|
12052
|
+
const loaded = this._sourceItems.get(String(source.id)) ?? [];
|
|
12053
|
+
return source.order === "source" ? loaded.filter((item) => visibleForQuery(item.when, query)) : rankLauncherItems(
|
|
12054
|
+
loaded.filter((item) => visibleForQuery(item.when, query)),
|
|
12055
|
+
query,
|
|
12056
|
+
this.options.maxResults
|
|
12057
|
+
);
|
|
12058
|
+
});
|
|
12059
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12060
|
+
const combined = [...rankedLocal.map((item) => ({ ...item, _source: null })), ...remote].filter((item) => {
|
|
12061
|
+
const key = String(item.id);
|
|
12062
|
+
if (seen.has(key)) return false;
|
|
12063
|
+
seen.add(key);
|
|
12064
|
+
return true;
|
|
12065
|
+
}).slice(0, Math.max(0, Number(this.options.maxResults) || 0));
|
|
12066
|
+
const groups = /* @__PURE__ */ new Map();
|
|
12067
|
+
for (const item of combined) {
|
|
12068
|
+
const group = String(item.group ?? "");
|
|
12069
|
+
if (!groups.has(group)) groups.set(group, []);
|
|
12070
|
+
groups.get(group).push(item);
|
|
12071
|
+
}
|
|
12072
|
+
this._results = [...groups.values()].flat();
|
|
12073
|
+
this._renderedQuery = queryKey;
|
|
12074
|
+
const preserved = activeId === null ? -1 : this._results.findIndex((item) => !item.disabled && String(item.id) === activeId);
|
|
12075
|
+
this._active = preserved >= 0 ? preserved : this._results.findIndex((item) => !item.disabled);
|
|
12076
|
+
const nodes = [];
|
|
12077
|
+
let index = 0;
|
|
12078
|
+
for (const [group, items] of groups) {
|
|
12079
|
+
const options = items.map((item) => this._option(item, index++));
|
|
12080
|
+
if (group) nodes.push(h("div", {
|
|
12081
|
+
class: "zx-launcher__group",
|
|
12082
|
+
role: "group",
|
|
12083
|
+
ariaLabel: group,
|
|
12084
|
+
dataset: { layout: items.every((item) => item.kind === "application") ? "grid" : "list" }
|
|
12085
|
+
}, h("div", { class: "zx-launcher__group-label", ariaHidden: "true" }, group), options));
|
|
12086
|
+
else nodes.push(...options);
|
|
12087
|
+
}
|
|
12088
|
+
this.refs.results.replaceChildren(...nodes);
|
|
12089
|
+
this._syncActive();
|
|
12090
|
+
this._renderStatus();
|
|
12091
|
+
}
|
|
12092
|
+
/** @param {RankedLauncherItem} item @param {number} index @returns {HTMLElement} */
|
|
12093
|
+
_option(item, index) {
|
|
12094
|
+
const href = safeHref(item.href);
|
|
12095
|
+
const tag = href ? "a" : "button";
|
|
12096
|
+
const children = [];
|
|
12097
|
+
const visual = launcherItemIcon(item);
|
|
12098
|
+
if (visual) children.push(h("span", {
|
|
12099
|
+
class: "zx-launcher__icon",
|
|
12100
|
+
ariaHidden: "true"
|
|
12101
|
+
}, visual));
|
|
12102
|
+
children.push(h(
|
|
12103
|
+
"span",
|
|
12104
|
+
{ class: "zx-launcher__copy" },
|
|
12105
|
+
h("span", { class: "zx-launcher__label" }, item.label),
|
|
12106
|
+
item.description ? h("span", { class: "zx-launcher__description" }, item.description) : null
|
|
12107
|
+
));
|
|
12108
|
+
if (item.badge != null) children.push(h("span", { class: "zx-launcher__badge" }, String(item.badge)));
|
|
12109
|
+
return (
|
|
12110
|
+
/** @type {HTMLElement} */
|
|
12111
|
+
h(tag, {
|
|
12112
|
+
class: "zx-launcher__option",
|
|
12113
|
+
type: tag === "button" ? "button" : null,
|
|
12114
|
+
href,
|
|
12115
|
+
target: item.target ?? null,
|
|
12116
|
+
rel: tag === "a" ? "noopener" : null,
|
|
12117
|
+
role: "option",
|
|
12118
|
+
id: uid("zx-launcher-option"),
|
|
12119
|
+
tabIndex: -1,
|
|
12120
|
+
ariaSelected: "false",
|
|
12121
|
+
ariaDisabled: item.disabled ? "true" : null,
|
|
12122
|
+
ariaCurrent: item.current ? "page" : null,
|
|
12123
|
+
dataset: {
|
|
12124
|
+
launcherIndex: index,
|
|
12125
|
+
pinned: item.pinned !== void 0 && item.pinned !== false ? "true" : null,
|
|
12126
|
+
kind: normalizeLauncherKind(item.kind)
|
|
12127
|
+
}
|
|
12128
|
+
}, children)
|
|
12129
|
+
);
|
|
12130
|
+
}
|
|
12131
|
+
/** @returns {void} */
|
|
12132
|
+
_renderStatus() {
|
|
12133
|
+
if (this._busy) this.refs.status.textContent = String(this.options.loadingText);
|
|
12134
|
+
else if (this._results.length === 0) this.refs.status.textContent = String(this.options.emptyText);
|
|
12135
|
+
else this.refs.status.textContent = "";
|
|
12136
|
+
this.refs.status.hidden = !this.refs.status.textContent;
|
|
12137
|
+
}
|
|
12138
|
+
/** @returns {void} */
|
|
12139
|
+
_syncActive() {
|
|
12140
|
+
const options = [...this.refs.results.querySelectorAll("[data-launcher-index]")];
|
|
12141
|
+
for (const option of options) {
|
|
12142
|
+
const selected = Number(option.getAttribute("data-launcher-index")) === this._active;
|
|
12143
|
+
option.setAttribute("aria-selected", String(selected));
|
|
12144
|
+
}
|
|
12145
|
+
const active = options.find((option) => Number(option.getAttribute("data-launcher-index")) === this._active);
|
|
12146
|
+
if (active) this.refs.input.setAttribute("aria-activedescendant", active.id);
|
|
12147
|
+
else this.refs.input.removeAttribute("aria-activedescendant");
|
|
12148
|
+
}
|
|
12149
|
+
/** @param {KeyboardEvent} event @returns {void} */
|
|
12150
|
+
_onKeydown(event) {
|
|
12151
|
+
if (event.key === "Escape") {
|
|
12152
|
+
this.close();
|
|
12153
|
+
return;
|
|
12154
|
+
}
|
|
12155
|
+
if (event.key === "Enter") {
|
|
12156
|
+
if (this._active < 0) return;
|
|
12157
|
+
event.preventDefault();
|
|
12158
|
+
const option = this.refs.results.querySelector(`[data-launcher-index="${this._active}"]`);
|
|
12159
|
+
option?.click();
|
|
12160
|
+
return;
|
|
12161
|
+
}
|
|
12162
|
+
const enabled = this._results.map((item, index) => item.disabled ? -1 : index).filter((index) => index >= 0);
|
|
12163
|
+
if (enabled.length === 0) return;
|
|
12164
|
+
if (/^Arrow(?:Left|Right|Up|Down)$/.test(event.key)) {
|
|
12165
|
+
const next = this._spatialIndex(event.key, enabled);
|
|
12166
|
+
if (next !== null) {
|
|
12167
|
+
event.preventDefault();
|
|
12168
|
+
this._active = next;
|
|
12169
|
+
this._syncActive();
|
|
12170
|
+
this.refs.results.querySelector(`[data-launcher-index="${this._active}"]`)?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
12171
|
+
return;
|
|
12172
|
+
}
|
|
12173
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") return;
|
|
12174
|
+
}
|
|
12175
|
+
let position2 = enabled.indexOf(this._active);
|
|
12176
|
+
if (event.key === "Home" || event.key === "PageUp") position2 = 0;
|
|
12177
|
+
else if (event.key === "End" || event.key === "PageDown") position2 = enabled.length - 1;
|
|
12178
|
+
else if (event.key === "ArrowDown") position2 = (position2 + 1 + enabled.length) % enabled.length;
|
|
12179
|
+
else if (event.key === "ArrowUp") position2 = (position2 - 1 + enabled.length) % enabled.length;
|
|
12180
|
+
else return;
|
|
12181
|
+
event.preventDefault();
|
|
12182
|
+
this._active = enabled[position2];
|
|
12183
|
+
this._syncActive();
|
|
12184
|
+
this.refs.results.querySelector(`[data-launcher-index="${this._active}"]`)?.scrollIntoView({ block: "nearest" });
|
|
12185
|
+
}
|
|
12186
|
+
/** @param {string} direction @param {number[]} enabled @returns {number|null} */
|
|
12187
|
+
_spatialIndex(direction, enabled) {
|
|
12188
|
+
const current = this.refs.results.querySelector(`[data-launcher-index="${this._active}"]`);
|
|
12189
|
+
if (!(current instanceof HTMLElement)) return null;
|
|
12190
|
+
if ((direction === "ArrowLeft" || direction === "ArrowRight") && this._results[this._active]?.kind !== "application") return null;
|
|
12191
|
+
const candidates = [...this.refs.results.querySelectorAll("[data-launcher-index]")].filter((option) => enabled.includes(Number(option.getAttribute("data-launcher-index"))) && (direction !== "ArrowLeft" && direction !== "ArrowRight" || option.getAttribute("data-kind") === "application"));
|
|
12192
|
+
const next = findSpatialLauncherOption(candidates, current, direction);
|
|
12193
|
+
return next ? Number(next.getAttribute("data-launcher-index")) : null;
|
|
12194
|
+
}
|
|
12195
|
+
/** @param {number} index @param {Event} event @returns {void} */
|
|
12196
|
+
_activate(index, event) {
|
|
12197
|
+
const item = this._results[index];
|
|
12198
|
+
if (!item || item.disabled) {
|
|
12199
|
+
event.preventDefault();
|
|
12200
|
+
return;
|
|
12201
|
+
}
|
|
12202
|
+
const selected = this.emit("select", {
|
|
12203
|
+
item,
|
|
12204
|
+
value: item.value ?? item.id,
|
|
12205
|
+
source: item._source ?? null,
|
|
12206
|
+
query: this._query.trim()
|
|
12207
|
+
});
|
|
12208
|
+
if (selected.defaultPrevented) {
|
|
12209
|
+
event.preventDefault();
|
|
12210
|
+
return;
|
|
12211
|
+
}
|
|
12212
|
+
if (!item.href) {
|
|
12213
|
+
event.preventDefault();
|
|
12214
|
+
item.invoke?.();
|
|
12215
|
+
}
|
|
12216
|
+
this.close();
|
|
12217
|
+
}
|
|
12218
|
+
/** @param {KeyboardEvent} event @returns {void} */
|
|
12219
|
+
_onDocumentKeydown(event) {
|
|
12220
|
+
if (this.options.shortcut !== "mod+k" || event.isComposing || event.key.toLowerCase() !== "k" || Number(event.metaKey) + Number(event.ctrlKey) !== 1 || event.altKey || event.shiftKey) return;
|
|
12221
|
+
const active = document.activeElement;
|
|
12222
|
+
if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement || active instanceof HTMLSelectElement || active instanceof HTMLElement && active.isContentEditable) return;
|
|
12223
|
+
const modal = document.querySelector("dialog:modal");
|
|
12224
|
+
if (modal && modal !== this.el) return;
|
|
12225
|
+
event.preventDefault();
|
|
12226
|
+
this.open();
|
|
12227
|
+
}
|
|
12228
|
+
};
|
|
12229
|
+
function launcherShortcutLabel() {
|
|
12230
|
+
const platform = String(
|
|
12231
|
+
/** @type {any} */
|
|
12232
|
+
navigator.userAgentData?.platform ?? navigator.platform ?? ""
|
|
12233
|
+
);
|
|
12234
|
+
return /mac|iphone|ipad/i.test(platform) ? "\u2318 K" : "Ctrl K";
|
|
12235
|
+
}
|
|
12236
|
+
function launcherHints(hints) {
|
|
12237
|
+
if (hints === false) return null;
|
|
12238
|
+
const labels = hints && typeof hints === "object" ? hints : {};
|
|
12239
|
+
return h(
|
|
12240
|
+
"footer",
|
|
12241
|
+
{ class: "zx-launcher__hints", ariaHidden: "true" },
|
|
12242
|
+
h("span", {}, h("kbd", {}, "\u2191\u2193\u2190\u2192"), labels.move ?? "Move"),
|
|
12243
|
+
h("span", {}, h("kbd", {}, "\u21B5"), labels.open ?? "Open"),
|
|
12244
|
+
h("span", {}, h("kbd", {}, "Esc"), labels.close ?? "Close")
|
|
12245
|
+
);
|
|
12246
|
+
}
|
|
12247
|
+
function launcherItemIcon(item) {
|
|
12248
|
+
let visual = item.icon;
|
|
12249
|
+
if (typeof visual === "function") visual = visual(item);
|
|
12250
|
+
if (typeof visual === "string") return icon(visual);
|
|
12251
|
+
if (visual instanceof Node) return visual.cloneNode(true);
|
|
12252
|
+
if (visual && typeof visual === "object" && typeof /** @type {any} */
|
|
12253
|
+
visual.toElement === "function") {
|
|
12254
|
+
const element = (
|
|
12255
|
+
/** @type {any} */
|
|
12256
|
+
visual.toElement()
|
|
12257
|
+
);
|
|
12258
|
+
return element instanceof Node ? element.cloneNode(true) : null;
|
|
12259
|
+
}
|
|
12260
|
+
return null;
|
|
12261
|
+
}
|
|
12262
|
+
function normalizeLauncherKind(kind) {
|
|
12263
|
+
return kind === "application" || kind === "record" ? kind : "action";
|
|
12264
|
+
}
|
|
12265
|
+
function visibleForQuery(when, query) {
|
|
12266
|
+
if (when === "empty") return query === "";
|
|
12267
|
+
if (when === "query") return query !== "";
|
|
12268
|
+
return true;
|
|
12269
|
+
}
|
|
12270
|
+
function findSpatialLauncherOption(options, current, direction) {
|
|
12271
|
+
const origin = current.getBoundingClientRect();
|
|
12272
|
+
if (origin.width === 0 && origin.height === 0) return null;
|
|
12273
|
+
const originX = origin.left + origin.width / 2;
|
|
12274
|
+
const originY = origin.top + origin.height / 2;
|
|
12275
|
+
let best = null;
|
|
12276
|
+
let bestScore = Infinity;
|
|
12277
|
+
for (const option of options) {
|
|
12278
|
+
if (option === current) continue;
|
|
12279
|
+
const rect = option.getBoundingClientRect();
|
|
12280
|
+
const dx = rect.left + rect.width / 2 - originX;
|
|
12281
|
+
const dy = rect.top + rect.height / 2 - originY;
|
|
12282
|
+
let valid = false;
|
|
12283
|
+
let score = Infinity;
|
|
12284
|
+
if (direction === "ArrowLeft") {
|
|
12285
|
+
valid = dx < -2 && Math.abs(dy) < Math.max(origin.height, rect.height) / 2;
|
|
12286
|
+
score = Math.abs(dx) + Math.abs(dy) * 4;
|
|
12287
|
+
} else if (direction === "ArrowRight") {
|
|
12288
|
+
valid = dx > 2 && Math.abs(dy) < Math.max(origin.height, rect.height) / 2;
|
|
12289
|
+
score = Math.abs(dx) + Math.abs(dy) * 4;
|
|
12290
|
+
} else if (direction === "ArrowUp") {
|
|
12291
|
+
valid = dy < -2;
|
|
12292
|
+
score = Math.abs(dy) * 10 + Math.abs(dx);
|
|
12293
|
+
} else if (direction === "ArrowDown") {
|
|
12294
|
+
valid = dy > 2;
|
|
12295
|
+
score = Math.abs(dy) * 10 + Math.abs(dx);
|
|
12296
|
+
}
|
|
12297
|
+
if (valid && score < bestScore) {
|
|
12298
|
+
best = option;
|
|
12299
|
+
bestScore = score;
|
|
12300
|
+
}
|
|
12301
|
+
}
|
|
12302
|
+
return best;
|
|
12303
|
+
}
|
|
12304
|
+
function rankLauncherItems(items, query, limit = 100) {
|
|
12305
|
+
const needle = searchable(query).trim();
|
|
12306
|
+
const ranked = items.map((item, order) => ({ item, order, score: launcherScore(item, needle) })).filter(({ score }) => score < Infinity);
|
|
12307
|
+
ranked.sort((a, b) => {
|
|
12308
|
+
const aGroup = finiteOrder(a.item.groupOrder);
|
|
12309
|
+
const bGroup = finiteOrder(b.item.groupOrder);
|
|
12310
|
+
if (aGroup !== bGroup) return aGroup - bGroup;
|
|
12311
|
+
if (needle === "") {
|
|
12312
|
+
const aPinned = pinOrder(a.item.pinned, a.order);
|
|
12313
|
+
const bPinned = pinOrder(b.item.pinned, b.order);
|
|
12314
|
+
if (aPinned !== bPinned) return aPinned - bPinned;
|
|
12315
|
+
}
|
|
12316
|
+
return a.score - b.score || finiteOrder(a.item.itemOrder) - finiteOrder(b.item.itemOrder) || compareLauncherText(a.item.label, b.item.label) || a.order - b.order;
|
|
12317
|
+
});
|
|
12318
|
+
const maximum = Math.max(0, Number(limit) || 0);
|
|
12319
|
+
return ranked.slice(0, maximum || 0).map(({ item, score }) => ({ ...item, _score: score }));
|
|
12320
|
+
}
|
|
12321
|
+
function launcherScore(item, needle) {
|
|
12322
|
+
if (!needle) return item.pinned ? -1 : 0;
|
|
12323
|
+
const label = searchable(item.label);
|
|
12324
|
+
const identifier = searchable(item.id);
|
|
12325
|
+
const keywords = searchable(Array.isArray(item.keywords) ? item.keywords.join(" ") : item.keywords ?? "");
|
|
12326
|
+
if (label === needle) return 0;
|
|
12327
|
+
if (label.startsWith(needle)) return 10;
|
|
12328
|
+
if (label.split(/[^\p{L}\p{N}]+/u).some((word) => word.startsWith(needle))) return 20;
|
|
12329
|
+
if (label.includes(needle) || keywords.includes(needle)) return 30;
|
|
12330
|
+
if (identifier.includes(needle)) return 40;
|
|
12331
|
+
const acronym = label.split(/[^\p{L}\p{N}]+/u).filter(Boolean).map((word) => word[0]).join("");
|
|
12332
|
+
if (acronym.startsWith(needle)) return 50;
|
|
12333
|
+
return Infinity;
|
|
12334
|
+
}
|
|
12335
|
+
function pinOrder(pinned, order) {
|
|
12336
|
+
if (typeof pinned === "number" && Number.isFinite(pinned)) return pinned;
|
|
12337
|
+
if (pinned) return order;
|
|
12338
|
+
return Number.MAX_SAFE_INTEGER;
|
|
12339
|
+
}
|
|
12340
|
+
function finiteOrder(value) {
|
|
12341
|
+
return typeof value === "number" && Number.isFinite(value) ? value : Number.MAX_SAFE_INTEGER;
|
|
12342
|
+
}
|
|
12343
|
+
function searchable(value) {
|
|
12344
|
+
return String(value ?? "").normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase();
|
|
12345
|
+
}
|
|
12346
|
+
function compareLauncherText(left, right) {
|
|
12347
|
+
const a = searchable(left);
|
|
12348
|
+
const b = searchable(right);
|
|
12349
|
+
if (a < b) return -1;
|
|
12350
|
+
if (a > b) return 1;
|
|
12351
|
+
const rawA = String(left ?? "").normalize("NFC");
|
|
12352
|
+
const rawB = String(right ?? "").normalize("NFC");
|
|
12353
|
+
return rawA < rawB ? -1 : rawA > rawB ? 1 : 0;
|
|
12354
|
+
}
|
|
12355
|
+
function normalizeItems(items) {
|
|
12356
|
+
if (!Array.isArray(items)) return [];
|
|
12357
|
+
return items.filter((item) => item && typeof item === "object" && item.id != null && item.label != null).map((item) => normalizeLauncherItem(item));
|
|
12358
|
+
}
|
|
12359
|
+
function normalizeLauncherItem(item) {
|
|
12360
|
+
const normalized = { ...item, label: String(item.label) };
|
|
12361
|
+
if (item.href == null) return normalized;
|
|
12362
|
+
const href = safeHref(item.href);
|
|
12363
|
+
if (href !== null) normalized.href = href;
|
|
12364
|
+
else {
|
|
12365
|
+
delete normalized.href;
|
|
12366
|
+
if (typeof normalized.invoke !== "function") normalized.disabled = true;
|
|
12367
|
+
}
|
|
12368
|
+
return normalized;
|
|
12369
|
+
}
|
|
12370
|
+
function normalizeSources(sources) {
|
|
12371
|
+
if (!Array.isArray(sources)) return [];
|
|
12372
|
+
return sources.filter((source) => source && typeof source === "object" && source.id != null && typeof source.load === "function").map((source) => ({ ...source, id: String(source.id) }));
|
|
12373
|
+
}
|
|
12374
|
+
|
|
10887
12375
|
// src/components/message/message.js
|
|
10888
12376
|
var MessageQueue = class {
|
|
10889
12377
|
#limit;
|
|
@@ -11993,6 +13481,7 @@ var zeyosModules = Object.freeze({
|
|
|
11993
13481
|
weblets: { label: "Weblets", icon: "zeyos-weblets", color: "#4f46e5", fa: "window-restore" }
|
|
11994
13482
|
});
|
|
11995
13483
|
var moduleAliases = Object.freeze({
|
|
13484
|
+
main: "zeyos",
|
|
11996
13485
|
addresses: "contacts",
|
|
11997
13486
|
clocking: "actionsteps",
|
|
11998
13487
|
comments: "notes",
|
|
@@ -12099,26 +13588,338 @@ function moduleIcon(name, options = {}) {
|
|
|
12099
13588
|
return icon(standard ? `fa:${info.fa}` : `kit:${info.icon}`, rest);
|
|
12100
13589
|
}
|
|
12101
13590
|
function moduleChip(name, options = {}) {
|
|
12102
|
-
const {
|
|
13591
|
+
const {
|
|
13592
|
+
size = 24,
|
|
13593
|
+
iconSize = "52%",
|
|
13594
|
+
label = null,
|
|
13595
|
+
title = false,
|
|
13596
|
+
standard = false,
|
|
13597
|
+
color = null
|
|
13598
|
+
} = options;
|
|
12103
13599
|
const key = normalizeModuleName(name);
|
|
12104
13600
|
const info = moduleInfo(name);
|
|
12105
|
-
const
|
|
12106
|
-
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12115
|
-
|
|
12116
|
-
|
|
13601
|
+
const background = isCssColor(color) ? color : info.color;
|
|
13602
|
+
const chip = appIcon({
|
|
13603
|
+
// AppIcon owns glyph sizing. Keep the provider glyph at 1em so the percentage is not applied
|
|
13604
|
+
// once to the wrapper and a second time to a Font Awesome <i>.
|
|
13605
|
+
icon: options.icon ?? moduleIcon(name, { size: "1em", standard }),
|
|
13606
|
+
size,
|
|
13607
|
+
iconSize,
|
|
13608
|
+
color: background,
|
|
13609
|
+
label,
|
|
13610
|
+
glass: options.glass ?? "subtle",
|
|
13611
|
+
class: ["zx-module-icon", options.class].flat().filter(Boolean)
|
|
13612
|
+
});
|
|
13613
|
+
chip.dataset.module = key;
|
|
13614
|
+
chip.style.setProperty("--zx-module-color", background);
|
|
13615
|
+
chip.style.setProperty("--zx-module-glyph", moduleGlyphColor(background));
|
|
13616
|
+
chip.style.setProperty("--zx-app-icon-glyph", moduleGlyphColor(background));
|
|
13617
|
+
if (title) chip.title = info.label;
|
|
13618
|
+
return chip;
|
|
13619
|
+
}
|
|
13620
|
+
function zeyosAppIcon(name, options = {}) {
|
|
13621
|
+
return moduleChip(name, options);
|
|
13622
|
+
}
|
|
13623
|
+
|
|
13624
|
+
// src/zeyos/launcher.js
|
|
13625
|
+
var ZEYOS_LAUNCHER_APPLICATIONS = Object.freeze([
|
|
13626
|
+
"accounts",
|
|
13627
|
+
"actionsteps",
|
|
13628
|
+
"admin",
|
|
13629
|
+
"auth",
|
|
13630
|
+
"billing",
|
|
13631
|
+
"calendar",
|
|
13632
|
+
"campaigns",
|
|
13633
|
+
"collection",
|
|
13634
|
+
"contacts",
|
|
13635
|
+
"contracts",
|
|
13636
|
+
"control",
|
|
13637
|
+
"coupons",
|
|
13638
|
+
"dev",
|
|
13639
|
+
"documents",
|
|
13640
|
+
"inventory",
|
|
13641
|
+
"mailinglists",
|
|
13642
|
+
"main",
|
|
13643
|
+
"messages",
|
|
13644
|
+
"links",
|
|
13645
|
+
"notes",
|
|
13646
|
+
"objects",
|
|
13647
|
+
"opportunities",
|
|
13648
|
+
"payments",
|
|
13649
|
+
"pricelists",
|
|
13650
|
+
"procurement",
|
|
13651
|
+
"production",
|
|
13652
|
+
"projects",
|
|
13653
|
+
"tasks",
|
|
13654
|
+
"tickets"
|
|
13655
|
+
]);
|
|
13656
|
+
function buildZeyosLauncherConfig(state = {}, options = {}) {
|
|
13657
|
+
const {
|
|
13658
|
+
standardIcons = false,
|
|
13659
|
+
items: extraItems = [],
|
|
13660
|
+
sources: extraSources = [],
|
|
13661
|
+
...launcherOptions
|
|
13662
|
+
} = options;
|
|
13663
|
+
const labels = launcherLabels(state.labels);
|
|
13664
|
+
const iconOptions = {
|
|
13665
|
+
standardIcons,
|
|
13666
|
+
renderApplicationIcon: state.renderApplicationIcon,
|
|
13667
|
+
renderWebletIcon: state.renderWebletIcon
|
|
13668
|
+
};
|
|
13669
|
+
const applications = normalizeZeyosLauncherApplications(state, iconOptions);
|
|
13670
|
+
const recent = typeof state.loadRecent === "function" ? [] : normalizeZeyosLauncherRecords(state.recent ?? [], {
|
|
13671
|
+
query: "",
|
|
13672
|
+
labels,
|
|
13673
|
+
resolveRecord: state.resolveRecord,
|
|
13674
|
+
renderRecordIcon: state.renderRecordIcon,
|
|
13675
|
+
standardIcons
|
|
13676
|
+
});
|
|
13677
|
+
const sources = [];
|
|
13678
|
+
if (typeof state.loadRecent === "function") {
|
|
13679
|
+
sources.push({
|
|
13680
|
+
id: "zeyos-recent",
|
|
13681
|
+
minQuery: 0,
|
|
13682
|
+
when: "empty",
|
|
13683
|
+
order: "source",
|
|
13684
|
+
load: async (_query, context) => normalizeZeyosLauncherRecords(
|
|
13685
|
+
await state.loadRecent(context),
|
|
13686
|
+
{
|
|
13687
|
+
query: "",
|
|
13688
|
+
labels,
|
|
13689
|
+
resolveRecord: state.resolveRecord,
|
|
13690
|
+
renderRecordIcon: state.renderRecordIcon,
|
|
13691
|
+
standardIcons
|
|
13692
|
+
}
|
|
13693
|
+
)
|
|
13694
|
+
});
|
|
13695
|
+
}
|
|
13696
|
+
if (typeof state.search === "function") {
|
|
13697
|
+
sources.push({
|
|
13698
|
+
id: "zeyos-records",
|
|
13699
|
+
minQuery: 1,
|
|
13700
|
+
when: "query",
|
|
13701
|
+
order: "source",
|
|
13702
|
+
load: async (query, context) => normalizeZeyosLauncherRecords(
|
|
13703
|
+
await state.search(query, context),
|
|
13704
|
+
{
|
|
13705
|
+
query,
|
|
13706
|
+
labels,
|
|
13707
|
+
resolveRecord: state.resolveRecord,
|
|
13708
|
+
renderRecordIcon: state.renderRecordIcon,
|
|
13709
|
+
standardIcons
|
|
13710
|
+
}
|
|
13711
|
+
)
|
|
13712
|
+
});
|
|
13713
|
+
}
|
|
13714
|
+
sources.push(...extraSources);
|
|
13715
|
+
return {
|
|
13716
|
+
...launcherOptions,
|
|
13717
|
+
items: [...applications, ...recent, ...extraItems],
|
|
13718
|
+
sources,
|
|
13719
|
+
hints: options.hints ?? {
|
|
13720
|
+
move: labels.move,
|
|
13721
|
+
open: labels.open,
|
|
13722
|
+
close: labels.close
|
|
13723
|
+
}
|
|
13724
|
+
};
|
|
13725
|
+
}
|
|
13726
|
+
function zeyosLauncher(state = {}, options = {}) {
|
|
13727
|
+
return new Launcher(null, buildZeyosLauncherConfig(state, options));
|
|
13728
|
+
}
|
|
13729
|
+
function normalizeZeyosLauncherApplications(state = {}, options = {}) {
|
|
13730
|
+
const labels = launcherLabels(state.labels);
|
|
13731
|
+
const pinned = Array.isArray(state.pinned) ? state.pinned.map(String) : [];
|
|
13732
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13733
|
+
const items = [];
|
|
13734
|
+
const add = (raw2, fallbackId = null, fallbackLabel = null) => {
|
|
13735
|
+
const app = normalizeApplication(raw2, fallbackId, fallbackLabel);
|
|
13736
|
+
if (!app || seen.has(app.identifier)) return;
|
|
13737
|
+
if (app.type === "module" && typeof state.canUseModule === "function" && !state.canUseModule(app.module)) return;
|
|
13738
|
+
seen.add(app.identifier);
|
|
13739
|
+
const resolved = mergeDestination(app.raw, state.resolveApplication);
|
|
13740
|
+
const pin = pinned.indexOf(app.identifier);
|
|
13741
|
+
const icon2 = () => {
|
|
13742
|
+
const custom = typeof options.renderApplicationIcon === "function" ? options.renderApplicationIcon(app.raw, app) : app.type === "weblet" && typeof options.renderWebletIcon === "function" ? options.renderWebletIcon(app.raw, app) : null;
|
|
13743
|
+
return custom ?? moduleChip(app.module, {
|
|
13744
|
+
size: 36,
|
|
13745
|
+
standard: Boolean(options.standardIcons),
|
|
13746
|
+
color: app.color
|
|
13747
|
+
});
|
|
13748
|
+
};
|
|
13749
|
+
items.push({
|
|
13750
|
+
id: app.identifier,
|
|
13751
|
+
label: app.label,
|
|
13752
|
+
keywords: [app.identifier, app.module],
|
|
13753
|
+
group: labels.applications,
|
|
13754
|
+
groupOrder: 0,
|
|
13755
|
+
kind: "application",
|
|
13756
|
+
icon: icon2,
|
|
13757
|
+
pinned: pin >= 0 ? pin : void 0,
|
|
13758
|
+
current: app.identifier === state.activeIdentifier,
|
|
13759
|
+
disabled: !resolved.href && typeof resolved.invoke !== "function",
|
|
13760
|
+
...resolved,
|
|
13761
|
+
_zeyos: app
|
|
13762
|
+
});
|
|
13763
|
+
};
|
|
13764
|
+
for (const raw2 of Array.isArray(state.menuApps) ? state.menuApps : []) add(raw2);
|
|
13765
|
+
if (Array.isArray(state.modules)) {
|
|
13766
|
+
for (const module of state.modules) {
|
|
13767
|
+
if (typeof module === "string") add([module, moduleInfo(module).label]);
|
|
13768
|
+
else add(module, module?.id ?? module?.module, module?.label);
|
|
13769
|
+
}
|
|
12117
13770
|
} else {
|
|
12118
|
-
|
|
12119
|
-
|
|
13771
|
+
for (const [module, label] of Object.entries(state.modules ?? {})) add([module, label]);
|
|
13772
|
+
}
|
|
13773
|
+
return items;
|
|
13774
|
+
}
|
|
13775
|
+
function normalizeZeyosLauncherRecords(payload, options = {}) {
|
|
13776
|
+
const query = String(options.query ?? "");
|
|
13777
|
+
const labels = launcherLabels(options.labels);
|
|
13778
|
+
const records = flattenRecords(payload, query);
|
|
13779
|
+
const groupOrders = /* @__PURE__ */ new Map();
|
|
13780
|
+
return records.flatMap(({ raw: raw2, group }, itemOrder) => {
|
|
13781
|
+
const record = normalizeRecord(raw2, group, query);
|
|
13782
|
+
if (!record) return [];
|
|
13783
|
+
const groupName = query === "" ? labels.recent : record.group === "" ? labels.results : labels.entities[record.group] ?? record.group;
|
|
13784
|
+
if (!groupOrders.has(groupName)) groupOrders.set(groupName, groupOrders.size + 1);
|
|
13785
|
+
const resolved = mergeDestination(record.raw, options.resolveRecord);
|
|
13786
|
+
return [{
|
|
13787
|
+
id: `record:${record.entity}:${record.fork ?? ""}:${record.ID}`,
|
|
13788
|
+
label: record.name,
|
|
13789
|
+
description: record.sec,
|
|
13790
|
+
keywords: [record.entity, record.ID, record.fork ?? ""],
|
|
13791
|
+
group: groupName,
|
|
13792
|
+
groupOrder: groupOrders.get(groupName),
|
|
13793
|
+
itemOrder,
|
|
13794
|
+
kind: "record",
|
|
13795
|
+
when: query === "" ? "empty" : "query",
|
|
13796
|
+
icon: () => {
|
|
13797
|
+
const custom = typeof options.renderRecordIcon === "function" ? options.renderRecordIcon(record.raw, record) : null;
|
|
13798
|
+
return custom ?? moduleChip(record.entity, {
|
|
13799
|
+
size: 28,
|
|
13800
|
+
standard: Boolean(options.standardIcons)
|
|
13801
|
+
});
|
|
13802
|
+
},
|
|
13803
|
+
disabled: !resolved.href && typeof resolved.invoke !== "function",
|
|
13804
|
+
...resolved,
|
|
13805
|
+
_zeyos: record
|
|
13806
|
+
}];
|
|
13807
|
+
});
|
|
13808
|
+
}
|
|
13809
|
+
function zeyosActiveIdentifier(state = {}) {
|
|
13810
|
+
if (state.weblet != null && state.weblet !== "") return `weblet#${state.weblet}`;
|
|
13811
|
+
if (state.fork != null && state.fork !== "") return `fork#${state.fork}`;
|
|
13812
|
+
return String(state.module ?? "");
|
|
13813
|
+
}
|
|
13814
|
+
function normalizeApplication(raw2, fallbackId, fallbackLabel) {
|
|
13815
|
+
if (Array.isArray(raw2)) {
|
|
13816
|
+
if (raw2.length < 2) return null;
|
|
13817
|
+
if (raw2.length === 2) {
|
|
13818
|
+
const module = String(raw2[0]);
|
|
13819
|
+
return { raw: raw2, identifier: module, label: String(raw2[1]), module, type: "module", color: null };
|
|
13820
|
+
}
|
|
13821
|
+
if (raw2.length === 4) {
|
|
13822
|
+
const module = String(raw2[2] || "default");
|
|
13823
|
+
return {
|
|
13824
|
+
raw: raw2,
|
|
13825
|
+
identifier: `fork#${raw2[0]}`,
|
|
13826
|
+
label: String(raw2[1]),
|
|
13827
|
+
module,
|
|
13828
|
+
type: "fork",
|
|
13829
|
+
color: normalizeServerColor(raw2[3])
|
|
13830
|
+
};
|
|
13831
|
+
}
|
|
13832
|
+
return {
|
|
13833
|
+
raw: raw2,
|
|
13834
|
+
identifier: `weblet#${raw2[0]}`,
|
|
13835
|
+
label: String(raw2[1]),
|
|
13836
|
+
module: "weblets",
|
|
13837
|
+
type: "weblet",
|
|
13838
|
+
color: normalizeServerColor(raw2[8])
|
|
13839
|
+
};
|
|
12120
13840
|
}
|
|
12121
|
-
return
|
|
13841
|
+
if (!raw2 || typeof raw2 !== "object") return null;
|
|
13842
|
+
const identifier = String(raw2.identifier ?? raw2.id ?? fallbackId ?? "");
|
|
13843
|
+
if (!identifier) return null;
|
|
13844
|
+
const type = identifier.startsWith("fork#") ? "fork" : identifier.startsWith("weblet#") ? "weblet" : "module";
|
|
13845
|
+
return {
|
|
13846
|
+
raw: raw2,
|
|
13847
|
+
identifier,
|
|
13848
|
+
label: String(raw2.label ?? fallbackLabel ?? moduleInfo(identifier).label),
|
|
13849
|
+
module: String(raw2.module ?? (type === "module" ? identifier : type === "fork" ? "default" : "weblets")),
|
|
13850
|
+
type,
|
|
13851
|
+
color: normalizeServerColor(raw2.color)
|
|
13852
|
+
};
|
|
13853
|
+
}
|
|
13854
|
+
function flattenRecords(payload, query) {
|
|
13855
|
+
if (Array.isArray(payload)) return payload.map((raw2) => ({ raw: raw2, group: raw2?.group ?? "" }));
|
|
13856
|
+
if (!payload || typeof payload !== "object") return [];
|
|
13857
|
+
const result = [];
|
|
13858
|
+
for (const [group, rows] of Object.entries(payload)) {
|
|
13859
|
+
if (!Array.isArray(rows)) continue;
|
|
13860
|
+
for (const row of rows) {
|
|
13861
|
+
if (!Array.isArray(row)) {
|
|
13862
|
+
result.push({ raw: row, group });
|
|
13863
|
+
continue;
|
|
13864
|
+
}
|
|
13865
|
+
const raw2 = group === "" && row.length >= 5 ? { ID: row[0], fork: row[1], name: row[2], sec: row[3], entity: row[4], group } : { ID: row[0], fork: row[1], name: row[2], sec: row[3], entity: group, group };
|
|
13866
|
+
result.push({ raw: raw2, group });
|
|
13867
|
+
}
|
|
13868
|
+
}
|
|
13869
|
+
return result;
|
|
13870
|
+
}
|
|
13871
|
+
function normalizeRecord(raw2, group, query) {
|
|
13872
|
+
if (Array.isArray(raw2)) {
|
|
13873
|
+
if (query === "" && raw2.length >= 4) {
|
|
13874
|
+
return {
|
|
13875
|
+
raw: raw2,
|
|
13876
|
+
entity: String(raw2[0]),
|
|
13877
|
+
ID: raw2[1],
|
|
13878
|
+
fork: raw2[2] ?? null,
|
|
13879
|
+
name: String(raw2[3]),
|
|
13880
|
+
sec: String(raw2[4] ?? ""),
|
|
13881
|
+
group: ""
|
|
13882
|
+
};
|
|
13883
|
+
}
|
|
13884
|
+
return null;
|
|
13885
|
+
}
|
|
13886
|
+
if (!raw2 || typeof raw2 !== "object" || raw2.ID == null || raw2.name == null) return null;
|
|
13887
|
+
return {
|
|
13888
|
+
raw: raw2,
|
|
13889
|
+
entity: String(raw2.entity ?? group ?? "records"),
|
|
13890
|
+
ID: raw2.ID,
|
|
13891
|
+
fork: raw2.fork ?? null,
|
|
13892
|
+
name: String(raw2.name),
|
|
13893
|
+
sec: String(raw2.sec ?? ""),
|
|
13894
|
+
group: String(raw2.group ?? group ?? "")
|
|
13895
|
+
};
|
|
13896
|
+
}
|
|
13897
|
+
function mergeDestination(raw2, resolver) {
|
|
13898
|
+
const direct = raw2 && typeof raw2 === "object" && !Array.isArray(raw2) ? raw2 : {};
|
|
13899
|
+
const resolved = typeof resolver === "function" ? resolver(raw2) : null;
|
|
13900
|
+
const destination = resolved && typeof resolved === "object" ? resolved : {};
|
|
13901
|
+
return Object.fromEntries(["href", "target", "invoke", "value"].flatMap((key) => {
|
|
13902
|
+
const value = destination[key] ?? direct[key];
|
|
13903
|
+
return value === void 0 ? [] : [[key, value]];
|
|
13904
|
+
}));
|
|
13905
|
+
}
|
|
13906
|
+
function normalizeServerColor(value) {
|
|
13907
|
+
const source = String(value ?? "").trim();
|
|
13908
|
+
if (!source) return null;
|
|
13909
|
+
const color = source.startsWith("#") ? source : `#${source}`;
|
|
13910
|
+
return /^#[\da-f]{3}(?:[\da-f]{3})?$/i.test(color) ? color : null;
|
|
13911
|
+
}
|
|
13912
|
+
function launcherLabels(input) {
|
|
13913
|
+
const labels = input && typeof input === "object" ? input : {};
|
|
13914
|
+
return {
|
|
13915
|
+
applications: String(labels.applications ?? "Applications"),
|
|
13916
|
+
recent: String(labels.recent ?? "Recent"),
|
|
13917
|
+
results: String(labels.results ?? "Top results"),
|
|
13918
|
+
entities: labels.entities && typeof labels.entities === "object" ? labels.entities : {},
|
|
13919
|
+
move: String(labels.move ?? "Move"),
|
|
13920
|
+
open: String(labels.open ?? "Open"),
|
|
13921
|
+
close: String(labels.close ?? "Close")
|
|
13922
|
+
};
|
|
12122
13923
|
}
|
|
12123
13924
|
|
|
12124
13925
|
// src/zeyos/select.js
|
|
@@ -12162,6 +13963,92 @@ function zeyosSelect(client, resource, opts = {}) {
|
|
|
12162
13963
|
select.ready = hydrateInitialValue(select, client, resource, requestedValue);
|
|
12163
13964
|
return select;
|
|
12164
13965
|
}
|
|
13966
|
+
function buildZeyosEntitySelectConfig(client, resource, opts = {}) {
|
|
13967
|
+
const {
|
|
13968
|
+
fields,
|
|
13969
|
+
labelKey = inferLabelKey(client, resource),
|
|
13970
|
+
valueKey = "ID",
|
|
13971
|
+
searchFields = inferSearchFields(client, resource),
|
|
13972
|
+
subtitleKey = null,
|
|
13973
|
+
moduleKey = () => resource,
|
|
13974
|
+
groupKey = null,
|
|
13975
|
+
none = true,
|
|
13976
|
+
recent = [],
|
|
13977
|
+
create = false,
|
|
13978
|
+
renderIcon = defaultEntityIcon,
|
|
13979
|
+
renderItem,
|
|
13980
|
+
renderValueAdornment,
|
|
13981
|
+
...rest
|
|
13982
|
+
} = opts;
|
|
13983
|
+
const projection = fields ?? defaultProjection(valueKey, labelKey, [
|
|
13984
|
+
...searchFields,
|
|
13985
|
+
...[subtitleKey, moduleKey, groupKey].filter((reader) => typeof reader === "string")
|
|
13986
|
+
]);
|
|
13987
|
+
const base = buildZeyosSelectConfig(client, resource, {
|
|
13988
|
+
...rest,
|
|
13989
|
+
fields: projection,
|
|
13990
|
+
labelKey,
|
|
13991
|
+
valueKey,
|
|
13992
|
+
searchFields,
|
|
13993
|
+
groupKey,
|
|
13994
|
+
clearable: none === false ? Boolean(rest.clearable) : true
|
|
13995
|
+
});
|
|
13996
|
+
base.recent = Array.isArray(recent) ? recent : [];
|
|
13997
|
+
const renderModuleIcon = (item, selected = false) => {
|
|
13998
|
+
if (renderIcon === null) return null;
|
|
13999
|
+
const module = readRecord(item, moduleKey) ?? resource;
|
|
14000
|
+
return renderIcon(item, { resource, module: String(module), selected });
|
|
14001
|
+
};
|
|
14002
|
+
base.renderItem = renderItem ?? ((item) => {
|
|
14003
|
+
const visual = renderModuleIcon(item);
|
|
14004
|
+
return h(
|
|
14005
|
+
"span",
|
|
14006
|
+
{ class: "zx-select__entity" },
|
|
14007
|
+
visual ? h("span", {
|
|
14008
|
+
class: "zx-select__entity-icon",
|
|
14009
|
+
ariaHidden: "true"
|
|
14010
|
+
}, visual) : null,
|
|
14011
|
+
h(
|
|
14012
|
+
"span",
|
|
14013
|
+
{ class: "zx-select__entity-copy" },
|
|
14014
|
+
h("span", { class: "zx-select__entity-label" }, String(readRecord(item, labelKey) ?? "")),
|
|
14015
|
+
subtitleKey && readRecord(item, subtitleKey) != null ? h("span", { class: "zx-select__entity-subtitle" }, String(readRecord(item, subtitleKey))) : null
|
|
14016
|
+
)
|
|
14017
|
+
);
|
|
14018
|
+
});
|
|
14019
|
+
base.renderValueAdornment = renderValueAdornment ?? ((item) => renderModuleIcon(item, true));
|
|
14020
|
+
if (none !== false) {
|
|
14021
|
+
base.noneLabel = typeof none === "string" ? none : rest.noneLabel ?? "No selection";
|
|
14022
|
+
base.renderNone = rest.renderNone ?? ((label) => h(
|
|
14023
|
+
"span",
|
|
14024
|
+
{ class: "zx-select__entity-none" },
|
|
14025
|
+
h("span", { class: "zx-select__entity-none-icon", ariaHidden: "true" }),
|
|
14026
|
+
h("span", {}, label)
|
|
14027
|
+
));
|
|
14028
|
+
}
|
|
14029
|
+
if (create) {
|
|
14030
|
+
const descriptor = create === true ? {} : create;
|
|
14031
|
+
const invoke = typeof descriptor.oninvoke === "function" ? descriptor.oninvoke : null;
|
|
14032
|
+
base.actions = [...Array.isArray(base.actions) ? base.actions : [], {
|
|
14033
|
+
id: descriptor.id ?? "create",
|
|
14034
|
+
label: descriptor.label ?? "Create new\u2026",
|
|
14035
|
+
group: descriptor.group ?? "New",
|
|
14036
|
+
icon: descriptor.icon ?? "plus",
|
|
14037
|
+
invoke: (detail) => invoke?.({ ...detail, resource })
|
|
14038
|
+
}];
|
|
14039
|
+
}
|
|
14040
|
+
return base;
|
|
14041
|
+
}
|
|
14042
|
+
function zeyosEntitySelect(client, resource, opts = {}) {
|
|
14043
|
+
const config2 = buildZeyosEntitySelectConfig(client, resource, opts);
|
|
14044
|
+
const requestedValue = opts.value ?? null;
|
|
14045
|
+
const select = (
|
|
14046
|
+
/** @type {Select & {ready: Promise<Select>}} */
|
|
14047
|
+
Select.entity(null, config2)
|
|
14048
|
+
);
|
|
14049
|
+
select.ready = hydrateInitialValue(select, client, resource, requestedValue);
|
|
14050
|
+
return select;
|
|
14051
|
+
}
|
|
12165
14052
|
function normalizeZeyosListResult(client, result) {
|
|
12166
14053
|
if (typeof client?.normalizeListResult === "function") {
|
|
12167
14054
|
return normalizeLocalListResult(client.normalizeListResult(result));
|
|
@@ -12178,12 +14065,12 @@ function normalizeLocalListResult(result) {
|
|
|
12178
14065
|
async function hydrateInitialValue(select, client, resource, value) {
|
|
12179
14066
|
if (value == null) return select;
|
|
12180
14067
|
const record = value && typeof value === "object" ? value : await connect(client).get(resource, value);
|
|
12181
|
-
const normalized =
|
|
14068
|
+
const normalized = normalizeRecord2(record);
|
|
12182
14069
|
select.setItems([normalized]);
|
|
12183
14070
|
select.set(normalized, { silent: true });
|
|
12184
14071
|
return select;
|
|
12185
14072
|
}
|
|
12186
|
-
function
|
|
14073
|
+
function normalizeRecord2(result) {
|
|
12187
14074
|
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
12188
14075
|
const nested = result.data;
|
|
12189
14076
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) return nested;
|
|
@@ -12191,6 +14078,14 @@ function normalizeRecord(result) {
|
|
|
12191
14078
|
}
|
|
12192
14079
|
throw new TypeError("ZeyOS get operation did not return a record");
|
|
12193
14080
|
}
|
|
14081
|
+
function readRecord(item, reader) {
|
|
14082
|
+
if (typeof reader === "function") return reader(item);
|
|
14083
|
+
if (item && typeof item === "object" && typeof reader === "string") return item[reader];
|
|
14084
|
+
return null;
|
|
14085
|
+
}
|
|
14086
|
+
function defaultEntityIcon(_item, context) {
|
|
14087
|
+
return moduleChip(String(context.module ?? "default"), { size: 20 });
|
|
14088
|
+
}
|
|
12194
14089
|
function defaultProjection(valueKey, labelKey, searchFields) {
|
|
12195
14090
|
const keys = [valueKey, labelKey, ...searchFields].filter((key) => typeof key === "string" && key);
|
|
12196
14091
|
return keys.length ? [...new Set(keys)] : void 0;
|
|
@@ -12270,7 +14165,7 @@ function zeyosForm(client, resource, opts = {}) {
|
|
|
12270
14165
|
await Promise.all(config2.entityFields.map(async ({ id, resource: referencedResource2 }) => {
|
|
12271
14166
|
const value = record[id];
|
|
12272
14167
|
if (value == null) return;
|
|
12273
|
-
const selected = value && typeof value === "object" ? value :
|
|
14168
|
+
const selected = value && typeof value === "object" ? value : normalizeRecord3(await facade.get(referencedResource2, value));
|
|
12274
14169
|
const select = Select.from(form.getField(id)?.getInput());
|
|
12275
14170
|
if (select instanceof Select) select.setItems([selected]);
|
|
12276
14171
|
}));
|
|
@@ -12279,7 +14174,7 @@ function zeyosForm(client, resource, opts = {}) {
|
|
|
12279
14174
|
};
|
|
12280
14175
|
const load = async (id) => {
|
|
12281
14176
|
if (id == null || id === "") throw new TypeError("zeyosForm.load() requires an id");
|
|
12282
|
-
const record =
|
|
14177
|
+
const record = normalizeRecord3(await facade.get(resource, id));
|
|
12283
14178
|
currentId = record.ID ?? id;
|
|
12284
14179
|
await applyRecord(record);
|
|
12285
14180
|
return record;
|
|
@@ -12298,7 +14193,7 @@ function zeyosForm(client, resource, opts = {}) {
|
|
|
12298
14193
|
throw error;
|
|
12299
14194
|
}
|
|
12300
14195
|
const result = currentId == null ? await facade.create(resource, payload) : await facade.update(resource, { ID: currentId, ...payload });
|
|
12301
|
-
const record =
|
|
14196
|
+
const record = normalizeRecord3(result);
|
|
12302
14197
|
currentId = record.ID ?? currentId;
|
|
12303
14198
|
if (opts.onSaved) {
|
|
12304
14199
|
try {
|
|
@@ -12376,7 +14271,7 @@ function normalizeColumns(value) {
|
|
|
12376
14271
|
[1, 2, 3].includes(number) ? number : 2
|
|
12377
14272
|
);
|
|
12378
14273
|
}
|
|
12379
|
-
function
|
|
14274
|
+
function normalizeRecord3(result) {
|
|
12380
14275
|
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
12381
14276
|
const nested = result.data;
|
|
12382
14277
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) return nested;
|
|
@@ -12571,7 +14466,11 @@ function copyFilters(filters) {
|
|
|
12571
14466
|
}
|
|
12572
14467
|
export {
|
|
12573
14468
|
ZEYOS_ICON_KIT,
|
|
14469
|
+
ZEYOS_LAUNCHER_APPLICATIONS,
|
|
12574
14470
|
buildListQuery,
|
|
14471
|
+
buildZeyosEntitySelectConfig,
|
|
14472
|
+
buildZeyosLauncherConfig,
|
|
14473
|
+
buildZeyosSelectConfig,
|
|
12575
14474
|
connect,
|
|
12576
14475
|
dataFilterStateToFilters,
|
|
12577
14476
|
dateToUnixSeconds,
|
|
@@ -12586,13 +14485,19 @@ export {
|
|
|
12586
14485
|
moduleInfo,
|
|
12587
14486
|
moduleKeys,
|
|
12588
14487
|
normalizeModuleName,
|
|
14488
|
+
normalizeZeyosLauncherApplications,
|
|
14489
|
+
normalizeZeyosLauncherRecords,
|
|
12589
14490
|
registerFieldType,
|
|
12590
14491
|
registerModules,
|
|
12591
14492
|
resolveFields,
|
|
12592
14493
|
tableSortToQuery,
|
|
12593
14494
|
unixSecondsToDate,
|
|
12594
14495
|
useZeyosIcons,
|
|
14496
|
+
zeyosActiveIdentifier,
|
|
14497
|
+
zeyosAppIcon,
|
|
14498
|
+
zeyosEntitySelect,
|
|
12595
14499
|
zeyosForm,
|
|
14500
|
+
zeyosLauncher,
|
|
12596
14501
|
zeyosModules,
|
|
12597
14502
|
zeyosSelect,
|
|
12598
14503
|
zeyosTable
|