@symbiotejs/symbiote 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/build/symbiote.base.jsdoc.js +697 -0
- package/build/symbiote.base.min.js +1 -0
- package/build/symbiote.jsdoc.js +1394 -0
- package/build/symbiote.min.js +1 -0
- package/package.json +108 -0
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
/** @returns {Object<string, any>} */
|
|
2
|
+
function cloneObj(obj) {
|
|
3
|
+
let clone = (o) => {
|
|
4
|
+
for (let prop in o) {
|
|
5
|
+
if (o[prop]?.constructor === Object) {
|
|
6
|
+
o[prop] = clone(o[prop]);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
return { ...o };
|
|
10
|
+
};
|
|
11
|
+
return clone(obj);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class Data {
|
|
15
|
+
/**
|
|
16
|
+
* @param {Object} src
|
|
17
|
+
* @param {String} [src.name]
|
|
18
|
+
* @param {Object<string, any>} src.schema
|
|
19
|
+
*/
|
|
20
|
+
constructor(src) {
|
|
21
|
+
this.uid = Symbol();
|
|
22
|
+
this.name = src.name || null;
|
|
23
|
+
if (src.schema.constructor === Object) {
|
|
24
|
+
this.store = cloneObj(src.schema);
|
|
25
|
+
} else {
|
|
26
|
+
// For Proxy support:
|
|
27
|
+
this._storeIsProxy = true;
|
|
28
|
+
this.store = src.schema;
|
|
29
|
+
}
|
|
30
|
+
/** @type {Object<String, Set<Function>>} */
|
|
31
|
+
this.callbackMap = Object.create(null);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {String} actionName
|
|
36
|
+
* @param {String} prop
|
|
37
|
+
*/
|
|
38
|
+
static warn(actionName, prop) {
|
|
39
|
+
console.warn(`Symbiote Data: cannot ${actionName}. Prop name: ` + prop);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** @param {String} prop */
|
|
43
|
+
read(prop) {
|
|
44
|
+
if (!this._storeIsProxy && !this.store.hasOwnProperty(prop)) {
|
|
45
|
+
Data.warn('read', prop);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return this.store[prop];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @param {String} prop */
|
|
52
|
+
has(prop) {
|
|
53
|
+
return this._storeIsProxy ? this.store[prop] !== undefined : this.store.hasOwnProperty(prop);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {String} prop
|
|
58
|
+
* @param {any} val
|
|
59
|
+
* @param {Boolean} [rewrite]
|
|
60
|
+
*/
|
|
61
|
+
add(prop, val, rewrite = true) {
|
|
62
|
+
if (!rewrite && Object.keys(this.store).includes(prop)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.store[prop] = val;
|
|
66
|
+
if (this.callbackMap[prop]) {
|
|
67
|
+
this.callbackMap[prop].forEach((callback) => {
|
|
68
|
+
callback(this.store[prop]);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {String} prop
|
|
75
|
+
* @param {any} val
|
|
76
|
+
*/
|
|
77
|
+
pub(prop, val) {
|
|
78
|
+
if (!this._storeIsProxy && !this.store.hasOwnProperty(prop)) {
|
|
79
|
+
Data.warn('publish', prop);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
this.add(prop, val);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** @param {Object<string, any>} updObj */
|
|
86
|
+
multiPub(updObj) {
|
|
87
|
+
for (let prop in updObj) {
|
|
88
|
+
this.pub(prop, updObj[prop]);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** @param {String} prop */
|
|
93
|
+
notify(prop) {
|
|
94
|
+
if (this.callbackMap[prop]) {
|
|
95
|
+
this.callbackMap[prop].forEach((callback) => {
|
|
96
|
+
callback(this.store[prop]);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {String} prop
|
|
103
|
+
* @param {Function} callback
|
|
104
|
+
* @param {Boolean} [init]
|
|
105
|
+
*/
|
|
106
|
+
sub(prop, callback, init = true) {
|
|
107
|
+
if (!this._storeIsProxy && !this.store.hasOwnProperty(prop)) {
|
|
108
|
+
Data.warn('subscribe', prop);
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (!this.callbackMap[prop]) {
|
|
112
|
+
this.callbackMap[prop] = new Set();
|
|
113
|
+
}
|
|
114
|
+
this.callbackMap[prop].add(callback);
|
|
115
|
+
if (init) {
|
|
116
|
+
callback(this.store[prop]);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
remove: () => {
|
|
120
|
+
this.callbackMap[prop].delete(callback);
|
|
121
|
+
if (!this.callbackMap[prop].size) {
|
|
122
|
+
delete this.callbackMap[prop];
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
callback,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
remove() {
|
|
130
|
+
delete Data.globalStore[this.uid];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** @param {Object<string, any>} schema */
|
|
134
|
+
static registerLocalCtx(schema) {
|
|
135
|
+
let state = new Data({
|
|
136
|
+
schema,
|
|
137
|
+
});
|
|
138
|
+
Data.globalStore[state.uid] = state;
|
|
139
|
+
return state;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @param {String} ctxName
|
|
144
|
+
* @param {Object<string, any>} schema
|
|
145
|
+
* @returns {Data}
|
|
146
|
+
*/
|
|
147
|
+
static registerNamedCtx(ctxName, schema) {
|
|
148
|
+
/** @type {Data} */
|
|
149
|
+
let state = Data.globalStore[ctxName];
|
|
150
|
+
if (state) {
|
|
151
|
+
console.warn('State: context name "' + ctxName + '" already in use');
|
|
152
|
+
} else {
|
|
153
|
+
state = new Data({
|
|
154
|
+
name: ctxName,
|
|
155
|
+
schema,
|
|
156
|
+
});
|
|
157
|
+
Data.globalStore[ctxName] = state;
|
|
158
|
+
}
|
|
159
|
+
return state;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** @param {String} ctxName */
|
|
163
|
+
static clearNamedCtx(ctxName) {
|
|
164
|
+
delete Data.globalStore[ctxName];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @param {String} ctxName
|
|
169
|
+
* @param {Boolean} [notify]
|
|
170
|
+
* @returns {Data}
|
|
171
|
+
*/
|
|
172
|
+
static getNamedCtx(ctxName, notify = true) {
|
|
173
|
+
return Data.globalStore[ctxName] || (notify && console.warn('State: wrong context name - "' + ctxName + '"'), null);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
Data.globalStore = Object.create(null);
|
|
178
|
+
|
|
179
|
+
const DICT = Object.freeze({
|
|
180
|
+
// Template data binding attribute:
|
|
181
|
+
BIND_ATTR: 'set',
|
|
182
|
+
// Local state binding attribute name:
|
|
183
|
+
ATTR_BIND_PRFX: '@',
|
|
184
|
+
// External prop prefix:
|
|
185
|
+
EXT_DATA_CTX_PRFX: '*',
|
|
186
|
+
// Named data context property splitter:
|
|
187
|
+
NAMED_DATA_CTX_SPLTR: '/',
|
|
188
|
+
// Data context name attribute:
|
|
189
|
+
CTX_NAME_ATTR: 'ctx-name',
|
|
190
|
+
// Data context name in CSS custom property:
|
|
191
|
+
CSS_CTX_PROP: '--ctx-name',
|
|
192
|
+
// Element reference attribute:
|
|
193
|
+
EL_REF_ATTR: 'ref',
|
|
194
|
+
// Prefix for auto generated tag names:
|
|
195
|
+
AUTO_TAG_PRFX: 'sym',
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const CHARS = '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm';
|
|
199
|
+
const CHLENGTH = CHARS.length - 1;
|
|
200
|
+
|
|
201
|
+
class UID {
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
*
|
|
205
|
+
* @param {String} [pattern] any symbols sequence with dashes. Default dash is used for human readability
|
|
206
|
+
* @returns {String} output example: v6xYaSk7C-kzZ
|
|
207
|
+
*/
|
|
208
|
+
static generate(pattern = 'XXXXXXXXX-XXX') {
|
|
209
|
+
let uid = '';
|
|
210
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
211
|
+
uid += pattern[i] === '-' ? pattern[i] : CHARS.charAt(Math.random() * CHLENGTH);
|
|
212
|
+
}
|
|
213
|
+
return uid;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function slotProcessor(fr, fnCtx) {
|
|
218
|
+
if (fnCtx.renderShadow) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
let slots = [...fr.querySelectorAll('slot')];
|
|
222
|
+
if (fnCtx.__initChildren.length && slots.length) {
|
|
223
|
+
let slotMap = {};
|
|
224
|
+
slots.forEach((slot) => {
|
|
225
|
+
let slotName = slot.getAttribute('name');
|
|
226
|
+
if (slotName) {
|
|
227
|
+
slotMap[slotName] = {
|
|
228
|
+
slot,
|
|
229
|
+
fr: document.createDocumentFragment(),
|
|
230
|
+
};
|
|
231
|
+
} else {
|
|
232
|
+
slotMap.__default__ = {
|
|
233
|
+
slot,
|
|
234
|
+
fr: document.createDocumentFragment(),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
fnCtx.__initChildren.forEach((/** @type {Element} */ child) => {
|
|
239
|
+
let slotName = child.getAttribute?.('slot');
|
|
240
|
+
if (slotName) {
|
|
241
|
+
child.removeAttribute('slot');
|
|
242
|
+
slotMap[slotName].fr.appendChild(child);
|
|
243
|
+
} else if (slotMap.__default__) {
|
|
244
|
+
slotMap.__default__.fr.appendChild(child);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
Object.values(slotMap).forEach((mapObj) => {
|
|
248
|
+
mapObj.slot.parentNode.insertBefore(mapObj.fr, mapObj.slot);
|
|
249
|
+
mapObj.slot.remove();
|
|
250
|
+
});
|
|
251
|
+
} else {
|
|
252
|
+
fnCtx.innerHTML = '';
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function refProcessor(fr, fnCtx) {
|
|
257
|
+
[...fr.querySelectorAll(`[${DICT.EL_REF_ATTR}]`)].forEach((/** @type {HTMLElement} */ el) => {
|
|
258
|
+
let refName = el.getAttribute(DICT.EL_REF_ATTR);
|
|
259
|
+
fnCtx.ref[refName] = el;
|
|
260
|
+
el.removeAttribute(DICT.EL_REF_ATTR);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* @param {DocumentFragment} fr
|
|
266
|
+
* @param {any} fnCtx
|
|
267
|
+
*/
|
|
268
|
+
function domSetProcessor(fr, fnCtx) {
|
|
269
|
+
[...fr.querySelectorAll(`[${DICT.BIND_ATTR}]`)].forEach((el) => {
|
|
270
|
+
let subStr = el.getAttribute(DICT.BIND_ATTR);
|
|
271
|
+
let keyValsArr = subStr.split(';');
|
|
272
|
+
keyValsArr.forEach((keyValStr) => {
|
|
273
|
+
if (!keyValStr) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
let kv = keyValStr.split(':').map((str) => str.trim());
|
|
277
|
+
let prop = kv[0];
|
|
278
|
+
let isAttr;
|
|
279
|
+
|
|
280
|
+
if (prop.indexOf(DICT.ATTR_BIND_PRFX) === 0) {
|
|
281
|
+
isAttr = true;
|
|
282
|
+
prop = prop.replace(DICT.ATTR_BIND_PRFX, '');
|
|
283
|
+
}
|
|
284
|
+
/** @type {String[]} */
|
|
285
|
+
let valKeysArr = kv[1].split(',').map((valKey) => {
|
|
286
|
+
return valKey.trim();
|
|
287
|
+
});
|
|
288
|
+
// Deep property:
|
|
289
|
+
let isDeep, parent, lastStep, dive;
|
|
290
|
+
if (prop.includes('.')) {
|
|
291
|
+
isDeep = true;
|
|
292
|
+
let propPath = prop.split('.');
|
|
293
|
+
dive = () => {
|
|
294
|
+
parent = el;
|
|
295
|
+
propPath.forEach((step, idx) => {
|
|
296
|
+
if (idx < propPath.length - 1) {
|
|
297
|
+
parent = parent[step];
|
|
298
|
+
} else {
|
|
299
|
+
lastStep = step;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
};
|
|
303
|
+
dive();
|
|
304
|
+
}
|
|
305
|
+
for (let valKey of valKeysArr) {
|
|
306
|
+
fnCtx.sub(valKey, (val) => {
|
|
307
|
+
if (isAttr) {
|
|
308
|
+
if (val?.constructor === Boolean) {
|
|
309
|
+
val ? el.setAttribute(prop, '') : el.removeAttribute(prop);
|
|
310
|
+
} else {
|
|
311
|
+
el.setAttribute(prop, val);
|
|
312
|
+
}
|
|
313
|
+
} else if (isDeep) {
|
|
314
|
+
if (parent) {
|
|
315
|
+
parent[lastStep] = val;
|
|
316
|
+
} else {
|
|
317
|
+
// Custom element instances are not constructed properly at this time, so:
|
|
318
|
+
window.setTimeout(() => {
|
|
319
|
+
dive();
|
|
320
|
+
parent[lastStep] = val;
|
|
321
|
+
});
|
|
322
|
+
// TODO: investigate how to do it better ^^^
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
el[prop] = val;
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
el.removeAttribute(DICT.BIND_ATTR);
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
var PROCESSORS = [slotProcessor, refProcessor, domSetProcessor];
|
|
335
|
+
|
|
336
|
+
let autoTagsCount = 0;
|
|
337
|
+
|
|
338
|
+
class BaseComponent extends HTMLElement {
|
|
339
|
+
/**
|
|
340
|
+
* @param {String | DocumentFragment} [template]
|
|
341
|
+
* @param {Boolean} [shadow]
|
|
342
|
+
*/
|
|
343
|
+
render(template, shadow = this.renderShadow) {
|
|
344
|
+
/** @type {DocumentFragment} */
|
|
345
|
+
let fr;
|
|
346
|
+
if (template || this.constructor['template']) {
|
|
347
|
+
if (this.constructor['template'] && !this.constructor['__tpl']) {
|
|
348
|
+
this.constructor['__tpl'] = document.createElement('template');
|
|
349
|
+
this.constructor['__tpl'].innerHTML = this.constructor['template'];
|
|
350
|
+
}
|
|
351
|
+
while (this.firstChild) {
|
|
352
|
+
this.firstChild.remove();
|
|
353
|
+
}
|
|
354
|
+
if (template?.constructor === DocumentFragment) {
|
|
355
|
+
fr = template;
|
|
356
|
+
} else if (template?.constructor === String) {
|
|
357
|
+
let tpl = document.createElement('template');
|
|
358
|
+
tpl.innerHTML = template;
|
|
359
|
+
// @ts-ignore
|
|
360
|
+
fr = tpl.content.cloneNode(true);
|
|
361
|
+
} else if (this.constructor['__tpl']) {
|
|
362
|
+
fr = this.constructor['__tpl'].content.cloneNode(true);
|
|
363
|
+
}
|
|
364
|
+
for (let fn of this.tplProcessors) {
|
|
365
|
+
fn(fr, this);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (shadow) {
|
|
369
|
+
if (!this.shadowRoot) {
|
|
370
|
+
this.attachShadow({
|
|
371
|
+
mode: 'open',
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
fr && this.shadowRoot.appendChild(fr);
|
|
375
|
+
} else {
|
|
376
|
+
fr && this.appendChild(fr);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** @param {(fr: DocumentFragment, fnCtx: any) => any} processorFn */
|
|
381
|
+
addTemplateProcessor(processorFn) {
|
|
382
|
+
this.tplProcessors.add(processorFn);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
constructor() {
|
|
386
|
+
super();
|
|
387
|
+
/** @type {Object<string, any>} */
|
|
388
|
+
this.init$ = Object.create(null);
|
|
389
|
+
/** @type {Set<(fr: DocumentFragment, fnCtx: any) => any>} */
|
|
390
|
+
this.tplProcessors = new Set();
|
|
391
|
+
/** @type {Object<string, HTMLElement>} */
|
|
392
|
+
this.ref = Object.create(null);
|
|
393
|
+
this.allSubs = new Set();
|
|
394
|
+
this.pauseRender = false;
|
|
395
|
+
this.renderShadow = false;
|
|
396
|
+
this.readyToDestroy = true;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
get autoCtxName() {
|
|
400
|
+
if (!this.__autoCtxName) {
|
|
401
|
+
this.__autoCtxName = UID.generate();
|
|
402
|
+
this.style.setProperty(DICT.CSS_CTX_PROP, `'${this.__autoCtxName}'`);
|
|
403
|
+
}
|
|
404
|
+
return this.__autoCtxName;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
get cssCtxName() {
|
|
408
|
+
return this.getCssData(DICT.CSS_CTX_PROP, true);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
get ctxName() {
|
|
412
|
+
return this.getAttribute(DICT.CTX_NAME_ATTR)?.trim() || this.cssCtxName || this.autoCtxName;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
get localCtx() {
|
|
416
|
+
if (!this.__localCtx) {
|
|
417
|
+
this.__localCtx = Data.registerLocalCtx({});
|
|
418
|
+
}
|
|
419
|
+
return this.__localCtx;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
get nodeCtx() {
|
|
423
|
+
return Data.getNamedCtx(this.ctxName, false) || Data.registerNamedCtx(this.ctxName, {});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* @param {String} prop
|
|
428
|
+
* @param {any} fnCtx
|
|
429
|
+
*/
|
|
430
|
+
static __parseProp(prop, fnCtx) {
|
|
431
|
+
/** @type {Data} */
|
|
432
|
+
let ctx;
|
|
433
|
+
/** @type {String} */
|
|
434
|
+
let name;
|
|
435
|
+
if (prop.startsWith(DICT.EXT_DATA_CTX_PRFX)) {
|
|
436
|
+
ctx = fnCtx.nodeCtx;
|
|
437
|
+
name = prop.replace(DICT.EXT_DATA_CTX_PRFX, '');
|
|
438
|
+
} else if (prop.includes(DICT.NAMED_DATA_CTX_SPLTR)) {
|
|
439
|
+
let pArr = prop.split(DICT.NAMED_DATA_CTX_SPLTR);
|
|
440
|
+
ctx = Data.getNamedCtx(pArr[0]);
|
|
441
|
+
name = pArr[1];
|
|
442
|
+
} else {
|
|
443
|
+
ctx = fnCtx.localCtx;
|
|
444
|
+
name = prop;
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
ctx,
|
|
448
|
+
name,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* @param {String} prop
|
|
454
|
+
* @param {(value: any) => void} handler
|
|
455
|
+
*/
|
|
456
|
+
sub(prop, handler) {
|
|
457
|
+
let parsed = BaseComponent.__parseProp(prop, this);
|
|
458
|
+
this.allSubs.add(parsed.ctx.sub(parsed.name, handler));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/** @param {String} prop */
|
|
462
|
+
notify(prop) {
|
|
463
|
+
let parsed = BaseComponent.__parseProp(prop, this);
|
|
464
|
+
parsed.ctx.notify(parsed.name);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/** @param {String} prop */
|
|
468
|
+
has(prop) {
|
|
469
|
+
let parsed = BaseComponent.__parseProp(prop, this);
|
|
470
|
+
return parsed.ctx.has(parsed.name);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* @param {String} prop
|
|
475
|
+
* @param {any} val
|
|
476
|
+
*/
|
|
477
|
+
add(prop, val) {
|
|
478
|
+
let parsed = BaseComponent.__parseProp(prop, this);
|
|
479
|
+
parsed.ctx.add(parsed.name, val, false);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/** @param {Object<string, any>} obj */
|
|
483
|
+
add$(obj) {
|
|
484
|
+
for (let prop in obj) {
|
|
485
|
+
this.add(prop, obj[prop]);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
get $() {
|
|
490
|
+
if (!this.__stateProxy) {
|
|
491
|
+
/** @type {Object<string, any>} */
|
|
492
|
+
let o = Object.create(null);
|
|
493
|
+
this.__stateProxy = new Proxy(o, {
|
|
494
|
+
set: (obj, /** @type {String} */ prop, val) => {
|
|
495
|
+
let parsed = BaseComponent.__parseProp(prop, this);
|
|
496
|
+
parsed.ctx.pub(parsed.name, val);
|
|
497
|
+
return true;
|
|
498
|
+
},
|
|
499
|
+
get: (obj, /** @type {String} */ prop) => {
|
|
500
|
+
let parsed = BaseComponent.__parseProp(prop, this);
|
|
501
|
+
return parsed.ctx.read(parsed.name);
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
return this.__stateProxy;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/** @param {Object<string, any>} kvObj */
|
|
509
|
+
set$(kvObj) {
|
|
510
|
+
for (let key in kvObj) {
|
|
511
|
+
this.$[key] = kvObj[key];
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
initCallback() {}
|
|
516
|
+
|
|
517
|
+
__initDataCtx() {
|
|
518
|
+
let attrDesc = this.constructor['__attrDesc'];
|
|
519
|
+
if (attrDesc) {
|
|
520
|
+
for (let prop of Object.values(attrDesc)) {
|
|
521
|
+
if (!Object.keys(this.init$).includes(prop)) {
|
|
522
|
+
this.init$[prop] = '';
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
for (let prop in this.init$) {
|
|
527
|
+
if (prop.startsWith(DICT.EXT_DATA_CTX_PRFX)) {
|
|
528
|
+
this.nodeCtx.add(prop.replace(DICT.EXT_DATA_CTX_PRFX, ''), this.init$[prop]);
|
|
529
|
+
} else if (prop.includes(DICT.NAMED_DATA_CTX_SPLTR)) {
|
|
530
|
+
let propArr = prop.split(DICT.NAMED_DATA_CTX_SPLTR);
|
|
531
|
+
let ctxName = propArr[0].trim();
|
|
532
|
+
let propName = propArr[1].trim();
|
|
533
|
+
if (ctxName && propName) {
|
|
534
|
+
let namedCtx = Data.getNamedCtx(ctxName, false);
|
|
535
|
+
if (!namedCtx) {
|
|
536
|
+
namedCtx = Data.registerNamedCtx(ctxName, {});
|
|
537
|
+
}
|
|
538
|
+
namedCtx.add(propName, this.init$[prop]);
|
|
539
|
+
}
|
|
540
|
+
} else {
|
|
541
|
+
this.localCtx.add(prop, this.init$[prop]);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
this.__dataCtxInitialized = true;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
connectedCallback() {
|
|
548
|
+
if (this.__disconnectTimeout) {
|
|
549
|
+
window.clearTimeout(this.__disconnectTimeout);
|
|
550
|
+
}
|
|
551
|
+
if (!this.connectedOnce) {
|
|
552
|
+
let ctxNameAttrVal = this.getAttribute(DICT.CTX_NAME_ATTR)?.trim();
|
|
553
|
+
if (ctxNameAttrVal) {
|
|
554
|
+
this.style.setProperty(DICT.CSS_CTX_PROP, `'${ctxNameAttrVal}'`);
|
|
555
|
+
}
|
|
556
|
+
this.__initDataCtx();
|
|
557
|
+
this.__initChildren = [...this.childNodes];
|
|
558
|
+
for (let proc of PROCESSORS) {
|
|
559
|
+
this.addTemplateProcessor(proc);
|
|
560
|
+
}
|
|
561
|
+
if (!this.pauseRender) {
|
|
562
|
+
this.render();
|
|
563
|
+
}
|
|
564
|
+
this.initCallback?.();
|
|
565
|
+
}
|
|
566
|
+
this.connectedOnce = true;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
destroyCallback() {}
|
|
570
|
+
|
|
571
|
+
disconnectedCallback() {
|
|
572
|
+
this.dropCssDataCache();
|
|
573
|
+
if (!this.readyToDestroy) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
if (this.__disconnectTimeout) {
|
|
577
|
+
window.clearTimeout(this.__disconnectTimeout);
|
|
578
|
+
}
|
|
579
|
+
this.__disconnectTimeout = window.setTimeout(() => {
|
|
580
|
+
this.destroyCallback();
|
|
581
|
+
for (let sub of this.allSubs) {
|
|
582
|
+
sub.remove();
|
|
583
|
+
this.allSubs.delete(sub);
|
|
584
|
+
}
|
|
585
|
+
for (let proc of this.tplProcessors) {
|
|
586
|
+
this.tplProcessors.delete(proc);
|
|
587
|
+
}
|
|
588
|
+
}, 100);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* @param {String} [tagName]
|
|
593
|
+
* @param {Boolean} [isAlias]
|
|
594
|
+
*/
|
|
595
|
+
static reg(tagName, isAlias = false) {
|
|
596
|
+
if (!tagName) {
|
|
597
|
+
autoTagsCount++;
|
|
598
|
+
tagName = `${DICT.AUTO_TAG_PRFX}-${autoTagsCount}`;
|
|
599
|
+
}
|
|
600
|
+
this.__tag = tagName;
|
|
601
|
+
if (window.customElements.get(tagName)) {
|
|
602
|
+
console.warn(`${tagName} - is already in "customElements" registry`);
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
window.customElements.define(tagName, isAlias ? class extends this {} : this);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
static get is() {
|
|
609
|
+
if (!this.__tag) {
|
|
610
|
+
this.reg();
|
|
611
|
+
}
|
|
612
|
+
return this.__tag;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/** @param {Object<string, string>} desc */
|
|
616
|
+
static bindAttributes(desc) {
|
|
617
|
+
this.observedAttributes = Object.keys(desc);
|
|
618
|
+
this.__attrDesc = desc;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
622
|
+
if (oldVal === newVal) {
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
/** @type {String} */
|
|
626
|
+
let $prop = this.constructor['__attrDesc'][name];
|
|
627
|
+
if ($prop) {
|
|
628
|
+
if (this.__dataCtxInitialized) {
|
|
629
|
+
this.$[$prop] = newVal;
|
|
630
|
+
} else {
|
|
631
|
+
this.init$[$prop] = newVal;
|
|
632
|
+
}
|
|
633
|
+
} else {
|
|
634
|
+
this[name] = newVal;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* @param {String} propName
|
|
640
|
+
* @param {Boolean} [silentCheck]
|
|
641
|
+
*/
|
|
642
|
+
getCssData(propName, silentCheck = false) {
|
|
643
|
+
if (!this.__cssDataCache) {
|
|
644
|
+
this.__cssDataCache = Object.create(null);
|
|
645
|
+
}
|
|
646
|
+
if (!Object.keys(this.__cssDataCache).includes(propName)) {
|
|
647
|
+
if (!this.__computedStyle) {
|
|
648
|
+
this.__computedStyle = window.getComputedStyle(this);
|
|
649
|
+
}
|
|
650
|
+
let val = this.__computedStyle.getPropertyValue(propName).trim();
|
|
651
|
+
// Firefox doesn't transform string values into JSON format:
|
|
652
|
+
if (val.startsWith(`'`) && val.endsWith(`'`)) {
|
|
653
|
+
val = val.replace(/\'/g, '"');
|
|
654
|
+
}
|
|
655
|
+
try {
|
|
656
|
+
this.__cssDataCache[propName] = JSON.parse(val);
|
|
657
|
+
} catch (e) {
|
|
658
|
+
!silentCheck && console.warn(`CSS Data error: ${propName}`);
|
|
659
|
+
this.__cssDataCache[propName] = null;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
return this.__cssDataCache[propName];
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
dropCssDataCache() {
|
|
666
|
+
this.__cssDataCache = null;
|
|
667
|
+
this.__computedStyle = null;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* @param {String} propName
|
|
672
|
+
* @param {Function} [handler]
|
|
673
|
+
* @param {Boolean} [isAsync]
|
|
674
|
+
*/
|
|
675
|
+
defineAccessor(propName, handler, isAsync) {
|
|
676
|
+
let localPropName = '__' + propName;
|
|
677
|
+
this[localPropName] = this[propName];
|
|
678
|
+
Object.defineProperty(this, propName, {
|
|
679
|
+
set: (val) => {
|
|
680
|
+
this[localPropName] = val;
|
|
681
|
+
if (isAsync) {
|
|
682
|
+
window.setTimeout(() => {
|
|
683
|
+
handler?.(val);
|
|
684
|
+
});
|
|
685
|
+
} else {
|
|
686
|
+
handler?.(val);
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
get: () => {
|
|
690
|
+
return this[localPropName];
|
|
691
|
+
},
|
|
692
|
+
});
|
|
693
|
+
this[propName] = this[localPropName];
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
export { BaseComponent };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function y(h){let t=e=>{var s;for(let i in e)((s=e[i])==null?void 0:s.constructor)===Object&&(e[i]=t(e[i]));return{...e}};return t(h)}var l=class{constructor(t){this.uid=Symbol(),this.name=t.name||null,t.schema.constructor===Object?this.store=y(t.schema):(this._storeIsProxy=!0,this.store=t.schema),this.callbackMap=Object.create(null)}static warn(t,e){console.warn(`Symbiote Data: cannot ${t}. Prop name: `+e)}read(t){return!this._storeIsProxy&&!this.store.hasOwnProperty(t)?(l.warn("read",t),null):this.store[t]}has(t){return this._storeIsProxy?this.store[t]!==void 0:this.store.hasOwnProperty(t)}add(t,e,s=!0){!s&&Object.keys(this.store).includes(t)||(this.store[t]=e,this.callbackMap[t]&&this.callbackMap[t].forEach(i=>{i(this.store[t])}))}pub(t,e){if(!this._storeIsProxy&&!this.store.hasOwnProperty(t)){l.warn("publish",t);return}this.add(t,e)}multiPub(t){for(let e in t)this.pub(e,t[e])}notify(t){this.callbackMap[t]&&this.callbackMap[t].forEach(e=>{e(this.store[t])})}sub(t,e,s=!0){return!this._storeIsProxy&&!this.store.hasOwnProperty(t)?(l.warn("subscribe",t),null):(this.callbackMap[t]||(this.callbackMap[t]=new Set),this.callbackMap[t].add(e),s&&e(this.store[t]),{remove:()=>{this.callbackMap[t].delete(e),this.callbackMap[t].size||delete this.callbackMap[t]},callback:e})}remove(){delete l.globalStore[this.uid]}static registerLocalCtx(t){let e=new l({schema:t});return l.globalStore[e.uid]=e,e}static registerNamedCtx(t,e){let s=l.globalStore[t];return s?console.warn('State: context name "'+t+'" already in use'):(s=new l({name:t,schema:e}),l.globalStore[t]=s),s}static clearNamedCtx(t){delete l.globalStore[t]}static getNamedCtx(t,e=!0){return l.globalStore[t]||(e&&console.warn('State: wrong context name - "'+t+'"'),null)}};l.globalStore=Object.create(null);var r=Object.freeze({BIND_ATTR:"set",ATTR_BIND_PRFX:"@",EXT_DATA_CTX_PRFX:"*",NAMED_DATA_CTX_SPLTR:"/",CTX_NAME_ATTR:"ctx-name",CSS_CTX_PROP:"--ctx-name",EL_REF_ATTR:"ref",AUTO_TAG_PRFX:"sym"});var b="1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm",p=b.length-1,m=class{static generate(t="XXXXXXXXX-XXX"){let e="";for(let s=0;s<t.length;s++)e+=t[s]==="-"?t[s]:b.charAt(Math.random()*p);return e}};function R(h,t){if(t.renderShadow)return;let e=[...h.querySelectorAll("slot")];if(t.__initChildren.length&&e.length){let s={};e.forEach(i=>{let o=i.getAttribute("name");o?s[o]={slot:i,fr:document.createDocumentFragment()}:s.__default__={slot:i,fr:document.createDocumentFragment()}}),t.__initChildren.forEach(i=>{var c;let o=(c=i.getAttribute)==null?void 0:c.call(i,"slot");o?(i.removeAttribute("slot"),s[o].fr.appendChild(i)):s.__default__&&s.__default__.fr.appendChild(i)}),Object.values(s).forEach(i=>{i.slot.parentNode.insertBefore(i.fr,i.slot),i.slot.remove()})}else t.innerHTML=""}function X(h,t){[...h.querySelectorAll(`[${r.EL_REF_ATTR}]`)].forEach(e=>{let s=e.getAttribute(r.EL_REF_ATTR);t.ref[s]=e,e.removeAttribute(r.EL_REF_ATTR)})}function D(h,t){[...h.querySelectorAll(`[${r.BIND_ATTR}]`)].forEach(e=>{e.getAttribute(r.BIND_ATTR).split(";").forEach(o=>{if(!o)return;let c=o.split(":").map(u=>u.trim()),n=c[0],C;n.indexOf(r.ATTR_BIND_PRFX)===0&&(C=!0,n=n.replace(r.ATTR_BIND_PRFX,""));let S=c[1].split(",").map(u=>u.trim()),A,d,f,T;if(n.includes(".")){A=!0;let u=n.split(".");T=()=>{d=e,u.forEach((a,g)=>{g<u.length-1?d=d[a]:f=a})},T()}for(let u of S)t.sub(u,a=>{C?(a==null?void 0:a.constructor)===Boolean?a?e.setAttribute(n,""):e.removeAttribute(n):e.setAttribute(n,a):A?d?d[f]=a:window.setTimeout(()=>{T(),d[f]=a}):e[n]=a})}),e.removeAttribute(r.BIND_ATTR)})}var P=[R,X,D];var x=0,_=class extends HTMLElement{render(t,e=this.renderShadow){let s;if(t||this.constructor.template){for(this.constructor.template&&!this.constructor.__tpl&&(this.constructor.__tpl=document.createElement("template"),this.constructor.__tpl.innerHTML=this.constructor.template);this.firstChild;)this.firstChild.remove();if((t==null?void 0:t.constructor)===DocumentFragment)s=t;else if((t==null?void 0:t.constructor)===String){let i=document.createElement("template");i.innerHTML=t,s=i.content.cloneNode(!0)}else this.constructor.__tpl&&(s=this.constructor.__tpl.content.cloneNode(!0));for(let i of this.tplProcessors)i(s,this)}e?(this.shadowRoot||this.attachShadow({mode:"open"}),s&&this.shadowRoot.appendChild(s)):s&&this.appendChild(s)}addTemplateProcessor(t){this.tplProcessors.add(t)}constructor(){super();this.init$=Object.create(null),this.tplProcessors=new Set,this.ref=Object.create(null),this.allSubs=new Set,this.pauseRender=!1,this.renderShadow=!1,this.readyToDestroy=!0}get autoCtxName(){return this.__autoCtxName||(this.__autoCtxName=m.generate(),this.style.setProperty(r.CSS_CTX_PROP,`'${this.__autoCtxName}'`)),this.__autoCtxName}get cssCtxName(){return this.getCssData(r.CSS_CTX_PROP,!0)}get ctxName(){var t;return((t=this.getAttribute(r.CTX_NAME_ATTR))==null?void 0:t.trim())||this.cssCtxName||this.autoCtxName}get localCtx(){return this.__localCtx||(this.__localCtx=l.registerLocalCtx({})),this.__localCtx}get nodeCtx(){return l.getNamedCtx(this.ctxName,!1)||l.registerNamedCtx(this.ctxName,{})}static __parseProp(t,e){let s,i;if(t.startsWith(r.EXT_DATA_CTX_PRFX))s=e.nodeCtx,i=t.replace(r.EXT_DATA_CTX_PRFX,"");else if(t.includes(r.NAMED_DATA_CTX_SPLTR)){let o=t.split(r.NAMED_DATA_CTX_SPLTR);s=l.getNamedCtx(o[0]),i=o[1]}else s=e.localCtx,i=t;return{ctx:s,name:i}}sub(t,e){let s=_.__parseProp(t,this);this.allSubs.add(s.ctx.sub(s.name,e))}notify(t){let e=_.__parseProp(t,this);e.ctx.notify(e.name)}has(t){let e=_.__parseProp(t,this);return e.ctx.has(e.name)}add(t,e){let s=_.__parseProp(t,this);s.ctx.add(s.name,e,!1)}add$(t){for(let e in t)this.add(e,t[e])}get $(){if(!this.__stateProxy){let t=Object.create(null);this.__stateProxy=new Proxy(t,{set:(e,s,i)=>{let o=_.__parseProp(s,this);return o.ctx.pub(o.name,i),!0},get:(e,s)=>{let i=_.__parseProp(s,this);return i.ctx.read(i.name)}})}return this.__stateProxy}set$(t){for(let e in t)this.$[e]=t[e]}initCallback(){}__initDataCtx(){let t=this.constructor.__attrDesc;if(t)for(let e of Object.values(t))Object.keys(this.init$).includes(e)||(this.init$[e]="");for(let e in this.init$)if(e.startsWith(r.EXT_DATA_CTX_PRFX))this.nodeCtx.add(e.replace(r.EXT_DATA_CTX_PRFX,""),this.init$[e]);else if(e.includes(r.NAMED_DATA_CTX_SPLTR)){let s=e.split(r.NAMED_DATA_CTX_SPLTR),i=s[0].trim(),o=s[1].trim();if(i&&o){let c=l.getNamedCtx(i,!1);c||(c=l.registerNamedCtx(i,{})),c.add(o,this.init$[e])}}else this.localCtx.add(e,this.init$[e]);this.__dataCtxInitialized=!0}connectedCallback(){var t,e;if(this.__disconnectTimeout&&window.clearTimeout(this.__disconnectTimeout),!this.connectedOnce){let s=(t=this.getAttribute(r.CTX_NAME_ATTR))==null?void 0:t.trim();s&&this.style.setProperty(r.CSS_CTX_PROP,`'${s}'`),this.__initDataCtx(),this.__initChildren=[...this.childNodes];for(let i of P)this.addTemplateProcessor(i);this.pauseRender||this.render(),(e=this.initCallback)==null||e.call(this)}this.connectedOnce=!0}destroyCallback(){}disconnectedCallback(){this.dropCssDataCache(),!!this.readyToDestroy&&(this.__disconnectTimeout&&window.clearTimeout(this.__disconnectTimeout),this.__disconnectTimeout=window.setTimeout(()=>{this.destroyCallback();for(let t of this.allSubs)t.remove(),this.allSubs.delete(t);for(let t of this.tplProcessors)this.tplProcessors.delete(t)},100))}static reg(t,e=!1){if(t||(x++,t=`${r.AUTO_TAG_PRFX}-${x}`),this.__tag=t,window.customElements.get(t)){console.warn(`${t} - is already in "customElements" registry`);return}window.customElements.define(t,e?class extends this{}:this)}static get is(){return this.__tag||this.reg(),this.__tag}static bindAttributes(t){this.observedAttributes=Object.keys(t),this.__attrDesc=t}attributeChangedCallback(t,e,s){if(e===s)return;let i=this.constructor.__attrDesc[t];i?this.__dataCtxInitialized?this.$[i]=s:this.init$[i]=s:this[t]=s}getCssData(t,e=!1){if(this.__cssDataCache||(this.__cssDataCache=Object.create(null)),!Object.keys(this.__cssDataCache).includes(t)){this.__computedStyle||(this.__computedStyle=window.getComputedStyle(this));let s=this.__computedStyle.getPropertyValue(t).trim();s.startsWith("'")&&s.endsWith("'")&&(s=s.replace(/\'/g,'"'));try{this.__cssDataCache[t]=JSON.parse(s)}catch{!e&&console.warn(`CSS Data error: ${t}`),this.__cssDataCache[t]=null}}return this.__cssDataCache[t]}dropCssDataCache(){this.__cssDataCache=null,this.__computedStyle=null}defineAccessor(t,e,s){let i="__"+t;this[i]=this[t],Object.defineProperty(this,t,{set:o=>{this[i]=o,s?window.setTimeout(()=>{e==null||e(o)}):e==null||e(o)},get:()=>this[i]}),this[t]=this[i]}};export{_ as BaseComponent};
|