@zeyos/zx 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/DESIGN-SYSTEM.md +28 -68
- package/README.md +16 -37
- 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/launcher/launcher.d.ts +65 -6
- package/dist/types/components/select/select.d.ts +51 -0
- package/dist/types/components/table/table.d.ts +4 -3
- package/dist/types/core/dom.d.ts +8 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/{components/app-rail → internal}/app-rail.d.ts +34 -19
- package/dist/types/zeyos/icons.d.ts +5 -0
- package/dist/types/zeyos/index.d.ts +2 -1
- package/dist/types/zeyos/launcher.d.ts +147 -0
- package/dist/types/zeyos/select.d.ts +39 -0
- package/dist/zx-zeyos.esm.js +1324 -46
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +400 -12
- package/dist/zx.esm.js +1059 -189
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +1059 -189
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +258 -145
- package/docs/llms.md +93 -112
- package/docs/llms.txt +16 -15
- package/package.json +8 -17
- 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/grid/grid.js +10 -3
- package/src/components/launcher/launcher.css +113 -3
- package/src/components/launcher/launcher.js +234 -26
- package/src/components/select/field-adapter.js +9 -4
- package/src/components/select/select.css +144 -10
- package/src/components/select/select.js +259 -27
- package/src/components/table/table.js +8 -7
- package/src/core/dom.js +20 -0
- package/src/index.js +2 -1
- package/src/{components/app-rail → internal}/app-rail.js +80 -30
- package/src/zeyos/icons.js +8 -3
- package/src/zeyos/index.js +7 -1
- package/src/zeyos/launcher.js +338 -0
- package/src/zeyos/modules.js +1 -0
- package/src/zeyos/select.js +115 -1
- package/styles/tokens/global.css +1 -1
- package/styles/tokens/modules.css +1 -0
- package/styles/zx.css +1 -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
|
@@ -524,6 +524,18 @@ function cloneValue(value) {
|
|
|
524
524
|
|
|
525
525
|
// src/core/dom.js
|
|
526
526
|
var activeRefs = null;
|
|
527
|
+
function safeHref(value) {
|
|
528
|
+
if (typeof value !== "string") return null;
|
|
529
|
+
const href = value.trim();
|
|
530
|
+
if (!href) return null;
|
|
531
|
+
try {
|
|
532
|
+
const protocol = new URL(href, globalThis.location?.href ?? "https://zx.invalid/").protocol;
|
|
533
|
+
if (["javascript:", "data:", "vbscript:"].includes(protocol.toLowerCase())) return null;
|
|
534
|
+
} catch {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
return href;
|
|
538
|
+
}
|
|
527
539
|
function h(tag, props, ...children) {
|
|
528
540
|
const element = document.createElement(tag);
|
|
529
541
|
let properties = props;
|
|
@@ -6533,7 +6545,18 @@ var PRIORITY_KEYS = [
|
|
|
6533
6545
|
"priority.high",
|
|
6534
6546
|
"priority.highest"
|
|
6535
6547
|
];
|
|
6536
|
-
var PRIORITY_FALLBACKS = ["Lowest", "Low", "
|
|
6548
|
+
var PRIORITY_FALLBACKS = ["Lowest", "Low", "Medium", "High", "Highest"];
|
|
6549
|
+
var DEFAULT_STATUSES = Object.freeze([
|
|
6550
|
+
["backlog", "status.backlog", "Backlog", "dotted", "muted"],
|
|
6551
|
+
["todo", "status.todo", "Todo", "ring", "neutral"],
|
|
6552
|
+
["in-progress", "status.inProgress", "In Progress", "progress", "warning"],
|
|
6553
|
+
["in-review", "status.inReview", "In Review", "review", "info"],
|
|
6554
|
+
["ready", "status.ready", "Ready to Merge", "ready", "accent"],
|
|
6555
|
+
["waiting", "status.waiting", "Waiting for feedback", "waiting", "warning"],
|
|
6556
|
+
["done", "status.done", "Done", "done", "info"],
|
|
6557
|
+
["canceled", "status.canceled", "Canceled", "canceled", "muted"],
|
|
6558
|
+
["duplicate", "status.duplicate", "Duplicate", "duplicate", "muted"]
|
|
6559
|
+
]);
|
|
6537
6560
|
var Select = class _Select extends Component {
|
|
6538
6561
|
static cssName = "select";
|
|
6539
6562
|
/** @type {SelectOptions} */
|
|
@@ -6544,6 +6567,10 @@ var Select = class _Select extends Component {
|
|
|
6544
6567
|
labelKey: "name",
|
|
6545
6568
|
renderItem: null,
|
|
6546
6569
|
renderValue: null,
|
|
6570
|
+
renderValueAdornment: null,
|
|
6571
|
+
renderNone: null,
|
|
6572
|
+
noneLabel: null,
|
|
6573
|
+
actions: [],
|
|
6547
6574
|
value: null,
|
|
6548
6575
|
disabled: false,
|
|
6549
6576
|
placeholder: "",
|
|
@@ -6561,6 +6588,7 @@ var Select = class _Select extends Component {
|
|
|
6561
6588
|
this._visibleItems = [];
|
|
6562
6589
|
this._fixedItems = [];
|
|
6563
6590
|
this._visibleFixed = [];
|
|
6591
|
+
this._actions = [];
|
|
6564
6592
|
this._selected = null;
|
|
6565
6593
|
this._optionEntries = [];
|
|
6566
6594
|
this._activeIndex = -1;
|
|
@@ -6599,6 +6627,12 @@ var Select = class _Select extends Component {
|
|
|
6599
6627
|
placeholder: String(this.options.placeholder ?? ""),
|
|
6600
6628
|
readOnly: !this.options.filter
|
|
6601
6629
|
});
|
|
6630
|
+
const valueAdornment = h("span", {
|
|
6631
|
+
ref: "valueAdornment",
|
|
6632
|
+
class: "zx-select__value-adornment",
|
|
6633
|
+
ariaHidden: "true",
|
|
6634
|
+
hidden: true
|
|
6635
|
+
});
|
|
6602
6636
|
const clear = h("button", {
|
|
6603
6637
|
ref: "clear",
|
|
6604
6638
|
class: "zx-icon-btn zx-select__clear",
|
|
@@ -6624,6 +6658,7 @@ var Select = class _Select extends Component {
|
|
|
6624
6658
|
const control = h(
|
|
6625
6659
|
"div",
|
|
6626
6660
|
{ ref: "control", class: "zx-select__control" },
|
|
6661
|
+
valueAdornment,
|
|
6627
6662
|
input,
|
|
6628
6663
|
clear,
|
|
6629
6664
|
loading,
|
|
@@ -6646,9 +6681,10 @@ var Select = class _Select extends Component {
|
|
|
6646
6681
|
this._visibleItems = this._items.slice();
|
|
6647
6682
|
this._fixedItems = Array.isArray(this.options.fixedItems) ? this.options.fixedItems.slice() : [];
|
|
6648
6683
|
this._visibleFixed = this._fixedItems.slice();
|
|
6684
|
+
this._actions = normalizeSelectActions(this.options.actions);
|
|
6649
6685
|
this._disabled = Boolean(this.options.disabled);
|
|
6650
6686
|
this._handleTypeahead = typeahead(
|
|
6651
|
-
() => this._optionEntries.filter((entry) => !entry.clear).map((entry) => entry.element),
|
|
6687
|
+
() => this._optionEntries.filter((entry) => !entry.clear && !entry.action).map((entry) => entry.element),
|
|
6652
6688
|
(_option, index) => {
|
|
6653
6689
|
if (!this._open) this.open();
|
|
6654
6690
|
this._setActive(this._itemEntryIndex(index));
|
|
@@ -6743,6 +6779,13 @@ var Select = class _Select extends Component {
|
|
|
6743
6779
|
this._position?.update();
|
|
6744
6780
|
return this;
|
|
6745
6781
|
}
|
|
6782
|
+
/** Replaces command rows without changing the selected value. @param {SelectAction[]} actions @returns {this} */
|
|
6783
|
+
setActions(actions) {
|
|
6784
|
+
this._actions = normalizeSelectActions(actions);
|
|
6785
|
+
this._renderOptions();
|
|
6786
|
+
this._position?.update();
|
|
6787
|
+
return this;
|
|
6788
|
+
}
|
|
6746
6789
|
/**
|
|
6747
6790
|
* Restores the configured initial value.
|
|
6748
6791
|
* @param {{silent?: boolean}} [options={}] Reset behavior.
|
|
@@ -6827,26 +6870,51 @@ var Select = class _Select extends Component {
|
|
|
6827
6870
|
value: null,
|
|
6828
6871
|
items: [],
|
|
6829
6872
|
renderItem(item) {
|
|
6830
|
-
return
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
class: "zx-select__priority-icon",
|
|
6835
|
-
dataset: { level: item.level },
|
|
6836
|
-
ariaHidden: "true"
|
|
6837
|
-
}),
|
|
6838
|
-
h("span", {}, String(item.name))
|
|
6839
|
-
);
|
|
6873
|
+
return priorityContent(item, true);
|
|
6874
|
+
},
|
|
6875
|
+
renderValueAdornment(item) {
|
|
6876
|
+
return priorityIndicator(item.level);
|
|
6840
6877
|
}
|
|
6841
6878
|
});
|
|
6842
|
-
select.setItems(PRIORITY_KEYS.map((key, level) =>
|
|
6843
|
-
|
|
6844
|
-
name
|
|
6845
|
-
level
|
|
6846
|
-
|
|
6879
|
+
select.setItems(PRIORITY_KEYS.map((key, level) => {
|
|
6880
|
+
let name = level === 2 ? select.msg("priority.medium") : key;
|
|
6881
|
+
if (name === "priority.medium") name = select._message(key, PRIORITY_FALLBACKS[level]);
|
|
6882
|
+
else if (level !== 2) name = select._message(key, PRIORITY_FALLBACKS[level]);
|
|
6883
|
+
return { ID: level, name, level };
|
|
6884
|
+
}));
|
|
6847
6885
|
select.set(requestedValue, { silent: true });
|
|
6848
6886
|
return select;
|
|
6849
6887
|
}
|
|
6888
|
+
/**
|
|
6889
|
+
* Creates a status picker with shape- and color-distinct workflow indicators.
|
|
6890
|
+
* @param {Element|string|null} target Component target.
|
|
6891
|
+
* @param {SelectOptions & {items?: Array<Record<string, any>>}} [options={}] Select overrides and status items.
|
|
6892
|
+
* @returns {Select}
|
|
6893
|
+
*/
|
|
6894
|
+
static status(target, options = {}) {
|
|
6895
|
+
const { items = null, value = null, ...rest } = options;
|
|
6896
|
+
const select = new _Select(target, {
|
|
6897
|
+
...rest,
|
|
6898
|
+
value: null,
|
|
6899
|
+
items: [],
|
|
6900
|
+
renderItem(item) {
|
|
6901
|
+
return statusContent(item);
|
|
6902
|
+
},
|
|
6903
|
+
renderValueAdornment(item) {
|
|
6904
|
+
return statusIndicator(item);
|
|
6905
|
+
}
|
|
6906
|
+
});
|
|
6907
|
+
const statuses = Array.isArray(items) ? items.map(normalizeStatusItem).filter(Boolean) : DEFAULT_STATUSES.map(([ID, key, fallback, indicator, tone], index) => ({
|
|
6908
|
+
ID,
|
|
6909
|
+
name: select._message(key, fallback),
|
|
6910
|
+
indicator,
|
|
6911
|
+
tone,
|
|
6912
|
+
shortcut: String(index + 1)
|
|
6913
|
+
}));
|
|
6914
|
+
select.setItems(statuses);
|
|
6915
|
+
select.set(value, { silent: true });
|
|
6916
|
+
return select;
|
|
6917
|
+
}
|
|
6850
6918
|
/**
|
|
6851
6919
|
* Creates the record-permission picker: the fixed Private and Public choices above the groups a
|
|
6852
6920
|
* record can be shared with. The value is the tri-state ZeyOS stores — `false` for private,
|
|
@@ -6859,12 +6927,21 @@ var Select = class _Select extends Component {
|
|
|
6859
6927
|
* @returns {Select}
|
|
6860
6928
|
*/
|
|
6861
6929
|
static permission(target, options = {}) {
|
|
6862
|
-
const {
|
|
6930
|
+
const {
|
|
6931
|
+
groups = [],
|
|
6932
|
+
value = true,
|
|
6933
|
+
valueKey = "ID",
|
|
6934
|
+
labelKey = "name",
|
|
6935
|
+
filter = "local",
|
|
6936
|
+
...rest
|
|
6937
|
+
} = options;
|
|
6863
6938
|
const loader = typeof groups === "function" ? groups : null;
|
|
6864
6939
|
const shareable = loader ? [] : groups;
|
|
6865
6940
|
const choices = [{ ID: false, name: "Private" }, { ID: true, name: "Public" }];
|
|
6866
6941
|
const select = new _Select(target, {
|
|
6867
|
-
|
|
6942
|
+
// An async group loader is the data source and cannot be disconnected by an incidental
|
|
6943
|
+
// `filter` override. Array-backed groups still honor local/disabled filtering.
|
|
6944
|
+
filter: loader ?? filter,
|
|
6868
6945
|
...rest,
|
|
6869
6946
|
value: null,
|
|
6870
6947
|
items: shareable,
|
|
@@ -6900,6 +6977,18 @@ var Select = class _Select extends Component {
|
|
|
6900
6977
|
/** @param {KeyboardEvent} event @returns {void} */
|
|
6901
6978
|
_onKeydown(event) {
|
|
6902
6979
|
if (this._disabled) return;
|
|
6980
|
+
if (this._open && isPrintable(event)) {
|
|
6981
|
+
const shortcut = this._optionEntries.findIndex((entry) => !entry.clear && !entry.action && String(
|
|
6982
|
+
/** @type {Record<string, any>|null} */
|
|
6983
|
+
entry.item?.shortcut ?? ""
|
|
6984
|
+
) === event.key);
|
|
6985
|
+
if (shortcut >= 0) {
|
|
6986
|
+
event.preventDefault();
|
|
6987
|
+
this._setActive(shortcut);
|
|
6988
|
+
this._chooseEntry(this._optionEntries[shortcut]);
|
|
6989
|
+
return;
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6903
6992
|
if (event.key === "ArrowDown") {
|
|
6904
6993
|
event.preventDefault();
|
|
6905
6994
|
if (!this._open) this.open();
|
|
@@ -6947,6 +7036,7 @@ var Select = class _Select extends Component {
|
|
|
6947
7036
|
/** @type {HTMLInputElement} */
|
|
6948
7037
|
this.refs.input.value
|
|
6949
7038
|
);
|
|
7039
|
+
this._syncValueAdornment();
|
|
6950
7040
|
if (!this._open) this.open();
|
|
6951
7041
|
if (this.options.filter === "local") {
|
|
6952
7042
|
this._visibleFixed = this._matchFixed(query);
|
|
@@ -6970,6 +7060,22 @@ var Select = class _Select extends Component {
|
|
|
6970
7060
|
/** @param {SelectOptionEntry|undefined} entry @returns {void} */
|
|
6971
7061
|
_chooseEntry(entry) {
|
|
6972
7062
|
if (!entry) return;
|
|
7063
|
+
if (entry.action) {
|
|
7064
|
+
const detail = {
|
|
7065
|
+
id: entry.action.id,
|
|
7066
|
+
action: entry.action,
|
|
7067
|
+
query: (
|
|
7068
|
+
/** @type {HTMLInputElement} */
|
|
7069
|
+
this.refs.input.value
|
|
7070
|
+
),
|
|
7071
|
+
select: this
|
|
7072
|
+
};
|
|
7073
|
+
const action = this.emit("action", detail);
|
|
7074
|
+
if (!action.defaultPrevented) entry.action.invoke?.(detail);
|
|
7075
|
+
this.close();
|
|
7076
|
+
this.focus();
|
|
7077
|
+
return;
|
|
7078
|
+
}
|
|
6973
7079
|
this.set(entry.clear ? null : entry.item);
|
|
6974
7080
|
this.close();
|
|
6975
7081
|
this.focus();
|
|
@@ -7003,7 +7109,7 @@ var Select = class _Select extends Component {
|
|
|
7003
7109
|
this._optionEntries = [];
|
|
7004
7110
|
let lastGroup = /* @__PURE__ */ Symbol("initial-group");
|
|
7005
7111
|
if (this.options.clearable) {
|
|
7006
|
-
this._appendOption(null, true, this.options.
|
|
7112
|
+
this._appendOption(null, true, this.options.noneLabel ?? this._message("select.none", "No selection"));
|
|
7007
7113
|
}
|
|
7008
7114
|
for (const item of this._visibleFixed) this._appendOption(item, false);
|
|
7009
7115
|
if (this._visibleFixed.length > 0 && this._visibleItems.length > 0 && !this.options.groupKey) {
|
|
@@ -7022,7 +7128,23 @@ var Select = class _Select extends Component {
|
|
|
7022
7128
|
}
|
|
7023
7129
|
this._appendOption(item, false);
|
|
7024
7130
|
}
|
|
7025
|
-
if (this.
|
|
7131
|
+
if (this._actions.length > 0) {
|
|
7132
|
+
if (this._visibleFixed.length > 0 || this._visibleItems.length > 0 || this.options.clearable) {
|
|
7133
|
+
list.append(h("div", { class: "zx-select__separator", role: "presentation" }));
|
|
7134
|
+
}
|
|
7135
|
+
let actionGroup = /* @__PURE__ */ Symbol("initial-action-group");
|
|
7136
|
+
for (const action of this._actions) {
|
|
7137
|
+
if (action.group && action.group !== actionGroup) {
|
|
7138
|
+
list.append(h("div", {
|
|
7139
|
+
class: "zx-select__group",
|
|
7140
|
+
role: "presentation"
|
|
7141
|
+
}, action.group));
|
|
7142
|
+
actionGroup = action.group;
|
|
7143
|
+
}
|
|
7144
|
+
this._appendAction(action);
|
|
7145
|
+
}
|
|
7146
|
+
}
|
|
7147
|
+
if (this._visibleItems.length === 0 && this._visibleFixed.length === 0 && this._actions.length === 0 && !this._loading) {
|
|
7026
7148
|
list.append(h("div", {
|
|
7027
7149
|
class: "zx-select__empty",
|
|
7028
7150
|
role: "status"
|
|
@@ -7034,7 +7156,7 @@ var Select = class _Select extends Component {
|
|
|
7034
7156
|
_appendOption(item, clear, label) {
|
|
7035
7157
|
const selected = clear ? this._selected === null : this._selected !== null && Object.is(this._itemValue(item), this.value);
|
|
7036
7158
|
const index = this._optionEntries.length;
|
|
7037
|
-
const content = clear ? String(label ?? "") : this._renderItem(item);
|
|
7159
|
+
const content = clear && typeof this.options.renderNone === "function" ? normalizeRenderedContent(this.options.renderNone(String(label ?? ""))) : clear ? String(label ?? "") : this._renderItem(item);
|
|
7038
7160
|
const option = h("div", {
|
|
7039
7161
|
class: "zx-select__option",
|
|
7040
7162
|
id: uid("zx-select-option"),
|
|
@@ -7044,16 +7166,37 @@ var Select = class _Select extends Component {
|
|
|
7044
7166
|
}, h("span", { class: "zx-select__option-content" }, content));
|
|
7045
7167
|
if (selected) option.append(icon("check", { size: 15 }));
|
|
7046
7168
|
this.refs.list.append(option);
|
|
7047
|
-
this._optionEntries.push({ item, clear, element: option });
|
|
7169
|
+
this._optionEntries.push({ item, clear, action: null, element: option });
|
|
7170
|
+
}
|
|
7171
|
+
/** @param {SelectAction} action @returns {void} */
|
|
7172
|
+
_appendAction(action) {
|
|
7173
|
+
const index = this._optionEntries.length;
|
|
7174
|
+
const visual = selectActionIcon(action.icon);
|
|
7175
|
+
const option = h("div", {
|
|
7176
|
+
class: "zx-select__option zx-select__action",
|
|
7177
|
+
id: uid("zx-select-option"),
|
|
7178
|
+
role: "option",
|
|
7179
|
+
ariaSelected: "false",
|
|
7180
|
+
dataset: { index, kind: "action" }
|
|
7181
|
+
}, h(
|
|
7182
|
+
"span",
|
|
7183
|
+
{ class: "zx-select__option-content zx-select__action-content" },
|
|
7184
|
+
visual ? h("span", { class: "zx-select__action-icon", ariaHidden: "true" }, visual) : null,
|
|
7185
|
+
h(
|
|
7186
|
+
"span",
|
|
7187
|
+
{ class: "zx-select__action-copy" },
|
|
7188
|
+
h("span", { class: "zx-select__action-label" }, action.label),
|
|
7189
|
+
action.description ? h("span", { class: "zx-select__action-description" }, action.description) : null
|
|
7190
|
+
)
|
|
7191
|
+
));
|
|
7192
|
+
this.refs.list.append(option);
|
|
7193
|
+
this._optionEntries.push({ item: null, clear: false, action, element: option });
|
|
7048
7194
|
}
|
|
7049
7195
|
/** @param {SelectItem|null} item @returns {Node|string} */
|
|
7050
7196
|
_renderItem(item) {
|
|
7051
7197
|
if (typeof this.options.renderItem === "function") {
|
|
7052
7198
|
const rendered = this.options.renderItem(item);
|
|
7053
|
-
return rendered
|
|
7054
|
-
/** @type {Node} */
|
|
7055
|
-
rendered
|
|
7056
|
-
) : String(rendered ?? "");
|
|
7199
|
+
return normalizeRenderedContent(rendered);
|
|
7057
7200
|
}
|
|
7058
7201
|
return this._itemLabel(item);
|
|
7059
7202
|
}
|
|
@@ -7124,9 +7267,29 @@ var Select = class _Select extends Component {
|
|
|
7124
7267
|
this.refs.input
|
|
7125
7268
|
);
|
|
7126
7269
|
input.value = this._selected === null ? "" : typeof this.options.renderValue === "function" ? String(this.options.renderValue(this._selected) ?? "") : this._itemLabel(this._selected);
|
|
7270
|
+
this._syncValueAdornment();
|
|
7127
7271
|
this.refs.clear.hidden = !this.options.clearable || this._selected === null;
|
|
7128
7272
|
}
|
|
7129
7273
|
/** @returns {void} */
|
|
7274
|
+
_syncValueAdornment() {
|
|
7275
|
+
const host = this.refs.valueAdornment;
|
|
7276
|
+
if (!host) return;
|
|
7277
|
+
const selectedLabel = this._selected === null ? "" : typeof this.options.renderValue === "function" ? String(this.options.renderValue(this._selected) ?? "") : this._itemLabel(this._selected);
|
|
7278
|
+
const query = String(
|
|
7279
|
+
/** @type {HTMLInputElement} */
|
|
7280
|
+
this.refs.input?.value ?? ""
|
|
7281
|
+
);
|
|
7282
|
+
if (this._selected === null || typeof this.options.renderValueAdornment !== "function" || Boolean(this.options.filter && query !== selectedLabel)) {
|
|
7283
|
+
host.replaceChildren();
|
|
7284
|
+
host.hidden = true;
|
|
7285
|
+
return;
|
|
7286
|
+
}
|
|
7287
|
+
const rendered = this.options.renderValueAdornment(this._selected);
|
|
7288
|
+
const content = normalizeRenderedContent(rendered);
|
|
7289
|
+
host.replaceChildren(content instanceof Node ? content : document.createTextNode(content));
|
|
7290
|
+
host.hidden = rendered == null || typeof content === "string" && content === "";
|
|
7291
|
+
}
|
|
7292
|
+
/** @returns {void} */
|
|
7130
7293
|
_syncDisabled() {
|
|
7131
7294
|
const input = (
|
|
7132
7295
|
/** @type {HTMLInputElement} */
|
|
@@ -7203,6 +7366,82 @@ var Select = class _Select extends Component {
|
|
|
7203
7366
|
return message === key ? fallback : message;
|
|
7204
7367
|
}
|
|
7205
7368
|
};
|
|
7369
|
+
function normalizeRenderedContent(rendered) {
|
|
7370
|
+
return rendered && typeof rendered === "object" && "nodeType" in rendered ? (
|
|
7371
|
+
/** @type {Node} */
|
|
7372
|
+
rendered
|
|
7373
|
+
) : String(rendered ?? "");
|
|
7374
|
+
}
|
|
7375
|
+
function normalizeSelectActions(actions) {
|
|
7376
|
+
if (!Array.isArray(actions)) return [];
|
|
7377
|
+
return actions.filter((action) => action && typeof action === "object" && action.id != null && action.label != null).map((action) => ({
|
|
7378
|
+
...action,
|
|
7379
|
+
label: String(action.label),
|
|
7380
|
+
description: action.description == null ? void 0 : String(action.description),
|
|
7381
|
+
group: action.group == null ? void 0 : String(action.group)
|
|
7382
|
+
}));
|
|
7383
|
+
}
|
|
7384
|
+
function selectActionIcon(visual) {
|
|
7385
|
+
if (typeof visual === "string") return icon(visual);
|
|
7386
|
+
if (visual instanceof Node) return visual.cloneNode(true);
|
|
7387
|
+
return null;
|
|
7388
|
+
}
|
|
7389
|
+
function priorityContent(item, label) {
|
|
7390
|
+
return h(
|
|
7391
|
+
"span",
|
|
7392
|
+
{ class: "zx-select__priority" },
|
|
7393
|
+
priorityIndicator(item.level),
|
|
7394
|
+
label ? h("span", {}, String(item.name)) : null
|
|
7395
|
+
);
|
|
7396
|
+
}
|
|
7397
|
+
function priorityIndicator(level) {
|
|
7398
|
+
const normalized = Math.max(0, Math.min(4, Math.trunc(Number(level) || 0)));
|
|
7399
|
+
return h("span", {
|
|
7400
|
+
class: "zx-select__priority-icon",
|
|
7401
|
+
dataset: { level: normalized },
|
|
7402
|
+
ariaHidden: "true"
|
|
7403
|
+
}, Array.from({ length: 5 }, (_, index) => h("span", {
|
|
7404
|
+
dataset: { filled: String(index <= normalized) }
|
|
7405
|
+
})));
|
|
7406
|
+
}
|
|
7407
|
+
function normalizeStatusItem(input) {
|
|
7408
|
+
if (!input || typeof input !== "object" || input.ID == null || input.name == null) return null;
|
|
7409
|
+
const indicators = ["dotted", "ring", "progress", "review", "ready", "waiting", "done", "canceled", "duplicate"];
|
|
7410
|
+
const tones = ["muted", "neutral", "warning", "info", "accent", "success", "danger"];
|
|
7411
|
+
return {
|
|
7412
|
+
...input,
|
|
7413
|
+
name: String(input.name),
|
|
7414
|
+
indicator: indicators.includes(String(input.indicator)) ? String(input.indicator) : "ring",
|
|
7415
|
+
tone: tones.includes(String(input.tone)) ? String(input.tone) : "muted",
|
|
7416
|
+
description: input.description == null ? "" : String(input.description),
|
|
7417
|
+
shortcut: input.shortcut == null ? "" : String(input.shortcut)
|
|
7418
|
+
};
|
|
7419
|
+
}
|
|
7420
|
+
function statusContent(item) {
|
|
7421
|
+
return h(
|
|
7422
|
+
"span",
|
|
7423
|
+
{ class: "zx-select__status" },
|
|
7424
|
+
statusIndicator(item),
|
|
7425
|
+
h(
|
|
7426
|
+
"span",
|
|
7427
|
+
{ class: "zx-select__status-copy" },
|
|
7428
|
+
h("span", { class: "zx-select__status-label" }, String(item.name)),
|
|
7429
|
+
item.description ? h("span", { class: "zx-select__status-description" }, item.description) : null
|
|
7430
|
+
),
|
|
7431
|
+
item.shortcut ? h("kbd", { class: "zx-select__status-shortcut" }, item.shortcut) : null
|
|
7432
|
+
);
|
|
7433
|
+
}
|
|
7434
|
+
function statusIndicator(item) {
|
|
7435
|
+
const indicator = String(item.indicator ?? "ring");
|
|
7436
|
+
const marker = h("span", {
|
|
7437
|
+
class: "zx-select__status-indicator",
|
|
7438
|
+
dataset: { indicator, tone: String(item.tone ?? "muted") },
|
|
7439
|
+
ariaHidden: "true"
|
|
7440
|
+
});
|
|
7441
|
+
if (indicator === "done") marker.append(icon("check", { size: 8 }));
|
|
7442
|
+
if (indicator === "canceled") marker.append(icon("x", { size: 7 }));
|
|
7443
|
+
return marker;
|
|
7444
|
+
}
|
|
7206
7445
|
function readItem(item, reader) {
|
|
7207
7446
|
if (typeof reader === "function") return reader(item);
|
|
7208
7447
|
if (item === null || typeof item !== "object") return item;
|
|
@@ -7215,10 +7454,11 @@ function isPrintable(event) {
|
|
|
7215
7454
|
// src/components/select/field-adapter.js
|
|
7216
7455
|
function registerSelectFieldAdapter() {
|
|
7217
7456
|
Field.register("zxselect", (field, options) => {
|
|
7218
|
-
const
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7457
|
+
const props = options.props ?? {};
|
|
7458
|
+
const preset = props.preset;
|
|
7459
|
+
const selectOptions = { ...props, value: options.value };
|
|
7460
|
+
delete selectOptions.preset;
|
|
7461
|
+
const select = field.own(preset === "priority" ? Select.priority(null, selectOptions) : preset === "status" ? Select.status(null, selectOptions) : new Select(null, selectOptions));
|
|
7222
7462
|
field.listen(select.el, "change", (event) => event.stopImmediatePropagation());
|
|
7223
7463
|
select.on("change", (event) => field.emit("change", { value: event.detail.value }));
|
|
7224
7464
|
return {
|
|
@@ -9268,12 +9508,14 @@ var Table = class extends Component {
|
|
|
9268
9508
|
}
|
|
9269
9509
|
/** @returns {HTMLElement} Table root. */
|
|
9270
9510
|
render() {
|
|
9511
|
+
const created = this.el === null;
|
|
9271
9512
|
const root = (
|
|
9272
9513
|
/** @type {HTMLElement} */
|
|
9273
9514
|
this.el ?? h("div")
|
|
9274
9515
|
);
|
|
9275
9516
|
this.el = root;
|
|
9276
|
-
this.
|
|
9517
|
+
this._createdRoot = created;
|
|
9518
|
+
this._original = created ? null : snapshotTarget(root);
|
|
9277
9519
|
this._columns = Array.isArray(this.options.columns) ? this.options.columns.map((column) => ({ ...column })) : [];
|
|
9278
9520
|
this._data = Array.isArray(this.options.data) ? [...this.options.data] : [];
|
|
9279
9521
|
this._selected = /* @__PURE__ */ new Set();
|
|
@@ -9348,8 +9590,8 @@ var Table = class extends Component {
|
|
|
9348
9590
|
}
|
|
9349
9591
|
/**
|
|
9350
9592
|
* 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
|
-
*
|
|
9593
|
+
* shown, and `aria-busy` is set — a lightweight enterprise-table busy and skeleton pattern.
|
|
9594
|
+
* Call `setLoading(false)` (or `setData`) when done.
|
|
9353
9595
|
* @param {boolean} [loading=true] Whether the table is loading.
|
|
9354
9596
|
* @returns {this}
|
|
9355
9597
|
*/
|
|
@@ -9898,9 +10140,8 @@ var Table = class extends Component {
|
|
|
9898
10140
|
this._teardownEdit();
|
|
9899
10141
|
this._width?.destroy();
|
|
9900
10142
|
this._width = null;
|
|
9901
|
-
if (this.
|
|
9902
|
-
|
|
9903
|
-
this.el.replaceChildren(...this._originalChildren);
|
|
10143
|
+
if (!this._restored && !this._createdRoot) {
|
|
10144
|
+
restoreTarget(this.el, this._original);
|
|
9904
10145
|
this._restored = true;
|
|
9905
10146
|
}
|
|
9906
10147
|
super.destroy();
|
|
@@ -10884,6 +11125,630 @@ function growStep(growing) {
|
|
|
10884
11125
|
return Number.isFinite(step) && step > 0 ? step : null;
|
|
10885
11126
|
}
|
|
10886
11127
|
|
|
11128
|
+
// src/components/launcher/launcher.js
|
|
11129
|
+
var Launcher = class extends Component {
|
|
11130
|
+
static cssName = "launcher";
|
|
11131
|
+
/** @type {Readonly<LauncherOptions>} */
|
|
11132
|
+
static defaults = {
|
|
11133
|
+
items: [],
|
|
11134
|
+
sources: [],
|
|
11135
|
+
query: "",
|
|
11136
|
+
debounce: 250,
|
|
11137
|
+
minQuery: 0,
|
|
11138
|
+
maxResults: 100,
|
|
11139
|
+
placeholder: "Search applications and records",
|
|
11140
|
+
label: "Launcher",
|
|
11141
|
+
closeLabel: "Close launcher",
|
|
11142
|
+
resultsLabel: "Launcher results",
|
|
11143
|
+
emptyText: "No results",
|
|
11144
|
+
loadingText: "Searching\u2026",
|
|
11145
|
+
shortcut: "mod+k",
|
|
11146
|
+
hints: { move: "Move", open: "Open", close: "Close" }
|
|
11147
|
+
};
|
|
11148
|
+
/**
|
|
11149
|
+
* Creates an owned launcher dialog or enhances an existing `<dialog>` target.
|
|
11150
|
+
* @param {HTMLDialogElement|string|null} [target=null] Dialog target, selector, or null.
|
|
11151
|
+
* @param {LauncherOptions} [options={}] Launcher options.
|
|
11152
|
+
*/
|
|
11153
|
+
constructor(target = null, options = {}) {
|
|
11154
|
+
super(target, options);
|
|
11155
|
+
}
|
|
11156
|
+
/** @returns {HTMLDialogElement} */
|
|
11157
|
+
render() {
|
|
11158
|
+
if (this.el !== null && !(this.el instanceof HTMLDialogElement)) {
|
|
11159
|
+
throw new TypeError("Launcher target must be a <dialog> element");
|
|
11160
|
+
}
|
|
11161
|
+
this._createdRoot = this.el === null;
|
|
11162
|
+
const dialog = (
|
|
11163
|
+
/** @type {HTMLDialogElement} */
|
|
11164
|
+
this.el ?? h("dialog")
|
|
11165
|
+
);
|
|
11166
|
+
this.el = dialog;
|
|
11167
|
+
this._original = this._createdRoot ? null : {
|
|
11168
|
+
attributes: Array.from(dialog.attributes, (attribute) => [attribute.name, attribute.value]),
|
|
11169
|
+
children: Array.from(dialog.childNodes)
|
|
11170
|
+
};
|
|
11171
|
+
this._items = normalizeItems(this.options.items);
|
|
11172
|
+
this._sources = normalizeSources(this.options.sources);
|
|
11173
|
+
this._sourceItems = /* @__PURE__ */ new Map();
|
|
11174
|
+
this._query = String(this.options.query ?? "");
|
|
11175
|
+
this._renderedQuery = null;
|
|
11176
|
+
this._results = [];
|
|
11177
|
+
this._active = -1;
|
|
11178
|
+
this._request = null;
|
|
11179
|
+
this._requestSequence = 0;
|
|
11180
|
+
this._queryTimer = null;
|
|
11181
|
+
this._busy = false;
|
|
11182
|
+
this._destroyed = false;
|
|
11183
|
+
const listId = uid("zx-launcher-results");
|
|
11184
|
+
const input = h("input", {
|
|
11185
|
+
ref: "input",
|
|
11186
|
+
class: "zx-launcher__input",
|
|
11187
|
+
type: "search",
|
|
11188
|
+
role: "combobox",
|
|
11189
|
+
autocomplete: "off",
|
|
11190
|
+
spellcheck: false,
|
|
11191
|
+
ariaAutocomplete: "list",
|
|
11192
|
+
ariaControls: listId,
|
|
11193
|
+
ariaExpanded: "false",
|
|
11194
|
+
ariaLabel: this.options.label,
|
|
11195
|
+
placeholder: this.options.placeholder,
|
|
11196
|
+
value: this._query
|
|
11197
|
+
});
|
|
11198
|
+
const close = h("button", {
|
|
11199
|
+
ref: "close",
|
|
11200
|
+
class: "zx-icon-btn zx-launcher__close",
|
|
11201
|
+
type: "button",
|
|
11202
|
+
ariaLabel: this.options.closeLabel
|
|
11203
|
+
}, icon("x"));
|
|
11204
|
+
const status = h("div", {
|
|
11205
|
+
ref: "status",
|
|
11206
|
+
class: "zx-launcher__status",
|
|
11207
|
+
role: "status",
|
|
11208
|
+
ariaLive: "polite"
|
|
11209
|
+
});
|
|
11210
|
+
const results = h("div", {
|
|
11211
|
+
ref: "results",
|
|
11212
|
+
class: "zx-launcher__results",
|
|
11213
|
+
id: listId,
|
|
11214
|
+
role: "listbox",
|
|
11215
|
+
ariaLabel: this.options.resultsLabel
|
|
11216
|
+
});
|
|
11217
|
+
const shortcut = this.options.shortcut === "mod+k" ? h("kbd", {
|
|
11218
|
+
class: "zx-launcher__shortcut",
|
|
11219
|
+
ariaHidden: "true"
|
|
11220
|
+
}, launcherShortcutLabel()) : null;
|
|
11221
|
+
const footer = launcherHints(this.options.hints);
|
|
11222
|
+
dialog.setAttribute("aria-label", String(this.options.label));
|
|
11223
|
+
dialog.replaceChildren(
|
|
11224
|
+
h(
|
|
11225
|
+
"div",
|
|
11226
|
+
{ class: "zx-launcher__surface" },
|
|
11227
|
+
h("div", {
|
|
11228
|
+
class: "zx-launcher__search",
|
|
11229
|
+
dataset: { shortcut: shortcut ? "true" : "false" }
|
|
11230
|
+
}, icon("search"), input, shortcut, close),
|
|
11231
|
+
status,
|
|
11232
|
+
results,
|
|
11233
|
+
footer
|
|
11234
|
+
)
|
|
11235
|
+
);
|
|
11236
|
+
dialog.dataset.state = "closed";
|
|
11237
|
+
if (this._createdRoot) document.body.append(dialog);
|
|
11238
|
+
this.listen(input, "input", () => {
|
|
11239
|
+
this._query = /** @type {HTMLInputElement} */
|
|
11240
|
+
input.value;
|
|
11241
|
+
this.emit("query", { query: this._query.trim() });
|
|
11242
|
+
this._refresh();
|
|
11243
|
+
});
|
|
11244
|
+
this.listen(input, "keydown", (event) => this._onKeydown(
|
|
11245
|
+
/** @type {KeyboardEvent} */
|
|
11246
|
+
event
|
|
11247
|
+
));
|
|
11248
|
+
this.listen(close, "click", () => this.close());
|
|
11249
|
+
this.listen(results, "click", (event) => {
|
|
11250
|
+
const option = (
|
|
11251
|
+
/** @type {Element|null} */
|
|
11252
|
+
event.target?.closest?.("[data-launcher-index]")
|
|
11253
|
+
);
|
|
11254
|
+
if (!option || !results.contains(option)) return;
|
|
11255
|
+
this._activate(Number(option.getAttribute("data-launcher-index")), event);
|
|
11256
|
+
});
|
|
11257
|
+
this.listen(results, "pointermove", (event) => {
|
|
11258
|
+
const option = (
|
|
11259
|
+
/** @type {Element|null} */
|
|
11260
|
+
event.target?.closest?.("[data-launcher-index]")
|
|
11261
|
+
);
|
|
11262
|
+
if (!option || !results.contains(option)) return;
|
|
11263
|
+
const index = Number(option.getAttribute("data-launcher-index"));
|
|
11264
|
+
if (!Number.isInteger(index) || this._results[index]?.disabled || index === this._active) return;
|
|
11265
|
+
this._active = index;
|
|
11266
|
+
this._syncActive();
|
|
11267
|
+
});
|
|
11268
|
+
this.listen(dialog, "cancel", (event) => {
|
|
11269
|
+
event.preventDefault();
|
|
11270
|
+
this.close();
|
|
11271
|
+
});
|
|
11272
|
+
this.listen(dialog, "close", () => {
|
|
11273
|
+
dialog.dataset.state = "closed";
|
|
11274
|
+
input.setAttribute("aria-expanded", "false");
|
|
11275
|
+
this._abortRequest();
|
|
11276
|
+
this.emit("close");
|
|
11277
|
+
});
|
|
11278
|
+
this.listen(document, "keydown", (event) => this._onDocumentKeydown(
|
|
11279
|
+
/** @type {KeyboardEvent} */
|
|
11280
|
+
event
|
|
11281
|
+
));
|
|
11282
|
+
this._renderResults();
|
|
11283
|
+
return dialog;
|
|
11284
|
+
}
|
|
11285
|
+
/** Opens the launcher and focuses/selects its query. @returns {this} @fires Launcher#open */
|
|
11286
|
+
open() {
|
|
11287
|
+
if (this.isOpen() || this._destroyed) return this;
|
|
11288
|
+
this.el.showModal();
|
|
11289
|
+
this.el.dataset.state = "open";
|
|
11290
|
+
this.refs.input.setAttribute("aria-expanded", "true");
|
|
11291
|
+
this._refresh();
|
|
11292
|
+
queueMicrotask(() => {
|
|
11293
|
+
if (!this.isOpen()) return;
|
|
11294
|
+
this.refs.input.focus();
|
|
11295
|
+
this.refs.input.select();
|
|
11296
|
+
});
|
|
11297
|
+
this.emit("open");
|
|
11298
|
+
return this;
|
|
11299
|
+
}
|
|
11300
|
+
/** Closes the launcher. @returns {this} */
|
|
11301
|
+
close() {
|
|
11302
|
+
if (this.isOpen()) this.el.close();
|
|
11303
|
+
return this;
|
|
11304
|
+
}
|
|
11305
|
+
/** Toggles the launcher. @returns {this} */
|
|
11306
|
+
toggle() {
|
|
11307
|
+
return this.isOpen() ? this.close() : this.open();
|
|
11308
|
+
}
|
|
11309
|
+
/** Reports whether the launcher is open. @returns {boolean} */
|
|
11310
|
+
isOpen() {
|
|
11311
|
+
return Boolean(this.el.open);
|
|
11312
|
+
}
|
|
11313
|
+
/** Focuses and selects the query field. @returns {this} */
|
|
11314
|
+
focus() {
|
|
11315
|
+
this.refs.input.focus();
|
|
11316
|
+
this.refs.input.select();
|
|
11317
|
+
return this;
|
|
11318
|
+
}
|
|
11319
|
+
/** Replaces the query and refreshes local and remote results. @param {string} query @returns {this} */
|
|
11320
|
+
setQuery(query) {
|
|
11321
|
+
this._query = String(query ?? "");
|
|
11322
|
+
this.refs.input.value = this._query;
|
|
11323
|
+
this.emit("query", { query: this._query.trim() });
|
|
11324
|
+
this._refresh();
|
|
11325
|
+
return this;
|
|
11326
|
+
}
|
|
11327
|
+
/** Returns the untrimmed query. @returns {string} */
|
|
11328
|
+
getQuery() {
|
|
11329
|
+
return this._query;
|
|
11330
|
+
}
|
|
11331
|
+
/** Replaces local catalogue items. @param {LauncherItem[]} items @returns {this} */
|
|
11332
|
+
setItems(items) {
|
|
11333
|
+
this._items = normalizeItems(items);
|
|
11334
|
+
this._renderResults();
|
|
11335
|
+
return this;
|
|
11336
|
+
}
|
|
11337
|
+
/** Marks one local item as the current destination. @param {string|number|null} id Item id. @returns {this} */
|
|
11338
|
+
setCurrent(id) {
|
|
11339
|
+
const key = id == null ? null : String(id);
|
|
11340
|
+
this._items = this._items.map((item) => ({ ...item, current: key !== null && String(item.id) === key }));
|
|
11341
|
+
this._renderResults();
|
|
11342
|
+
return this;
|
|
11343
|
+
}
|
|
11344
|
+
/** Replaces asynchronous sources and aborts work from the old set. @param {LauncherSource[]} sources @returns {this} */
|
|
11345
|
+
setSources(sources) {
|
|
11346
|
+
this._abortRequest();
|
|
11347
|
+
this._sources = normalizeSources(sources);
|
|
11348
|
+
this._sourceItems.clear();
|
|
11349
|
+
this._refresh();
|
|
11350
|
+
return this;
|
|
11351
|
+
}
|
|
11352
|
+
/** Aborts sources and removes the owned dialog. @returns {void} */
|
|
11353
|
+
destroy() {
|
|
11354
|
+
if (this._destroyed) return;
|
|
11355
|
+
this._destroyed = true;
|
|
11356
|
+
const original = this._original;
|
|
11357
|
+
this._abortRequest();
|
|
11358
|
+
if (this.isOpen()) this.el.close();
|
|
11359
|
+
super.destroy();
|
|
11360
|
+
if (!this._createdRoot && original) {
|
|
11361
|
+
for (const attribute of Array.from(this.el.attributes)) this.el.removeAttribute(attribute.name);
|
|
11362
|
+
for (const [name, value] of original.attributes) this.el.setAttribute(name, value);
|
|
11363
|
+
this.el.replaceChildren(...original.children);
|
|
11364
|
+
}
|
|
11365
|
+
}
|
|
11366
|
+
/** @returns {void} */
|
|
11367
|
+
_refresh() {
|
|
11368
|
+
this._sourceItems.clear();
|
|
11369
|
+
this._renderResults();
|
|
11370
|
+
this._abortRequest();
|
|
11371
|
+
if (this._sources.length === 0) return;
|
|
11372
|
+
const delay = Math.max(0, Number(this.options.debounce) || 0);
|
|
11373
|
+
this._queryTimer = setTimeout(() => this._loadSources(), delay);
|
|
11374
|
+
}
|
|
11375
|
+
/** @returns {Promise<void>} */
|
|
11376
|
+
async _loadSources() {
|
|
11377
|
+
this._queryTimer = null;
|
|
11378
|
+
const query = this._query.trim();
|
|
11379
|
+
const sources = this._sources.filter((source) => visibleForQuery(source.when, query) && query.length >= Math.max(Number(this.options.minQuery) || 0, Number(source.minQuery) || 0));
|
|
11380
|
+
if (sources.length === 0) return;
|
|
11381
|
+
const request = new AbortController();
|
|
11382
|
+
const sequence = ++this._requestSequence;
|
|
11383
|
+
this._request = request;
|
|
11384
|
+
this._setBusy(true);
|
|
11385
|
+
await Promise.all(sources.map(async (source) => {
|
|
11386
|
+
try {
|
|
11387
|
+
const loaded = await source.load(query, { signal: request.signal });
|
|
11388
|
+
if (request.signal.aborted || sequence !== this._requestSequence) return;
|
|
11389
|
+
this._sourceItems.set(source.id, normalizeItems(loaded).map((item, itemOrder) => ({
|
|
11390
|
+
...item,
|
|
11391
|
+
_source: source.id,
|
|
11392
|
+
_sourceOrder: source.order === "source",
|
|
11393
|
+
group: item.group ?? source.label ?? "",
|
|
11394
|
+
itemOrder: item.itemOrder ?? (source.order === "source" ? itemOrder : void 0)
|
|
11395
|
+
})));
|
|
11396
|
+
this._renderResults();
|
|
11397
|
+
} catch (error) {
|
|
11398
|
+
if (request.signal.aborted || sequence !== this._requestSequence) return;
|
|
11399
|
+
this.emit("error", { source, error });
|
|
11400
|
+
}
|
|
11401
|
+
}));
|
|
11402
|
+
if (request.signal.aborted || sequence !== this._requestSequence) return;
|
|
11403
|
+
this._request = null;
|
|
11404
|
+
this._setBusy(false);
|
|
11405
|
+
}
|
|
11406
|
+
/** @param {boolean} busy @returns {void} */
|
|
11407
|
+
_setBusy(busy) {
|
|
11408
|
+
this._busy = busy;
|
|
11409
|
+
this.el.setAttribute("aria-busy", String(busy));
|
|
11410
|
+
this._renderStatus();
|
|
11411
|
+
}
|
|
11412
|
+
/** @returns {void} */
|
|
11413
|
+
_abortRequest() {
|
|
11414
|
+
if (this._queryTimer !== null) clearTimeout(this._queryTimer);
|
|
11415
|
+
this._queryTimer = null;
|
|
11416
|
+
this._request?.abort();
|
|
11417
|
+
this._request = null;
|
|
11418
|
+
this._requestSequence += 1;
|
|
11419
|
+
if (this._busy) this._setBusy(false);
|
|
11420
|
+
}
|
|
11421
|
+
/** @returns {void} */
|
|
11422
|
+
_renderResults() {
|
|
11423
|
+
const queryKey = this._query;
|
|
11424
|
+
const activeId = this._renderedQuery === queryKey && this._active >= 0 && this._results[this._active] ? String(this._results[this._active].id) : null;
|
|
11425
|
+
const query = this._query.trim();
|
|
11426
|
+
const local = this._items.filter((item) => visibleForQuery(item.when, query));
|
|
11427
|
+
const rankedLocal = rankLauncherItems(local, query, this.options.maxResults);
|
|
11428
|
+
const remote = this._sources.flatMap((source) => {
|
|
11429
|
+
const loaded = this._sourceItems.get(String(source.id)) ?? [];
|
|
11430
|
+
return source.order === "source" ? loaded.filter((item) => visibleForQuery(item.when, query)) : rankLauncherItems(
|
|
11431
|
+
loaded.filter((item) => visibleForQuery(item.when, query)),
|
|
11432
|
+
query,
|
|
11433
|
+
this.options.maxResults
|
|
11434
|
+
);
|
|
11435
|
+
});
|
|
11436
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11437
|
+
const combined = [...rankedLocal.map((item) => ({ ...item, _source: null })), ...remote].filter((item) => {
|
|
11438
|
+
const key = String(item.id);
|
|
11439
|
+
if (seen.has(key)) return false;
|
|
11440
|
+
seen.add(key);
|
|
11441
|
+
return true;
|
|
11442
|
+
}).slice(0, Math.max(0, Number(this.options.maxResults) || 0));
|
|
11443
|
+
const groups = /* @__PURE__ */ new Map();
|
|
11444
|
+
for (const item of combined) {
|
|
11445
|
+
const group = String(item.group ?? "");
|
|
11446
|
+
if (!groups.has(group)) groups.set(group, []);
|
|
11447
|
+
groups.get(group).push(item);
|
|
11448
|
+
}
|
|
11449
|
+
this._results = [...groups.values()].flat();
|
|
11450
|
+
this._renderedQuery = queryKey;
|
|
11451
|
+
const preserved = activeId === null ? -1 : this._results.findIndex((item) => !item.disabled && String(item.id) === activeId);
|
|
11452
|
+
this._active = preserved >= 0 ? preserved : this._results.findIndex((item) => !item.disabled);
|
|
11453
|
+
const nodes = [];
|
|
11454
|
+
let index = 0;
|
|
11455
|
+
for (const [group, items] of groups) {
|
|
11456
|
+
const options = items.map((item) => this._option(item, index++));
|
|
11457
|
+
if (group) nodes.push(h("div", {
|
|
11458
|
+
class: "zx-launcher__group",
|
|
11459
|
+
role: "group",
|
|
11460
|
+
ariaLabel: group,
|
|
11461
|
+
dataset: { layout: items.every((item) => item.kind === "application") ? "grid" : "list" }
|
|
11462
|
+
}, h("div", { class: "zx-launcher__group-label", ariaHidden: "true" }, group), options));
|
|
11463
|
+
else nodes.push(...options);
|
|
11464
|
+
}
|
|
11465
|
+
this.refs.results.replaceChildren(...nodes);
|
|
11466
|
+
this._syncActive();
|
|
11467
|
+
this._renderStatus();
|
|
11468
|
+
}
|
|
11469
|
+
/** @param {RankedLauncherItem} item @param {number} index @returns {HTMLElement} */
|
|
11470
|
+
_option(item, index) {
|
|
11471
|
+
const href = safeHref(item.href);
|
|
11472
|
+
const tag = href ? "a" : "button";
|
|
11473
|
+
const children = [];
|
|
11474
|
+
const visual = launcherItemIcon(item);
|
|
11475
|
+
if (visual) children.push(h("span", {
|
|
11476
|
+
class: "zx-launcher__icon",
|
|
11477
|
+
ariaHidden: "true"
|
|
11478
|
+
}, visual));
|
|
11479
|
+
children.push(h(
|
|
11480
|
+
"span",
|
|
11481
|
+
{ class: "zx-launcher__copy" },
|
|
11482
|
+
h("span", { class: "zx-launcher__label" }, item.label),
|
|
11483
|
+
item.description ? h("span", { class: "zx-launcher__description" }, item.description) : null
|
|
11484
|
+
));
|
|
11485
|
+
if (item.badge != null) children.push(h("span", { class: "zx-launcher__badge" }, String(item.badge)));
|
|
11486
|
+
return (
|
|
11487
|
+
/** @type {HTMLElement} */
|
|
11488
|
+
h(tag, {
|
|
11489
|
+
class: "zx-launcher__option",
|
|
11490
|
+
type: tag === "button" ? "button" : null,
|
|
11491
|
+
href,
|
|
11492
|
+
target: item.target ?? null,
|
|
11493
|
+
rel: tag === "a" ? "noopener" : null,
|
|
11494
|
+
role: "option",
|
|
11495
|
+
id: uid("zx-launcher-option"),
|
|
11496
|
+
tabIndex: -1,
|
|
11497
|
+
ariaSelected: "false",
|
|
11498
|
+
ariaDisabled: item.disabled ? "true" : null,
|
|
11499
|
+
ariaCurrent: item.current ? "page" : null,
|
|
11500
|
+
dataset: {
|
|
11501
|
+
launcherIndex: index,
|
|
11502
|
+
pinned: item.pinned !== void 0 && item.pinned !== false ? "true" : null,
|
|
11503
|
+
kind: normalizeLauncherKind(item.kind)
|
|
11504
|
+
}
|
|
11505
|
+
}, children)
|
|
11506
|
+
);
|
|
11507
|
+
}
|
|
11508
|
+
/** @returns {void} */
|
|
11509
|
+
_renderStatus() {
|
|
11510
|
+
if (this._busy) this.refs.status.textContent = String(this.options.loadingText);
|
|
11511
|
+
else if (this._results.length === 0) this.refs.status.textContent = String(this.options.emptyText);
|
|
11512
|
+
else this.refs.status.textContent = "";
|
|
11513
|
+
this.refs.status.hidden = !this.refs.status.textContent;
|
|
11514
|
+
}
|
|
11515
|
+
/** @returns {void} */
|
|
11516
|
+
_syncActive() {
|
|
11517
|
+
const options = [...this.refs.results.querySelectorAll("[data-launcher-index]")];
|
|
11518
|
+
for (const option of options) {
|
|
11519
|
+
const selected = Number(option.getAttribute("data-launcher-index")) === this._active;
|
|
11520
|
+
option.setAttribute("aria-selected", String(selected));
|
|
11521
|
+
}
|
|
11522
|
+
const active = options.find((option) => Number(option.getAttribute("data-launcher-index")) === this._active);
|
|
11523
|
+
if (active) this.refs.input.setAttribute("aria-activedescendant", active.id);
|
|
11524
|
+
else this.refs.input.removeAttribute("aria-activedescendant");
|
|
11525
|
+
}
|
|
11526
|
+
/** @param {KeyboardEvent} event @returns {void} */
|
|
11527
|
+
_onKeydown(event) {
|
|
11528
|
+
if (event.key === "Escape") {
|
|
11529
|
+
this.close();
|
|
11530
|
+
return;
|
|
11531
|
+
}
|
|
11532
|
+
if (event.key === "Enter") {
|
|
11533
|
+
if (this._active < 0) return;
|
|
11534
|
+
event.preventDefault();
|
|
11535
|
+
const option = this.refs.results.querySelector(`[data-launcher-index="${this._active}"]`);
|
|
11536
|
+
option?.click();
|
|
11537
|
+
return;
|
|
11538
|
+
}
|
|
11539
|
+
const enabled = this._results.map((item, index) => item.disabled ? -1 : index).filter((index) => index >= 0);
|
|
11540
|
+
if (enabled.length === 0) return;
|
|
11541
|
+
if (/^Arrow(?:Left|Right|Up|Down)$/.test(event.key)) {
|
|
11542
|
+
const next = this._spatialIndex(event.key, enabled);
|
|
11543
|
+
if (next !== null) {
|
|
11544
|
+
event.preventDefault();
|
|
11545
|
+
this._active = next;
|
|
11546
|
+
this._syncActive();
|
|
11547
|
+
this.refs.results.querySelector(`[data-launcher-index="${this._active}"]`)?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
11548
|
+
return;
|
|
11549
|
+
}
|
|
11550
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") return;
|
|
11551
|
+
}
|
|
11552
|
+
let position2 = enabled.indexOf(this._active);
|
|
11553
|
+
if (event.key === "Home" || event.key === "PageUp") position2 = 0;
|
|
11554
|
+
else if (event.key === "End" || event.key === "PageDown") position2 = enabled.length - 1;
|
|
11555
|
+
else if (event.key === "ArrowDown") position2 = (position2 + 1 + enabled.length) % enabled.length;
|
|
11556
|
+
else if (event.key === "ArrowUp") position2 = (position2 - 1 + enabled.length) % enabled.length;
|
|
11557
|
+
else return;
|
|
11558
|
+
event.preventDefault();
|
|
11559
|
+
this._active = enabled[position2];
|
|
11560
|
+
this._syncActive();
|
|
11561
|
+
this.refs.results.querySelector(`[data-launcher-index="${this._active}"]`)?.scrollIntoView({ block: "nearest" });
|
|
11562
|
+
}
|
|
11563
|
+
/** @param {string} direction @param {number[]} enabled @returns {number|null} */
|
|
11564
|
+
_spatialIndex(direction, enabled) {
|
|
11565
|
+
const current = this.refs.results.querySelector(`[data-launcher-index="${this._active}"]`);
|
|
11566
|
+
if (!(current instanceof HTMLElement)) return null;
|
|
11567
|
+
if ((direction === "ArrowLeft" || direction === "ArrowRight") && this._results[this._active]?.kind !== "application") return null;
|
|
11568
|
+
const candidates = [...this.refs.results.querySelectorAll("[data-launcher-index]")].filter((option) => enabled.includes(Number(option.getAttribute("data-launcher-index"))) && (direction !== "ArrowLeft" && direction !== "ArrowRight" || option.getAttribute("data-kind") === "application"));
|
|
11569
|
+
const next = findSpatialLauncherOption(candidates, current, direction);
|
|
11570
|
+
return next ? Number(next.getAttribute("data-launcher-index")) : null;
|
|
11571
|
+
}
|
|
11572
|
+
/** @param {number} index @param {Event} event @returns {void} */
|
|
11573
|
+
_activate(index, event) {
|
|
11574
|
+
const item = this._results[index];
|
|
11575
|
+
if (!item || item.disabled) {
|
|
11576
|
+
event.preventDefault();
|
|
11577
|
+
return;
|
|
11578
|
+
}
|
|
11579
|
+
const selected = this.emit("select", {
|
|
11580
|
+
item,
|
|
11581
|
+
value: item.value ?? item.id,
|
|
11582
|
+
source: item._source ?? null,
|
|
11583
|
+
query: this._query.trim()
|
|
11584
|
+
});
|
|
11585
|
+
if (selected.defaultPrevented) {
|
|
11586
|
+
event.preventDefault();
|
|
11587
|
+
return;
|
|
11588
|
+
}
|
|
11589
|
+
if (!item.href) {
|
|
11590
|
+
event.preventDefault();
|
|
11591
|
+
item.invoke?.();
|
|
11592
|
+
}
|
|
11593
|
+
this.close();
|
|
11594
|
+
}
|
|
11595
|
+
/** @param {KeyboardEvent} event @returns {void} */
|
|
11596
|
+
_onDocumentKeydown(event) {
|
|
11597
|
+
if (this.options.shortcut !== "mod+k" || event.isComposing || event.key.toLowerCase() !== "k" || Number(event.metaKey) + Number(event.ctrlKey) !== 1 || event.altKey || event.shiftKey) return;
|
|
11598
|
+
const active = document.activeElement;
|
|
11599
|
+
if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement || active instanceof HTMLSelectElement || active instanceof HTMLElement && active.isContentEditable) return;
|
|
11600
|
+
const modal = document.querySelector("dialog:modal");
|
|
11601
|
+
if (modal && modal !== this.el) return;
|
|
11602
|
+
event.preventDefault();
|
|
11603
|
+
this.open();
|
|
11604
|
+
}
|
|
11605
|
+
};
|
|
11606
|
+
function launcherShortcutLabel() {
|
|
11607
|
+
const platform = String(
|
|
11608
|
+
/** @type {any} */
|
|
11609
|
+
navigator.userAgentData?.platform ?? navigator.platform ?? ""
|
|
11610
|
+
);
|
|
11611
|
+
return /mac|iphone|ipad/i.test(platform) ? "\u2318 K" : "Ctrl K";
|
|
11612
|
+
}
|
|
11613
|
+
function launcherHints(hints) {
|
|
11614
|
+
if (hints === false) return null;
|
|
11615
|
+
const labels = hints && typeof hints === "object" ? hints : {};
|
|
11616
|
+
return h(
|
|
11617
|
+
"footer",
|
|
11618
|
+
{ class: "zx-launcher__hints", ariaHidden: "true" },
|
|
11619
|
+
h("span", {}, h("kbd", {}, "\u2191\u2193\u2190\u2192"), labels.move ?? "Move"),
|
|
11620
|
+
h("span", {}, h("kbd", {}, "\u21B5"), labels.open ?? "Open"),
|
|
11621
|
+
h("span", {}, h("kbd", {}, "Esc"), labels.close ?? "Close")
|
|
11622
|
+
);
|
|
11623
|
+
}
|
|
11624
|
+
function launcherItemIcon(item) {
|
|
11625
|
+
let visual = item.icon;
|
|
11626
|
+
if (typeof visual === "function") visual = visual(item);
|
|
11627
|
+
if (typeof visual === "string") return icon(visual);
|
|
11628
|
+
if (visual instanceof Node) return visual.cloneNode(true);
|
|
11629
|
+
if (visual && typeof visual === "object" && typeof /** @type {any} */
|
|
11630
|
+
visual.toElement === "function") {
|
|
11631
|
+
const element = (
|
|
11632
|
+
/** @type {any} */
|
|
11633
|
+
visual.toElement()
|
|
11634
|
+
);
|
|
11635
|
+
return element instanceof Node ? element.cloneNode(true) : null;
|
|
11636
|
+
}
|
|
11637
|
+
return null;
|
|
11638
|
+
}
|
|
11639
|
+
function normalizeLauncherKind(kind) {
|
|
11640
|
+
return kind === "application" || kind === "record" ? kind : "action";
|
|
11641
|
+
}
|
|
11642
|
+
function visibleForQuery(when, query) {
|
|
11643
|
+
if (when === "empty") return query === "";
|
|
11644
|
+
if (when === "query") return query !== "";
|
|
11645
|
+
return true;
|
|
11646
|
+
}
|
|
11647
|
+
function findSpatialLauncherOption(options, current, direction) {
|
|
11648
|
+
const origin = current.getBoundingClientRect();
|
|
11649
|
+
if (origin.width === 0 && origin.height === 0) return null;
|
|
11650
|
+
const originX = origin.left + origin.width / 2;
|
|
11651
|
+
const originY = origin.top + origin.height / 2;
|
|
11652
|
+
let best = null;
|
|
11653
|
+
let bestScore = Infinity;
|
|
11654
|
+
for (const option of options) {
|
|
11655
|
+
if (option === current) continue;
|
|
11656
|
+
const rect = option.getBoundingClientRect();
|
|
11657
|
+
const dx = rect.left + rect.width / 2 - originX;
|
|
11658
|
+
const dy = rect.top + rect.height / 2 - originY;
|
|
11659
|
+
let valid = false;
|
|
11660
|
+
let score = Infinity;
|
|
11661
|
+
if (direction === "ArrowLeft") {
|
|
11662
|
+
valid = dx < -2 && Math.abs(dy) < Math.max(origin.height, rect.height) / 2;
|
|
11663
|
+
score = Math.abs(dx) + Math.abs(dy) * 4;
|
|
11664
|
+
} else if (direction === "ArrowRight") {
|
|
11665
|
+
valid = dx > 2 && Math.abs(dy) < Math.max(origin.height, rect.height) / 2;
|
|
11666
|
+
score = Math.abs(dx) + Math.abs(dy) * 4;
|
|
11667
|
+
} else if (direction === "ArrowUp") {
|
|
11668
|
+
valid = dy < -2;
|
|
11669
|
+
score = Math.abs(dy) * 10 + Math.abs(dx);
|
|
11670
|
+
} else if (direction === "ArrowDown") {
|
|
11671
|
+
valid = dy > 2;
|
|
11672
|
+
score = Math.abs(dy) * 10 + Math.abs(dx);
|
|
11673
|
+
}
|
|
11674
|
+
if (valid && score < bestScore) {
|
|
11675
|
+
best = option;
|
|
11676
|
+
bestScore = score;
|
|
11677
|
+
}
|
|
11678
|
+
}
|
|
11679
|
+
return best;
|
|
11680
|
+
}
|
|
11681
|
+
function rankLauncherItems(items, query, limit = 100) {
|
|
11682
|
+
const needle = searchable(query).trim();
|
|
11683
|
+
const ranked = items.map((item, order) => ({ item, order, score: launcherScore(item, needle) })).filter(({ score }) => score < Infinity);
|
|
11684
|
+
ranked.sort((a, b) => {
|
|
11685
|
+
const aGroup = finiteOrder(a.item.groupOrder);
|
|
11686
|
+
const bGroup = finiteOrder(b.item.groupOrder);
|
|
11687
|
+
if (aGroup !== bGroup) return aGroup - bGroup;
|
|
11688
|
+
if (needle === "") {
|
|
11689
|
+
const aPinned = pinOrder(a.item.pinned, a.order);
|
|
11690
|
+
const bPinned = pinOrder(b.item.pinned, b.order);
|
|
11691
|
+
if (aPinned !== bPinned) return aPinned - bPinned;
|
|
11692
|
+
}
|
|
11693
|
+
return a.score - b.score || finiteOrder(a.item.itemOrder) - finiteOrder(b.item.itemOrder) || compareLauncherText(a.item.label, b.item.label) || a.order - b.order;
|
|
11694
|
+
});
|
|
11695
|
+
const maximum = Math.max(0, Number(limit) || 0);
|
|
11696
|
+
return ranked.slice(0, maximum || 0).map(({ item, score }) => ({ ...item, _score: score }));
|
|
11697
|
+
}
|
|
11698
|
+
function launcherScore(item, needle) {
|
|
11699
|
+
if (!needle) return item.pinned ? -1 : 0;
|
|
11700
|
+
const label = searchable(item.label);
|
|
11701
|
+
const identifier = searchable(item.id);
|
|
11702
|
+
const keywords = searchable(Array.isArray(item.keywords) ? item.keywords.join(" ") : item.keywords ?? "");
|
|
11703
|
+
if (label === needle) return 0;
|
|
11704
|
+
if (label.startsWith(needle)) return 10;
|
|
11705
|
+
if (label.split(/[^\p{L}\p{N}]+/u).some((word) => word.startsWith(needle))) return 20;
|
|
11706
|
+
if (label.includes(needle) || keywords.includes(needle)) return 30;
|
|
11707
|
+
if (identifier.includes(needle)) return 40;
|
|
11708
|
+
const acronym = label.split(/[^\p{L}\p{N}]+/u).filter(Boolean).map((word) => word[0]).join("");
|
|
11709
|
+
if (acronym.startsWith(needle)) return 50;
|
|
11710
|
+
return Infinity;
|
|
11711
|
+
}
|
|
11712
|
+
function pinOrder(pinned, order) {
|
|
11713
|
+
if (typeof pinned === "number" && Number.isFinite(pinned)) return pinned;
|
|
11714
|
+
if (pinned) return order;
|
|
11715
|
+
return Number.MAX_SAFE_INTEGER;
|
|
11716
|
+
}
|
|
11717
|
+
function finiteOrder(value) {
|
|
11718
|
+
return typeof value === "number" && Number.isFinite(value) ? value : Number.MAX_SAFE_INTEGER;
|
|
11719
|
+
}
|
|
11720
|
+
function searchable(value) {
|
|
11721
|
+
return String(value ?? "").normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase();
|
|
11722
|
+
}
|
|
11723
|
+
function compareLauncherText(left, right) {
|
|
11724
|
+
const a = searchable(left);
|
|
11725
|
+
const b = searchable(right);
|
|
11726
|
+
if (a < b) return -1;
|
|
11727
|
+
if (a > b) return 1;
|
|
11728
|
+
const rawA = String(left ?? "").normalize("NFC");
|
|
11729
|
+
const rawB = String(right ?? "").normalize("NFC");
|
|
11730
|
+
return rawA < rawB ? -1 : rawA > rawB ? 1 : 0;
|
|
11731
|
+
}
|
|
11732
|
+
function normalizeItems(items) {
|
|
11733
|
+
if (!Array.isArray(items)) return [];
|
|
11734
|
+
return items.filter((item) => item && typeof item === "object" && item.id != null && item.label != null).map((item) => normalizeLauncherItem(item));
|
|
11735
|
+
}
|
|
11736
|
+
function normalizeLauncherItem(item) {
|
|
11737
|
+
const normalized = { ...item, label: String(item.label) };
|
|
11738
|
+
if (item.href == null) return normalized;
|
|
11739
|
+
const href = safeHref(item.href);
|
|
11740
|
+
if (href !== null) normalized.href = href;
|
|
11741
|
+
else {
|
|
11742
|
+
delete normalized.href;
|
|
11743
|
+
if (typeof normalized.invoke !== "function") normalized.disabled = true;
|
|
11744
|
+
}
|
|
11745
|
+
return normalized;
|
|
11746
|
+
}
|
|
11747
|
+
function normalizeSources(sources) {
|
|
11748
|
+
if (!Array.isArray(sources)) return [];
|
|
11749
|
+
return sources.filter((source) => source && typeof source === "object" && source.id != null && typeof source.load === "function").map((source) => ({ ...source, id: String(source.id) }));
|
|
11750
|
+
}
|
|
11751
|
+
|
|
10887
11752
|
// src/components/message/message.js
|
|
10888
11753
|
var MessageQueue = class {
|
|
10889
11754
|
#limit;
|
|
@@ -11993,6 +12858,7 @@ var zeyosModules = Object.freeze({
|
|
|
11993
12858
|
weblets: { label: "Weblets", icon: "zeyos-weblets", color: "#4f46e5", fa: "window-restore" }
|
|
11994
12859
|
});
|
|
11995
12860
|
var moduleAliases = Object.freeze({
|
|
12861
|
+
main: "zeyos",
|
|
11996
12862
|
addresses: "contacts",
|
|
11997
12863
|
clocking: "actionsteps",
|
|
11998
12864
|
comments: "notes",
|
|
@@ -12099,15 +12965,23 @@ function moduleIcon(name, options = {}) {
|
|
|
12099
12965
|
return icon(standard ? `fa:${info.fa}` : `kit:${info.icon}`, rest);
|
|
12100
12966
|
}
|
|
12101
12967
|
function moduleChip(name, options = {}) {
|
|
12102
|
-
const {
|
|
12968
|
+
const {
|
|
12969
|
+
size = 24,
|
|
12970
|
+
iconSize = "1em",
|
|
12971
|
+
label = null,
|
|
12972
|
+
title = false,
|
|
12973
|
+
standard = false,
|
|
12974
|
+
color = null
|
|
12975
|
+
} = options;
|
|
12103
12976
|
const key = normalizeModuleName(name);
|
|
12104
12977
|
const info = moduleInfo(name);
|
|
12978
|
+
const background = color || info.color;
|
|
12105
12979
|
const chip = h("span", {
|
|
12106
12980
|
class: ["zx-module-icon", options.class].flat().filter(Boolean),
|
|
12107
12981
|
dataset: { module: key },
|
|
12108
12982
|
style: {
|
|
12109
|
-
"--zx-module-color":
|
|
12110
|
-
"--zx-module-glyph": moduleGlyphColor(
|
|
12983
|
+
"--zx-module-color": background,
|
|
12984
|
+
"--zx-module-glyph": moduleGlyphColor(background),
|
|
12111
12985
|
"--zx-module-icon-size": typeof size === "number" ? `${size}px` : String(size)
|
|
12112
12986
|
},
|
|
12113
12987
|
title: title ? info.label : null
|
|
@@ -12121,6 +12995,307 @@ function moduleChip(name, options = {}) {
|
|
|
12121
12995
|
return chip;
|
|
12122
12996
|
}
|
|
12123
12997
|
|
|
12998
|
+
// src/zeyos/launcher.js
|
|
12999
|
+
var ZEYOS_LAUNCHER_APPLICATIONS = Object.freeze([
|
|
13000
|
+
"accounts",
|
|
13001
|
+
"actionsteps",
|
|
13002
|
+
"admin",
|
|
13003
|
+
"auth",
|
|
13004
|
+
"billing",
|
|
13005
|
+
"calendar",
|
|
13006
|
+
"campaigns",
|
|
13007
|
+
"collection",
|
|
13008
|
+
"contacts",
|
|
13009
|
+
"contracts",
|
|
13010
|
+
"control",
|
|
13011
|
+
"coupons",
|
|
13012
|
+
"dev",
|
|
13013
|
+
"documents",
|
|
13014
|
+
"inventory",
|
|
13015
|
+
"mailinglists",
|
|
13016
|
+
"main",
|
|
13017
|
+
"messages",
|
|
13018
|
+
"links",
|
|
13019
|
+
"notes",
|
|
13020
|
+
"objects",
|
|
13021
|
+
"opportunities",
|
|
13022
|
+
"payments",
|
|
13023
|
+
"pricelists",
|
|
13024
|
+
"procurement",
|
|
13025
|
+
"production",
|
|
13026
|
+
"projects",
|
|
13027
|
+
"tasks",
|
|
13028
|
+
"tickets"
|
|
13029
|
+
]);
|
|
13030
|
+
function buildZeyosLauncherConfig(state = {}, options = {}) {
|
|
13031
|
+
const {
|
|
13032
|
+
standardIcons = false,
|
|
13033
|
+
items: extraItems = [],
|
|
13034
|
+
sources: extraSources = [],
|
|
13035
|
+
...launcherOptions
|
|
13036
|
+
} = options;
|
|
13037
|
+
const labels = launcherLabels(state.labels);
|
|
13038
|
+
const iconOptions = {
|
|
13039
|
+
standardIcons,
|
|
13040
|
+
renderApplicationIcon: state.renderApplicationIcon,
|
|
13041
|
+
renderWebletIcon: state.renderWebletIcon
|
|
13042
|
+
};
|
|
13043
|
+
const applications = normalizeZeyosLauncherApplications(state, iconOptions);
|
|
13044
|
+
const recent = typeof state.loadRecent === "function" ? [] : normalizeZeyosLauncherRecords(state.recent ?? [], {
|
|
13045
|
+
query: "",
|
|
13046
|
+
labels,
|
|
13047
|
+
resolveRecord: state.resolveRecord,
|
|
13048
|
+
renderRecordIcon: state.renderRecordIcon,
|
|
13049
|
+
standardIcons
|
|
13050
|
+
});
|
|
13051
|
+
const sources = [];
|
|
13052
|
+
if (typeof state.loadRecent === "function") {
|
|
13053
|
+
sources.push({
|
|
13054
|
+
id: "zeyos-recent",
|
|
13055
|
+
minQuery: 0,
|
|
13056
|
+
when: "empty",
|
|
13057
|
+
order: "source",
|
|
13058
|
+
load: async (_query, context) => normalizeZeyosLauncherRecords(
|
|
13059
|
+
await state.loadRecent(context),
|
|
13060
|
+
{
|
|
13061
|
+
query: "",
|
|
13062
|
+
labels,
|
|
13063
|
+
resolveRecord: state.resolveRecord,
|
|
13064
|
+
renderRecordIcon: state.renderRecordIcon,
|
|
13065
|
+
standardIcons
|
|
13066
|
+
}
|
|
13067
|
+
)
|
|
13068
|
+
});
|
|
13069
|
+
}
|
|
13070
|
+
if (typeof state.search === "function") {
|
|
13071
|
+
sources.push({
|
|
13072
|
+
id: "zeyos-records",
|
|
13073
|
+
minQuery: 1,
|
|
13074
|
+
when: "query",
|
|
13075
|
+
order: "source",
|
|
13076
|
+
load: async (query, context) => normalizeZeyosLauncherRecords(
|
|
13077
|
+
await state.search(query, context),
|
|
13078
|
+
{
|
|
13079
|
+
query,
|
|
13080
|
+
labels,
|
|
13081
|
+
resolveRecord: state.resolveRecord,
|
|
13082
|
+
renderRecordIcon: state.renderRecordIcon,
|
|
13083
|
+
standardIcons
|
|
13084
|
+
}
|
|
13085
|
+
)
|
|
13086
|
+
});
|
|
13087
|
+
}
|
|
13088
|
+
sources.push(...extraSources);
|
|
13089
|
+
return {
|
|
13090
|
+
...launcherOptions,
|
|
13091
|
+
items: [...applications, ...recent, ...extraItems],
|
|
13092
|
+
sources,
|
|
13093
|
+
hints: options.hints ?? {
|
|
13094
|
+
move: labels.move,
|
|
13095
|
+
open: labels.open,
|
|
13096
|
+
close: labels.close
|
|
13097
|
+
}
|
|
13098
|
+
};
|
|
13099
|
+
}
|
|
13100
|
+
function zeyosLauncher(state = {}, options = {}) {
|
|
13101
|
+
return new Launcher(null, buildZeyosLauncherConfig(state, options));
|
|
13102
|
+
}
|
|
13103
|
+
function normalizeZeyosLauncherApplications(state = {}, options = {}) {
|
|
13104
|
+
const labels = launcherLabels(state.labels);
|
|
13105
|
+
const pinned = Array.isArray(state.pinned) ? state.pinned.map(String) : [];
|
|
13106
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13107
|
+
const items = [];
|
|
13108
|
+
const add = (raw2, fallbackId = null, fallbackLabel = null) => {
|
|
13109
|
+
const app = normalizeApplication(raw2, fallbackId, fallbackLabel);
|
|
13110
|
+
if (!app || seen.has(app.identifier)) return;
|
|
13111
|
+
if (app.type === "module" && typeof state.canUseModule === "function" && !state.canUseModule(app.module)) return;
|
|
13112
|
+
seen.add(app.identifier);
|
|
13113
|
+
const resolved = mergeDestination(app.raw, state.resolveApplication);
|
|
13114
|
+
const pin = pinned.indexOf(app.identifier);
|
|
13115
|
+
const icon2 = () => {
|
|
13116
|
+
const custom = typeof options.renderApplicationIcon === "function" ? options.renderApplicationIcon(app.raw, app) : app.type === "weblet" && typeof options.renderWebletIcon === "function" ? options.renderWebletIcon(app.raw, app) : null;
|
|
13117
|
+
return custom ?? moduleChip(app.module, {
|
|
13118
|
+
size: 36,
|
|
13119
|
+
standard: Boolean(options.standardIcons),
|
|
13120
|
+
color: app.color
|
|
13121
|
+
});
|
|
13122
|
+
};
|
|
13123
|
+
items.push({
|
|
13124
|
+
id: app.identifier,
|
|
13125
|
+
label: app.label,
|
|
13126
|
+
keywords: [app.identifier, app.module],
|
|
13127
|
+
group: labels.applications,
|
|
13128
|
+
groupOrder: 0,
|
|
13129
|
+
kind: "application",
|
|
13130
|
+
icon: icon2,
|
|
13131
|
+
pinned: pin >= 0 ? pin : void 0,
|
|
13132
|
+
current: app.identifier === state.activeIdentifier,
|
|
13133
|
+
disabled: !resolved.href && typeof resolved.invoke !== "function",
|
|
13134
|
+
...resolved,
|
|
13135
|
+
_zeyos: app
|
|
13136
|
+
});
|
|
13137
|
+
};
|
|
13138
|
+
for (const raw2 of Array.isArray(state.menuApps) ? state.menuApps : []) add(raw2);
|
|
13139
|
+
if (Array.isArray(state.modules)) {
|
|
13140
|
+
for (const module of state.modules) {
|
|
13141
|
+
if (typeof module === "string") add([module, moduleInfo(module).label]);
|
|
13142
|
+
else add(module, module?.id ?? module?.module, module?.label);
|
|
13143
|
+
}
|
|
13144
|
+
} else {
|
|
13145
|
+
for (const [module, label] of Object.entries(state.modules ?? {})) add([module, label]);
|
|
13146
|
+
}
|
|
13147
|
+
return items;
|
|
13148
|
+
}
|
|
13149
|
+
function normalizeZeyosLauncherRecords(payload, options = {}) {
|
|
13150
|
+
const query = String(options.query ?? "");
|
|
13151
|
+
const labels = launcherLabels(options.labels);
|
|
13152
|
+
const records = flattenRecords(payload, query);
|
|
13153
|
+
const groupOrders = /* @__PURE__ */ new Map();
|
|
13154
|
+
return records.flatMap(({ raw: raw2, group }, itemOrder) => {
|
|
13155
|
+
const record = normalizeRecord(raw2, group, query);
|
|
13156
|
+
if (!record) return [];
|
|
13157
|
+
const groupName = query === "" ? labels.recent : record.group === "" ? labels.results : labels.entities[record.group] ?? record.group;
|
|
13158
|
+
if (!groupOrders.has(groupName)) groupOrders.set(groupName, groupOrders.size + 1);
|
|
13159
|
+
const resolved = mergeDestination(record.raw, options.resolveRecord);
|
|
13160
|
+
return [{
|
|
13161
|
+
id: `record:${record.entity}:${record.fork ?? ""}:${record.ID}`,
|
|
13162
|
+
label: record.name,
|
|
13163
|
+
description: record.sec,
|
|
13164
|
+
keywords: [record.entity, record.ID, record.fork ?? ""],
|
|
13165
|
+
group: groupName,
|
|
13166
|
+
groupOrder: groupOrders.get(groupName),
|
|
13167
|
+
itemOrder,
|
|
13168
|
+
kind: "record",
|
|
13169
|
+
when: query === "" ? "empty" : "query",
|
|
13170
|
+
icon: () => {
|
|
13171
|
+
const custom = typeof options.renderRecordIcon === "function" ? options.renderRecordIcon(record.raw, record) : null;
|
|
13172
|
+
return custom ?? moduleChip(record.entity, {
|
|
13173
|
+
size: 28,
|
|
13174
|
+
standard: Boolean(options.standardIcons)
|
|
13175
|
+
});
|
|
13176
|
+
},
|
|
13177
|
+
disabled: !resolved.href && typeof resolved.invoke !== "function",
|
|
13178
|
+
...resolved,
|
|
13179
|
+
_zeyos: record
|
|
13180
|
+
}];
|
|
13181
|
+
});
|
|
13182
|
+
}
|
|
13183
|
+
function zeyosActiveIdentifier(state = {}) {
|
|
13184
|
+
if (state.weblet != null && state.weblet !== "") return `weblet#${state.weblet}`;
|
|
13185
|
+
if (state.fork != null && state.fork !== "") return `fork#${state.fork}`;
|
|
13186
|
+
return String(state.module ?? "");
|
|
13187
|
+
}
|
|
13188
|
+
function normalizeApplication(raw2, fallbackId, fallbackLabel) {
|
|
13189
|
+
if (Array.isArray(raw2)) {
|
|
13190
|
+
if (raw2.length < 2) return null;
|
|
13191
|
+
if (raw2.length === 2) {
|
|
13192
|
+
const module = String(raw2[0]);
|
|
13193
|
+
return { raw: raw2, identifier: module, label: String(raw2[1]), module, type: "module", color: null };
|
|
13194
|
+
}
|
|
13195
|
+
if (raw2.length === 4) {
|
|
13196
|
+
const module = String(raw2[2] || "default");
|
|
13197
|
+
return {
|
|
13198
|
+
raw: raw2,
|
|
13199
|
+
identifier: `fork#${raw2[0]}`,
|
|
13200
|
+
label: String(raw2[1]),
|
|
13201
|
+
module,
|
|
13202
|
+
type: "fork",
|
|
13203
|
+
color: normalizeServerColor(raw2[3])
|
|
13204
|
+
};
|
|
13205
|
+
}
|
|
13206
|
+
return {
|
|
13207
|
+
raw: raw2,
|
|
13208
|
+
identifier: `weblet#${raw2[0]}`,
|
|
13209
|
+
label: String(raw2[1]),
|
|
13210
|
+
module: "weblets",
|
|
13211
|
+
type: "weblet",
|
|
13212
|
+
color: normalizeServerColor(raw2[8])
|
|
13213
|
+
};
|
|
13214
|
+
}
|
|
13215
|
+
if (!raw2 || typeof raw2 !== "object") return null;
|
|
13216
|
+
const identifier = String(raw2.identifier ?? raw2.id ?? fallbackId ?? "");
|
|
13217
|
+
if (!identifier) return null;
|
|
13218
|
+
const type = identifier.startsWith("fork#") ? "fork" : identifier.startsWith("weblet#") ? "weblet" : "module";
|
|
13219
|
+
return {
|
|
13220
|
+
raw: raw2,
|
|
13221
|
+
identifier,
|
|
13222
|
+
label: String(raw2.label ?? fallbackLabel ?? moduleInfo(identifier).label),
|
|
13223
|
+
module: String(raw2.module ?? (type === "module" ? identifier : type === "fork" ? "default" : "weblets")),
|
|
13224
|
+
type,
|
|
13225
|
+
color: normalizeServerColor(raw2.color)
|
|
13226
|
+
};
|
|
13227
|
+
}
|
|
13228
|
+
function flattenRecords(payload, query) {
|
|
13229
|
+
if (Array.isArray(payload)) return payload.map((raw2) => ({ raw: raw2, group: raw2?.group ?? "" }));
|
|
13230
|
+
if (!payload || typeof payload !== "object") return [];
|
|
13231
|
+
const result = [];
|
|
13232
|
+
for (const [group, rows] of Object.entries(payload)) {
|
|
13233
|
+
if (!Array.isArray(rows)) continue;
|
|
13234
|
+
for (const row of rows) {
|
|
13235
|
+
if (!Array.isArray(row)) {
|
|
13236
|
+
result.push({ raw: row, group });
|
|
13237
|
+
continue;
|
|
13238
|
+
}
|
|
13239
|
+
const raw2 = group === "" && row.length >= 5 ? { ID: row[0], fork: row[1], name: row[2], sec: row[3], entity: row[4], group } : { ID: row[0], fork: row[1], name: row[2], sec: row[3], entity: group, group };
|
|
13240
|
+
result.push({ raw: raw2, group });
|
|
13241
|
+
}
|
|
13242
|
+
}
|
|
13243
|
+
return result;
|
|
13244
|
+
}
|
|
13245
|
+
function normalizeRecord(raw2, group, query) {
|
|
13246
|
+
if (Array.isArray(raw2)) {
|
|
13247
|
+
if (query === "" && raw2.length >= 4) {
|
|
13248
|
+
return {
|
|
13249
|
+
raw: raw2,
|
|
13250
|
+
entity: String(raw2[0]),
|
|
13251
|
+
ID: raw2[1],
|
|
13252
|
+
fork: raw2[2] ?? null,
|
|
13253
|
+
name: String(raw2[3]),
|
|
13254
|
+
sec: String(raw2[4] ?? ""),
|
|
13255
|
+
group: ""
|
|
13256
|
+
};
|
|
13257
|
+
}
|
|
13258
|
+
return null;
|
|
13259
|
+
}
|
|
13260
|
+
if (!raw2 || typeof raw2 !== "object" || raw2.ID == null || raw2.name == null) return null;
|
|
13261
|
+
return {
|
|
13262
|
+
raw: raw2,
|
|
13263
|
+
entity: String(raw2.entity ?? group ?? "records"),
|
|
13264
|
+
ID: raw2.ID,
|
|
13265
|
+
fork: raw2.fork ?? null,
|
|
13266
|
+
name: String(raw2.name),
|
|
13267
|
+
sec: String(raw2.sec ?? ""),
|
|
13268
|
+
group: String(raw2.group ?? group ?? "")
|
|
13269
|
+
};
|
|
13270
|
+
}
|
|
13271
|
+
function mergeDestination(raw2, resolver) {
|
|
13272
|
+
const direct = raw2 && typeof raw2 === "object" && !Array.isArray(raw2) ? raw2 : {};
|
|
13273
|
+
const resolved = typeof resolver === "function" ? resolver(raw2) : null;
|
|
13274
|
+
const destination = resolved && typeof resolved === "object" ? resolved : {};
|
|
13275
|
+
return Object.fromEntries(["href", "target", "invoke", "value"].flatMap((key) => {
|
|
13276
|
+
const value = destination[key] ?? direct[key];
|
|
13277
|
+
return value === void 0 ? [] : [[key, value]];
|
|
13278
|
+
}));
|
|
13279
|
+
}
|
|
13280
|
+
function normalizeServerColor(value) {
|
|
13281
|
+
const source = String(value ?? "").trim();
|
|
13282
|
+
if (!source) return null;
|
|
13283
|
+
const color = source.startsWith("#") ? source : `#${source}`;
|
|
13284
|
+
return /^#[\da-f]{3}(?:[\da-f]{3})?$/i.test(color) ? color : null;
|
|
13285
|
+
}
|
|
13286
|
+
function launcherLabels(input) {
|
|
13287
|
+
const labels = input && typeof input === "object" ? input : {};
|
|
13288
|
+
return {
|
|
13289
|
+
applications: String(labels.applications ?? "Applications"),
|
|
13290
|
+
recent: String(labels.recent ?? "Recent"),
|
|
13291
|
+
results: String(labels.results ?? "Top results"),
|
|
13292
|
+
entities: labels.entities && typeof labels.entities === "object" ? labels.entities : {},
|
|
13293
|
+
move: String(labels.move ?? "Move"),
|
|
13294
|
+
open: String(labels.open ?? "Open"),
|
|
13295
|
+
close: String(labels.close ?? "Close")
|
|
13296
|
+
};
|
|
13297
|
+
}
|
|
13298
|
+
|
|
12124
13299
|
// src/zeyos/select.js
|
|
12125
13300
|
function buildZeyosSelectConfig(client, resource, opts = {}) {
|
|
12126
13301
|
const {
|
|
@@ -12162,6 +13337,90 @@ function zeyosSelect(client, resource, opts = {}) {
|
|
|
12162
13337
|
select.ready = hydrateInitialValue(select, client, resource, requestedValue);
|
|
12163
13338
|
return select;
|
|
12164
13339
|
}
|
|
13340
|
+
function buildZeyosEntitySelectConfig(client, resource, opts = {}) {
|
|
13341
|
+
const {
|
|
13342
|
+
fields,
|
|
13343
|
+
labelKey = inferLabelKey(client, resource),
|
|
13344
|
+
valueKey = "ID",
|
|
13345
|
+
searchFields = inferSearchFields(client, resource),
|
|
13346
|
+
subtitleKey = null,
|
|
13347
|
+
moduleKey = () => resource,
|
|
13348
|
+
groupKey = null,
|
|
13349
|
+
none = true,
|
|
13350
|
+
create = false,
|
|
13351
|
+
renderIcon = defaultEntityIcon,
|
|
13352
|
+
renderItem,
|
|
13353
|
+
renderValueAdornment,
|
|
13354
|
+
...rest
|
|
13355
|
+
} = opts;
|
|
13356
|
+
const projection = fields ?? defaultProjection(valueKey, labelKey, [
|
|
13357
|
+
...searchFields,
|
|
13358
|
+
...[subtitleKey, moduleKey, groupKey].filter((reader) => typeof reader === "string")
|
|
13359
|
+
]);
|
|
13360
|
+
const base = buildZeyosSelectConfig(client, resource, {
|
|
13361
|
+
...rest,
|
|
13362
|
+
fields: projection,
|
|
13363
|
+
labelKey,
|
|
13364
|
+
valueKey,
|
|
13365
|
+
searchFields,
|
|
13366
|
+
groupKey,
|
|
13367
|
+
clearable: none === false ? Boolean(rest.clearable) : true
|
|
13368
|
+
});
|
|
13369
|
+
const renderModuleIcon = (item, selected = false) => {
|
|
13370
|
+
if (renderIcon === null) return null;
|
|
13371
|
+
const module = readRecord(item, moduleKey) ?? resource;
|
|
13372
|
+
return renderIcon(item, { resource, module: String(module), selected });
|
|
13373
|
+
};
|
|
13374
|
+
base.renderItem = renderItem ?? ((item) => {
|
|
13375
|
+
const visual = renderModuleIcon(item);
|
|
13376
|
+
return h(
|
|
13377
|
+
"span",
|
|
13378
|
+
{ class: "zx-select__entity" },
|
|
13379
|
+
visual ? h("span", {
|
|
13380
|
+
class: "zx-select__entity-icon",
|
|
13381
|
+
ariaHidden: "true"
|
|
13382
|
+
}, visual) : null,
|
|
13383
|
+
h(
|
|
13384
|
+
"span",
|
|
13385
|
+
{ class: "zx-select__entity-copy" },
|
|
13386
|
+
h("span", { class: "zx-select__entity-label" }, String(readRecord(item, labelKey) ?? "")),
|
|
13387
|
+
subtitleKey && readRecord(item, subtitleKey) != null ? h("span", { class: "zx-select__entity-subtitle" }, String(readRecord(item, subtitleKey))) : null
|
|
13388
|
+
)
|
|
13389
|
+
);
|
|
13390
|
+
});
|
|
13391
|
+
base.renderValueAdornment = renderValueAdornment ?? ((item) => renderModuleIcon(item, true));
|
|
13392
|
+
if (none !== false) {
|
|
13393
|
+
base.noneLabel = typeof none === "string" ? none : rest.noneLabel ?? "No selection";
|
|
13394
|
+
base.renderNone = rest.renderNone ?? ((label) => h(
|
|
13395
|
+
"span",
|
|
13396
|
+
{ class: "zx-select__entity-none" },
|
|
13397
|
+
h("span", { class: "zx-select__entity-none-icon", ariaHidden: "true" }),
|
|
13398
|
+
h("span", {}, label)
|
|
13399
|
+
));
|
|
13400
|
+
}
|
|
13401
|
+
if (create) {
|
|
13402
|
+
const descriptor = create === true ? {} : create;
|
|
13403
|
+
const invoke = typeof descriptor.oninvoke === "function" ? descriptor.oninvoke : null;
|
|
13404
|
+
base.actions = [...Array.isArray(base.actions) ? base.actions : [], {
|
|
13405
|
+
id: descriptor.id ?? "create",
|
|
13406
|
+
label: descriptor.label ?? "Create new\u2026",
|
|
13407
|
+
group: descriptor.group ?? "New",
|
|
13408
|
+
icon: descriptor.icon ?? "plus",
|
|
13409
|
+
invoke: (detail) => invoke?.({ ...detail, resource })
|
|
13410
|
+
}];
|
|
13411
|
+
}
|
|
13412
|
+
return base;
|
|
13413
|
+
}
|
|
13414
|
+
function zeyosEntitySelect(client, resource, opts = {}) {
|
|
13415
|
+
const config2 = buildZeyosEntitySelectConfig(client, resource, opts);
|
|
13416
|
+
const requestedValue = opts.value ?? null;
|
|
13417
|
+
const select = (
|
|
13418
|
+
/** @type {Select & {ready: Promise<Select>}} */
|
|
13419
|
+
new Select(null, config2)
|
|
13420
|
+
);
|
|
13421
|
+
select.ready = hydrateInitialValue(select, client, resource, requestedValue);
|
|
13422
|
+
return select;
|
|
13423
|
+
}
|
|
12165
13424
|
function normalizeZeyosListResult(client, result) {
|
|
12166
13425
|
if (typeof client?.normalizeListResult === "function") {
|
|
12167
13426
|
return normalizeLocalListResult(client.normalizeListResult(result));
|
|
@@ -12178,12 +13437,12 @@ function normalizeLocalListResult(result) {
|
|
|
12178
13437
|
async function hydrateInitialValue(select, client, resource, value) {
|
|
12179
13438
|
if (value == null) return select;
|
|
12180
13439
|
const record = value && typeof value === "object" ? value : await connect(client).get(resource, value);
|
|
12181
|
-
const normalized =
|
|
13440
|
+
const normalized = normalizeRecord2(record);
|
|
12182
13441
|
select.setItems([normalized]);
|
|
12183
13442
|
select.set(normalized, { silent: true });
|
|
12184
13443
|
return select;
|
|
12185
13444
|
}
|
|
12186
|
-
function
|
|
13445
|
+
function normalizeRecord2(result) {
|
|
12187
13446
|
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
12188
13447
|
const nested = result.data;
|
|
12189
13448
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) return nested;
|
|
@@ -12191,6 +13450,16 @@ function normalizeRecord(result) {
|
|
|
12191
13450
|
}
|
|
12192
13451
|
throw new TypeError("ZeyOS get operation did not return a record");
|
|
12193
13452
|
}
|
|
13453
|
+
function readRecord(item, reader) {
|
|
13454
|
+
if (typeof reader === "function") return reader(item);
|
|
13455
|
+
if (item && typeof item === "object" && typeof reader === "string") return item[reader];
|
|
13456
|
+
return null;
|
|
13457
|
+
}
|
|
13458
|
+
function defaultEntityIcon(_item, context) {
|
|
13459
|
+
const chip = moduleChip(String(context.module ?? "default"), { size: 20 });
|
|
13460
|
+
chip.replaceChildren(icon("square", { size: 10 }));
|
|
13461
|
+
return chip;
|
|
13462
|
+
}
|
|
12194
13463
|
function defaultProjection(valueKey, labelKey, searchFields) {
|
|
12195
13464
|
const keys = [valueKey, labelKey, ...searchFields].filter((key) => typeof key === "string" && key);
|
|
12196
13465
|
return keys.length ? [...new Set(keys)] : void 0;
|
|
@@ -12270,7 +13539,7 @@ function zeyosForm(client, resource, opts = {}) {
|
|
|
12270
13539
|
await Promise.all(config2.entityFields.map(async ({ id, resource: referencedResource2 }) => {
|
|
12271
13540
|
const value = record[id];
|
|
12272
13541
|
if (value == null) return;
|
|
12273
|
-
const selected = value && typeof value === "object" ? value :
|
|
13542
|
+
const selected = value && typeof value === "object" ? value : normalizeRecord3(await facade.get(referencedResource2, value));
|
|
12274
13543
|
const select = Select.from(form.getField(id)?.getInput());
|
|
12275
13544
|
if (select instanceof Select) select.setItems([selected]);
|
|
12276
13545
|
}));
|
|
@@ -12279,7 +13548,7 @@ function zeyosForm(client, resource, opts = {}) {
|
|
|
12279
13548
|
};
|
|
12280
13549
|
const load = async (id) => {
|
|
12281
13550
|
if (id == null || id === "") throw new TypeError("zeyosForm.load() requires an id");
|
|
12282
|
-
const record =
|
|
13551
|
+
const record = normalizeRecord3(await facade.get(resource, id));
|
|
12283
13552
|
currentId = record.ID ?? id;
|
|
12284
13553
|
await applyRecord(record);
|
|
12285
13554
|
return record;
|
|
@@ -12298,7 +13567,7 @@ function zeyosForm(client, resource, opts = {}) {
|
|
|
12298
13567
|
throw error;
|
|
12299
13568
|
}
|
|
12300
13569
|
const result = currentId == null ? await facade.create(resource, payload) : await facade.update(resource, { ID: currentId, ...payload });
|
|
12301
|
-
const record =
|
|
13570
|
+
const record = normalizeRecord3(result);
|
|
12302
13571
|
currentId = record.ID ?? currentId;
|
|
12303
13572
|
if (opts.onSaved) {
|
|
12304
13573
|
try {
|
|
@@ -12376,7 +13645,7 @@ function normalizeColumns(value) {
|
|
|
12376
13645
|
[1, 2, 3].includes(number) ? number : 2
|
|
12377
13646
|
);
|
|
12378
13647
|
}
|
|
12379
|
-
function
|
|
13648
|
+
function normalizeRecord3(result) {
|
|
12380
13649
|
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
12381
13650
|
const nested = result.data;
|
|
12382
13651
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) return nested;
|
|
@@ -12571,7 +13840,11 @@ function copyFilters(filters) {
|
|
|
12571
13840
|
}
|
|
12572
13841
|
export {
|
|
12573
13842
|
ZEYOS_ICON_KIT,
|
|
13843
|
+
ZEYOS_LAUNCHER_APPLICATIONS,
|
|
12574
13844
|
buildListQuery,
|
|
13845
|
+
buildZeyosEntitySelectConfig,
|
|
13846
|
+
buildZeyosLauncherConfig,
|
|
13847
|
+
buildZeyosSelectConfig,
|
|
12575
13848
|
connect,
|
|
12576
13849
|
dataFilterStateToFilters,
|
|
12577
13850
|
dateToUnixSeconds,
|
|
@@ -12586,13 +13859,18 @@ export {
|
|
|
12586
13859
|
moduleInfo,
|
|
12587
13860
|
moduleKeys,
|
|
12588
13861
|
normalizeModuleName,
|
|
13862
|
+
normalizeZeyosLauncherApplications,
|
|
13863
|
+
normalizeZeyosLauncherRecords,
|
|
12589
13864
|
registerFieldType,
|
|
12590
13865
|
registerModules,
|
|
12591
13866
|
resolveFields,
|
|
12592
13867
|
tableSortToQuery,
|
|
12593
13868
|
unixSecondsToDate,
|
|
12594
13869
|
useZeyosIcons,
|
|
13870
|
+
zeyosActiveIdentifier,
|
|
13871
|
+
zeyosEntitySelect,
|
|
12595
13872
|
zeyosForm,
|
|
13873
|
+
zeyosLauncher,
|
|
12596
13874
|
zeyosModules,
|
|
12597
13875
|
zeyosSelect,
|
|
12598
13876
|
zeyosTable
|