@trintel/zooy 1.0.1-beta.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/LICENSE +190 -0
- package/README.md +471 -0
- package/dist/chunks/16-30f5E9no.js +1 -0
- package/dist/chunks/16-41wFkj7S.js +1 -0
- package/dist/chunks/16-4Ffg4DOO.js +1 -0
- package/dist/chunks/16-8pr5mbIO.js +28 -0
- package/dist/chunks/16-B56B50XT.js +1 -0
- package/dist/chunks/16-BFJN6B6d.js +39 -0
- package/dist/chunks/16-BTonF5Je.js +29 -0
- package/dist/chunks/16-BiShwKPX.js +28 -0
- package/dist/chunks/16-BwDdccyH.js +33 -0
- package/dist/chunks/16-C7Xs-wKv.js +1 -0
- package/dist/chunks/16-CDRTWf-C.js +21 -0
- package/dist/chunks/16-CSWwhDUY.js +21 -0
- package/dist/chunks/16-ChlL35BX.js +62 -0
- package/dist/chunks/16-CpNRIuiB.js +1 -0
- package/dist/chunks/16-CqC9427V.js +21 -0
- package/dist/chunks/16-Cubm18Em.js +1 -0
- package/dist/chunks/16-D3Ol0pdg.js +21 -0
- package/dist/chunks/16-DH311Of_.js +1 -0
- package/dist/chunks/16-Dlq3qxDh.js +1 -0
- package/dist/chunks/16-Drl9-5Gn.js +21 -0
- package/dist/chunks/16-Dt4v0bcc.js +1 -0
- package/dist/chunks/16-DylBiJnm.js +1 -0
- package/dist/chunks/20-CZPIOG-A.js +84 -0
- package/dist/chunks/20-gmyNFvDv.js +1 -0
- package/dist/chunks/button-DdJepOoE.js +271 -0
- package/dist/chunks/button-skeleton-COf2kCDL.js +48 -0
- package/dist/chunks/button-skeleton-rf3WDPTZ.js +21 -0
- package/dist/chunks/button-zHEPxpXa.js +66 -0
- package/dist/chunks/carbon-element-CbfPUYRT.js +1 -0
- package/dist/chunks/carbon-element-DzDuXiyl.js +25 -0
- package/dist/chunks/checkbox-B5mHvx0J.js +200 -0
- package/dist/chunks/checkbox-BRPCg-DN.js +40 -0
- package/dist/chunks/class-map-BfNAkUL_.js +5 -0
- package/dist/chunks/class-map-DEHG4tmw.js +32 -0
- package/dist/chunks/collection-helpers-BwUB2CEz.js +8 -0
- package/dist/chunks/collection-helpers-CYLIiyii.js +1 -0
- package/dist/chunks/content-switcher-item-DeGjVRQY.js +350 -0
- package/dist/chunks/content-switcher-item-DucclV1b.js +27 -0
- package/dist/chunks/defs-DMmigODC.js +1077 -0
- package/dist/chunks/defs-DPD20sPK.js +1 -0
- package/dist/chunks/directive-Dp4ZTz-F.js +5 -0
- package/dist/chunks/directive-kLG6oqUu.js +27 -0
- package/dist/chunks/dropdown-item-BgLQ1VLo.js +69 -0
- package/dist/chunks/dropdown-item-DPdwOION.js +620 -0
- package/dist/chunks/focus-B9JYe97T.js +20 -0
- package/dist/chunks/focus-rQSNiwy7.js +1 -0
- package/dist/chunks/form-D6Tce4-W.js +1 -0
- package/dist/chunks/form-DpI-v6DA.js +20 -0
- package/dist/chunks/host-listener-CN245INl.js +37 -0
- package/dist/chunks/host-listener-DD3oGxvg.js +16 -0
- package/dist/chunks/host-listener-gxrTpxgv.js +1 -0
- package/dist/chunks/host-listener-rUcT_LUW.js +1 -0
- package/dist/chunks/icon-loader-Dk_AU26w.js +7 -0
- package/dist/chunks/icon-loader-o_JYDmJX.js +1 -0
- package/dist/chunks/icon-loader-utils-BBjK16O-.js +5 -0
- package/dist/chunks/icon-loader-utils-BEOGXn0q.js +117 -0
- package/dist/chunks/if-defined-BdZRLDJ6.js +5 -0
- package/dist/chunks/if-defined-C0yJsxi2.js +10 -0
- package/dist/chunks/if-non-empty-CEHK-hqx.js +1 -0
- package/dist/chunks/if-non-empty-DTGflHtA.js +5 -0
- package/dist/chunks/index-16wphaSo.js +4 -0
- package/dist/chunks/index-27oFQaOS.js +37 -0
- package/dist/chunks/index-45EApaFs.js +205 -0
- package/dist/chunks/index-5jmZyISW.js +1 -0
- package/dist/chunks/index-B1vQ44Hl.js +363 -0
- package/dist/chunks/index-B3CDjjXh.js +214 -0
- package/dist/chunks/index-B3mXrMt1.js +24 -0
- package/dist/chunks/index-B7mGUQhz.js +79 -0
- package/dist/chunks/index-BBNaYDog.js +22 -0
- package/dist/chunks/index-BBt9OmO5.js +12 -0
- package/dist/chunks/index-BIaF1Dfx.js +107 -0
- package/dist/chunks/index-BJAuRs3y.js +75 -0
- package/dist/chunks/index-BJKftWYn.js +89 -0
- package/dist/chunks/index-BLjNrhkN.js +4 -0
- package/dist/chunks/index-BOTu9u-3.js +86 -0
- package/dist/chunks/index-BPVpH8ll.js +160 -0
- package/dist/chunks/index-BRuvyoCz.js +15 -0
- package/dist/chunks/index-BSgHSOVT.js +65 -0
- package/dist/chunks/index-BSh7UYcP.js +9 -0
- package/dist/chunks/index-BUxB7aWd.js +96 -0
- package/dist/chunks/index-BXIxcS00.js +22 -0
- package/dist/chunks/index-BXczHDk-.js +187 -0
- package/dist/chunks/index-BaVyBv2V.js +25 -0
- package/dist/chunks/index-BgbqX0uv.js +234 -0
- package/dist/chunks/index-BjQlt8g2.js +451 -0
- package/dist/chunks/index-BoNLj7PW.js +44 -0
- package/dist/chunks/index-BwIoRMDI.js +24 -0
- package/dist/chunks/index-Bwix1iwB.js +38 -0
- package/dist/chunks/index-BxVW3p3I.js +258 -0
- package/dist/chunks/index-ByKyGGzb.js +3 -0
- package/dist/chunks/index-ByTSSU1x.js +1 -0
- package/dist/chunks/index-C8LyfVmf.js +428 -0
- package/dist/chunks/index-CBbsFlsv.js +290 -0
- package/dist/chunks/index-CByffhPq.js +762 -0
- package/dist/chunks/index-CIbRjtWm.js +32 -0
- package/dist/chunks/index-CIzeMGQv.js +81 -0
- package/dist/chunks/index-CLGxSjLW.js +62 -0
- package/dist/chunks/index-CMF5Cawn.js +70 -0
- package/dist/chunks/index-CO-A2KY9.js +562 -0
- package/dist/chunks/index-CONgwvp0.js +12 -0
- package/dist/chunks/index-CT43RMOu.js +20 -0
- package/dist/chunks/index-CUgiXqyk.js +4 -0
- package/dist/chunks/index-C_K070Kn.js +284 -0
- package/dist/chunks/index-C_ttE2g9.js +16 -0
- package/dist/chunks/index-CcgnA1uu.js +29 -0
- package/dist/chunks/index-Cd7aoWrX.js +336 -0
- package/dist/chunks/index-Ce-pMzBC.js +19 -0
- package/dist/chunks/index-CgYZ66P_.js +187 -0
- package/dist/chunks/index-ChcHMeAS.js +35 -0
- package/dist/chunks/index-Cixdzvwd.js +682 -0
- package/dist/chunks/index-Clq0Fl19.js +15 -0
- package/dist/chunks/index-CtxTJnIm.js +58 -0
- package/dist/chunks/index-Cv1hWa_y.js +242 -0
- package/dist/chunks/index-CzTjJ8lP.js +432 -0
- package/dist/chunks/index-D0xRAPD5.js +75 -0
- package/dist/chunks/index-D25buhRm.js +79 -0
- package/dist/chunks/index-D4DqJG5_.js +16 -0
- package/dist/chunks/index-D5GGFPcS.js +251 -0
- package/dist/chunks/index-D5kiHJpX.js +413 -0
- package/dist/chunks/index-DEjRiv5B.js +1 -0
- package/dist/chunks/index-DFA4_spk.js +160 -0
- package/dist/chunks/index-DFZsXFsx.js +19 -0
- package/dist/chunks/index-DGauWNwV.js +156 -0
- package/dist/chunks/index-DIR0jr3f.js +127 -0
- package/dist/chunks/index-DNeagy5a.js +65 -0
- package/dist/chunks/index-DO0fmDR9.js +2 -0
- package/dist/chunks/index-DSitHBEh.js +36 -0
- package/dist/chunks/index-DW7ST0tB.js +1 -0
- package/dist/chunks/index-DWfFgEXE.js +110 -0
- package/dist/chunks/index-DX6WAtmo.js +874 -0
- package/dist/chunks/index-DaPfSuLu.js +393 -0
- package/dist/chunks/index-DbKlVm7y.js +74 -0
- package/dist/chunks/index-DdNZTQw1.js +48 -0
- package/dist/chunks/index-Dgi-m1K-.js +1 -0
- package/dist/chunks/index-Dh4uigJs.js +454 -0
- package/dist/chunks/index-DnYNK1mz.js +8 -0
- package/dist/chunks/index-DnierBep.js +510 -0
- package/dist/chunks/index-DnpSGM23.js +213 -0
- package/dist/chunks/index-DntY4deR.js +41 -0
- package/dist/chunks/index-Dp4A8k6C.js +103 -0
- package/dist/chunks/index-Dphpk5r2.js +1 -0
- package/dist/chunks/index-DrBrgF-v.js +49 -0
- package/dist/chunks/index-Dr_cqQ-_.js +77 -0
- package/dist/chunks/index-Dt-nBtrQ.js +26 -0
- package/dist/chunks/index-DurPWBYc.js +24 -0
- package/dist/chunks/index-DwTj7cFr.js +57 -0
- package/dist/chunks/index-DyZVAYLv.js +52 -0
- package/dist/chunks/index-Hh597Yu6.js +121 -0
- package/dist/chunks/index-LxkAuMAA.js +1 -0
- package/dist/chunks/index-OaQhvPR1.js +46 -0
- package/dist/chunks/index-UPr2EvBX.js +149 -0
- package/dist/chunks/index-WNXgzThF.js +2340 -0
- package/dist/chunks/index-WvrCPen2.js +367 -0
- package/dist/chunks/index-cTXIVcWI.js +45 -0
- package/dist/chunks/index-caLn8cu1.js +1603 -0
- package/dist/chunks/index-hLI0Bnyb.js +77 -0
- package/dist/chunks/index-jBse-XgU.js +35 -0
- package/dist/chunks/index-oTjafsfN.js +25 -0
- package/dist/chunks/index-qNmuI3RB.js +149 -0
- package/dist/chunks/index-qiqzy9MI.js +23 -0
- package/dist/chunks/index-rwBsdvh6.js +87 -0
- package/dist/chunks/index-siqJUhKa.js +1 -0
- package/dist/chunks/index-w-G9CFId.js +1 -0
- package/dist/chunks/index-x6SeA7x7.js +405 -0
- package/dist/chunks/loading-icon-DJQIjg97.js +23 -0
- package/dist/chunks/loading-icon-DhJR6LyV.js +16 -0
- package/dist/chunks/main-DUusfqrI.js +18 -0
- package/dist/chunks/main-Dp7wHHgP.js +2882 -0
- package/dist/chunks/on-BPhwbVf3.js +10 -0
- package/dist/chunks/on-XRofyGQh.js +1 -0
- package/dist/chunks/property-DCYiZTFF.js +5 -0
- package/dist/chunks/property-pT1z5CCa.js +36 -0
- package/dist/chunks/query-CbRg6ZGy.js +23 -0
- package/dist/chunks/query-Dh8joWcT.js +9 -0
- package/dist/chunks/radio-group-manager-DBAEmhDM.js +1 -0
- package/dist/chunks/radio-group-manager-Du5wSOS1.js +102 -0
- package/dist/chunks/register-BK21_Wkj.js +11035 -0
- package/dist/chunks/register-BMNs2hNG.js +2157 -0
- package/dist/chunks/register-Cc9zkA5F.js +1 -0
- package/dist/chunks/register-JwwMwdRf.js +1273 -0
- package/dist/chunks/search-C3raqgOD.js +30 -0
- package/dist/chunks/search-CIhxkVjj.js +239 -0
- package/dist/chunks/select-item-4aISXm0r.js +76 -0
- package/dist/chunks/select-item-DjoxYopL.js +356 -0
- package/dist/chunks/settings-BPOpwVkr.js +594 -0
- package/dist/chunks/settings-CK2vhfPu.js +78 -0
- package/dist/chunks/shared-enums-BJDDuJr7.js +1 -0
- package/dist/chunks/shared-enums-DALKjQ7e.js +7 -0
- package/dist/chunks/state-Bpmgn27B.js +12 -0
- package/dist/chunks/state-FnSmMH94.js +5 -0
- package/dist/chunks/text-input-BygzMjjc.js +371 -0
- package/dist/chunks/text-input-cjcnMXO-.js +68 -0
- package/dist/chunks/tooltip-content-BjdZJ_Ui.js +134 -0
- package/dist/chunks/tooltip-content-DHyDYyOH.js +3 -0
- package/dist/chunks/tslib.es6-B07k2eac.js +1 -0
- package/dist/chunks/tslib.es6-C5gLNDSD.js +160 -0
- package/dist/chunks/unsafe-html-C_MKAcuC.js +5 -0
- package/dist/chunks/unsafe-html-D49sIpJ5.js +27 -0
- package/dist/chunks/validity-BMLNCMmP.js +1 -0
- package/dist/chunks/validity-CTz7ivUd.js +59 -0
- package/dist/zooy.cjs.js +1 -0
- package/dist/zooy.es.js +4 -0
- package/package.json +57 -0
- package/src/sass/carbon-icons.scss +46 -0
- package/src/sass/main.scss +7 -0
- package/src/sass/panels.scss +642 -0
- package/src/sass/reset.scss +43 -0
- package/src/sass/split.scss +131 -0
|
@@ -0,0 +1,2882 @@
|
|
|
1
|
+
const numericString = "0123456789".split("");
|
|
2
|
+
[...numericString];
|
|
3
|
+
const alphaLower = "abcdefghijklmnopqrstuvwxyz".split(""), alphaUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
|
|
4
|
+
[...alphaLower, ...alphaUpper, ...numericString];
|
|
5
|
+
(/* @__PURE__ */ new Map()).set("true", !0).set("false", !1);
|
|
6
|
+
const compose = (...n) => (...e) => n.reduce((t, r) => (i) => t(r(i)))(...e), identity = (n) => n, whatType = (n) => typeof n, isDef = (n) => n !== void 0, isUndefined = (n) => n === void 0, isDefAndNotNull = (n) => n != null, isString = (n) => whatType(n) === "string", isNumber = (n) => whatType(n) === "number" && !Number.isNaN(
|
|
7
|
+
/** @type number */
|
|
8
|
+
n
|
|
9
|
+
), isArray = (n) => n instanceof Array, reverse = (n) => Array.from(n).reduce((e, t) => [t, ...e], []), toLowerCase = (n) => n.toLowerCase(), toString = (n) => n + "", toNumber = (n) => +n;
|
|
10
|
+
compose(toLowerCase, toString);
|
|
11
|
+
const makeRandomString = () => {
|
|
12
|
+
let n = 2147483648;
|
|
13
|
+
return Math.floor(Math.random() * n).toString(36) + Math.abs(Math.floor(Math.random() * n) ^ Date.now()).toString(36);
|
|
14
|
+
}, stripLeadingChar = (n) => (e) => e.startsWith(n) ? e.slice(n.length) : e, join = (n) => (e) => e.join(n);
|
|
15
|
+
compose(join(""), reverse);
|
|
16
|
+
const pathOr = (n, e) => (t) => {
|
|
17
|
+
const r = e.reduce((i, o) => {
|
|
18
|
+
try {
|
|
19
|
+
return i[maybeNumber(o)];
|
|
20
|
+
} catch {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
}, t);
|
|
24
|
+
return r === void 0 ? n : r;
|
|
25
|
+
}, randomId = (n) => {
|
|
26
|
+
const e = makeRandomString();
|
|
27
|
+
return n ? e.substr(0, n) : e;
|
|
28
|
+
}, maybeNumber = (n) => {
|
|
29
|
+
if (n === null || whatType(n) === "string" && n.length > 1 && n.startsWith("0") && !n.startsWith("0."))
|
|
30
|
+
return n;
|
|
31
|
+
const e = 1 * n;
|
|
32
|
+
return e > Number.MAX_SAFE_INTEGER || Number.isNaN(e) ? n : e;
|
|
33
|
+
};
|
|
34
|
+
compose(toNumber, join(""), reverse, toString);
|
|
35
|
+
class EVT extends EventTarget {
|
|
36
|
+
/**
|
|
37
|
+
* Set this to true to get some debug in the console.
|
|
38
|
+
* @type {boolean}
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
#e = !1;
|
|
42
|
+
/**
|
|
43
|
+
* True if this is disposed.
|
|
44
|
+
* @type {boolean}
|
|
45
|
+
* @private
|
|
46
|
+
*/
|
|
47
|
+
#t = !1;
|
|
48
|
+
/**
|
|
49
|
+
* A map of listener targets to an object of event: functions
|
|
50
|
+
* When adding a listener, immediately also create the un-listen functions
|
|
51
|
+
* and store those in an object keyed with the event.
|
|
52
|
+
* Store these objects against the target in a map
|
|
53
|
+
* @type {Map<!EventTarget, !Object<string, !Function>>}
|
|
54
|
+
* @private
|
|
55
|
+
*/
|
|
56
|
+
#s = /* @__PURE__ */ new Map();
|
|
57
|
+
/**
|
|
58
|
+
* A set of components that are currently listening to this component
|
|
59
|
+
* @type {!Set<!EventTarget>}
|
|
60
|
+
* @private
|
|
61
|
+
*/
|
|
62
|
+
#n = /* @__PURE__ */ new Set();
|
|
63
|
+
/**
|
|
64
|
+
* A set of interval timers as defined by setInterval.
|
|
65
|
+
* Use this to store the timers created particular to this component.
|
|
66
|
+
* On destruction, these timers will be cleared.
|
|
67
|
+
* @type {Set<any>}
|
|
68
|
+
* @private
|
|
69
|
+
*/
|
|
70
|
+
#r = /* @__PURE__ */ new Set();
|
|
71
|
+
//--[ Static ]--
|
|
72
|
+
/**
|
|
73
|
+
* Creates a CustomEvent with the given type and data payload.
|
|
74
|
+
* @param {string} event The event type
|
|
75
|
+
* @param {*} data The data to attach to the event's detail property
|
|
76
|
+
* @return {!CustomEvent} A new CustomEvent instance
|
|
77
|
+
*/
|
|
78
|
+
static makeEvent(e, t) {
|
|
79
|
+
return new CustomEvent(e, { detail: t });
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Sets debug mode for this component. When enabled, debug messages
|
|
83
|
+
* will be logged to the console.
|
|
84
|
+
* @param {boolean} bool True to enable debug mode, false to disable
|
|
85
|
+
* @throws {Error} If the value is not a boolean
|
|
86
|
+
*/
|
|
87
|
+
set debugMode(e) {
|
|
88
|
+
if (typeof e != "boolean")
|
|
89
|
+
throw new Error("This must be a boolean");
|
|
90
|
+
this.#e = e;
|
|
91
|
+
}
|
|
92
|
+
get debugMode() {
|
|
93
|
+
return this.#e;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Returns whether this component has been disposed.
|
|
97
|
+
* @return {boolean} True if disposed, false otherwise
|
|
98
|
+
*/
|
|
99
|
+
get disposed() {
|
|
100
|
+
return this.#t;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Returns the map of targets this component is listening to.
|
|
104
|
+
* @return {!Map<!EventTarget, !Object<string, !Function>>} Map of targets to their event handlers
|
|
105
|
+
*/
|
|
106
|
+
get listeningTo() {
|
|
107
|
+
return this.#s;
|
|
108
|
+
}
|
|
109
|
+
get isObservedBy() {
|
|
110
|
+
return this.#n;
|
|
111
|
+
}
|
|
112
|
+
get activeIntervals() {
|
|
113
|
+
return this.#r;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Logs debug messages to the console if debug mode is enabled.
|
|
117
|
+
* Automatically prepends the constructor name to the output.
|
|
118
|
+
* @param {...*} s Arguments to log
|
|
119
|
+
*/
|
|
120
|
+
debugMe(...e) {
|
|
121
|
+
this.#e && console.log.apply(null, [this.constructor.name, "DEBUG:", ...e]);
|
|
122
|
+
}
|
|
123
|
+
//--[ Listeners and Listening ]--
|
|
124
|
+
/**
|
|
125
|
+
* @param {!EventTarget|!EVT} comp
|
|
126
|
+
*/
|
|
127
|
+
isListenedToBy(e) {
|
|
128
|
+
this.#n.add(e);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* WARNING! DO not use options={passive: true} here. It disallows us to
|
|
132
|
+
* use preventDefault when we intercept the form, and that leads to a
|
|
133
|
+
* general inability to POST forms.
|
|
134
|
+
*
|
|
135
|
+
* @param {!EventTarget|!EVT|!Node} target
|
|
136
|
+
* @param {string} event
|
|
137
|
+
* @param {!Function} action
|
|
138
|
+
* @param {boolean|!Object} options
|
|
139
|
+
*/
|
|
140
|
+
listen(e, t, r, i = !1) {
|
|
141
|
+
e.addEventListener(t, r, i);
|
|
142
|
+
const o = this.listeningTo.get(e) || {};
|
|
143
|
+
o[t] = () => e.removeEventListener(t, r, i), this.listeningTo.set(e, o), isDef(e.isListenedToBy) && e.isListenedToBy(this);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Remove self from all components tt are listening to me.
|
|
147
|
+
*/
|
|
148
|
+
stopBeingListenedTo() {
|
|
149
|
+
for (const e of this.isObservedBy)
|
|
150
|
+
e.stopListeningTo(this), this.isObservedBy.delete(e);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Stop listening to all events on target.
|
|
154
|
+
* @param {!EventTarget|!EVT|!Node|undefined} target
|
|
155
|
+
* @param {string=} opt_event
|
|
156
|
+
*/
|
|
157
|
+
stopListeningTo(e, t) {
|
|
158
|
+
e && this.listeningTo.has(e) && (isDef(e.isObservedBy) && e.isObservedBy.delete(this), isDef(t) ? (Object.entries(this.listeningTo.get(e)).forEach(([r, i]) => {
|
|
159
|
+
r === t && i();
|
|
160
|
+
}), Object.keys(this.listeningTo.get(e)).length || this.listeningTo.delete(e)) : (Object.values(this.listeningTo.get(e)).forEach((r) => r()), this.listeningTo.delete(e)));
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Removes all the event listeners that is managed by this
|
|
164
|
+
* component.
|
|
165
|
+
*/
|
|
166
|
+
removeAllListener() {
|
|
167
|
+
for (const e of this.listeningTo.keys())
|
|
168
|
+
this.stopListeningTo(e);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Clears all active intervals created by this component.
|
|
172
|
+
* Called automatically during disposal to prevent memory leaks.
|
|
173
|
+
*/
|
|
174
|
+
clearAllIntervals() {
|
|
175
|
+
for (const e of this.activeIntervals)
|
|
176
|
+
clearInterval(e), this.activeIntervals.delete(e);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Executes a function repeatedly at a specified interval.
|
|
180
|
+
* The interval is tracked and will be automatically cleared on disposal.
|
|
181
|
+
* @param {!Function} f The function to execute on each interval
|
|
182
|
+
* @param {number} interval The interval in milliseconds
|
|
183
|
+
*/
|
|
184
|
+
doOnBeat(e, t) {
|
|
185
|
+
const r = setInterval(e, t);
|
|
186
|
+
this.activeIntervals.add(r);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Disposes of the component. Calls `exitDocument`, which is expected to
|
|
190
|
+
* remove event handlers and clean up the component. Propagates the call to
|
|
191
|
+
* the component's children, if any. Removes the component's DOM from the
|
|
192
|
+
* document unless it was decorated.
|
|
193
|
+
* @protected
|
|
194
|
+
*/
|
|
195
|
+
disposeInternal() {
|
|
196
|
+
this.stopBeingListenedTo(), this.removeAllListener(), this.clearAllIntervals(), this.#t = !0;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Public method to dispose of this component. Calls disposeInternal()
|
|
200
|
+
* to perform cleanup of event listeners, intervals, and observers.
|
|
201
|
+
* After calling this method, the component should not be used.
|
|
202
|
+
*/
|
|
203
|
+
dispose() {
|
|
204
|
+
this.disposeInternal();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
class ZooyEventData {
|
|
208
|
+
constructor(e, t) {
|
|
209
|
+
this.value_ = e, this.data_ = t || {};
|
|
210
|
+
}
|
|
211
|
+
getValue() {
|
|
212
|
+
return this.value_;
|
|
213
|
+
}
|
|
214
|
+
getData() {
|
|
215
|
+
return this.data_;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const UiEventType = {
|
|
219
|
+
/**
|
|
220
|
+
* Dispatched after the content from the template is in the DOM
|
|
221
|
+
* and the in-line scripts from the AJAX call has been eval'd.
|
|
222
|
+
*/
|
|
223
|
+
COMP: randomId(),
|
|
224
|
+
COMP_DRAG_START: randomId(),
|
|
225
|
+
COMP_DRAG_MOVE: randomId(),
|
|
226
|
+
COMP_DRAG_END: randomId(),
|
|
227
|
+
PANEL: randomId(),
|
|
228
|
+
VIEW: randomId(),
|
|
229
|
+
SPLIT: randomId(),
|
|
230
|
+
SPLIT_WILL_OPEN: randomId(),
|
|
231
|
+
SPLIT_DID_OPEN: randomId(),
|
|
232
|
+
SPLIT_WILL_CLOSE: randomId(),
|
|
233
|
+
SPLIT_DID_CLOSE: randomId(),
|
|
234
|
+
SPLIT_TRANSITION_END: randomId(),
|
|
235
|
+
READY: randomId(),
|
|
236
|
+
PANEL_MINIMIZE: randomId(),
|
|
237
|
+
FORM_SUBMIT_SUCCESS: randomId()
|
|
238
|
+
};
|
|
239
|
+
var EN_US = ["second", "minute", "hour", "day", "week", "month", "year"];
|
|
240
|
+
function en_US(n, e) {
|
|
241
|
+
if (e === 0)
|
|
242
|
+
return ["just now", "right now"];
|
|
243
|
+
var t = EN_US[Math.floor(e / 2)];
|
|
244
|
+
return n > 1 && (t += "s"), [n + " " + t + " ago", "in " + n + " " + t];
|
|
245
|
+
}
|
|
246
|
+
var ZH_CN = ["秒", "分钟", "小时", "天", "周", "个月", "年"];
|
|
247
|
+
function zh_CN(n, e) {
|
|
248
|
+
if (e === 0)
|
|
249
|
+
return ["刚刚", "片刻后"];
|
|
250
|
+
var t = ZH_CN[~~(e / 2)];
|
|
251
|
+
return [n + " " + t + "前", n + " " + t + "后"];
|
|
252
|
+
}
|
|
253
|
+
var Locales = {}, register = function(n, e) {
|
|
254
|
+
Locales[n] = e;
|
|
255
|
+
}, getLocale = function(n) {
|
|
256
|
+
return Locales[n] || Locales.en_US;
|
|
257
|
+
}, SEC_ARRAY = [
|
|
258
|
+
60,
|
|
259
|
+
60,
|
|
260
|
+
24,
|
|
261
|
+
7,
|
|
262
|
+
365 / 7 / 12,
|
|
263
|
+
12
|
|
264
|
+
];
|
|
265
|
+
function toDate(n) {
|
|
266
|
+
return n instanceof Date ? n : !isNaN(n) || /^\d+$/.test(n) ? new Date(parseInt(n)) : (n = (n || "").trim().replace(/\.\d+/, "").replace(/-/, "/").replace(/-/, "/").replace(/(\d)T(\d)/, "$1 $2").replace(/Z/, " UTC").replace(/([+-]\d\d):?(\d\d)/, " $1$2"), new Date(n));
|
|
267
|
+
}
|
|
268
|
+
function formatDiff(n, e) {
|
|
269
|
+
var t = n < 0 ? 1 : 0;
|
|
270
|
+
n = Math.abs(n);
|
|
271
|
+
for (var r = n, i = 0; n >= SEC_ARRAY[i] && i < SEC_ARRAY.length; i++)
|
|
272
|
+
n /= SEC_ARRAY[i];
|
|
273
|
+
return n = Math.floor(n), i *= 2, n > (i === 0 ? 9 : 1) && (i += 1), e(n, i, r)[t].replace("%s", n.toString());
|
|
274
|
+
}
|
|
275
|
+
function diffSec(n, e) {
|
|
276
|
+
var t = /* @__PURE__ */ new Date();
|
|
277
|
+
return (+t - +toDate(n)) / 1e3;
|
|
278
|
+
}
|
|
279
|
+
var format = function(n, e, t) {
|
|
280
|
+
var r = diffSec(n);
|
|
281
|
+
return formatDiff(r, getLocale(e));
|
|
282
|
+
};
|
|
283
|
+
register("en_US", en_US);
|
|
284
|
+
register("zh_CN", zh_CN);
|
|
285
|
+
const getInputChecked_ = function(n) {
|
|
286
|
+
return n.checked ? (
|
|
287
|
+
/** @type {?} */
|
|
288
|
+
n.value
|
|
289
|
+
) : null;
|
|
290
|
+
}, getSelectSingle_ = function(n) {
|
|
291
|
+
const e = (
|
|
292
|
+
/** @type {!HTMLSelectElement} */
|
|
293
|
+
n.selectedIndex
|
|
294
|
+
);
|
|
295
|
+
return e >= 0 ? (
|
|
296
|
+
/** @type {!HTMLSelectElement} */
|
|
297
|
+
n.options[e].value
|
|
298
|
+
) : null;
|
|
299
|
+
}, getSelectMultiple_ = function(n) {
|
|
300
|
+
const e = [];
|
|
301
|
+
for (let t, r = 0; t = /** @type {!HTMLSelectElement} */
|
|
302
|
+
n.options[r]; r++)
|
|
303
|
+
t.selected && e.push(t.value);
|
|
304
|
+
return e.length ? e : null;
|
|
305
|
+
}, getValue = function(n) {
|
|
306
|
+
const e = (
|
|
307
|
+
/** @type {!HTMLInputElement} */
|
|
308
|
+
n.type
|
|
309
|
+
);
|
|
310
|
+
switch (isString(e) && e.toLowerCase()) {
|
|
311
|
+
case "checkbox":
|
|
312
|
+
case "radio":
|
|
313
|
+
return getInputChecked_(n);
|
|
314
|
+
case "select-one":
|
|
315
|
+
return getSelectSingle_(n);
|
|
316
|
+
case "select-multiple":
|
|
317
|
+
return getSelectMultiple_(n);
|
|
318
|
+
default:
|
|
319
|
+
return n.value !== null && n.value !== void 0 ? n.value : null;
|
|
320
|
+
}
|
|
321
|
+
}, formToJSON = (n) => [...n].reduce((e, t) => (e[t.name] = t.value, e), {}), replaceNode = (n, e) => {
|
|
322
|
+
const t = e.parentNode;
|
|
323
|
+
t && t.replaceChild(n, e);
|
|
324
|
+
}, removeNode = (n) => n && n.parentNode ? n.parentNode.removeChild(n) : null, insertSiblingAfter = (n, e) => {
|
|
325
|
+
e.parentNode && e.parentNode.insertBefore(n, e.nextSibling);
|
|
326
|
+
}, isInPage = (n) => n === document.body ? !1 : document.body.contains(n), getPos = (n) => [n.offsetLeft, n.offsetTop], randomColour = (n) => [1, 2, 3].map(() => Math.floor(Math.random() * 256) + 1).reduce((e, t) => `${e}${t},`, "rgba(") + `${n || 0.5}`, totalWidth = (n) => {
|
|
327
|
+
const e = window.getComputedStyle(n), t = n.offsetWidth, r = parseFloat(e.marginLeft) + parseFloat(e.marginRight), i = parseFloat(e.paddingLeft) + parseFloat(e.paddingRight), o = parseFloat(e.borderLeftWidth) + parseFloat(e.borderRightWidth);
|
|
328
|
+
return t + r - i + o;
|
|
329
|
+
}, totalHeight = (n) => {
|
|
330
|
+
const e = window.getComputedStyle(n), t = n.offsetHeight, r = parseFloat(e.marginTop) + parseFloat(e.marginBottom), i = parseFloat(e.paddingTop) + parseFloat(e.paddingBottom), o = parseFloat(e.borderTopWidth) + parseFloat(e.borderBottomWidth);
|
|
331
|
+
return t + r - i + o;
|
|
332
|
+
}, evalScripts = (comp) => (arr) => {
|
|
333
|
+
arr && Array.from(arr).forEach((s) => {
|
|
334
|
+
(function() {
|
|
335
|
+
eval(s.text);
|
|
336
|
+
}).bind(comp)();
|
|
337
|
+
});
|
|
338
|
+
}, evalModules = (n) => (e) => {
|
|
339
|
+
e && Array.from(e).forEach((t) => {
|
|
340
|
+
(function() {
|
|
341
|
+
const r = document.createElement("script");
|
|
342
|
+
r.type = "module", r.textContent = t.textContent;
|
|
343
|
+
const i = n.getElement(), o = getElDataMap(t).zv_comp_access;
|
|
344
|
+
o && (i.zvComponent = n, i.classList.add(o)), i.appendChild(r);
|
|
345
|
+
}).bind(n)();
|
|
346
|
+
});
|
|
347
|
+
}, splitScripts = (n) => {
|
|
348
|
+
const t = new DOMParser().parseFromString(n, "text/html");
|
|
349
|
+
return {
|
|
350
|
+
html: t.body.firstElementChild,
|
|
351
|
+
scripts: t.querySelectorAll('script:not([type="module"])'),
|
|
352
|
+
modules: t.querySelectorAll('script[type="module"]')
|
|
353
|
+
};
|
|
354
|
+
}, handleTemplateProm = (n) => Promise.resolve(splitScripts(n)), enableClass = (n, e, t) => {
|
|
355
|
+
t ? n.classList.add(e) : n.classList.remove(e);
|
|
356
|
+
}, toggleClass = (n, e) => {
|
|
357
|
+
const t = !Array.from(n.classList).includes(e);
|
|
358
|
+
return enableClass(n, e, t), t;
|
|
359
|
+
}, getElDataMap = (n) => n ? Object.assign({}, n.dataset || {}) : {}, dtFormatter = {
|
|
360
|
+
year: "numeric",
|
|
361
|
+
month: "short",
|
|
362
|
+
day: "numeric",
|
|
363
|
+
hour: "2-digit",
|
|
364
|
+
minute: "2-digit",
|
|
365
|
+
second: "2-digit"
|
|
366
|
+
}, dFormatter = {
|
|
367
|
+
year: "numeric",
|
|
368
|
+
month: "short",
|
|
369
|
+
day: "numeric"
|
|
370
|
+
}, dtf = new Intl.DateTimeFormat("en", {
|
|
371
|
+
day: "2-digit",
|
|
372
|
+
month: "short",
|
|
373
|
+
year: "2-digit",
|
|
374
|
+
hour: "2-digit",
|
|
375
|
+
minute: "2-digit",
|
|
376
|
+
second: "2-digit",
|
|
377
|
+
// hour12: false
|
|
378
|
+
hourCycle: "h23"
|
|
379
|
+
}), dateToZooyStdTimeString = (n) => {
|
|
380
|
+
const [{ value: e }, , { value: t }, , { value: r }, , { value: i }, , { value: o }, , { value: a }, ,] = dtf.formatToParts(n);
|
|
381
|
+
return `${t} ${e} ${r} ${i}:${o}:${a}`;
|
|
382
|
+
}, parseMomentAgo = (n, e = "both", t = void 0) => {
|
|
383
|
+
let r = n;
|
|
384
|
+
isNumber(r) && r < 9466848e5 ? r = r * 1e3 : t && (r = r + t);
|
|
385
|
+
const i = new Date(r), o = format(i), a = dateToZooyStdTimeString(i);
|
|
386
|
+
switch (e) {
|
|
387
|
+
case "time|ago":
|
|
388
|
+
return `${a}|${o}`;
|
|
389
|
+
case "ago":
|
|
390
|
+
return `${o}`;
|
|
391
|
+
case "datetime":
|
|
392
|
+
case "time":
|
|
393
|
+
return `${a}`;
|
|
394
|
+
case "both":
|
|
395
|
+
case "ago (time)":
|
|
396
|
+
default:
|
|
397
|
+
return `${o} (${a})`;
|
|
398
|
+
}
|
|
399
|
+
}, parseObfuscated = (n) => {
|
|
400
|
+
const t = ((i) => (o) => o < i.length - 4)(n);
|
|
401
|
+
return ((i) => i.split("").map((o, a) => t(a) ? "*" : o).join(""))(n);
|
|
402
|
+
}, parseMap = (/* @__PURE__ */ new Map()).set(void 0, (n, e, t) => identity(n)).set("", (n, e, t) => identity(n)).set("class_update_only", (n, e, t) => identity(n)).set("frac_100", (n, e, t) => Math.round(n * 100 * 100) / 100).set("date_and_time", (n, e, t) => new Date(n).toLocaleString(void 0, dtFormatter)).set("date", (n, e, t) => new Date(n).toLocaleString(void 0, dFormatter)).set("moment_ago", (n, e, t) => parseMomentAgo(n, "both", t.zdd_date_tz || void 0)).set("moment_ago_only", (n, e, t) => parseMomentAgo(n, "ago", t.zdd_date_tz || void 0)).set("moment_ago_datetime", (n, e, t) => parseMomentAgo(n, "datetime", t.zdd_date_tz || void 0)).set("pretty-json", (n, e, t) => JSON.stringify(n, null, 4)).set("obfuscated", (n, e, t) => parseObfuscated(n)).set("linear-progress", (n, e, t) => {
|
|
403
|
+
const r = toNumber(t.zpmax), i = e.linProg;
|
|
404
|
+
return r && i && (i.progress = n / r, n = void 0), n;
|
|
405
|
+
}), parseDataPropKvDef = (n) => n.trim().split(",").map((r) => r.trim()).map((r) => {
|
|
406
|
+
const [i, o] = r.split(":").map((a) => a.trim());
|
|
407
|
+
return [i, o];
|
|
408
|
+
}).filter(([r, i]) => r !== "" && i !== ""), parseDataProps = (n, e) => {
|
|
409
|
+
const t = getElDataMap(n);
|
|
410
|
+
delete n.dataset.zoodatapropkv;
|
|
411
|
+
const r = t.zoodatapropkv || "";
|
|
412
|
+
return parseDataPropKvDef(r).forEach(([o, a]) => {
|
|
413
|
+
n.dataset[o] = pathOr(void 0, a.split("."))(e);
|
|
414
|
+
}), n;
|
|
415
|
+
}, mapDataToEls = (n, e, t = /* @__PURE__ */ new Map(), r = void 0) => {
|
|
416
|
+
if (!e)
|
|
417
|
+
return;
|
|
418
|
+
const i = new Map([...parseMap, ...t]);
|
|
419
|
+
[...n.querySelectorAll("[data-zdd],[data-zdd_class_update],[data-zdd_template_loop_index]")].forEach((o) => {
|
|
420
|
+
const a = getElDataMap(o), p = a.zdd || "", f = a.zdd_parse_as, _ = a.zdd_render_effect || "", g = a.zdd_class_update, v = a.zdd_template_loop_index, c = a.zdd_units || "";
|
|
421
|
+
let l = pathOr(void 0, p.split("."))(e);
|
|
422
|
+
if (isDefAndNotNull(l) && (l = i.has(f) ? i.get(f)(l, o, a) : l), v && isNumber(r) && (console.log("loopIndex: ", v, " opt_loopIndex: ", r), v === "zero_indexed" ? o.innerHTML = r : v === "one_indexed" ? o.innerHTML = r + 1 : o.innerHTML = r), g) {
|
|
423
|
+
const [d, h, u] = g.split(":"), m = pathOr(void 0, d.split("."))(e);
|
|
424
|
+
if (isDefAndNotNull(m))
|
|
425
|
+
switch (h) {
|
|
426
|
+
case "swap": {
|
|
427
|
+
const E = /\{}/gi, y = u.replace(E, ""), T = u.replace(E, m);
|
|
428
|
+
o.classList.remove(...[...o.classList].filter((b) => b.includes(y))), o.classList.add(T);
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
case "remove_on_data":
|
|
432
|
+
o.classList.remove(u);
|
|
433
|
+
break;
|
|
434
|
+
case "choice_map":
|
|
435
|
+
u.split("|").filter((E) => E !== "").forEach((E) => {
|
|
436
|
+
const [y, T] = E.split(">");
|
|
437
|
+
y === m + "" ? o.classList.add.apply(o.classList, T.split(".")) : o.classList.remove.apply(o.classList, T.split("."));
|
|
438
|
+
});
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (f === "templated-array" && isArray(l))
|
|
443
|
+
if (l.length === 0)
|
|
444
|
+
o.classList.add("display__none");
|
|
445
|
+
else {
|
|
446
|
+
o.classList.remove("display__none"), l = l || [];
|
|
447
|
+
const d = o.firstElementChild;
|
|
448
|
+
d.classList.remove("display__none"), o.replaceChildren(...l.map((h, u) => {
|
|
449
|
+
const m = d.cloneNode(!0), E = parseDataProps(m, h);
|
|
450
|
+
return mapDataToEls(E, h, void 0, u), E;
|
|
451
|
+
}));
|
|
452
|
+
}
|
|
453
|
+
else if (isDefAndNotNull(l))
|
|
454
|
+
if (_ === "typewriter") {
|
|
455
|
+
let d = 0;
|
|
456
|
+
const h = l + c, u = 5, m = () => {
|
|
457
|
+
d < h.length && (o.innerHTML += h.charAt(d), d++, setTimeout(m, u));
|
|
458
|
+
};
|
|
459
|
+
o.innerHTML = "", m();
|
|
460
|
+
} else
|
|
461
|
+
o.innerHTML = l + c;
|
|
462
|
+
});
|
|
463
|
+
}, domUtils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
464
|
+
__proto__: null,
|
|
465
|
+
dateToZooyStdTimeString,
|
|
466
|
+
dtf,
|
|
467
|
+
enableClass,
|
|
468
|
+
evalModules,
|
|
469
|
+
evalScripts,
|
|
470
|
+
formToJSON,
|
|
471
|
+
getElDataMap,
|
|
472
|
+
getPos,
|
|
473
|
+
getValue,
|
|
474
|
+
handleTemplateProm,
|
|
475
|
+
insertSiblingAfter,
|
|
476
|
+
isInPage,
|
|
477
|
+
mapDataToEls,
|
|
478
|
+
parseDataPropKvDef,
|
|
479
|
+
parseMap,
|
|
480
|
+
parseMomentAgo,
|
|
481
|
+
randomColour,
|
|
482
|
+
removeNode,
|
|
483
|
+
replaceNode,
|
|
484
|
+
splitScripts,
|
|
485
|
+
toggleClass,
|
|
486
|
+
totalHeight,
|
|
487
|
+
totalWidth
|
|
488
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
489
|
+
class ComponentLibraryRegistry {
|
|
490
|
+
/**
|
|
491
|
+
* Internal storage for registered component libraries.
|
|
492
|
+
* @type {Map<string, {render: Function, cache: Map, config: Object}>}
|
|
493
|
+
* @private
|
|
494
|
+
*/
|
|
495
|
+
static #e = /* @__PURE__ */ new Map();
|
|
496
|
+
/**
|
|
497
|
+
* Registers a pluggable component library.
|
|
498
|
+
*
|
|
499
|
+
* @param {string} name - Library identifier (e.g., 'carbon', 'material')
|
|
500
|
+
* @param {Object} library - Library configuration object
|
|
501
|
+
* @param {Function} library.render - Main render function: async function(panel, cache)
|
|
502
|
+
* @param {Function} [library.dispose] - Optional cleanup function: function(element)
|
|
503
|
+
* @param {Object} [library.config] - Optional library-specific configuration
|
|
504
|
+
*
|
|
505
|
+
* @throws {Error} If library doesn't provide a render function
|
|
506
|
+
*
|
|
507
|
+
* @example
|
|
508
|
+
* ComponentLibraryRegistry.register('carbon', {
|
|
509
|
+
* render: async function(panel, cache) {
|
|
510
|
+
* // Scan, load, and attach components
|
|
511
|
+
* },
|
|
512
|
+
* dispose: function(element) {
|
|
513
|
+
* // Optional cleanup logic
|
|
514
|
+
* },
|
|
515
|
+
* config: { version: '2.0' }
|
|
516
|
+
* });
|
|
517
|
+
*/
|
|
518
|
+
static register(e, t) {
|
|
519
|
+
if (!t.render)
|
|
520
|
+
throw new Error(`Component library '${e}' must provide a render function`);
|
|
521
|
+
this.#e.set(e, {
|
|
522
|
+
...t,
|
|
523
|
+
// Preserve all library properties (utils, helpers, etc.)
|
|
524
|
+
dispose: t.dispose || null,
|
|
525
|
+
// Optional disposal function
|
|
526
|
+
cache: /* @__PURE__ */ new Map(),
|
|
527
|
+
// Each library gets its own import cache
|
|
528
|
+
config: t.config || {}
|
|
529
|
+
}), console.debug(`[ComponentLibraryRegistry] Registered: ${e}`);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Gets a registered component library by name.
|
|
533
|
+
*
|
|
534
|
+
* @param {string} name - Library identifier
|
|
535
|
+
* @returns {Object} Library object with render, cache, config
|
|
536
|
+
* @throws {Error} If library is not registered
|
|
537
|
+
*/
|
|
538
|
+
static get(e) {
|
|
539
|
+
if (!this.#e.has(e)) {
|
|
540
|
+
const t = Array.from(this.#e.keys()).join(", ");
|
|
541
|
+
throw new Error(
|
|
542
|
+
`Component library '${e}' is not registered. Available: ${t || "none"}`
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
return this.#e.get(e);
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Checks if a component library is registered.
|
|
549
|
+
*
|
|
550
|
+
* @param {string} name - Library identifier
|
|
551
|
+
* @returns {boolean} True if registered
|
|
552
|
+
*/
|
|
553
|
+
static has(e) {
|
|
554
|
+
return this.#e.has(e);
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Gets all registered library names.
|
|
558
|
+
*
|
|
559
|
+
* @returns {string[]} Array of registered library names
|
|
560
|
+
*/
|
|
561
|
+
static getRegisteredLibraries() {
|
|
562
|
+
return Array.from(this.#e.keys());
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Gets cache statistics for all registered component libraries.
|
|
566
|
+
* Useful for debugging and monitoring.
|
|
567
|
+
*
|
|
568
|
+
* @returns {Object} Map of library name → cache stats
|
|
569
|
+
*/
|
|
570
|
+
static getCacheStats() {
|
|
571
|
+
const e = {};
|
|
572
|
+
for (const [t, r] of this.#e.entries())
|
|
573
|
+
e[t] = {
|
|
574
|
+
cacheSize: r.cache.size,
|
|
575
|
+
config: r.config
|
|
576
|
+
};
|
|
577
|
+
return e;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Clears import caches for all component libraries.
|
|
581
|
+
* Useful for development/hot-reload scenarios.
|
|
582
|
+
*/
|
|
583
|
+
static clearAllCaches() {
|
|
584
|
+
for (const [e, t] of this.#e.entries())
|
|
585
|
+
t.cache.clear(), console.debug(`[ComponentLibraryRegistry] Cleared cache for: ${e}`);
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Clears cache for a specific library.
|
|
589
|
+
*
|
|
590
|
+
* @param {string} name - Library identifier
|
|
591
|
+
* @throws {Error} If library is not registered
|
|
592
|
+
*/
|
|
593
|
+
static clearCache(e) {
|
|
594
|
+
this.get(e).cache.clear(), console.debug(`[ComponentLibraryRegistry] Cleared cache for: ${e}`);
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Unregisters a component library (primarily for testing).
|
|
598
|
+
*
|
|
599
|
+
* @param {string} name - Library identifier
|
|
600
|
+
* @returns {boolean} True if library was unregistered, false if not found
|
|
601
|
+
*/
|
|
602
|
+
static unregister(e) {
|
|
603
|
+
return this.#e.delete(e);
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Unregisters all component libraries (primarily for testing).
|
|
607
|
+
*/
|
|
608
|
+
static unregisterAll() {
|
|
609
|
+
this.#e.clear();
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
const ComponentError = {
|
|
613
|
+
/**
|
|
614
|
+
* Error when a method is not supported.
|
|
615
|
+
*/
|
|
616
|
+
NOT_SUPPORTED: "Method not supported",
|
|
617
|
+
/**
|
|
618
|
+
* Error when the given element can not be decorated.
|
|
619
|
+
*/
|
|
620
|
+
DECORATE_INVALID: "Invalid element to decorate",
|
|
621
|
+
/**
|
|
622
|
+
* Error when the component is already rendered and another render attempt is
|
|
623
|
+
* made.
|
|
624
|
+
*/
|
|
625
|
+
ALREADY_RENDERED: "Component already rendered",
|
|
626
|
+
/**
|
|
627
|
+
* Error when an already disposed component is attempted to be rendered.
|
|
628
|
+
*/
|
|
629
|
+
ALREADY_DISPOSED: "Component already disposed",
|
|
630
|
+
/**
|
|
631
|
+
* Error when an attempt is made to set the parent of a component in a way
|
|
632
|
+
* that would result in an inconsistent object graph.
|
|
633
|
+
*/
|
|
634
|
+
PARENT_UNABLE_TO_BE_SET: "Unable to set parent component",
|
|
635
|
+
/**
|
|
636
|
+
* Error when an attempt is made to add a child component at an out-of-bounds
|
|
637
|
+
* index. We don't support sparse child arrays.
|
|
638
|
+
*/
|
|
639
|
+
CHILD_INDEX_OUT_OF_BOUNDS: "Child component index out of bounds",
|
|
640
|
+
/**
|
|
641
|
+
* Error when an attempt is made to remove a child component from a component
|
|
642
|
+
* other than its parent.
|
|
643
|
+
*/
|
|
644
|
+
NOT_OUR_CHILD: "Child is not in parent component",
|
|
645
|
+
/**
|
|
646
|
+
* Error when an operation requiring DOM interaction is made when the
|
|
647
|
+
* component is not in the document
|
|
648
|
+
*/
|
|
649
|
+
NOT_IN_DOCUMENT: "Operation not supported while component is not in document",
|
|
650
|
+
/**
|
|
651
|
+
* Error when an invalid component state is encountered.
|
|
652
|
+
*/
|
|
653
|
+
STATE_INVALID: "Invalid component state"
|
|
654
|
+
};
|
|
655
|
+
class Component extends EVT {
|
|
656
|
+
//--[ Static ]--
|
|
657
|
+
/**
|
|
658
|
+
* Returns the component event type code used for dispatching component events.
|
|
659
|
+
* @return {string} The COMP event type
|
|
660
|
+
*/
|
|
661
|
+
static compEventCode() {
|
|
662
|
+
return UiEventType.COMP;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Returns the component ready event type code.
|
|
666
|
+
* @return {string} The READY event type
|
|
667
|
+
*/
|
|
668
|
+
static compReadyCode() {
|
|
669
|
+
return UiEventType.READY;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Returns the component error enumeration object.
|
|
673
|
+
* @return {!Object<string, string>} The ComponentError enum
|
|
674
|
+
*/
|
|
675
|
+
static compErrors() {
|
|
676
|
+
return ComponentError;
|
|
677
|
+
}
|
|
678
|
+
#e;
|
|
679
|
+
#t;
|
|
680
|
+
#s;
|
|
681
|
+
#n;
|
|
682
|
+
#r;
|
|
683
|
+
#o;
|
|
684
|
+
#i;
|
|
685
|
+
#a;
|
|
686
|
+
constructor() {
|
|
687
|
+
super(), this.#e = () => (
|
|
688
|
+
/** @type {!HTMLElement} */
|
|
689
|
+
document.createElement("div")
|
|
690
|
+
), this.#t = !1, this.#s = void 0, this.#n = void 0, this.#r = void 0, this.#o = void 0, this.#i = /* @__PURE__ */ new Map(), this.outOfTreeElements = [], this.placeholderDom = void 0, this.#a = () => {
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
//--[ Getters and Setters ]--
|
|
694
|
+
/**
|
|
695
|
+
* @param {Element|undefined} e
|
|
696
|
+
*/
|
|
697
|
+
set target(e) {
|
|
698
|
+
this.#r = e;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* @returns {Element|undefined}
|
|
702
|
+
*/
|
|
703
|
+
get target() {
|
|
704
|
+
return this.#r;
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Sets the model associated with the UI component.
|
|
708
|
+
* @param {*} m
|
|
709
|
+
*/
|
|
710
|
+
set model(e) {
|
|
711
|
+
this.#n = e;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Returns the model associated with the UI component.
|
|
715
|
+
* @return {*}
|
|
716
|
+
*/
|
|
717
|
+
get model() {
|
|
718
|
+
return this.#n;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* @param {boolean} bool
|
|
722
|
+
*/
|
|
723
|
+
set isInDocument(e) {
|
|
724
|
+
this.#t = e;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* @return {boolean}
|
|
728
|
+
*/
|
|
729
|
+
get isInDocument() {
|
|
730
|
+
return this.#t;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* A function that makes a DOM element.
|
|
734
|
+
* @param {!function():(!HTMLElement|!Element|!DocumentFragment)} func
|
|
735
|
+
*/
|
|
736
|
+
set domFunc(e) {
|
|
737
|
+
this.#e = e;
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* @param {!Function} func A callback guaranteed to fire after the panels is
|
|
741
|
+
* ready, and in the document, but before the
|
|
742
|
+
* {@code UiEventType.READY} event is fired.
|
|
743
|
+
*/
|
|
744
|
+
set readyFunc(e) {
|
|
745
|
+
this.#a = e;
|
|
746
|
+
}
|
|
747
|
+
//--[ DOM Management ]--
|
|
748
|
+
/**
|
|
749
|
+
* Internal method to set the component's root element. Used primarily during
|
|
750
|
+
* rendering and re-rendering operations.
|
|
751
|
+
* @param {!Node} frag The DOM node to set as this component's element
|
|
752
|
+
* @private
|
|
753
|
+
*/
|
|
754
|
+
setElementInternal_(e) {
|
|
755
|
+
this.#s = e;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Gets the component's element.
|
|
759
|
+
* @return {!HTMLElement|undefined} The element
|
|
760
|
+
* for the component.
|
|
761
|
+
*/
|
|
762
|
+
getElement() {
|
|
763
|
+
return this.#s;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Creates the initial DOM representation for the component. The default
|
|
767
|
+
* implementation is to set this.element_ = div.
|
|
768
|
+
*/
|
|
769
|
+
createDom() {
|
|
770
|
+
this.#s = this.#e();
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Asserts that this component can be rendered asynchronously.
|
|
774
|
+
* Throws an error if the component has already been disposed.
|
|
775
|
+
* Used by async rendering methods to validate component state.
|
|
776
|
+
* @throws {Error} If the component is already disposed
|
|
777
|
+
*/
|
|
778
|
+
assertCanRenderAsync() {
|
|
779
|
+
if (this.disposed)
|
|
780
|
+
throw new Error(ComponentError.ALREADY_DISPOSED);
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Renders the component. If a parent element is supplied, the component's
|
|
784
|
+
* element will be appended to it. If there is no optional parent element and
|
|
785
|
+
* the element doesn't have a parentNode then it will be appended to the
|
|
786
|
+
* document body.
|
|
787
|
+
*
|
|
788
|
+
* If this component has a parent component, and the parent component is
|
|
789
|
+
* not in the document already, then this will not call `enterDocument`
|
|
790
|
+
* on this component.
|
|
791
|
+
*
|
|
792
|
+
* Throws an Error if the component is already rendered.
|
|
793
|
+
*
|
|
794
|
+
* @param {Element=} opt_parentElement Optional parent element to render the
|
|
795
|
+
* component into.
|
|
796
|
+
*/
|
|
797
|
+
render(e) {
|
|
798
|
+
this.render_(e);
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Renders the component. If a parent element is supplied, the component's
|
|
802
|
+
* element will be appended to it. If there is no optional parent element and
|
|
803
|
+
* the element doesn't have a parentNode then it will be appended to the
|
|
804
|
+
* document body.
|
|
805
|
+
*
|
|
806
|
+
* If this component has a parent component, and the parent component is
|
|
807
|
+
* not in the document already, then this will not call `enterDocument`
|
|
808
|
+
* on this component.
|
|
809
|
+
*
|
|
810
|
+
* Throws an Error if the component is already rendered.
|
|
811
|
+
*
|
|
812
|
+
* @param {Element=} opt_target Optional parent element to render the
|
|
813
|
+
* component into.
|
|
814
|
+
* @private
|
|
815
|
+
*/
|
|
816
|
+
render_(e) {
|
|
817
|
+
if (this.disposed)
|
|
818
|
+
throw new Error(ComponentError.ALREADY_DISPOSED);
|
|
819
|
+
if (this.isInDocument)
|
|
820
|
+
throw new Error(ComponentError.ALREADY_RENDERED);
|
|
821
|
+
this.#s || this.createDom();
|
|
822
|
+
const t = (
|
|
823
|
+
/** @type {!Node} */
|
|
824
|
+
this.#s
|
|
825
|
+
);
|
|
826
|
+
e && (this.#r = e), this.placeholderDom && removeNode(this.placeholderDom), this.#r ? isInPage(t) || this.#r.insertBefore(t, null) : isInPage(
|
|
827
|
+
/** @type {!Node} */
|
|
828
|
+
t
|
|
829
|
+
) || document.body.appendChild(t), (!this.#o || this.#o.isInDocument) && this.enterDocument();
|
|
830
|
+
}
|
|
831
|
+
//--[ Life-cycle ]--
|
|
832
|
+
/**
|
|
833
|
+
* @param {Node} el
|
|
834
|
+
* @param {Node} targetParent
|
|
835
|
+
*/
|
|
836
|
+
hoist(e, t = void 0) {
|
|
837
|
+
t || (t = document.querySelector("body")), t.appendChild(e), this.outOfTreeElements.push(e);
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Executes the beforeReady callback function if one was set.
|
|
841
|
+
* Called automatically during enterDocument, before the READY event is dispatched.
|
|
842
|
+
* Allows components to perform final setup operations once they're in the DOM.
|
|
843
|
+
*/
|
|
844
|
+
executeBeforeReady() {
|
|
845
|
+
this.#a();
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Called when the component's element is known to be in the document. Anything
|
|
849
|
+
* using document.getElementById etc. should be done at this stage.
|
|
850
|
+
*
|
|
851
|
+
* If the component contains child components, this call is propagated to its
|
|
852
|
+
* children.
|
|
853
|
+
*/
|
|
854
|
+
enterDocument() {
|
|
855
|
+
this.disposed ? removeNode(this.getElement()) : (this.isInDocument = !0, [...this.#i.values()].forEach((e) => {
|
|
856
|
+
!e.isInDocument && e.getElement() && e.enterDocument();
|
|
857
|
+
}), this.executeBeforeReady(), this.dispatchCompEvent(UiEventType.READY));
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Called by dispose to clean up the elements and listeners created by a
|
|
861
|
+
* component, or by a parent component/application who has removed the
|
|
862
|
+
* component from the document but wants to reuse it later.
|
|
863
|
+
*
|
|
864
|
+
* If the component contains child components, this call is propagated to its
|
|
865
|
+
* children.
|
|
866
|
+
*
|
|
867
|
+
* It should be possible for the component to be rendered again once this
|
|
868
|
+
* method has been called.
|
|
869
|
+
*/
|
|
870
|
+
exitDocument() {
|
|
871
|
+
[...this.#i.values()].forEach((e) => {
|
|
872
|
+
e.isInDocument && e.exitDocument();
|
|
873
|
+
}), this.stopBeingListenedTo(), this.removeAllListener(), this.isInDocument = !1, removeNode(this.getElement()), this.placeholderDom && removeNode(this.placeholderDom);
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Disposes of the component. Calls `exitDocument`, which is expected to
|
|
877
|
+
* remove event handlers and clean up the component. Propagates the call to
|
|
878
|
+
* the component's children, if any. Removes the component's DOM from the
|
|
879
|
+
* document unless it was decorated.
|
|
880
|
+
* @protected
|
|
881
|
+
*/
|
|
882
|
+
disposeInternal() {
|
|
883
|
+
this.isInDocument && this.exitDocument(), this.#i && [...this.#i.values()].forEach((e) => e.disposeInternal()), this.outOfTreeElements.forEach(removeNode), this.#s && removeNode(this.#s), this.placeholderDom && removeNode(this.placeholderDom), this.#i = null, this.#s = void 0, this.#n = null, this.#o = null, super.disposeInternal();
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Disposes of the component and performs cleanup. Aborts any pending async
|
|
887
|
+
* operations, asks registered component libraries to cleanup their components,
|
|
888
|
+
* and calls the parent dispose method.
|
|
889
|
+
* After calling this method, the component should not be used.
|
|
890
|
+
*/
|
|
891
|
+
dispose() {
|
|
892
|
+
this.abortController && this.abortController.abort();
|
|
893
|
+
const e = this.getElement();
|
|
894
|
+
if (e)
|
|
895
|
+
for (const t of ComponentLibraryRegistry.getRegisteredLibraries())
|
|
896
|
+
try {
|
|
897
|
+
const r = ComponentLibraryRegistry.get(t);
|
|
898
|
+
r.dispose && r.dispose(e);
|
|
899
|
+
} catch (r) {
|
|
900
|
+
console.debug(`[${t}] Cleanup error during component disposal (non-critical):`, r);
|
|
901
|
+
}
|
|
902
|
+
super.dispose();
|
|
903
|
+
}
|
|
904
|
+
//--[ Built in events ]--
|
|
905
|
+
/**
|
|
906
|
+
* Dispatches a {@code UiEventType.COMP} event.
|
|
907
|
+
* A shorthand method to get panels to dispatch uniform events.
|
|
908
|
+
* Views may listen just to this event, and act on the supplied value or
|
|
909
|
+
* data payload.
|
|
910
|
+
* Example:
|
|
911
|
+
* b.listen(a, Component.compEventCode(), e => {
|
|
912
|
+
* console.log('B got', Component.compEventCode(), e);
|
|
913
|
+
* console.log('Value is', e.detail.getValue());
|
|
914
|
+
* console.log('Data is', e.detail.getData());
|
|
915
|
+
* });
|
|
916
|
+
* @param {string|number} value
|
|
917
|
+
* @param {(string|number|?Object)=} opt_data
|
|
918
|
+
* @return {boolean} If anyone called preventDefault on the event object (or
|
|
919
|
+
* if any of the handlers returns false this will also return false.
|
|
920
|
+
*/
|
|
921
|
+
dispatchCompEvent(e, t) {
|
|
922
|
+
const r = new ZooyEventData(e, t), i = EVT.makeEvent(UiEventType.COMP, r);
|
|
923
|
+
return this.dispatchEvent(i);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
const EV = {
|
|
927
|
+
// Mouse events
|
|
928
|
+
CLICK: "click",
|
|
929
|
+
DBLCLICK: "dblclick",
|
|
930
|
+
MOUSEDOWN: "mousedown",
|
|
931
|
+
MOUSEUP: "mouseup",
|
|
932
|
+
MOUSEMOVE: "mousemove",
|
|
933
|
+
// Touch events
|
|
934
|
+
// Note that other touch events exist, but we should follow the W3C list here.
|
|
935
|
+
// http://www.w3.org/TR/touch-events/#list-of-touchevent-types
|
|
936
|
+
TOUCHSTART: "touchstart",
|
|
937
|
+
TOUCHMOVE: "touchmove",
|
|
938
|
+
TOUCHEND: "touchend",
|
|
939
|
+
// Form events
|
|
940
|
+
SUBMIT: "submit",
|
|
941
|
+
CHANGE: "change",
|
|
942
|
+
INPUT: "input",
|
|
943
|
+
INVALID: "invalid",
|
|
944
|
+
// Drag and drop events
|
|
945
|
+
DRAGSTART: "dragstart",
|
|
946
|
+
DRAGEND: "dragend",
|
|
947
|
+
DRAGOVER: "dragover",
|
|
948
|
+
DRAGENTER: "dragenter",
|
|
949
|
+
DRAGEXIT: "dragexit",
|
|
950
|
+
DRAGLEAVE: "dragleave",
|
|
951
|
+
DROP: "drop",
|
|
952
|
+
// Transitions
|
|
953
|
+
TRANSITIONEND: "transitionend"
|
|
954
|
+
}, touchEvents = [EV.TOUCHMOVE, EV.TOUCHSTART, EV.TOUCHEND], isTouchEvent = (n) => touchEvents.includes(n.type), normalizeEvent = (n) => (isTouchEvent(n) && (n.clientX = n.targetTouches[0].clientX, n.clientY = n.targetTouches[0].clientY), n), dragStartListener = (n, e, t, r) => (i) => {
|
|
955
|
+
i.preventDefault();
|
|
956
|
+
const o = normalizeEvent(i), a = (
|
|
957
|
+
/** @type {!HTMLElement} */
|
|
958
|
+
o.currentTarget
|
|
959
|
+
), [p, f] = getPos(a), _ = o.clientX - p, g = o.clientY - f, v = n(p, f, _, g, a), c = t(p, f, _, g, a), l = e(p, f, _, g, a);
|
|
960
|
+
let d = freeMoveListener(l, a, _, g);
|
|
961
|
+
["x", "ew"].includes(r) ? d = xMoveOnlyListener(l, a, _) : ["y", "ns"].includes(r) && (d = yMoveOnlyListener(l, a, _, g));
|
|
962
|
+
let h = !1;
|
|
963
|
+
const u = (E) => {
|
|
964
|
+
h = !0, d(E);
|
|
965
|
+
}, m = (E) => {
|
|
966
|
+
document.removeEventListener(EV.MOUSEMOVE, u, !0), document.removeEventListener(EV.TOUCHMOVE, u, !0), document.removeEventListener(EV.MOUSEUP, m, !0), document.removeEventListener(EV.TOUCHEND, m, !0), h && c(E);
|
|
967
|
+
};
|
|
968
|
+
return document.addEventListener(EV.MOUSEUP, m, !0), document.addEventListener(EV.TOUCHEND, m, !0), document.addEventListener(EV.MOUSEMOVE, u, !0), document.addEventListener(EV.TOUCHMOVE, u, !0), v(i), m;
|
|
969
|
+
}, freeMoveListener = (n, e, t, r) => (i) => {
|
|
970
|
+
i.preventDefault();
|
|
971
|
+
const o = normalizeEvent(i);
|
|
972
|
+
e.style.left = `${o.clientX - t}px`, e.style.top = `${o.clientY - r}px`, n(o);
|
|
973
|
+
}, xMoveOnlyListener = (n, e, t, r) => (i) => {
|
|
974
|
+
i.preventDefault();
|
|
975
|
+
const o = normalizeEvent(i);
|
|
976
|
+
e.style.left = `${o.clientX - t}px`, n(o);
|
|
977
|
+
}, yMoveOnlyListener = (n, e, t, r) => (i) => {
|
|
978
|
+
i.preventDefault();
|
|
979
|
+
const o = normalizeEvent(i);
|
|
980
|
+
e.style.top = `${o.clientY - r}px`, n(o);
|
|
981
|
+
}, makeEmitter = (n, e) => (t, r, i, o, a) => (p) => {
|
|
982
|
+
n.dispatchCompEvent(e, {
|
|
983
|
+
component: n,
|
|
984
|
+
browserEvent: p,
|
|
985
|
+
left: t,
|
|
986
|
+
top: r,
|
|
987
|
+
clientX: p.clientX,
|
|
988
|
+
clientY: p.clientY,
|
|
989
|
+
xOrg: i,
|
|
990
|
+
yOrg: o,
|
|
991
|
+
deltaX: p.clientX - i - t,
|
|
992
|
+
deltaY: p.clientY - o - r,
|
|
993
|
+
target: a
|
|
994
|
+
});
|
|
995
|
+
};
|
|
996
|
+
class Dragger extends Component {
|
|
997
|
+
#e;
|
|
998
|
+
#t;
|
|
999
|
+
#s;
|
|
1000
|
+
#n;
|
|
1001
|
+
/**
|
|
1002
|
+
* @param {string} freedom Restrict the directions in which the
|
|
1003
|
+
* dragger can be moved.
|
|
1004
|
+
*/
|
|
1005
|
+
constructor(e = "xy") {
|
|
1006
|
+
super(), this.#e = ["x", "y", "xy", "ew", "ns"].includes(e.toLowerCase()) ? e.toLowerCase() : "xy", this.#t = void 0, this.#s = !0, this.#n = (t) => null;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Cancels the current drag operation in progress, if any.
|
|
1010
|
+
* @param {!Event} event The event that triggered the cancel
|
|
1011
|
+
*/
|
|
1012
|
+
cancelDrag(e) {
|
|
1013
|
+
this.#n(e);
|
|
1014
|
+
}
|
|
1015
|
+
//--[ Getters and Setters ]--
|
|
1016
|
+
/**
|
|
1017
|
+
* Sets the direction in which the component can be dragged.
|
|
1018
|
+
* Only 'x' or 'y' will lock the movements to those directions.
|
|
1019
|
+
* Anything else will be considered free movement
|
|
1020
|
+
* @param {string} axis
|
|
1021
|
+
*/
|
|
1022
|
+
set moveFreedom(e) {
|
|
1023
|
+
this.#e = e, this.#s || (this.lock(), this.unlock());
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Get the direction in which this component can move.
|
|
1027
|
+
* @return {string}
|
|
1028
|
+
*/
|
|
1029
|
+
get moveFreedom() {
|
|
1030
|
+
return this.#e;
|
|
1031
|
+
}
|
|
1032
|
+
//--[ Override ]--
|
|
1033
|
+
/**
|
|
1034
|
+
* @inheritDoc
|
|
1035
|
+
*/
|
|
1036
|
+
executeBeforeReady() {
|
|
1037
|
+
this.unlock(), super.executeBeforeReady();
|
|
1038
|
+
}
|
|
1039
|
+
//--[ Dragger Specific ]--
|
|
1040
|
+
/**
|
|
1041
|
+
* Make the component draggable
|
|
1042
|
+
*/
|
|
1043
|
+
unlock() {
|
|
1044
|
+
const e = (t) => (r) => {
|
|
1045
|
+
this.#n = t(r);
|
|
1046
|
+
};
|
|
1047
|
+
if (this.isInDocument && this.#s) {
|
|
1048
|
+
const t = makeEmitter(this, UiEventType.COMP_DRAG_MOVE), r = makeEmitter(this, UiEventType.COMP_DRAG_START), i = makeEmitter(this, UiEventType.COMP_DRAG_END), o = dragStartListener(
|
|
1049
|
+
r,
|
|
1050
|
+
t,
|
|
1051
|
+
i,
|
|
1052
|
+
this.#e
|
|
1053
|
+
);
|
|
1054
|
+
this.#s = !1, this.#t = /** @type {!Node} */
|
|
1055
|
+
this.#t || this.getElement(), this.listen(this.#t, EV.MOUSEDOWN, e(o)), this.listen(this.#t, EV.TOUCHSTART, o), this.#t.classList.remove("locked");
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Lock the component in place.
|
|
1060
|
+
* Removes all the listeners added when it was made draggable.
|
|
1061
|
+
*/
|
|
1062
|
+
lock() {
|
|
1063
|
+
this.stopListeningTo(this.#t, EV.MOUSEDOWN), this.stopListeningTo(this.#t, EV.TOUCHSTART), this.#s = !0, this.#t.classList.add("locked");
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
const splitRe_ = new RegExp(
|
|
1067
|
+
"^(?:([^:/?#.]+):)?(?://(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\\?([^#]*))?(?:#([\\s\\S]*))?$"
|
|
1068
|
+
), ComponentIndex = {
|
|
1069
|
+
PATH: 5,
|
|
1070
|
+
QUERY_DATA: 6
|
|
1071
|
+
}, split = (n) => (
|
|
1072
|
+
/** @type {!Array<string|undefined>} */
|
|
1073
|
+
n.match(splitRe_)
|
|
1074
|
+
), decodeOrEmpty_ = (n, e) => n ? decodeURI(n.replace(/%25/g, "%2525")) : "", getPath = (n) => decodeOrEmpty_(split(n)[ComponentIndex.PATH]), getQueryData = (n) => decodeOrEmpty_(split(n)[ComponentIndex.QUERY_DATA]), objectToUrlParms = (n) => [...Object.entries(n)].map(
|
|
1075
|
+
(e) => `${e[0]}=${e[1]}`
|
|
1076
|
+
).join("&"), isValidValue = (n) => n != null && n !== "", queryDataToMap = (n) => n.split("&").reduce((e, t) => {
|
|
1077
|
+
const [r, i] = t.split("=");
|
|
1078
|
+
return isValidValue(r) && isValidValue(i) && e.set(r, i), e;
|
|
1079
|
+
}, /* @__PURE__ */ new Map()), uriUtils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1080
|
+
__proto__: null,
|
|
1081
|
+
getPath,
|
|
1082
|
+
getQueryData,
|
|
1083
|
+
objectToUrlParms,
|
|
1084
|
+
queryDataToMap
|
|
1085
|
+
}, Symbol.toStringTag, { value: "Module" })), stripLeadingSpace = stripLeadingChar(" "), stripLeadingSlash = stripLeadingChar("/"), getCookieByName = (n) => document.cookie.split(";").map((e) => e.split("=")).reduce((e, t) => e.set(stripLeadingSpace(t[0]), t[1]), /* @__PURE__ */ new Map()).get(n), spinner = (n) => {
|
|
1086
|
+
let e = 0, t;
|
|
1087
|
+
const r = (i) => {
|
|
1088
|
+
const o = i > 0;
|
|
1089
|
+
return o ? e += 1 : e -= 1, o ? e === 1 : e === 0;
|
|
1090
|
+
};
|
|
1091
|
+
return (i) => {
|
|
1092
|
+
t = t || document.getElementById(n), t && r(i) && t.classList.toggle("viz", e > 0);
|
|
1093
|
+
};
|
|
1094
|
+
}, spin = spinner("the_loader"), startSpin = () => Promise.resolve(spin(1)), stopSpin = (n) => (spin(0), Promise.resolve(n)), checkStatus = (n) => n.ok ? Promise.resolve(n) : Promise.reject(new Error(`${n.url} ${n.status} (${n.statusText})`)), checkStatusTwo = (n) => (e) => {
|
|
1095
|
+
if (e.type === "opaqueredirect" || e.redirected) {
|
|
1096
|
+
const t = stripLeadingSlash(getPath(e.url));
|
|
1097
|
+
n.setIsRedirected(!0, t), console.log("Redirect detected", t);
|
|
1098
|
+
}
|
|
1099
|
+
return checkStatus(e);
|
|
1100
|
+
}, getJson = (n) => n.status === 204 ? Promise.resolve({}) : n.json().then(
|
|
1101
|
+
(e) => Promise.resolve(e),
|
|
1102
|
+
(e) => Promise.reject(new Error(`Could not get JSON from response: ${e}`))
|
|
1103
|
+
), getText = (n) => n.text().then(
|
|
1104
|
+
(e) => Promise.resolve(e),
|
|
1105
|
+
(e) => Promise.reject(new Error(`Could not get text from response: ${e}`))
|
|
1106
|
+
), getTextOrJson = (n) => n.headers.get("Content-Type") === "application/json" ? getJson(n) : getText(n), jsonInit = (n, e, t = "POST", r = void 0) => {
|
|
1107
|
+
const i = new Headers();
|
|
1108
|
+
i.append("Content-type", "application/json"), i.append("X-Requested-With", "XMLHttpRequest"), n && n !== "" && i.append("Authorization", `Bearer ${n}`);
|
|
1109
|
+
const o = {
|
|
1110
|
+
cache: "no-cache",
|
|
1111
|
+
method: t,
|
|
1112
|
+
headers: i,
|
|
1113
|
+
credentials: "include",
|
|
1114
|
+
body: JSON.stringify(e)
|
|
1115
|
+
};
|
|
1116
|
+
return r && (o.signal = r), o;
|
|
1117
|
+
}, basicPutPostPatchInit = (n, e, t = !1, r = void 0) => {
|
|
1118
|
+
const i = new Headers();
|
|
1119
|
+
if (e && e !== "" && i.append("Authorization", `Bearer ${e}`), i.append("X-Requested-With", "XMLHttpRequest"), t) {
|
|
1120
|
+
const a = getCookieByName("csrftoken");
|
|
1121
|
+
a && t && i.append("X-CSRFToken", a);
|
|
1122
|
+
}
|
|
1123
|
+
const o = {
|
|
1124
|
+
cache: "no-cache",
|
|
1125
|
+
method: n,
|
|
1126
|
+
headers: i,
|
|
1127
|
+
redirect: "follow",
|
|
1128
|
+
// This is anyway the default.
|
|
1129
|
+
credentials: "include"
|
|
1130
|
+
};
|
|
1131
|
+
return r && (o.signal = r), o;
|
|
1132
|
+
}, formPostInit = (n, e, t = void 0) => {
|
|
1133
|
+
const i = basicPutPostPatchInit("POST", n, !1, t);
|
|
1134
|
+
return i.body = new FormData(e.formEl), i;
|
|
1135
|
+
}, basicGetInit = (n, e = void 0) => {
|
|
1136
|
+
const t = new Headers();
|
|
1137
|
+
t.append("Authorization", `Bearer ${n}`), t.append("X-Requested-With", "XMLHttpRequest");
|
|
1138
|
+
const r = {
|
|
1139
|
+
cache: "no-cache",
|
|
1140
|
+
headers: t,
|
|
1141
|
+
credentials: "include"
|
|
1142
|
+
};
|
|
1143
|
+
return e && (r.signal = e), r;
|
|
1144
|
+
}, putPostPatchNobody = (n, e) => {
|
|
1145
|
+
const t = new Request(n);
|
|
1146
|
+
return fetch(t, e).then(checkStatus).then(getText);
|
|
1147
|
+
}, genCatchClause = (n, e = void 0) => (t) => (stopSpin("").then(identity), t.name === "AbortError" ? console.info(n, "ABORTED!") : console.error(n, t), e);
|
|
1148
|
+
class UserManager {
|
|
1149
|
+
/**
|
|
1150
|
+
* @param {!Object=} opt_data
|
|
1151
|
+
*/
|
|
1152
|
+
constructor(e) {
|
|
1153
|
+
this.user_ = {}, this.jwt = "", e && this.updateProfileFromJwt(e).then(() => {
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* @param {Object} data
|
|
1158
|
+
* @param {boolean=} opt_onlyIfNoneExists
|
|
1159
|
+
* @return {Promise}
|
|
1160
|
+
* @private
|
|
1161
|
+
*/
|
|
1162
|
+
updateProfileFromJwt(e, t = !1) {
|
|
1163
|
+
return t && this.jwt !== "" ? Promise.resolve("User Profile Already exists") : e.non_field_errors ? Promise.reject(new Error(`JWT ${e.non_field_errors}`)) : (this.updateToken(e.token), this.updateProfile(e.user), Promise.resolve("User Profile Updated"));
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* @param {UserLikeType} data
|
|
1167
|
+
*/
|
|
1168
|
+
updateProfile(e) {
|
|
1169
|
+
this.user_ = e;
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* @param {string} t
|
|
1173
|
+
*/
|
|
1174
|
+
updateToken(e) {
|
|
1175
|
+
this.jwt = e;
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* @return {number|undefined}
|
|
1179
|
+
*/
|
|
1180
|
+
get id() {
|
|
1181
|
+
return this.user_.id;
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* @return {string|undefined}
|
|
1185
|
+
*/
|
|
1186
|
+
get name() {
|
|
1187
|
+
return this.user_.name;
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* @return {string|undefined}
|
|
1191
|
+
*/
|
|
1192
|
+
get surname() {
|
|
1193
|
+
return this.user_.surname;
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* @return {string|undefined}
|
|
1197
|
+
*/
|
|
1198
|
+
get salutation() {
|
|
1199
|
+
let e = this.name;
|
|
1200
|
+
const t = this.surname;
|
|
1201
|
+
return t && (e = e + " " + t), e;
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* @param {FormPanel} formPanel
|
|
1205
|
+
* @return {Promise}
|
|
1206
|
+
*/
|
|
1207
|
+
formSubmit(e) {
|
|
1208
|
+
const t = new Request(e.uri.toString()), r = e.processSubmitReply.bind(e), i = genCatchClause("Form submit error");
|
|
1209
|
+
return startSpin().then(() => fetch(t, formPostInit(
|
|
1210
|
+
this.jwt,
|
|
1211
|
+
e,
|
|
1212
|
+
e.abortController.signal
|
|
1213
|
+
))).then(checkStatusTwo(e)).then(stopSpin).then(getTextOrJson).then(r).catch(i);
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* @param {string} uri
|
|
1217
|
+
* @param {Boolean} useDocumentCookies
|
|
1218
|
+
* @param {AbortSignal|undefined} signal
|
|
1219
|
+
* @return {Promise}
|
|
1220
|
+
*/
|
|
1221
|
+
putNoBody(e, t = void 0, r = !1) {
|
|
1222
|
+
const i = genCatchClause("putNobody error"), o = basicPutPostPatchInit(
|
|
1223
|
+
"PUT",
|
|
1224
|
+
this.jwt,
|
|
1225
|
+
r,
|
|
1226
|
+
t
|
|
1227
|
+
);
|
|
1228
|
+
return putPostPatchNobody(e, o).catch(i);
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* @param {string} uri
|
|
1232
|
+
* @param {AbortSignal|undefined} signal
|
|
1233
|
+
* @return {Promise}
|
|
1234
|
+
*/
|
|
1235
|
+
fetch(e, t = void 0) {
|
|
1236
|
+
const r = new Request(e.toString()), i = genCatchClause(`UMan Text GET Fetch: ${e}`);
|
|
1237
|
+
return startSpin().then(() => fetch(r, basicGetInit(this.jwt, t))).then(checkStatus).then(stopSpin).then(getText).catch(i);
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Use this if you want to directly get a parsed template that does not go
|
|
1241
|
+
* through panel logic.
|
|
1242
|
+
* @param {string} uri
|
|
1243
|
+
* @param {AbortSignal|undefined} signal
|
|
1244
|
+
* @return {Promise}
|
|
1245
|
+
*/
|
|
1246
|
+
fetchAndSplit(e, t = void 0) {
|
|
1247
|
+
const r = genCatchClause(`fetchAndSplit: ${e}`);
|
|
1248
|
+
return this.fetch(e, t).then(handleTemplateProm).catch(r);
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* @param {string} uri
|
|
1252
|
+
* @param {AbortSignal|undefined} signal
|
|
1253
|
+
* @return {Promise}
|
|
1254
|
+
*/
|
|
1255
|
+
fetchJson(e, t = void 0) {
|
|
1256
|
+
const r = new Request(e.toString()), i = genCatchClause(`fetchJson: ${e}`, {}), o = basicGetInit(this.jwt, t);
|
|
1257
|
+
return startSpin().then(() => fetch(r, o)).then(checkStatus).then(stopSpin).then(getJson).catch(i);
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* @param {string} uri
|
|
1261
|
+
* @param {Object} payload
|
|
1262
|
+
* @param {AbortSignal|undefined} signal
|
|
1263
|
+
* @return {Promise}
|
|
1264
|
+
*/
|
|
1265
|
+
patchJson(e, t, r = void 0) {
|
|
1266
|
+
const i = new Request(e.toString()), o = genCatchClause(`patchJson: ${e}`), a = jsonInit(this.jwt, t, "PATCH", r);
|
|
1267
|
+
return fetch(i, a).then(checkStatus).then(getJson).catch(o);
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* @param {string} uri
|
|
1271
|
+
* @param {Object} payload
|
|
1272
|
+
* @param {AbortSignal|undefined} signal
|
|
1273
|
+
* @return {Promise}
|
|
1274
|
+
*/
|
|
1275
|
+
postJson(e, t, r = void 0) {
|
|
1276
|
+
const i = new Request(e.toString()), o = genCatchClause(`postJson: ${e}`), a = jsonInit(this.jwt, t, "POST", r);
|
|
1277
|
+
return fetch(i, a).then(checkStatus).then(getJson).catch(o);
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* @param {string} uri
|
|
1281
|
+
* @param {Object} payload
|
|
1282
|
+
* @param {AbortSignal|undefined} signal
|
|
1283
|
+
* @return {Promise}
|
|
1284
|
+
*/
|
|
1285
|
+
putJson(e, t, r = void 0) {
|
|
1286
|
+
const i = new Request(e.toString()), o = genCatchClause(`putJson: ${e}`), a = jsonInit(this.jwt, t, "PUT", r);
|
|
1287
|
+
return fetch(i, a).then(checkStatus).then(getJson).catch(o);
|
|
1288
|
+
}
|
|
1289
|
+
/**
|
|
1290
|
+
* @param {string} uri
|
|
1291
|
+
* @param {Object} payload
|
|
1292
|
+
* @param {AbortSignal|undefined} signal
|
|
1293
|
+
* @return {Promise}
|
|
1294
|
+
*/
|
|
1295
|
+
delJson(e, t, r = void 0) {
|
|
1296
|
+
const i = new Request(e.toString()), o = genCatchClause(`delJson: ${e}`), a = jsonInit(this.jwt, t, "DELETE", r);
|
|
1297
|
+
return fetch(i, a).then(checkStatus).then(getJson).catch(o);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
class Panel extends Component {
|
|
1301
|
+
/**
|
|
1302
|
+
* Returns the panel event type code used for dispatching panel events.
|
|
1303
|
+
* @return {string} The PANEL event type
|
|
1304
|
+
*/
|
|
1305
|
+
static panelEventCode() {
|
|
1306
|
+
return UiEventType.PANEL;
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Returns the component ready event type code.
|
|
1310
|
+
* @return {string} The READY event type
|
|
1311
|
+
*/
|
|
1312
|
+
static compReadyCode() {
|
|
1313
|
+
return UiEventType.READY;
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Creates a new Panel instance.
|
|
1317
|
+
* @param {string=} uri Optional URI for fetching panel content
|
|
1318
|
+
*/
|
|
1319
|
+
constructor(e) {
|
|
1320
|
+
super(), this.qParamMap_ = /* @__PURE__ */ new Map(), this.uri_ = "", e && this.parseUri(e), this.evalScripts = evalScripts(this), this.evalModules = evalModules(this), this.redirected = !1, this.responseObject = { html: null, scripts: null }, this.user_ = void 0, "AbortController" in window ? this.abortController = new AbortController() : this.abortController = {
|
|
1321
|
+
signal: void 0,
|
|
1322
|
+
abort: () => {
|
|
1323
|
+
}
|
|
1324
|
+
}, this.listMap = /* @__PURE__ */ new Map();
|
|
1325
|
+
}
|
|
1326
|
+
//--[ Getters and Setters ]--
|
|
1327
|
+
get uri() {
|
|
1328
|
+
const e = this.qParamMap_.size > 0 ? "?" + objectToUrlParms(Object.fromEntries(this.qParamMap_)) : "";
|
|
1329
|
+
return this.uri_ + e;
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* @param {!UserManager} user
|
|
1333
|
+
*/
|
|
1334
|
+
set user(e) {
|
|
1335
|
+
this.user_ = e;
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* @return {!UserManager}
|
|
1339
|
+
*/
|
|
1340
|
+
get user() {
|
|
1341
|
+
return this.user_ || (this.user_ = new UserManager()), this.user_;
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Parses a URI into its path and query parameters. Updates the panel's
|
|
1345
|
+
* internal URI and query parameter map.
|
|
1346
|
+
* @param {string} uri The URI to parse
|
|
1347
|
+
*/
|
|
1348
|
+
parseUri(e) {
|
|
1349
|
+
this.uri_ = getPath(e), this.qParamMap_ = queryDataToMap(getQueryData(e));
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Adds or updates a query parameter in the panel's query parameter map.
|
|
1353
|
+
* @param {string} k The parameter key
|
|
1354
|
+
* @param {string} v The parameter value
|
|
1355
|
+
*/
|
|
1356
|
+
addToQParams(e, t) {
|
|
1357
|
+
this.qParamMap_.set(e, t);
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* Removes a query parameter from the panel's query parameter map.
|
|
1361
|
+
* @param {string} k The parameter key to remove
|
|
1362
|
+
*/
|
|
1363
|
+
removeFromQParams(e) {
|
|
1364
|
+
this.qParamMap_.delete(e);
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Clears all query parameters from the panel's query parameter map.
|
|
1368
|
+
*/
|
|
1369
|
+
clearQParams() {
|
|
1370
|
+
this.qParamMap_.clear();
|
|
1371
|
+
}
|
|
1372
|
+
//--[ Template Render ]-----
|
|
1373
|
+
/**
|
|
1374
|
+
* Temporarily render placeholder DOM
|
|
1375
|
+
* @param {Node!} placeholder
|
|
1376
|
+
*/
|
|
1377
|
+
renderPlaceHolder(e) {
|
|
1378
|
+
this.target && (this.placeholderDom = e, this.target.insertBefore(this.placeholderDom, null));
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Expects HTML data from a call to the back.
|
|
1382
|
+
* @param {Function=} opt_callback An optional callback to call before
|
|
1383
|
+
* rendering the panel. This is useful for when you only want to attach the
|
|
1384
|
+
* new panel to the view right before you render it - meaning the existing
|
|
1385
|
+
* panel stays in place on the DOM for the duration of the fetch call.
|
|
1386
|
+
* @return {Promise} Returns a promise with this panel as value.
|
|
1387
|
+
*/
|
|
1388
|
+
renderWithTemplate(e) {
|
|
1389
|
+
const t = this.user;
|
|
1390
|
+
return t ? t.fetch(this.uri, this.abortController.signal).then((r) => (this.assertCanRenderAsync(), e && e(this), this.onRenderWithTemplateReply(r).catch((i) => {
|
|
1391
|
+
i.message !== Component.compErrors().ALREADY_DISPOSED && console.error("RenderWithTemplate Err:", i);
|
|
1392
|
+
}), this)).catch((r) => {
|
|
1393
|
+
throw console.error("Panel.renderWithTemplate fetch failed:", r), r;
|
|
1394
|
+
}) : Promise.reject(new Error("No UserManager instance available for Panel.renderWithTemplate()"));
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* @param {string} s
|
|
1398
|
+
* @private
|
|
1399
|
+
* @return {Promise}
|
|
1400
|
+
*/
|
|
1401
|
+
onRenderWithTemplateReply(e) {
|
|
1402
|
+
return this.responseObject = splitScripts(e), this.domFunc = () => (
|
|
1403
|
+
/** @type {!Element} */
|
|
1404
|
+
this.responseObject.html
|
|
1405
|
+
), this.render(), Promise.resolve(this);
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Partially replace panel's content.
|
|
1409
|
+
* If an ID query selector (#something) is passed in, then that single node is replaced
|
|
1410
|
+
* in the panel DOM with the single node in the content DOM
|
|
1411
|
+
*
|
|
1412
|
+
* If a class query selector (.some_class) is passed in, all elements in the
|
|
1413
|
+
* panel's dom with that class is replaced with the matching
|
|
1414
|
+
* element ID in the content DOM matching the target DOMs element IDs
|
|
1415
|
+
*
|
|
1416
|
+
* NOTE: For the spot replacement to work, you need both the classname
|
|
1417
|
+
* and a unique element ID on the element you intend to replace.
|
|
1418
|
+
*
|
|
1419
|
+
* NOTE: Top level replacements will clobber child replacements.
|
|
1420
|
+
*
|
|
1421
|
+
* @param content
|
|
1422
|
+
* @param qs
|
|
1423
|
+
*/
|
|
1424
|
+
onReplacePartialDom(e, t) {
|
|
1425
|
+
const r = this.getElement();
|
|
1426
|
+
if (isUndefined(r))
|
|
1427
|
+
return;
|
|
1428
|
+
const i = e.html;
|
|
1429
|
+
if (t.startsWith("."))
|
|
1430
|
+
[...r.querySelectorAll(t)].filter((o) => isDefAndNotNull(o.id)).map((o) => [o, i.querySelector(`#${o.id}`)]).filter(([o, a]) => isDefAndNotNull(a)).forEach(([o, a]) => {
|
|
1431
|
+
o.parentNode.replaceChild(a, o), this.parseContent(a);
|
|
1432
|
+
});
|
|
1433
|
+
else if (t.startsWith("#")) {
|
|
1434
|
+
const o = i.querySelector(t), a = r.querySelector(t);
|
|
1435
|
+
a.parentNode.replaceChild(o, a), this.parseContent(o);
|
|
1436
|
+
}
|
|
1437
|
+
this.evalScripts(e.scripts), this.evalModules(e.modules);
|
|
1438
|
+
}
|
|
1439
|
+
//--[ JSON Render ]-----
|
|
1440
|
+
/**
|
|
1441
|
+
* Equivalent to the @code{renderWithTemplate} method in that it is guaranteed
|
|
1442
|
+
* that a reply from the callback is received before @code{render} is called.
|
|
1443
|
+
* @param {function(Object, !Panel)} opt_callback The callback function
|
|
1444
|
+
* that will receive the reply event.
|
|
1445
|
+
*/
|
|
1446
|
+
renderWithJSON(e) {
|
|
1447
|
+
return this.user ? this.user.fetchJson(this.uri_, this.abortController.signal).then((r) => {
|
|
1448
|
+
this.assertCanRenderAsync(), e && e(r, this), this.onRenderWithJSON(r).then(identity);
|
|
1449
|
+
}).catch((r) => {
|
|
1450
|
+
throw console.error("Panel.renderWithJSON fetch failed:", r), r;
|
|
1451
|
+
}) : Promise.reject(new Error("No UserManager instance available for Panel.renderWithJSON()"));
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* On reply from a GET call to the panel URI
|
|
1455
|
+
* @param {Object} _json The callback function
|
|
1456
|
+
* that will receive the reply event.
|
|
1457
|
+
* @return {Promise}
|
|
1458
|
+
*/
|
|
1459
|
+
onRenderWithJSON(e) {
|
|
1460
|
+
return Promise.resolve(this);
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* @param {Element} _el
|
|
1464
|
+
* @param {Object} _elDataMap
|
|
1465
|
+
* @param {Object} _json
|
|
1466
|
+
*/
|
|
1467
|
+
onAsyncJsonReply(e, t, r) {
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Called on each async HTML response, after the content is in the DOM
|
|
1471
|
+
* and rendered.
|
|
1472
|
+
* @param {Element} _el
|
|
1473
|
+
* @param {Object} _elDataMap
|
|
1474
|
+
*/
|
|
1475
|
+
onAsyncHtmlReply(e, t) {
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Hook method called when the view broadcasts data to all its panels.
|
|
1479
|
+
* Override this method to handle broadcasted data in subclasses.
|
|
1480
|
+
* @param {*} _data The data broadcast from the view
|
|
1481
|
+
*/
|
|
1482
|
+
onViewDataBroadcast(e) {
|
|
1483
|
+
}
|
|
1484
|
+
//--[ JSON Render ]-----
|
|
1485
|
+
/**
|
|
1486
|
+
* @param {Element} panel
|
|
1487
|
+
*/
|
|
1488
|
+
parseContent(e) {
|
|
1489
|
+
if (this.debugMe("Enable interactions. Panel:", e), typeof window.customElements < "u" && ComponentLibraryRegistry.has("carbon")) {
|
|
1490
|
+
const c = ComponentLibraryRegistry.get("carbon");
|
|
1491
|
+
c.render.call(this, e, c.cache).catch((l) => console.error("[Zooy] Carbon initialization failed:", l));
|
|
1492
|
+
}
|
|
1493
|
+
if (ComponentLibraryRegistry.has("mdc")) {
|
|
1494
|
+
const c = ComponentLibraryRegistry.get("mdc");
|
|
1495
|
+
c.render.call(this, e, c.cache);
|
|
1496
|
+
}
|
|
1497
|
+
e.classList.contains("zoo__modal-base") && e.classList.contains("close_on_click") && this.listen(e, EV.CLICK, (c) => {
|
|
1498
|
+
c.target === e && this.dispatchPanelEvent("destroy_me");
|
|
1499
|
+
}), [...e.querySelectorAll(
|
|
1500
|
+
".zoo__button:not(.external):not(.mdc-data-table__row)"
|
|
1501
|
+
)].forEach((c) => {
|
|
1502
|
+
this.listen(c, EV.CLICK, (l) => {
|
|
1503
|
+
l.stopPropagation();
|
|
1504
|
+
const d = l.currentTarget, h = getElDataMap(d);
|
|
1505
|
+
this.dispatchPanelEvent(h.zv, Object.assign({
|
|
1506
|
+
orgEvt: l,
|
|
1507
|
+
trigger: d,
|
|
1508
|
+
href: d.href || h.href
|
|
1509
|
+
}, h));
|
|
1510
|
+
});
|
|
1511
|
+
}), [...e.querySelectorAll("[href]:not(.external)")].forEach((c) => {
|
|
1512
|
+
this.listen(c, EV.CLICK, (l) => {
|
|
1513
|
+
const d = l.currentTarget;
|
|
1514
|
+
l.preventDefault(), l.stopPropagation();
|
|
1515
|
+
const h = getElDataMap(d), u = h.zv || "href";
|
|
1516
|
+
this.dispatchPanelEvent(u, Object.assign({
|
|
1517
|
+
orgEvt: l,
|
|
1518
|
+
trigger: l.target,
|
|
1519
|
+
href: d.href || h.href
|
|
1520
|
+
}, h));
|
|
1521
|
+
});
|
|
1522
|
+
}), [...e.querySelectorAll("form.intercept_submit")].forEach((c) => {
|
|
1523
|
+
c.noValidate = !0, this.listen(c, EV.SUBMIT, (l) => {
|
|
1524
|
+
l.preventDefault(), l.stopPropagation();
|
|
1525
|
+
const d = getElDataMap(c), h = formToJSON(c.elements);
|
|
1526
|
+
this.dispatchPanelEvent(d.zv, Object.assign({
|
|
1527
|
+
orgEvt: l,
|
|
1528
|
+
trigger: l.target,
|
|
1529
|
+
formData: h,
|
|
1530
|
+
href: d.href
|
|
1531
|
+
}, d));
|
|
1532
|
+
});
|
|
1533
|
+
}), [...e.querySelectorAll(".zoo__toggle_class_driver")].forEach(
|
|
1534
|
+
(c) => {
|
|
1535
|
+
const l = getElDataMap(c), d = l.toggle_class_target_id, h = l.toggle_class, u = e.querySelector(`#${d}`);
|
|
1536
|
+
u && h && c.addEventListener(EV.CLICK, (m) => {
|
|
1537
|
+
m.stopPropagation(), u.classList.toggle(h);
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
);
|
|
1541
|
+
const t = Array.from(e.querySelectorAll(".folder_drop_zone")), r = Array.from(e.querySelectorAll("[draggable]")), i = (c) => {
|
|
1542
|
+
c.preventDefault(), c.target.classList.add("drag_over");
|
|
1543
|
+
}, o = (c) => {
|
|
1544
|
+
c.preventDefault();
|
|
1545
|
+
}, a = (c) => {
|
|
1546
|
+
c.preventDefault(), c.target.classList.remove("drag_over");
|
|
1547
|
+
}, p = (c) => {
|
|
1548
|
+
c.preventDefault(), c.target.classList.remove("drag_over");
|
|
1549
|
+
}, f = (c) => {
|
|
1550
|
+
c.preventDefault(), c.target.classList.remove("drag_over");
|
|
1551
|
+
}, _ = (c) => {
|
|
1552
|
+
c.dataTransfer.dropEffect = "move";
|
|
1553
|
+
const l = getElDataMap(c.target);
|
|
1554
|
+
c.dataTransfer.setData("text/plain", JSON.stringify(l)), c.target.classList.add("drag_in_progress");
|
|
1555
|
+
}, g = (c) => {
|
|
1556
|
+
c.target.classList.remove("drag_in_progress");
|
|
1557
|
+
}, v = (c) => {
|
|
1558
|
+
f(c), c.stopPropagation();
|
|
1559
|
+
const l = JSON.parse(c.dataTransfer.getData("text/plain")), d = getElDataMap(c.target);
|
|
1560
|
+
return this.dispatchPanelEvent("drop_on", {
|
|
1561
|
+
custom: { on: d, from: l }
|
|
1562
|
+
}), !1;
|
|
1563
|
+
};
|
|
1564
|
+
t.forEach((c) => {
|
|
1565
|
+
this.listen(c, EV.DRAGOVER, o), this.listen(c, EV.DRAGENTER, i), this.listen(c, EV.DRAGEXIT, p), this.listen(c, EV.DRAGLEAVE, a), this.listen(c, EV.DROP, v);
|
|
1566
|
+
}), r.forEach((c) => {
|
|
1567
|
+
this.listen(c, EV.DRAGSTART, _), this.listen(c, EV.DRAGEND, g);
|
|
1568
|
+
}), [...e.querySelectorAll(".zoo_async_json")].forEach((c) => {
|
|
1569
|
+
const l = getElDataMap(c), d = l.href, h = this.onAsyncJsonReply.bind(this, c, l);
|
|
1570
|
+
this.user.fetchJson(d, this.abortController.signal).then(h);
|
|
1571
|
+
const u = l.z_json_reusable;
|
|
1572
|
+
u && (this.jsonCallFuncs = this.jsonCallFuncs || {}, this.jsonCallFuncs[u] = () => {
|
|
1573
|
+
this.user.fetchJson(d, this.abortController.signal).then(h);
|
|
1574
|
+
});
|
|
1575
|
+
const m = toNumber(l.z_interval);
|
|
1576
|
+
isNumber(m) && this.doOnBeat(() => {
|
|
1577
|
+
this.user.fetchJson(d, this.abortController.signal).then(h);
|
|
1578
|
+
}, m * 60 * 1e3);
|
|
1579
|
+
}), [...e.querySelectorAll(".zoo_async_html")].forEach(
|
|
1580
|
+
/**
|
|
1581
|
+
* @param {Element} el
|
|
1582
|
+
*/
|
|
1583
|
+
(c) => {
|
|
1584
|
+
const l = getElDataMap(c), d = l.href;
|
|
1585
|
+
this.user.fetchAndSplit(d, this.abortController.signal).then((u) => {
|
|
1586
|
+
c.appendChild(u.html), this.parseContent(c), this.evalScripts(u.scripts), this.evalModules(u.modules), this.onAsyncHtmlReply(c, l);
|
|
1587
|
+
});
|
|
1588
|
+
const h = toNumber(l.z_interval);
|
|
1589
|
+
isNumber(h) && this.doOnBeat(() => {
|
|
1590
|
+
this.user.fetchAndSplit(d, this.abortController.signal).then((u) => {
|
|
1591
|
+
c.replaceChildren(u.html), this.parseContent(c), this.evalScripts(u.scripts), this.evalModules(u.modules);
|
|
1592
|
+
});
|
|
1593
|
+
}, h * 60 * 1e3);
|
|
1594
|
+
}
|
|
1595
|
+
);
|
|
1596
|
+
}
|
|
1597
|
+
/**
|
|
1598
|
+
* @inheritDoc
|
|
1599
|
+
* Called when the panel enters the document. Parses content to enable
|
|
1600
|
+
* Material Design Components, executes scripts and modules, then dispatches
|
|
1601
|
+
* the READY event.
|
|
1602
|
+
*/
|
|
1603
|
+
enterDocument() {
|
|
1604
|
+
const e = this.getElement();
|
|
1605
|
+
this.parseContent(e), this.evalScripts(this.responseObject.scripts), this.evalModules(this.responseObject.modules), super.enterDocument();
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* @inheritDoc
|
|
1609
|
+
* Called when the panel exits the document. Aborts any pending async
|
|
1610
|
+
* operations and performs cleanup.
|
|
1611
|
+
*/
|
|
1612
|
+
exitDocument() {
|
|
1613
|
+
this.abortController.abort(), super.exitDocument();
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1616
|
+
* @param {boolean} bool
|
|
1617
|
+
* @param {?string} url
|
|
1618
|
+
*/
|
|
1619
|
+
setIsRedirected(e, t) {
|
|
1620
|
+
this.redirected = e, this.redirected && t && (this.uri_ = t);
|
|
1621
|
+
}
|
|
1622
|
+
//--[ Built in events ]--
|
|
1623
|
+
/**
|
|
1624
|
+
* Dispatches a @code{UiEventType.PANEL} event.
|
|
1625
|
+
* A shorthand method to get panels to dispatch uniform events.
|
|
1626
|
+
* Views may listen just to this event, and act on the supplied value or
|
|
1627
|
+
* data payload.
|
|
1628
|
+
* @example
|
|
1629
|
+
* b.listen(a, Panel.panelEventCode(), e => {
|
|
1630
|
+
* console.log('B got', Panel.panelEventCode(), e);
|
|
1631
|
+
* console.log('Value is', e.detail.getValue());
|
|
1632
|
+
* console.log('Data is', e.detail.getData());
|
|
1633
|
+
* });
|
|
1634
|
+
* @param {string|number} value
|
|
1635
|
+
* @param {(string|number|?Object)=} opt_data
|
|
1636
|
+
* @return {boolean} If anyone called preventDefault on the event object
|
|
1637
|
+
* or if any of the handlers returns false this will also return false.
|
|
1638
|
+
*/
|
|
1639
|
+
dispatchPanelEvent(e, t) {
|
|
1640
|
+
const r = new ZooyEventData(e, t), i = EVT.makeEvent(UiEventType.PANEL, r);
|
|
1641
|
+
return this.debugMe("PANEL EVENT FIRED. Value:", e, "Opt DATA:", t), this.dispatchEvent(i);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
class FormPanel extends Panel {
|
|
1645
|
+
/**
|
|
1646
|
+
* @type {?HTMLFormElement}
|
|
1647
|
+
* @private
|
|
1648
|
+
*/
|
|
1649
|
+
#e = null;
|
|
1650
|
+
/**
|
|
1651
|
+
* Map of form fields to their error message DOM elements
|
|
1652
|
+
* @type {!Map<HTMLInputElement, Element>}
|
|
1653
|
+
* @private
|
|
1654
|
+
*/
|
|
1655
|
+
#t = /* @__PURE__ */ new Map();
|
|
1656
|
+
#s = !0;
|
|
1657
|
+
/**
|
|
1658
|
+
* Creates a new FormPanel instance.
|
|
1659
|
+
* @param {string=} uri Optional URI for fetching form content
|
|
1660
|
+
* @param {boolean} intercept If false, we do not try and intercept the forms
|
|
1661
|
+
*/
|
|
1662
|
+
constructor(e, t = !0) {
|
|
1663
|
+
super(e), this.#s = t, this.debugMode = !0, this.onSubmitSucFunc = (r, i) => null;
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Gets the form element managed by this panel.
|
|
1667
|
+
* @return {?HTMLFormElement} The form element, or null if not found
|
|
1668
|
+
*/
|
|
1669
|
+
get formEl() {
|
|
1670
|
+
return this.#e;
|
|
1671
|
+
}
|
|
1672
|
+
/**
|
|
1673
|
+
* @inheritDoc
|
|
1674
|
+
* Called when the form panel enters the document. Identifies the form element
|
|
1675
|
+
* and sets up submission interception and field error handling.
|
|
1676
|
+
*/
|
|
1677
|
+
enterDocument() {
|
|
1678
|
+
super.enterDocument(), this.formIdElementToForm_();
|
|
1679
|
+
}
|
|
1680
|
+
/**
|
|
1681
|
+
* Internal method to identify the form element and set up interception.
|
|
1682
|
+
* @private
|
|
1683
|
+
*/
|
|
1684
|
+
formIdElementToForm_() {
|
|
1685
|
+
this.#e = this.getFormFromId(), this.#e && (this.#a(this.#e), this.#n());
|
|
1686
|
+
}
|
|
1687
|
+
/**
|
|
1688
|
+
* @param {string=} string The id of the form we want to sterilise.
|
|
1689
|
+
* @return {?HTMLFormElement}
|
|
1690
|
+
*/
|
|
1691
|
+
getFormFromId(e) {
|
|
1692
|
+
let t = null, r = this.getElement().querySelector("form");
|
|
1693
|
+
return e && (r = document.getElementById(
|
|
1694
|
+
/** @type {string} */
|
|
1695
|
+
e
|
|
1696
|
+
) || r), r && r.tagName.toLowerCase() === "form" && (t = /** @type {HTMLFormElement} */
|
|
1697
|
+
r), t;
|
|
1698
|
+
}
|
|
1699
|
+
//--[ Field Validation Methods ]--
|
|
1700
|
+
/**
|
|
1701
|
+
* Initializes field validation. Sets up event listeners on the form
|
|
1702
|
+
* for change, input, and invalid events.
|
|
1703
|
+
* @private
|
|
1704
|
+
*/
|
|
1705
|
+
#n() {
|
|
1706
|
+
this.#e && (this.listen(this.#e, EV.CHANGE, (e) => {
|
|
1707
|
+
this.#i(e);
|
|
1708
|
+
}, { passive: !0 }), this.listen(this.#e, EV.INPUT, (e) => {
|
|
1709
|
+
this.clearAllValidationErrors(), this.#i(e);
|
|
1710
|
+
}), this.listen(this.#e, EV.INVALID, (e) => {
|
|
1711
|
+
e.preventDefault();
|
|
1712
|
+
const t = (
|
|
1713
|
+
/** @type {HTMLInputElement} */
|
|
1714
|
+
e.target
|
|
1715
|
+
);
|
|
1716
|
+
this.#o(t), this.displayFieldError(t);
|
|
1717
|
+
}, { passive: !0 }));
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Checks all form fields for validity. Returns true if all fields are valid.
|
|
1721
|
+
* Displays error messages for any invalid fields.
|
|
1722
|
+
* @return {boolean} True if all fields are valid, false otherwise
|
|
1723
|
+
*/
|
|
1724
|
+
checkAllFields() {
|
|
1725
|
+
const e = [...this.#e.elements].map((t) => [this.#r(t), t]).filter((t) => !t[0]);
|
|
1726
|
+
return e.forEach((t) => this.displayFieldError(t[1])), e.length === 0;
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* Clear all existing validation errors
|
|
1730
|
+
*/
|
|
1731
|
+
clearAllValidationErrors() {
|
|
1732
|
+
[...this.#e ? this.#e.elements : []].forEach((r) => this.#o(r)), [...this.#e.querySelectorAll(".non-field-errors")].forEach((r) => r.classList.remove("alert-error"));
|
|
1733
|
+
}
|
|
1734
|
+
/**
|
|
1735
|
+
* Format the message dom object and insert it into the DOM
|
|
1736
|
+
* @param {HTMLInputElement} field The field after which the alert will be inserted.
|
|
1737
|
+
* @param {string} msg The message in the alert.
|
|
1738
|
+
* @param {string} _css A CSS class name to add to the alert div.
|
|
1739
|
+
* @private
|
|
1740
|
+
*/
|
|
1741
|
+
displayAlert_(e, t, r) {
|
|
1742
|
+
const i = document.getElementById(`${e.id}-helper-text`) || document.createElement("p");
|
|
1743
|
+
i.textContent = t, this.#t.set(e, i);
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Check validation for a single field
|
|
1747
|
+
* @param {HTMLInputElement} field
|
|
1748
|
+
* @return {boolean} True if field is valid
|
|
1749
|
+
* @private
|
|
1750
|
+
*/
|
|
1751
|
+
#r(e) {
|
|
1752
|
+
this.#o(e);
|
|
1753
|
+
let t = !e.willValidate;
|
|
1754
|
+
return e.willValidate && (t = e.checkValidity()), t;
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Clear alert for a single field
|
|
1758
|
+
* @param {HTMLInputElement} field
|
|
1759
|
+
* @private
|
|
1760
|
+
*/
|
|
1761
|
+
#o(e) {
|
|
1762
|
+
e.classList.remove("error"), this.#t.has(e) && (this.#t.get(e).textContent = ""), this.#t.delete(e);
|
|
1763
|
+
}
|
|
1764
|
+
/**
|
|
1765
|
+
* Display the given error message on the given form field.
|
|
1766
|
+
* @param {HTMLInputElement} field
|
|
1767
|
+
* @param {string=} opt_msg
|
|
1768
|
+
*/
|
|
1769
|
+
displayFieldError(e, t) {
|
|
1770
|
+
const r = t || e.validationMessage;
|
|
1771
|
+
e.classList.add("error"), this.displayAlert_(e, r, "alert-error");
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* Display the given success message on the given form field.
|
|
1775
|
+
* @param {HTMLInputElement} field
|
|
1776
|
+
* @param {string} message
|
|
1777
|
+
*/
|
|
1778
|
+
displayFieldSuccess(e, t) {
|
|
1779
|
+
this.displayAlert_(e, t, "alert-success");
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Display the given information message on the given form field.
|
|
1783
|
+
* @param {HTMLInputElement} field
|
|
1784
|
+
* @param {string} message
|
|
1785
|
+
*/
|
|
1786
|
+
displayFieldInfo(e, t) {
|
|
1787
|
+
this.displayAlert_(e, t, "alert-info");
|
|
1788
|
+
}
|
|
1789
|
+
/**
|
|
1790
|
+
* Validate field on change event
|
|
1791
|
+
* @param {Event} e
|
|
1792
|
+
* @private
|
|
1793
|
+
*/
|
|
1794
|
+
#i(e) {
|
|
1795
|
+
this.#r(
|
|
1796
|
+
/** @type {HTMLInputElement} */
|
|
1797
|
+
e.target
|
|
1798
|
+
);
|
|
1799
|
+
}
|
|
1800
|
+
/**
|
|
1801
|
+
* Given a form id, get the form, and intercept and sterilise its submit.
|
|
1802
|
+
* Forms that passed through here will not be able to be submitted with a
|
|
1803
|
+
* normal submit button anymore, but built in HTML5 Constraint Validation
|
|
1804
|
+
* will still function on the form. This way, we can still have a button with
|
|
1805
|
+
* type="submit", which will trigger the validation, and we can submit
|
|
1806
|
+
* valid forms with xhrio which allows us to add callbacks to them.
|
|
1807
|
+
*
|
|
1808
|
+
* @param {HTMLFormElement} form The form we want to sterilise.
|
|
1809
|
+
* @return {HTMLFormElement}
|
|
1810
|
+
*/
|
|
1811
|
+
#a(e) {
|
|
1812
|
+
if (e && this.#s) {
|
|
1813
|
+
e.noValidate = !0;
|
|
1814
|
+
const t = this.user;
|
|
1815
|
+
this.listen(e, EV.SUBMIT, (r) => {
|
|
1816
|
+
r.preventDefault(), this.debugMe("Intercepted from SUBMIT"), this.checkAllFields() && t && t.formSubmit(this);
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
return e;
|
|
1820
|
+
}
|
|
1821
|
+
//--[ Round Trip ]--
|
|
1822
|
+
/**
|
|
1823
|
+
* @param {function(!FormPanel, (string|!ServerFormSuccessJsonType)=): (?|null|Promise<?>)} func
|
|
1824
|
+
*/
|
|
1825
|
+
onSubmitSuccess(e) {
|
|
1826
|
+
this.onSubmitSucFunc = e;
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Given a 'fetch' reply, replace the form.
|
|
1830
|
+
* This simply replaced the form element with what came back from the server
|
|
1831
|
+
* and re-reads the scripts.
|
|
1832
|
+
* @param {string} reply
|
|
1833
|
+
*/
|
|
1834
|
+
replaceForm(e) {
|
|
1835
|
+
if (this.responseObject = splitScripts(e), this.responseObject.html) {
|
|
1836
|
+
if (this.redirected) {
|
|
1837
|
+
this.removeAllListener();
|
|
1838
|
+
const t = this.getElement(), r = t.parentNode;
|
|
1839
|
+
this.setElementInternal_(this.responseObject.html), r.replaceChild(this.getElement(), t);
|
|
1840
|
+
} else {
|
|
1841
|
+
const t = (
|
|
1842
|
+
/** @type {!Element} */
|
|
1843
|
+
this.responseObject.html.querySelector("form")
|
|
1844
|
+
);
|
|
1845
|
+
t && replaceNode(t, this.#e);
|
|
1846
|
+
const r = (
|
|
1847
|
+
/** @type {!Element} */
|
|
1848
|
+
this.responseObject.html.querySelector('button[type="submit"]')
|
|
1849
|
+
), i = (
|
|
1850
|
+
/** @type {!Element} */
|
|
1851
|
+
this.getElement().querySelector('button[type="submit"]')
|
|
1852
|
+
);
|
|
1853
|
+
r && i && replaceNode(r, i);
|
|
1854
|
+
}
|
|
1855
|
+
this.enterDocument();
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
/**
|
|
1859
|
+
* Expects HTML data from a call to the back.
|
|
1860
|
+
* @return {Promise} Returns a promise with this panel as value.
|
|
1861
|
+
*/
|
|
1862
|
+
refreshFromFromServer() {
|
|
1863
|
+
const e = this.user, t = this.uri;
|
|
1864
|
+
return e ? e.fetch(t, this.abortController.signal).then(
|
|
1865
|
+
(r) => this.replaceForm(r)
|
|
1866
|
+
) : Promise.reject(new Error("No UserManager instance available for FormPanel.refreshFromFromServer()"));
|
|
1867
|
+
}
|
|
1868
|
+
/**
|
|
1869
|
+
* @param {string} reply
|
|
1870
|
+
* @return {Promise}
|
|
1871
|
+
*/
|
|
1872
|
+
processSubmitReply(e) {
|
|
1873
|
+
this.clearAllValidationErrors();
|
|
1874
|
+
let t = !1;
|
|
1875
|
+
if (whatType(e) === "object" && e.success)
|
|
1876
|
+
return Promise.resolve(this).then(() => {
|
|
1877
|
+
this.onSubmitSucFunc(this, e), this.dispatchCompEvent(UiEventType.FORM_SUBMIT_SUCCESS);
|
|
1878
|
+
});
|
|
1879
|
+
if (e === "success" ? (this.debugMe(`
|
|
1880
|
+
1.REDIRECTED: ${this.redirected}
|
|
1881
|
+
REPLY: ${e}`), t = !0) : e === `redirected_success
|
|
1882
|
+
` ? (this.debugMe(`
|
|
1883
|
+
2.REDIRECTED: ${this.redirected}
|
|
1884
|
+
REPLY: ${e}`), t = !0, this.redirected = !1) : (this.debugMe(`
|
|
1885
|
+
3.REDIRECTED: ${this.redirected}
|
|
1886
|
+
REPLY: Some form HTML`), this.replaceForm(e)), t && this.redirected)
|
|
1887
|
+
return this.debugMe(`
|
|
1888
|
+
4.REDIRECTED: ${this.redirected}
|
|
1889
|
+
SUCCESS: ${t}`), this.redirected = !1, Promise.resolve(this);
|
|
1890
|
+
if (t)
|
|
1891
|
+
return this.debugMe(`
|
|
1892
|
+
5.REDIRECTED: ${this.redirected}
|
|
1893
|
+
SUCCESS: ${t}`), Promise.resolve(this).then(() => {
|
|
1894
|
+
this.onSubmitSucFunc(this), this.dispatchCompEvent(UiEventType.FORM_SUBMIT_SUCCESS);
|
|
1895
|
+
});
|
|
1896
|
+
this.debugMe(`
|
|
1897
|
+
6.REDIRECTED: ${this.redirected}
|
|
1898
|
+
SUCCESS: ${t}`);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
class Split extends Component {
|
|
1902
|
+
/**
|
|
1903
|
+
* Returns the split event type code used for dispatching split events.
|
|
1904
|
+
* @return {string} The SPLIT event type
|
|
1905
|
+
*/
|
|
1906
|
+
static splitEventCode() {
|
|
1907
|
+
return UiEventType.SPLIT;
|
|
1908
|
+
}
|
|
1909
|
+
/**
|
|
1910
|
+
* Creates a new Split component instance. Initializes nest tracking and
|
|
1911
|
+
* callback management.
|
|
1912
|
+
*/
|
|
1913
|
+
constructor() {
|
|
1914
|
+
super(), this.dragImage_ = document.createElement("canvas"), this.nestMap_ = /* @__PURE__ */ new Map(), this.nestCallbackMap_ = /* @__PURE__ */ new Map(), this.splitNests_ = /* @__PURE__ */ new Set();
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* Returns all nest entries (name and config object) that have a valid element.
|
|
1918
|
+
* @return {!Array<!Array<string, !Object>>} Array of [nestName, nestConfig] pairs
|
|
1919
|
+
*/
|
|
1920
|
+
get nests() {
|
|
1921
|
+
return [...this.nestMap_.entries()].filter(([e, { nestEl: t }]) => t);
|
|
1922
|
+
}
|
|
1923
|
+
/**
|
|
1924
|
+
* Gets the nest element for a given nest name.
|
|
1925
|
+
* @param {string} nestName The nest name to look up
|
|
1926
|
+
* @return {!Element | undefined} The nest element, or undefined if not found
|
|
1927
|
+
*/
|
|
1928
|
+
getNest(e) {
|
|
1929
|
+
return this.nestMap_.get(e)?.nestEl;
|
|
1930
|
+
}
|
|
1931
|
+
/**
|
|
1932
|
+
* Returns the names of all nests that haven't been further split.
|
|
1933
|
+
* @return {!Array<string>} Array of nest names
|
|
1934
|
+
*/
|
|
1935
|
+
get nestNames() {
|
|
1936
|
+
return [...this.nestMap_.entries()].filter(([e, t]) => !this.splitNests_.has(t)).map(([e]) => e);
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* Closes and locks all nests in this split component.
|
|
1940
|
+
* @param {!Function=} callback Callback to execute after each nest closes
|
|
1941
|
+
* @param {boolean=} opt_skipAni If true, skips animation
|
|
1942
|
+
*/
|
|
1943
|
+
closeAndLockAll(e = identity, t = !1) {
|
|
1944
|
+
this.nestNames.forEach((r) => this.closeAndLock(r, e, t));
|
|
1945
|
+
}
|
|
1946
|
+
/**
|
|
1947
|
+
* Opens and unlocks all nests in this split component.
|
|
1948
|
+
* @param {!Function=} callback Callback to execute after each nest opens
|
|
1949
|
+
* @param {boolean=} opt_skipAni If true, skips animation
|
|
1950
|
+
*/
|
|
1951
|
+
openAndUnlockAll(e = identity, t = !1) {
|
|
1952
|
+
this.nestNames.forEach((r) => this.openAndUnlock(r, e, t));
|
|
1953
|
+
}
|
|
1954
|
+
// ---------------------------------------------------------------------------
|
|
1955
|
+
/**
|
|
1956
|
+
* Lock a dragger. Removes the listeners that make it draggable.
|
|
1957
|
+
* Adds the 'locked' class to the element.
|
|
1958
|
+
* Sets the element's "display" style to none.
|
|
1959
|
+
* Even though all these operations are performed on the the dragger, access
|
|
1960
|
+
* is vai the nest name - for consistency with the rest of the API.
|
|
1961
|
+
* @param {string} nestName Nest name
|
|
1962
|
+
*/
|
|
1963
|
+
lock(e) {
|
|
1964
|
+
const { dragEl: t } = this.nestMap_.get(e);
|
|
1965
|
+
t.classList.add("locked");
|
|
1966
|
+
}
|
|
1967
|
+
/**
|
|
1968
|
+
* The reverse of a lock. Removes the classes added by <@code>lock</> and
|
|
1969
|
+
* restores the draggable listeners.
|
|
1970
|
+
* Even though all these operations are performed on the the dragger, access
|
|
1971
|
+
* is vai the nest name - for consistency with the rest of the API.
|
|
1972
|
+
* @param {string} nestName Nest name
|
|
1973
|
+
*/
|
|
1974
|
+
unlock(e) {
|
|
1975
|
+
const { dragEl: t } = this.nestMap_.get(e);
|
|
1976
|
+
t.classList.remove("locked");
|
|
1977
|
+
}
|
|
1978
|
+
/**
|
|
1979
|
+
* Resize a nest.
|
|
1980
|
+
* @param {string} nestName Nest name
|
|
1981
|
+
* @param {number} size The required size of the resulting move.
|
|
1982
|
+
* @param {Function?} callback A callback function to call once
|
|
1983
|
+
* the resize completed.
|
|
1984
|
+
* @param {boolean?} opt_skipAni When true, the resize won't be animated, but
|
|
1985
|
+
* simply affected directly.
|
|
1986
|
+
*/
|
|
1987
|
+
resize(e, t, r = identity, i = !1) {
|
|
1988
|
+
const { sizeNow: o, nestEl: a, canResize: p } = this.nestMap_.get(e);
|
|
1989
|
+
p && (i || o === t ? (a.classList.remove("animated"), a.style.flexBasis = `${t}px`, this.nestMap_.get(e).sizeNow = t, r()) : (a.classList.add("animated"), this.nestCallbackMap_.set(e, () => {
|
|
1990
|
+
a.classList.remove("animated"), this.nestMap_.get(e).sizeNow = t, r();
|
|
1991
|
+
}), a.style.flexBasis = `${t}px`));
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* Close the nest and lock it. See <@code>lock</>
|
|
1995
|
+
* @param {string} nestName Nest name
|
|
1996
|
+
* @param {Function?} callback A callback function to call once
|
|
1997
|
+
* the resize completed.
|
|
1998
|
+
* @param {boolean?} opt_skipAni When true, the resize won't be animated, but
|
|
1999
|
+
* simply affected directly.
|
|
2000
|
+
*/
|
|
2001
|
+
closeAndLock(e, t = identity, r = !1) {
|
|
2002
|
+
const { nestEl: i, dragEl: o, canResize: a } = this.nestMap_.get(e);
|
|
2003
|
+
if (!a)
|
|
2004
|
+
return;
|
|
2005
|
+
const p = () => {
|
|
2006
|
+
o.classList.add("locked", "closed"), i.classList.add("closed"), t();
|
|
2007
|
+
};
|
|
2008
|
+
this.resize(e, 0, p, r);
|
|
2009
|
+
}
|
|
2010
|
+
closeAndUnlock(e, t = identity, r = !1) {
|
|
2011
|
+
const { nestEl: i, dragEl: o, canResize: a } = this.nestMap_.get(e);
|
|
2012
|
+
if (!a)
|
|
2013
|
+
return;
|
|
2014
|
+
const p = () => {
|
|
2015
|
+
i.classList.remove("closed"), o.classList.remove("closed", "locked"), o.classList.add("collapsed"), t();
|
|
2016
|
+
};
|
|
2017
|
+
this.resize(e, 0, p, r);
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* Open a closed and locked nest. Simply a convenience method to combine
|
|
2021
|
+
* the unlock and resize steps.
|
|
2022
|
+
* @param {string} nestName Nest name
|
|
2023
|
+
* @param {number?} size The required size of the resulting move.
|
|
2024
|
+
* @param {Function?} callback A callback function to call once
|
|
2025
|
+
* the resize completed.
|
|
2026
|
+
* @param {boolean?} opt_skipAni When true, the resize won't be animated, but
|
|
2027
|
+
* simply affected directly.
|
|
2028
|
+
*/
|
|
2029
|
+
openAndUnlock(e, t, r, i = !1) {
|
|
2030
|
+
const {
|
|
2031
|
+
nestEl: o,
|
|
2032
|
+
dragEl: a,
|
|
2033
|
+
defSize: p,
|
|
2034
|
+
lastSize: f,
|
|
2035
|
+
canResize: _
|
|
2036
|
+
} = this.nestMap_.get(e);
|
|
2037
|
+
if (!_)
|
|
2038
|
+
return;
|
|
2039
|
+
const g = t || f || p;
|
|
2040
|
+
o.classList.remove("closed"), a.classList.remove("closed", "locked", "collapsed"), setTimeout(() => {
|
|
2041
|
+
this.resize(e, g, r, i);
|
|
2042
|
+
}, 50);
|
|
2043
|
+
}
|
|
2044
|
+
/**
|
|
2045
|
+
* Open a closed and locked nest. Keep the nest locked.
|
|
2046
|
+
* @param {string} nestName Nest name
|
|
2047
|
+
* @param {number?} size The required size of the resulting move.
|
|
2048
|
+
* @param {Function?} callback A callback function to call once
|
|
2049
|
+
* the resize completed.
|
|
2050
|
+
* @param {boolean?} opt_skipAni When true, the resize won't be animated, but
|
|
2051
|
+
* simply affected directly.
|
|
2052
|
+
*/
|
|
2053
|
+
openAndLock(e, t, r, i = !1) {
|
|
2054
|
+
const {
|
|
2055
|
+
nestEl: o,
|
|
2056
|
+
dragEl: a,
|
|
2057
|
+
defSize: p,
|
|
2058
|
+
lastSize: f,
|
|
2059
|
+
canResize: _
|
|
2060
|
+
} = this.nestMap_.get(e);
|
|
2061
|
+
if (!_)
|
|
2062
|
+
return;
|
|
2063
|
+
const g = t || f || p;
|
|
2064
|
+
o.classList.remove("closed"), a.classList.remove("closed", "collapsed"), setTimeout(() => {
|
|
2065
|
+
this.resize(e, g, r, i);
|
|
2066
|
+
}, 50);
|
|
2067
|
+
}
|
|
2068
|
+
/**
|
|
2069
|
+
* Close nest. Move it out the way, but continue to make it draggable;
|
|
2070
|
+
* to the nest and dragger.
|
|
2071
|
+
* @param {string} nestName Nest name
|
|
2072
|
+
* @param {Function|undefined} callback A callback function to call once
|
|
2073
|
+
* the resize completed.
|
|
2074
|
+
* @param {boolean?} opt_skipAni When true, the resize won't be animated, but
|
|
2075
|
+
* simply affected directly.
|
|
2076
|
+
*/
|
|
2077
|
+
close(e, t = identity, r = !1) {
|
|
2078
|
+
const { dragEl: i, canResize: o, nestEl: a } = this.nestMap_.get(e);
|
|
2079
|
+
if (!o)
|
|
2080
|
+
return;
|
|
2081
|
+
this.dispatchSplitEvent(UiEventType.SPLIT_WILL_CLOSE, { nestName: e, nestEl: a });
|
|
2082
|
+
const p = () => {
|
|
2083
|
+
i.classList.add("collapsed"), this.dispatchSplitEvent(UiEventType.SPLIT_DID_CLOSE, { nestName: e, nestEl: a }), t();
|
|
2084
|
+
};
|
|
2085
|
+
this.resize(e, 0, p, r);
|
|
2086
|
+
}
|
|
2087
|
+
/**
|
|
2088
|
+
* Open the nest ignoring its current locked state.
|
|
2089
|
+
* @param {string} nestName Nest name
|
|
2090
|
+
* @param {Function|undefined} callback A callback function to call once
|
|
2091
|
+
* the resize completed.
|
|
2092
|
+
* @param {boolean?} opt_skipAni When true, the resize won't be animated, but
|
|
2093
|
+
* simply affected directly.
|
|
2094
|
+
*/
|
|
2095
|
+
open(e, t = identity, r = !1) {
|
|
2096
|
+
const { defSize: i, lastSize: o, dragEl: a, canResize: p, nestEl: f } = this.nestMap_.get(e);
|
|
2097
|
+
if (!p)
|
|
2098
|
+
return;
|
|
2099
|
+
this.dispatchSplitEvent(UiEventType.SPLIT_WILL_OPEN, { nestName: e, nestEl: f }), a.classList.remove("collapsed", "closed");
|
|
2100
|
+
const _ = o || i, g = () => {
|
|
2101
|
+
this.dispatchSplitEvent(UiEventType.SPLIT_DID_OPEN, { nestName: e, nestEl: f }), t();
|
|
2102
|
+
};
|
|
2103
|
+
this.resize(e, _, g, r);
|
|
2104
|
+
}
|
|
2105
|
+
/**
|
|
2106
|
+
* Close the nest ignoring its current locked state.
|
|
2107
|
+
* @param {string} nestName Nest name
|
|
2108
|
+
* @param {Function|undefined} callback A callback function to call once
|
|
2109
|
+
* the resize completed.
|
|
2110
|
+
* @param {boolean?} opt_skipAni When true, the resize won't be animated, but
|
|
2111
|
+
* simply affected directly.
|
|
2112
|
+
*/
|
|
2113
|
+
toggle(e, t, r = !1) {
|
|
2114
|
+
this.nestMap_.get(e).sizeNow === 0 ? this.open(e, t, r) : this.close(e, t, r);
|
|
2115
|
+
}
|
|
2116
|
+
//----------------------------------------------------------------------------
|
|
2117
|
+
/**
|
|
2118
|
+
* Split an element into parts, each separated by a dragger component.
|
|
2119
|
+
* The split creates 3 new elements inside root element. Thus it is not
|
|
2120
|
+
* truly splitting the reference element, but rather overlaying the
|
|
2121
|
+
* split functionality over the reference element.
|
|
2122
|
+
* A split can be either horizontal - called "EW" (east-west) or
|
|
2123
|
+
* vertical - called "NS" (north-south).
|
|
2124
|
+
* By convention the new elements created are called 'nests', and are named
|
|
2125
|
+
* in order "A", "B" and "C" respectively.
|
|
2126
|
+
* For EW splits the we name from left to right:
|
|
2127
|
+
* A B C
|
|
2128
|
+
* For NS splits we name from top to bottom:
|
|
2129
|
+
* A
|
|
2130
|
+
* B
|
|
2131
|
+
* C
|
|
2132
|
+
* Each newly created nest is a valid root for further splitting.
|
|
2133
|
+
* When a nest is split, the resultant nests names are prepended with the
|
|
2134
|
+
* name of the root nest that was split.
|
|
2135
|
+
* Example: If nest C is split, the resultant nests are named CA, CB, and CC.
|
|
2136
|
+
* When one of these (for example CA) is split further, the pattern continues
|
|
2137
|
+
* with the resulting nests being CAA, CAB, CAC etc.
|
|
2138
|
+
* @param {Element=} opt_el The element to split. If not given, the
|
|
2139
|
+
* components own element is used. Else, the element is checked to be
|
|
2140
|
+
* a member of this split-group, and if so, is split.
|
|
2141
|
+
* @param {?string} orientation Only 'EW' or 'NS'. Defaults to 'EW'
|
|
2142
|
+
* @param {?number} defSizeA Default size of the 'A' nest.
|
|
2143
|
+
* @param {?number} defSizeC Default size of the 'C' nest. The 'B' nest gets the
|
|
2144
|
+
* remainder of the 'A' and 'C' nests, and can not be set directly.
|
|
2145
|
+
* @param {?Array<string>} classArrA Extra class names to be added to this nest
|
|
2146
|
+
* @param {?Array<string>} classArrB Extra class names to be added to this nest
|
|
2147
|
+
* @param {?Array<string>} classArrC Extra class names to be added to this nest
|
|
2148
|
+
* @param {?Array<string>} classArrDragger Extra classes for the dragger element
|
|
2149
|
+
* @param {?Array<string>} classArrGrabber Extra classes for the grabber element
|
|
2150
|
+
*/
|
|
2151
|
+
addSplit(e = void 0, t = "EW", r = 100, i = 100, o = [], a = [], p = [], f = [], _ = []) {
|
|
2152
|
+
const g = e || this.getElement();
|
|
2153
|
+
if (this.splitNests_.has(g))
|
|
2154
|
+
return ["Already used!", g];
|
|
2155
|
+
convertElToSplit(g, t), this.splitNests_.add(g);
|
|
2156
|
+
let v = "";
|
|
2157
|
+
if (e) {
|
|
2158
|
+
const S = [...this.nestMap_.entries()].find(([C, { nestEl: D }]) => D === e);
|
|
2159
|
+
if (S)
|
|
2160
|
+
v = S[0];
|
|
2161
|
+
else
|
|
2162
|
+
return ["Element not managed by this component", e];
|
|
2163
|
+
}
|
|
2164
|
+
const c = `${v}A`, l = makeOuterNest(r, ["zoo_nest__A", c, ...o]), d = new Dragger(t);
|
|
2165
|
+
d.domFunc = makeDraggerEl([`zoo_drag_${c}`, ...f], _), d.render(g), this.nestMap_.set(c, {
|
|
2166
|
+
side: "A",
|
|
2167
|
+
nestEl: l,
|
|
2168
|
+
dragger: d,
|
|
2169
|
+
dragEl: d.getElement(),
|
|
2170
|
+
defSize: r,
|
|
2171
|
+
sizeNow: -1,
|
|
2172
|
+
canResize: !0
|
|
2173
|
+
});
|
|
2174
|
+
const h = `${v}B`, u = makeInnerNest(["zoo_nest__B", h, ...a]);
|
|
2175
|
+
this.nestMap_.set(h, { nestEl: u, canResize: !1 });
|
|
2176
|
+
const m = `${v}C`, E = new Dragger(t);
|
|
2177
|
+
E.domFunc = makeDraggerEl([`zoo_drag_${m}`, ...f], _), E.render(g);
|
|
2178
|
+
const y = makeOuterNest(i, ["zoo_nest__C", m, ...p]);
|
|
2179
|
+
this.nestMap_.set(m, {
|
|
2180
|
+
side: "C",
|
|
2181
|
+
nestEl: y,
|
|
2182
|
+
dragger: E,
|
|
2183
|
+
dragEl: E.getElement(),
|
|
2184
|
+
defSize: i,
|
|
2185
|
+
sizeNow: -1,
|
|
2186
|
+
canResize: !0
|
|
2187
|
+
}), [l, d.getElement(), u, E.getElement(), y].forEach((S) => g.appendChild(S));
|
|
2188
|
+
let T = 0;
|
|
2189
|
+
const b = (S, C, D, O) => {
|
|
2190
|
+
let M;
|
|
2191
|
+
const w = S === "NS" ? "height" : "width", A = S === "NS" ? "deltaY" : "deltaX";
|
|
2192
|
+
return (L) => {
|
|
2193
|
+
switch (L.detail.value_) {
|
|
2194
|
+
case UiEventType.COMP_DRAG_START:
|
|
2195
|
+
M = C.getBoundingClientRect()[w], T = u.getBoundingClientRect()[w] + M, this.nestMap_.get(D).dragEl.classList.remove("collapsed");
|
|
2196
|
+
break;
|
|
2197
|
+
case UiEventType.COMP_DRAG_MOVE: {
|
|
2198
|
+
const I = Math.min(T, M + O * L.detail.data_[A]);
|
|
2199
|
+
this.nestMap_.get(D).lastSize = I, C.style.flexBasis = I + "px";
|
|
2200
|
+
break;
|
|
2201
|
+
}
|
|
2202
|
+
case UiEventType.COMP_DRAG_END:
|
|
2203
|
+
this.dispatchSplitEvent(UiEventType.COMP_DRAG_END, this.nestMap_.get(D));
|
|
2204
|
+
break;
|
|
2205
|
+
}
|
|
2206
|
+
};
|
|
2207
|
+
};
|
|
2208
|
+
this.listen(d, Component.compEventCode(), b(t, l, c, 1)), this.listen(E, Component.compEventCode(), b(t, y, m, -1)), this.listen(d.getElement(), EV.DBLCLICK, () => this.toggle(c)), this.listen(E.getElement(), EV.DBLCLICK, () => this.toggle(m)), this.listen(d.getElement().querySelector(".zoo_grabber"), EV.CLICK, () => this.toggle(c)), this.listen(E.getElement().querySelector(".zoo_grabber"), EV.CLICK, () => this.toggle(m)), this.listen(l, EV.TRANSITIONEND, (S) => {
|
|
2209
|
+
if (S.propertyName === "flex-basis" && S.target === l) {
|
|
2210
|
+
const C = this.nestCallbackMap_.get(c);
|
|
2211
|
+
this.nestCallbackMap_.delete(c), C && C(), this.dispatchSplitEvent(UiEventType.SPLIT_TRANSITION_END, this.nestMap_.get(c));
|
|
2212
|
+
}
|
|
2213
|
+
}), this.listen(y, EV.TRANSITIONEND, (S) => {
|
|
2214
|
+
if (S.propertyName === "flex-basis" && S.target === y) {
|
|
2215
|
+
const C = this.nestCallbackMap_.get(m);
|
|
2216
|
+
this.nestCallbackMap_.delete(m), C && C(), this.dispatchSplitEvent(UiEventType.SPLIT_TRANSITION_END, this.nestMap_.get(m));
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
//--[ Built in events ]--
|
|
2221
|
+
/**
|
|
2222
|
+
* Dispatches a {@code UiEventType.SPLIT} event.
|
|
2223
|
+
* Views may listen just to this event, and act on the supplied value or
|
|
2224
|
+
* data payload.
|
|
2225
|
+
* @param {string|number} value
|
|
2226
|
+
* @param {(string|number|?Object)=} opt_data
|
|
2227
|
+
* @return {boolean} If anyone called preventDefault on the event object (or
|
|
2228
|
+
* if any of the handlers returns false this will also return false.
|
|
2229
|
+
*/
|
|
2230
|
+
dispatchSplitEvent(e, t) {
|
|
2231
|
+
const r = new ZooyEventData(e, t), i = EVT.makeEvent(UiEventType.SPLIT, r);
|
|
2232
|
+
return this.dispatchEvent(i);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
const convertElToSplit = (n, e) => (n.classList.add("zoo_split", `zoo_split__${e.toLowerCase()}`), n), makeOuterNest = (n, e = []) => {
|
|
2236
|
+
const t = document.createElement("div");
|
|
2237
|
+
return t.setAttribute("id", randomId(7)), t.classList.add("zoo_nest", "zoo_nest__outer", ...e), t.style.flexBasis = n + "px", t;
|
|
2238
|
+
}, makeInnerNest = (n = []) => {
|
|
2239
|
+
const e = document.createElement("div");
|
|
2240
|
+
return e.setAttribute("id", randomId(7)), e.classList.add("zoo_nest", "zoo_nest__inner", ...n), e;
|
|
2241
|
+
}, makeDraggerEl = (n = [], e = []) => () => {
|
|
2242
|
+
const t = document.createElement("div");
|
|
2243
|
+
t.setAttribute("id", randomId(7)), t.classList.add("zoo_dragger", ...n);
|
|
2244
|
+
const r = document.createElement("div");
|
|
2245
|
+
return r.classList.add("zoo_grabber", ...e), t.appendChild(r), t;
|
|
2246
|
+
}, needsToScroll = (n) => {
|
|
2247
|
+
const t = n.getBoundingClientRect().bottom;
|
|
2248
|
+
return (r) => r.getBoundingClientRect().bottom > t;
|
|
2249
|
+
}, treeNodeSelect = (n) => (e) => {
|
|
2250
|
+
const t = n.getElement().querySelector(".zv_tree_container"), r = needsToScroll(t), i = n.getElement().querySelectorAll(".tree-node");
|
|
2251
|
+
let o;
|
|
2252
|
+
[...i].forEach((a) => {
|
|
2253
|
+
enableClass(a, "mdc-deprecated-list-item--activated", a.id === `tree-node_${e}`), a.id === `tree-node_${e}` && (o = a);
|
|
2254
|
+
}), o && r(o) && o.scrollIntoView(!1);
|
|
2255
|
+
}, toggleTree = (n, e) => {
|
|
2256
|
+
const t = e.getElement().querySelector(".children"), r = (
|
|
2257
|
+
/**@type {boolean} */
|
|
2258
|
+
n.isOn
|
|
2259
|
+
), i = e.getElement().querySelectorAll(".children"), o = e.getElement().querySelectorAll(".zoo__reveal_icon");
|
|
2260
|
+
[...i].forEach((a) => enableClass(
|
|
2261
|
+
a,
|
|
2262
|
+
"zoo__tree-children__hidden",
|
|
2263
|
+
a !== t && !r
|
|
2264
|
+
)), [...o].forEach((a) => enableClass(
|
|
2265
|
+
a,
|
|
2266
|
+
"zoo__icon_rotated",
|
|
2267
|
+
a !== t && !r
|
|
2268
|
+
));
|
|
2269
|
+
}, toggleTreeChildren = (n, e) => {
|
|
2270
|
+
const t = e.trigger, r = t.getAttribute("data-child-id"), i = n.getElement().querySelector(`#${r}`);
|
|
2271
|
+
toggleClass(i, "zoo__tree-children__hidden"), toggleClass(t, "zoo__icon_rotated");
|
|
2272
|
+
}, _openTreeFromNodeUp = (n, e) => {
|
|
2273
|
+
const t = e.parentElement;
|
|
2274
|
+
t.classList.contains("children") && (enableClass(t, "zoo__tree-children__hidden", !1), _openTreeFromNodeUp(n, t));
|
|
2275
|
+
}, treeUtils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
2276
|
+
__proto__: null,
|
|
2277
|
+
_openTreeFromNodeUp,
|
|
2278
|
+
toggleTree,
|
|
2279
|
+
toggleTreeChildren,
|
|
2280
|
+
treeNodeSelect
|
|
2281
|
+
}, Symbol.toStringTag, { value: "Module" })), MdcTreeHandlers = {
|
|
2282
|
+
/**
|
|
2283
|
+
* Toggle the entire tree open or closed.
|
|
2284
|
+
* First level children remain open for better UX.
|
|
2285
|
+
*
|
|
2286
|
+
* @param {Object} eventData - Event data with isOn boolean
|
|
2287
|
+
* @param {Panel} ePanel - The panel containing the tree
|
|
2288
|
+
*/
|
|
2289
|
+
toggle_tree: function(n, e) {
|
|
2290
|
+
toggleTree(n, e);
|
|
2291
|
+
},
|
|
2292
|
+
/**
|
|
2293
|
+
* Toggle children visibility for a specific tree node.
|
|
2294
|
+
*
|
|
2295
|
+
* @param {Object} eventData - Event data with trigger element
|
|
2296
|
+
* @param {Panel} ePanel - The panel containing the tree
|
|
2297
|
+
*/
|
|
2298
|
+
"tree_toggle-children": function(n, e) {
|
|
2299
|
+
toggleTreeChildren(e, n);
|
|
2300
|
+
}
|
|
2301
|
+
}, SearchHandlers = {
|
|
2302
|
+
/**
|
|
2303
|
+
* Perform search with query string construction.
|
|
2304
|
+
* Builds href with search query and additional query parameters.
|
|
2305
|
+
*
|
|
2306
|
+
* @param {Object} eventData - Event data with href, formData.q, and targetval
|
|
2307
|
+
* @param {Panel} ePanel - The panel to update
|
|
2308
|
+
*/
|
|
2309
|
+
search: function(n, e) {
|
|
2310
|
+
let t = `${n.href}`;
|
|
2311
|
+
const r = n.formData.q, i = n.targetval;
|
|
2312
|
+
if (r !== "" && (t = `${t}?q=${r}`), i !== "") {
|
|
2313
|
+
let o = i;
|
|
2314
|
+
i.includes("page=") && (o = i.split("&").filter((a) => !a.includes("page=")).join("&")), t = r !== "" ? `${t}&${o}` : `${t}?${o}`;
|
|
2315
|
+
}
|
|
2316
|
+
this.user.fetchAndSplit(t, e.abortController.signal).then(
|
|
2317
|
+
(o) => e.onReplacePartialDom(o, n.zvptarget)
|
|
2318
|
+
);
|
|
2319
|
+
},
|
|
2320
|
+
/**
|
|
2321
|
+
* Reset search form and query parameters.
|
|
2322
|
+
* Clears the 'q' field in the closest form and removes 'q' from query params.
|
|
2323
|
+
*
|
|
2324
|
+
* @param {Object} eventData - Event data with trigger element
|
|
2325
|
+
* @param {Panel} ePanel - The panel to update
|
|
2326
|
+
*/
|
|
2327
|
+
reset_search: function(n, e) {
|
|
2328
|
+
const t = n.trigger.closest("form");
|
|
2329
|
+
e.removeFromQParams("q"), t.elements.q.value = "", t.dispatchEvent(new Event("submit"));
|
|
2330
|
+
}
|
|
2331
|
+
}, QueryParamHandlers = {
|
|
2332
|
+
/**
|
|
2333
|
+
* Handle pagination by fetching content with query parameters.
|
|
2334
|
+
*
|
|
2335
|
+
* @param {Object} eventData - Event data with href and targetval for pagination
|
|
2336
|
+
* @param {Panel} ePanel - The panel to update
|
|
2337
|
+
*/
|
|
2338
|
+
paginate: function(n, e) {
|
|
2339
|
+
const t = `${n.href}?${n.targetval}`;
|
|
2340
|
+
this.user.fetchAndSplit(t, e.abortController.signal).then(
|
|
2341
|
+
(r) => e.onReplacePartialDom(r, n.zvptarget)
|
|
2342
|
+
);
|
|
2343
|
+
},
|
|
2344
|
+
/**
|
|
2345
|
+
* Handle list filtering with query parameters.
|
|
2346
|
+
*
|
|
2347
|
+
* @param {Object} eventData - Event data with href and targetval for filtering
|
|
2348
|
+
* @param {Panel} ePanel - The panel to update
|
|
2349
|
+
*/
|
|
2350
|
+
list_filter: function(n, e) {
|
|
2351
|
+
const t = `${n.href}?${n.targetval}`;
|
|
2352
|
+
this.user.fetchAndSplit(t, e.abortController.signal).then(
|
|
2353
|
+
(r) => e.onReplacePartialDom(r, n.zvptarget)
|
|
2354
|
+
);
|
|
2355
|
+
}
|
|
2356
|
+
}, handlers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
2357
|
+
__proto__: null,
|
|
2358
|
+
MdcTreeHandlers,
|
|
2359
|
+
QueryParamHandlers,
|
|
2360
|
+
SearchHandlers
|
|
2361
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
2362
|
+
class View extends EVT {
|
|
2363
|
+
//--[ Static ]--
|
|
2364
|
+
/**
|
|
2365
|
+
* Returns the view event type code used for dispatching view events.
|
|
2366
|
+
* @return {string} The VIEW event type
|
|
2367
|
+
*/
|
|
2368
|
+
static viewEventCode() {
|
|
2369
|
+
return UiEventType.VIEW;
|
|
2370
|
+
}
|
|
2371
|
+
/**
|
|
2372
|
+
* Creates a new View instance. Initializes panel management, event handling,
|
|
2373
|
+
* and metadata storage.
|
|
2374
|
+
*/
|
|
2375
|
+
constructor() {
|
|
2376
|
+
super(), this.panelMap = /* @__PURE__ */ new Map(), this.user_ = void 0, this.split_ = void 0, this.panelEventMap_ = this.initPanelEventsInternal_(), this.switchViewMap_ = /* @__PURE__ */ new Map(), this.metaData_ = /* @__PURE__ */ new Map(), this.recordHistory_ = void 0, this.registerViewConstructor_ = void 0;
|
|
2377
|
+
}
|
|
2378
|
+
set recordHistory(e) {
|
|
2379
|
+
this.recordHistory_ = e;
|
|
2380
|
+
}
|
|
2381
|
+
get recordHistory() {
|
|
2382
|
+
return this.recordHistory_;
|
|
2383
|
+
}
|
|
2384
|
+
set registerViewConstructor(e) {
|
|
2385
|
+
this.registerViewConstructor_ = e;
|
|
2386
|
+
}
|
|
2387
|
+
get registerViewConstructor() {
|
|
2388
|
+
return this.registerViewConstructor_;
|
|
2389
|
+
}
|
|
2390
|
+
set split(e) {
|
|
2391
|
+
this.split_ = e, this.listen(this.split_, UiEventType.SPLIT, (t) => {
|
|
2392
|
+
const r = t.detail.getValue(), i = t.detail.getData();
|
|
2393
|
+
switch (r) {
|
|
2394
|
+
case UiEventType.SPLIT_WILL_OPEN:
|
|
2395
|
+
this.onSplitWillOpen(i);
|
|
2396
|
+
break;
|
|
2397
|
+
case UiEventType.SPLIT_WILL_CLOSE:
|
|
2398
|
+
this.onSplitWillClose(i);
|
|
2399
|
+
break;
|
|
2400
|
+
case UiEventType.SPLIT_DID_CLOSE:
|
|
2401
|
+
this.onSplitDidClose(i);
|
|
2402
|
+
break;
|
|
2403
|
+
case UiEventType.SPLIT_DID_OPEN:
|
|
2404
|
+
this.onSplitDidOpen(i);
|
|
2405
|
+
break;
|
|
2406
|
+
case UiEventType.SPLIT_TRANSITION_END:
|
|
2407
|
+
break;
|
|
2408
|
+
}
|
|
2409
|
+
});
|
|
2410
|
+
}
|
|
2411
|
+
get split() {
|
|
2412
|
+
if (!this.split_)
|
|
2413
|
+
throw new Error("No SPLIT component available");
|
|
2414
|
+
return this.split_;
|
|
2415
|
+
}
|
|
2416
|
+
/**
|
|
2417
|
+
* @param {!UserManager} user
|
|
2418
|
+
*/
|
|
2419
|
+
set user(e) {
|
|
2420
|
+
this.user_ = e, this.panelMap.forEach((t) => t.user = e);
|
|
2421
|
+
}
|
|
2422
|
+
/**
|
|
2423
|
+
* @return {!UserManager}
|
|
2424
|
+
*/
|
|
2425
|
+
get user() {
|
|
2426
|
+
return this.user_ || (this.user_ = new UserManager()), this.user_;
|
|
2427
|
+
}
|
|
2428
|
+
/**
|
|
2429
|
+
* Render each of the panels in this view.
|
|
2430
|
+
*/
|
|
2431
|
+
render() {
|
|
2432
|
+
this.initPanelEvents(), this.preRender(), this.configurePanels(), this.displayPanels(), this.postRender();
|
|
2433
|
+
}
|
|
2434
|
+
/**
|
|
2435
|
+
* Placeholder for subclasses to add panel event functions.
|
|
2436
|
+
*/
|
|
2437
|
+
initPanelEvents() {
|
|
2438
|
+
}
|
|
2439
|
+
/**
|
|
2440
|
+
* Run before the render.
|
|
2441
|
+
*/
|
|
2442
|
+
preRender() {
|
|
2443
|
+
}
|
|
2444
|
+
/**
|
|
2445
|
+
* Placeholder for panel configuration functionality;
|
|
2446
|
+
*/
|
|
2447
|
+
configurePanels() {
|
|
2448
|
+
}
|
|
2449
|
+
/**
|
|
2450
|
+
* Placeholder for panel display functionality;
|
|
2451
|
+
*/
|
|
2452
|
+
displayPanels() {
|
|
2453
|
+
}
|
|
2454
|
+
/**
|
|
2455
|
+
* Placeholder for post render functionality.
|
|
2456
|
+
*/
|
|
2457
|
+
postRender() {
|
|
2458
|
+
}
|
|
2459
|
+
/**
|
|
2460
|
+
* @inheritDoc
|
|
2461
|
+
*/
|
|
2462
|
+
dispose() {
|
|
2463
|
+
[...this.panelMap.values()].forEach((e) => e.dispose()), super.dispose();
|
|
2464
|
+
}
|
|
2465
|
+
/**
|
|
2466
|
+
* Add a panel as a child of the view.
|
|
2467
|
+
* @param {string} name The name of the panel - used as a key in the panel map.
|
|
2468
|
+
* @param {!Panel} panel The panel itself.
|
|
2469
|
+
*/
|
|
2470
|
+
addPanelToView(e, t) {
|
|
2471
|
+
t.user = this.user, this.removePanelByName(e), this.panelMap.set(e, t), this.listen(t, Panel.panelEventCode(), this.onPanelEvent.bind(this));
|
|
2472
|
+
}
|
|
2473
|
+
/**
|
|
2474
|
+
* Remove a panel from the view by name.
|
|
2475
|
+
* @param {string} name The name of the panel - used as a key in the panel map.
|
|
2476
|
+
*/
|
|
2477
|
+
removePanelByName(e) {
|
|
2478
|
+
this.panelMap.has(e) && this.panelMap.get(e).dispose();
|
|
2479
|
+
}
|
|
2480
|
+
/**
|
|
2481
|
+
* @param {Panel} panel
|
|
2482
|
+
*/
|
|
2483
|
+
removePanel(e) {
|
|
2484
|
+
const [t, r] = [...this.panelMap.entries()].find(([i, o]) => o === e);
|
|
2485
|
+
t && r && (r.dispose(), this.panelMap.delete(t));
|
|
2486
|
+
}
|
|
2487
|
+
/**
|
|
2488
|
+
* @param {string} name
|
|
2489
|
+
* @return {Panel|undefined}
|
|
2490
|
+
*/
|
|
2491
|
+
getPanelByName(e) {
|
|
2492
|
+
return this.panelMap.get(e);
|
|
2493
|
+
}
|
|
2494
|
+
/**
|
|
2495
|
+
* Broadcasts data to all panels in this view. Each panel receives the data
|
|
2496
|
+
* via its onViewDataBroadcast method.
|
|
2497
|
+
* @param {*} data The data to broadcast to all panels
|
|
2498
|
+
*/
|
|
2499
|
+
broadcastToPanels(e) {
|
|
2500
|
+
[...this.panelMap.values()].forEach((t) => t.onViewDataBroadcast(e));
|
|
2501
|
+
}
|
|
2502
|
+
/**
|
|
2503
|
+
* Initializes the internal panel event map with default event handlers.
|
|
2504
|
+
* Sets up handlers for panel lifecycle and view coordination.
|
|
2505
|
+
*
|
|
2506
|
+
* Note: This method also registers application-specific handlers (search,
|
|
2507
|
+
* pagination, etc.) for backwards compatibility. These will be removed in
|
|
2508
|
+
* a future version - applications should explicitly register handlers they need.
|
|
2509
|
+
*
|
|
2510
|
+
* @return {!Map<string, !Function>} Map of event names to handler functions
|
|
2511
|
+
* @private
|
|
2512
|
+
*/
|
|
2513
|
+
initPanelEventsInternal_() {
|
|
2514
|
+
const e = /* @__PURE__ */ new Map();
|
|
2515
|
+
return e.set("destroy_me", (t, r) => {
|
|
2516
|
+
this.removePanel(r);
|
|
2517
|
+
}), e.set("switch_view", (t, r) => {
|
|
2518
|
+
this.debugMe("switch_view received: eventData", t);
|
|
2519
|
+
const i = t.view;
|
|
2520
|
+
this.switchViewMap_.has(i) ? this.switchViewMap_.get(i)(t, r) : this.debugMe("NO VIEW FOUND FOR:", i, this.switchViewMap_);
|
|
2521
|
+
}), Object.entries({
|
|
2522
|
+
...SearchHandlers,
|
|
2523
|
+
...QueryParamHandlers
|
|
2524
|
+
}).forEach(([t, r]) => {
|
|
2525
|
+
e.set(t, r.bind(this));
|
|
2526
|
+
}), e;
|
|
2527
|
+
}
|
|
2528
|
+
/**
|
|
2529
|
+
* Maps a panel event name to a handler function. Allows dynamic registration
|
|
2530
|
+
* of custom panel event handlers at runtime.
|
|
2531
|
+
* @param {string} s The event name to map
|
|
2532
|
+
* @param {!Function} func The handler function that receives (eventData, ePanel)
|
|
2533
|
+
*/
|
|
2534
|
+
mapPanEv(e, t) {
|
|
2535
|
+
this.panelEventMap_.set(e, t);
|
|
2536
|
+
}
|
|
2537
|
+
/**
|
|
2538
|
+
* A map of string to function where the function receives an object and
|
|
2539
|
+
* a panel.
|
|
2540
|
+
* @param {string} s
|
|
2541
|
+
* @param {function(
|
|
2542
|
+
* {view:string, pk:string, emit:string, href:string}, Panel):?} func
|
|
2543
|
+
*/
|
|
2544
|
+
mapSwitchView(e, t) {
|
|
2545
|
+
this.switchViewMap_.set(e, t);
|
|
2546
|
+
}
|
|
2547
|
+
/**
|
|
2548
|
+
* @param {!CustomEvent} e
|
|
2549
|
+
*/
|
|
2550
|
+
onPanelEvent(e) {
|
|
2551
|
+
const t = e.detail.getValue(), r = e.detail.getData(), i = (
|
|
2552
|
+
/** @type {Panel} */
|
|
2553
|
+
e.target
|
|
2554
|
+
);
|
|
2555
|
+
this.panelEventMap_.has(t) ? this.panelEventMap_.get(t)(r, i) : this.debugMe(`NO EVENT MATCH
|
|
2556
|
+
oPe: ${e}
|
|
2557
|
+
eventValue: ${t}
|
|
2558
|
+
eventData: ${JSON.stringify(r, void 0, 2)}
|
|
2559
|
+
ePanel: ${i.constructor.name}`);
|
|
2560
|
+
}
|
|
2561
|
+
//--[ Split Events ]--
|
|
2562
|
+
/**
|
|
2563
|
+
* Triggered before a split will open.
|
|
2564
|
+
*
|
|
2565
|
+
* @param {{nestName: !string, nestEl:!HTMLElement}} _data
|
|
2566
|
+
*/
|
|
2567
|
+
onSplitWillOpen(e) {
|
|
2568
|
+
}
|
|
2569
|
+
/**
|
|
2570
|
+
* Triggered before a split will close.
|
|
2571
|
+
*
|
|
2572
|
+
* @param {{nestName: !string, nestEl:!HTMLElement}} _data
|
|
2573
|
+
*/
|
|
2574
|
+
onSplitWillClose(e) {
|
|
2575
|
+
}
|
|
2576
|
+
/**
|
|
2577
|
+
* Triggered after a split closed.
|
|
2578
|
+
*
|
|
2579
|
+
* @param {{nestName: !string, nestEl:!HTMLElement}} _data
|
|
2580
|
+
*/
|
|
2581
|
+
onSplitDidClose(e) {
|
|
2582
|
+
}
|
|
2583
|
+
/**
|
|
2584
|
+
* Triggered after a split opened.
|
|
2585
|
+
*
|
|
2586
|
+
* @param {{nestName: !string, nestEl:!HTMLElement}} _data
|
|
2587
|
+
*/
|
|
2588
|
+
onSplitDidOpen(e) {
|
|
2589
|
+
}
|
|
2590
|
+
//--[ Built in events ]--
|
|
2591
|
+
/**
|
|
2592
|
+
* Dispatches a {@code UiEventType.VIEW} event.
|
|
2593
|
+
* A shorthand method to get panels to dispatch uniform events.
|
|
2594
|
+
* Views may listen just to this event, and act on the supplied value or
|
|
2595
|
+
* data payload.
|
|
2596
|
+
* Example:
|
|
2597
|
+
* b.listen(a, Component.compEventCode(), e => {
|
|
2598
|
+
* console.log('B got', Component.compEventCode(), e);
|
|
2599
|
+
* console.log('Value is', e.detail.getValue());
|
|
2600
|
+
* console.log('Data is', e.detail.getData());
|
|
2601
|
+
* });
|
|
2602
|
+
* @param {string|number} value
|
|
2603
|
+
* @param {(string|number|?Object)=} opt_data
|
|
2604
|
+
* @return {boolean} If anyone called preventDefault on the event object (or
|
|
2605
|
+
* if any of the handlers returns false this will also return false.
|
|
2606
|
+
*/
|
|
2607
|
+
dispatchViewEvent(e, t) {
|
|
2608
|
+
const r = new ZooyEventData(e, t), i = EVT.makeEvent(UiEventType.VIEW, r);
|
|
2609
|
+
return this.dispatchEvent(i);
|
|
2610
|
+
}
|
|
2611
|
+
//--[ Meta Data ]--
|
|
2612
|
+
/**
|
|
2613
|
+
* Returns the metadata map associated with this view.
|
|
2614
|
+
* @return {!Map<string, *>} The metadata map
|
|
2615
|
+
*/
|
|
2616
|
+
get metaData() {
|
|
2617
|
+
return this.metaData_;
|
|
2618
|
+
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Sets a metadata key-value pair for this view.
|
|
2621
|
+
* @param {string} k The metadata key
|
|
2622
|
+
* @param {*} v The metadata value
|
|
2623
|
+
* @return {!Map<string, *>} The updated metadata map
|
|
2624
|
+
*/
|
|
2625
|
+
setMetaData(e, t) {
|
|
2626
|
+
return this.metaData_.set(e, t), this.metaData;
|
|
2627
|
+
}
|
|
2628
|
+
/**
|
|
2629
|
+
* Retrieves a metadata value by key.
|
|
2630
|
+
* @param {string} k The metadata key
|
|
2631
|
+
* @return {*} The metadata value, or undefined if not found
|
|
2632
|
+
*/
|
|
2633
|
+
getMetaData(e) {
|
|
2634
|
+
return this.metaData_.get(e);
|
|
2635
|
+
}
|
|
2636
|
+
/**
|
|
2637
|
+
* Checks if a metadata key exists.
|
|
2638
|
+
* @param {string} k The metadata key
|
|
2639
|
+
* @return {boolean} True if the key exists, false otherwise
|
|
2640
|
+
*/
|
|
2641
|
+
hasMetaData(e) {
|
|
2642
|
+
return this.metaData_.has(e);
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
class Conductor extends EVT {
|
|
2646
|
+
/**
|
|
2647
|
+
* Creates a new Conductor instance. Sets up browser history listening
|
|
2648
|
+
* and initializes view event handling.
|
|
2649
|
+
*/
|
|
2650
|
+
constructor() {
|
|
2651
|
+
super(), this.activeView_ = null, this.user_ = void 0, this.split_ = void 0, this.viewEventMap_ = this.initViewEventsInternal_(), this.viewConstructorMap_ = /* @__PURE__ */ new Map(), window.addEventListener("popstate", (e) => {
|
|
2652
|
+
e.state !== null && this.navTo(e.state);
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
set split(e) {
|
|
2656
|
+
this.split_ = e;
|
|
2657
|
+
}
|
|
2658
|
+
get split() {
|
|
2659
|
+
if (!this.split_)
|
|
2660
|
+
throw new Error("No SPLIT component available");
|
|
2661
|
+
return this.split_;
|
|
2662
|
+
}
|
|
2663
|
+
/**
|
|
2664
|
+
* @param {!UserManager} user
|
|
2665
|
+
*/
|
|
2666
|
+
set user(e) {
|
|
2667
|
+
this.user_ = e, this.activeView_ && (this.activeView_.user = this.user);
|
|
2668
|
+
}
|
|
2669
|
+
/**
|
|
2670
|
+
* @return {!UserManager}
|
|
2671
|
+
*/
|
|
2672
|
+
get user() {
|
|
2673
|
+
return this.user_ || (this.user_ = new UserManager()), this.user_;
|
|
2674
|
+
}
|
|
2675
|
+
/**
|
|
2676
|
+
* Initializes the internal view event map with default event handlers.
|
|
2677
|
+
* Sets up handlers for common view events like 'switch_view'.
|
|
2678
|
+
* @return {!Map<string, !Function>} Map of event names to handler functions
|
|
2679
|
+
* @private
|
|
2680
|
+
*/
|
|
2681
|
+
initViewEventsInternal_() {
|
|
2682
|
+
return (/* @__PURE__ */ new Map()).set("switch_view", (e, t) => {
|
|
2683
|
+
if (this.viewConstructorMap_.has(e.view)) {
|
|
2684
|
+
const r = this.viewConstructorMap_.get(e.view)(
|
|
2685
|
+
e.pk,
|
|
2686
|
+
e
|
|
2687
|
+
);
|
|
2688
|
+
this.switchView(r);
|
|
2689
|
+
}
|
|
2690
|
+
});
|
|
2691
|
+
}
|
|
2692
|
+
/**
|
|
2693
|
+
* Maps a view event name to a handler function. Allows dynamic registration
|
|
2694
|
+
* of custom view event handlers at runtime.
|
|
2695
|
+
* @param {string} s The event name to map
|
|
2696
|
+
* @param {!Function} func The handler function that receives (eventData, eView)
|
|
2697
|
+
*/
|
|
2698
|
+
mapViewEv(e, t) {
|
|
2699
|
+
this.viewEventMap_.set(e, t);
|
|
2700
|
+
}
|
|
2701
|
+
/**
|
|
2702
|
+
* Registers a view constructor function that can be used to create instances
|
|
2703
|
+
* of a specific view type.
|
|
2704
|
+
* @param {string} s The view type identifier
|
|
2705
|
+
* @param {!Function} f Constructor function that receives (pk, data) and returns a View
|
|
2706
|
+
*/
|
|
2707
|
+
registerViewConstructor(e, t) {
|
|
2708
|
+
this.viewConstructorMap_.set(e, t);
|
|
2709
|
+
}
|
|
2710
|
+
/**
|
|
2711
|
+
* @param {!CustomEvent} e Event object.
|
|
2712
|
+
*/
|
|
2713
|
+
onViewEvent(e) {
|
|
2714
|
+
const t = e.detail.getValue(), r = e.detail.getData(), i = (
|
|
2715
|
+
/** @type {Panel} */
|
|
2716
|
+
e.target
|
|
2717
|
+
);
|
|
2718
|
+
this.viewEventMap_.has(t) ? this.viewEventMap_.get(t)(r, i) : console.warn("Unhandled VIEW Event:", e, t, r, i);
|
|
2719
|
+
}
|
|
2720
|
+
/**
|
|
2721
|
+
* Make the given view active.
|
|
2722
|
+
* @param {!View} view
|
|
2723
|
+
*/
|
|
2724
|
+
setActiveView(e) {
|
|
2725
|
+
this.activeView_ && (this.stopListeningTo(this.activeView_), this.activeView_.dispose(), this.activeView_ = null, delete this.activeView_), this.activeView_ = e, this.activeView_.render();
|
|
2726
|
+
}
|
|
2727
|
+
/**
|
|
2728
|
+
* @param {!View} view
|
|
2729
|
+
* @returns {!View}
|
|
2730
|
+
*/
|
|
2731
|
+
initView(e) {
|
|
2732
|
+
return e.user = this.user, e.split = this.split, e.recordHistory = this.recordHistory.bind(this), e.registerViewConstructor = this.registerViewConstructor.bind(this), this.listen(e, View.viewEventCode(), this.onViewEvent.bind(this)), e;
|
|
2733
|
+
}
|
|
2734
|
+
//--[ Views Utilities ]--
|
|
2735
|
+
/**
|
|
2736
|
+
* @param {!View} view The view we want active.
|
|
2737
|
+
*/
|
|
2738
|
+
switchView(e) {
|
|
2739
|
+
this.setActiveView(this.initView(e));
|
|
2740
|
+
}
|
|
2741
|
+
//--[ History and Navigation ]--
|
|
2742
|
+
/**
|
|
2743
|
+
* Records the given item in browser history. If item.init is true, replaces
|
|
2744
|
+
* the current history state. Otherwise, pushes a new history state unless
|
|
2745
|
+
* item.history is true.
|
|
2746
|
+
* @param {!Object} item The history item containing view state
|
|
2747
|
+
*/
|
|
2748
|
+
recordHistory(e) {
|
|
2749
|
+
this.debugMe("HISTORY", e), e.init ? history.replaceState(e, "", document.location.href) : e.history || history.pushState(e, "", null);
|
|
2750
|
+
}
|
|
2751
|
+
/**
|
|
2752
|
+
* Navigates to a specific view based on the history item. Attempts to use
|
|
2753
|
+
* the active view's switchViewMap first, falling back to registered view
|
|
2754
|
+
* constructors if needed.
|
|
2755
|
+
* @param {!Object} item History item containing {view, pk, ...} properties
|
|
2756
|
+
*/
|
|
2757
|
+
navTo = (e) => {
|
|
2758
|
+
e.history = !0;
|
|
2759
|
+
const t = e.view, r = this.activeView_;
|
|
2760
|
+
if (r.switchViewMap_.has(t))
|
|
2761
|
+
this.debugMe("NAV:VIEW:MAP", e), r.switchViewMap_.get(t)(e);
|
|
2762
|
+
else if (this.viewConstructorMap_.has(t)) {
|
|
2763
|
+
const i = this.viewConstructorMap_.get(t)(e.pk, e);
|
|
2764
|
+
this.debugMe("NAV:NEW:VIEW", e), this.switchView(i);
|
|
2765
|
+
}
|
|
2766
|
+
};
|
|
2767
|
+
}
|
|
2768
|
+
async function loadIcon(n, e = 16) {
|
|
2769
|
+
try {
|
|
2770
|
+
return (await import(`@carbon/icons/es/${n}/${e}.js`)).default;
|
|
2771
|
+
} catch (t) {
|
|
2772
|
+
throw console.error(`[Zooy] Failed to load icon: ${n}/${e}`, t), new Error(`Icon not found: ${n}/${e}. Check https://carbondesignsystem.com/guidelines/icons/library/`);
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
function iconDataToElement(n, e = {}) {
|
|
2776
|
+
const t = document.createElementNS("http://www.w3.org/2000/svg", "svg"), r = {
|
|
2777
|
+
...n.attrs,
|
|
2778
|
+
focusable: "false",
|
|
2779
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
2780
|
+
"aria-hidden": "true",
|
|
2781
|
+
...e
|
|
2782
|
+
};
|
|
2783
|
+
return Object.entries(r).forEach(([i, o]) => {
|
|
2784
|
+
t.setAttribute(i, o);
|
|
2785
|
+
}), n.content && n.content.forEach((i) => {
|
|
2786
|
+
const o = document.createElementNS("http://www.w3.org/2000/svg", i.elem);
|
|
2787
|
+
i.attrs && Object.entries(i.attrs).forEach(([a, p]) => {
|
|
2788
|
+
o.setAttribute(a, p);
|
|
2789
|
+
}), t.appendChild(o);
|
|
2790
|
+
}), t;
|
|
2791
|
+
}
|
|
2792
|
+
async function createIcon(n, e = 16, t = {}) {
|
|
2793
|
+
const r = await loadIcon(n, e);
|
|
2794
|
+
return iconDataToElement(r, t);
|
|
2795
|
+
}
|
|
2796
|
+
async function getIconSVG(n, e = 16, t = {}) {
|
|
2797
|
+
return (await createIcon(n, e, t)).outerHTML;
|
|
2798
|
+
}
|
|
2799
|
+
const Icons = {
|
|
2800
|
+
// Actions
|
|
2801
|
+
ADD: "add",
|
|
2802
|
+
EDIT: "edit",
|
|
2803
|
+
DELETE: "trash-can",
|
|
2804
|
+
SAVE: "save",
|
|
2805
|
+
CLOSE: "close",
|
|
2806
|
+
SEARCH: "search",
|
|
2807
|
+
FILTER: "filter",
|
|
2808
|
+
SETTINGS: "settings",
|
|
2809
|
+
// Navigation
|
|
2810
|
+
CHEVRON_LEFT: "chevron--left",
|
|
2811
|
+
CHEVRON_RIGHT: "chevron--right",
|
|
2812
|
+
CHEVRON_UP: "chevron--up",
|
|
2813
|
+
CHEVRON_DOWN: "chevron--down",
|
|
2814
|
+
ARROW_LEFT: "arrow--left",
|
|
2815
|
+
ARROW_RIGHT: "arrow--right",
|
|
2816
|
+
MENU: "menu",
|
|
2817
|
+
// Status
|
|
2818
|
+
CHECKMARK: "checkmark",
|
|
2819
|
+
ERROR: "error",
|
|
2820
|
+
WARNING: "warning",
|
|
2821
|
+
INFO: "information",
|
|
2822
|
+
// Files & Folders
|
|
2823
|
+
FOLDER: "folder",
|
|
2824
|
+
DOCUMENT: "document",
|
|
2825
|
+
DOWNLOAD: "download",
|
|
2826
|
+
UPLOAD: "upload",
|
|
2827
|
+
// Common UI
|
|
2828
|
+
OVERFLOW_MENU: "overflow-menu--vertical",
|
|
2829
|
+
VIEW: "view",
|
|
2830
|
+
VIEW_OFF: "view--off",
|
|
2831
|
+
COPY: "copy",
|
|
2832
|
+
LINK: "link",
|
|
2833
|
+
USER: "user",
|
|
2834
|
+
NOTIFICATION: "notification"
|
|
2835
|
+
};
|
|
2836
|
+
async function carbonIcon(n, e = 16) {
|
|
2837
|
+
return createIcon(n, e, { slot: "icon" });
|
|
2838
|
+
}
|
|
2839
|
+
const icons = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
2840
|
+
__proto__: null,
|
|
2841
|
+
Icons,
|
|
2842
|
+
carbonIcon,
|
|
2843
|
+
createIcon,
|
|
2844
|
+
getIconSVG,
|
|
2845
|
+
iconDataToElement,
|
|
2846
|
+
loadIcon
|
|
2847
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
2848
|
+
async function registerCarbonLibrary() {
|
|
2849
|
+
const { registerCarbonLibrary: n } = await import("./register-JwwMwdRf.js");
|
|
2850
|
+
return n();
|
|
2851
|
+
}
|
|
2852
|
+
async function registerMdcLibrary() {
|
|
2853
|
+
const { registerMdcLibrary: n } = await import("./register-BK21_Wkj.js");
|
|
2854
|
+
return n();
|
|
2855
|
+
}
|
|
2856
|
+
const zooy = {
|
|
2857
|
+
EVT,
|
|
2858
|
+
Component,
|
|
2859
|
+
Dragger,
|
|
2860
|
+
Panel,
|
|
2861
|
+
FormPanel,
|
|
2862
|
+
Split,
|
|
2863
|
+
UserManager,
|
|
2864
|
+
View,
|
|
2865
|
+
Conductor,
|
|
2866
|
+
UiEventType,
|
|
2867
|
+
domUtils,
|
|
2868
|
+
uriUtils,
|
|
2869
|
+
icons,
|
|
2870
|
+
registerCarbonLibrary,
|
|
2871
|
+
registerMdcLibrary,
|
|
2872
|
+
ComponentLibraryRegistry,
|
|
2873
|
+
handlers
|
|
2874
|
+
};
|
|
2875
|
+
export {
|
|
2876
|
+
ComponentLibraryRegistry as C,
|
|
2877
|
+
treeUtils as a,
|
|
2878
|
+
getElDataMap as g,
|
|
2879
|
+
isDefAndNotNull as i,
|
|
2880
|
+
toLowerCase as t,
|
|
2881
|
+
zooy as z
|
|
2882
|
+
};
|