sibujs 4.5.0 → 4.7.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/README.md +1 -1
- package/dist/browser.cjs +326 -133
- package/dist/browser.d.cts +46 -14
- package/dist/browser.d.ts +46 -14
- package/dist/browser.js +8 -6
- package/dist/build.cjs +2094 -1033
- package/dist/build.d.cts +133 -44
- package/dist/build.d.ts +133 -44
- package/dist/build.js +1372 -574
- package/dist/cdn.dev.global.js +10 -10
- package/dist/cdn.full.dev.global.js +11 -11
- package/dist/cdn.full.global.js +10 -10
- package/dist/cdn.global.js +10 -10
- package/dist/{chunk-2DCGACUU.js → chunk-2BPG2XDA.js} +251 -66
- package/dist/{chunk-IXKSNWV5.js → chunk-2INLLLMZ.js} +1 -1
- package/dist/{chunk-4PMLNECI.js → chunk-3QWBSL5R.js} +98 -29
- package/dist/{chunk-KKLW7YWL.js → chunk-4AWA2PVD.js} +275 -144
- package/dist/chunk-5HZXGZ6T.js +24 -0
- package/dist/{chunk-PCT43HW3.js → chunk-7LUJQAOJ.js} +1 -1
- package/dist/{chunk-5DXA2J44.js → chunk-7XHATCIH.js} +5 -2
- package/dist/chunk-BC2SECJD.js +44 -0
- package/dist/{chunk-TBYTO6BS.js → chunk-BSY63EM6.js} +136 -80
- package/dist/{chunk-FTIR4QW2.js → chunk-DKXRACVN.js} +6 -8
- package/dist/{chunk-ONOHFDLG.js → chunk-FNJXNGYZ.js} +3 -3
- package/dist/{chunk-XZZOBQAY.js → chunk-FQRUXCEE.js} +6 -5
- package/dist/{chunk-NIOYEGBQ.js → chunk-HURREPU2.js} +27 -13
- package/dist/chunk-J6FW5TV6.js +233 -0
- package/dist/{chunk-UGRX3S57.js → chunk-JWKYU5GV.js} +22 -4
- package/dist/chunk-NYNYSPK7.js +318 -0
- package/dist/{chunk-RBTPLM32.js → chunk-RUSSKG6G.js} +13 -12
- package/dist/{chunk-7LN645I6.js → chunk-SLM3IA34.js} +3 -3
- package/dist/{chunk-R25EFXXC.js → chunk-TUCPL2HB.js} +3 -3
- package/dist/{chunk-B3WHI2QA.js → chunk-UNWRJRKC.js} +55 -31
- package/dist/{chunk-KEISJXBU.js → chunk-UOL2ECCS.js} +44 -17
- package/dist/{chunk-RJE2BNI4.js → chunk-VZSG24LS.js} +108 -45
- package/dist/{chunk-RIXRAYIU.js → chunk-WEQ3DMVL.js} +10 -4
- package/dist/{chunk-3JZ4L5TJ.js → chunk-WOLJZUFQ.js} +354 -90
- package/dist/{chunk-S373NSMK.js → chunk-XYV3EDB7.js} +283 -123
- package/dist/{chunk-VZKNK2V7.js → chunk-XZR4PXRE.js} +296 -38
- package/dist/{chunk-GW3SCCZG.js → chunk-Z3OHK6QT.js} +153 -110
- package/dist/{chunk-OMJJM3KM.js → chunk-ZVL7TY4K.js} +287 -130
- package/dist/{contracts-DBdg9J_a.d.ts → contracts-CLqzJnOV.d.ts} +36 -17
- package/dist/{contracts-DBdg9J_a.d.cts → contracts-CTOJXu-x.d.cts} +36 -17
- package/dist/{customElement-OB9CIsc5.d.cts → customElement-MmInOW1U.d.cts} +21 -0
- package/dist/{customElement-OB9CIsc5.d.ts → customElement-MmInOW1U.d.ts} +21 -0
- package/dist/data.cjs +410 -155
- package/dist/data.d.cts +166 -12
- package/dist/data.d.ts +166 -12
- package/dist/data.js +12 -9
- package/dist/devtools.cjs +98 -61
- package/dist/devtools.js +7 -8
- package/dist/dispose-GEIG2KOF.js +28 -0
- package/dist/ecosystem.cjs +372 -126
- package/dist/ecosystem.d.cts +20 -3
- package/dist/ecosystem.d.ts +20 -3
- package/dist/ecosystem.js +12 -12
- package/dist/extras.cjs +2366 -909
- package/dist/extras.d.cts +11 -9
- package/dist/extras.d.ts +11 -9
- package/dist/extras.js +39 -28
- package/dist/index.cjs +421 -158
- package/dist/index.d.cts +213 -171
- package/dist/index.d.ts +213 -171
- package/dist/index.js +24 -27
- package/dist/motion.cjs +118 -44
- package/dist/motion.js +5 -5
- package/dist/patterns.cjs +344 -53
- package/dist/patterns.d.cts +28 -9
- package/dist/patterns.d.ts +28 -9
- package/dist/patterns.js +8 -8
- package/dist/performance.cjs +325 -220
- package/dist/performance.d.cts +2 -2
- package/dist/performance.d.ts +2 -2
- package/dist/performance.js +8 -9
- package/dist/plugin-DVgSnTfK.d.cts +112 -0
- package/dist/plugin-DVgSnTfK.d.ts +112 -0
- package/dist/plugins.cjs +664 -233
- package/dist/plugins.d.cts +127 -14
- package/dist/plugins.d.ts +127 -14
- package/dist/plugins.js +96 -42
- package/dist/signal-EotCj4hS.d.cts +110 -0
- package/dist/signal-EotCj4hS.d.ts +110 -0
- package/dist/{ssr-BiPRdZ6n.d.cts → ssr-Bli9XRW5.d.cts} +5 -0
- package/dist/{ssr-BiPRdZ6n.d.ts → ssr-Bli9XRW5.d.ts} +5 -0
- package/dist/{ssr-Y7XOEPEN.js → ssr-XOTUASDO.js} +4 -5
- package/dist/ssr.cjs +229 -84
- package/dist/ssr.d.cts +9 -3
- package/dist/ssr.d.ts +9 -3
- package/dist/ssr.js +11 -12
- package/dist/{startup-BMpaiMhP.d.ts → startup-BLfSeL15.d.cts} +73 -22
- package/dist/{startup-BMpaiMhP.d.cts → startup-BLfSeL15.d.ts} +73 -22
- package/dist/tagFactory-8qL9LCIx.d.cts +156 -0
- package/dist/tagFactory-BL2fymez.d.ts +156 -0
- package/dist/testing.cjs +2503 -2191
- package/dist/testing.d.cts +56 -5
- package/dist/testing.d.ts +56 -5
- package/dist/testing.js +580 -307
- package/dist/types-CJFViL6Q.d.cts +26 -0
- package/dist/types-CJFViL6Q.d.ts +26 -0
- package/dist/ui.cjs +732 -329
- package/dist/ui.d.cts +41 -7
- package/dist/ui.d.ts +41 -7
- package/dist/ui.js +151 -56
- package/dist/widgets.cjs +267 -291
- package/dist/widgets.js +9 -10
- package/package.json +4 -2
- package/dist/chunk-2WLZ6757.js +0 -149
- package/dist/chunk-CCSJMTRN.js +0 -15
- package/dist/chunk-QKRPLZ2V.js +0 -108
- package/dist/chunk-VUF4ALSW.js +0 -60
- package/dist/chunk-WWV3SJ3L.js +0 -131
- package/dist/dispose-46BOMMQJ.js +0 -19
- package/dist/plugin-D30wlGW5.d.cts +0 -71
- package/dist/plugin-D30wlGW5.d.ts +0 -71
- package/dist/tagFactory-DVoDpHye.d.cts +0 -215
- package/dist/tagFactory-DVoDpHye.d.ts +0 -215
|
@@ -1,34 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
domBinding
|
|
3
|
+
} from "./chunk-BC2SECJD.js";
|
|
1
4
|
import {
|
|
2
5
|
bindAttribute,
|
|
3
6
|
setSafeAttribute
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-FNJXNGYZ.js";
|
|
5
8
|
import {
|
|
6
9
|
derived
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-RUSSKG6G.js";
|
|
8
11
|
import {
|
|
9
12
|
isEventHandlerAttr
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-2INLLLMZ.js";
|
|
11
14
|
import {
|
|
12
15
|
globalSingleton
|
|
13
16
|
} from "./chunk-7ZHH77QA.js";
|
|
14
17
|
import {
|
|
15
|
-
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import {
|
|
18
|
-
dispose,
|
|
19
|
-
registerDisposer,
|
|
20
|
-
replaceChildrenSafely
|
|
21
|
-
} from "./chunk-QKRPLZ2V.js";
|
|
18
|
+
adoptThenable
|
|
19
|
+
} from "./chunk-5HZXGZ6T.js";
|
|
22
20
|
import {
|
|
23
21
|
signal
|
|
24
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-FQRUXCEE.js";
|
|
25
23
|
import {
|
|
26
24
|
reactiveBinding,
|
|
27
25
|
track
|
|
28
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-JWKYU5GV.js";
|
|
29
27
|
import {
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
dispose,
|
|
29
|
+
disposeNodeOwn,
|
|
30
|
+
registerDisposer,
|
|
31
|
+
replaceChildrenSafely,
|
|
32
|
+
reportError,
|
|
33
|
+
unregisterDisposer,
|
|
34
|
+
withDisposerRollback
|
|
35
|
+
} from "./chunk-NYNYSPK7.js";
|
|
32
36
|
|
|
33
37
|
// src/ui/form.ts
|
|
34
38
|
function required(message = "This field is required") {
|
|
@@ -57,10 +61,14 @@ function maxLength(max2, message) {
|
|
|
57
61
|
}
|
|
58
62
|
function matchesPattern(regex, message = "Invalid format") {
|
|
59
63
|
return (value) => {
|
|
60
|
-
if (
|
|
61
|
-
|
|
64
|
+
if (!value) return null;
|
|
65
|
+
const savedLastIndex = regex.lastIndex;
|
|
66
|
+
regex.lastIndex = 0;
|
|
67
|
+
try {
|
|
68
|
+
return regex.test(value) ? null : message;
|
|
69
|
+
} finally {
|
|
70
|
+
regex.lastIndex = savedLastIndex;
|
|
62
71
|
}
|
|
63
|
-
return null;
|
|
64
72
|
};
|
|
65
73
|
}
|
|
66
74
|
function email(message = "Invalid email address") {
|
|
@@ -109,13 +117,13 @@ function bindField(field, extras) {
|
|
|
109
117
|
const mergedOn = extraOn && typeof extraOn === "object" ? { ...extraOn, ...fieldOn } : fieldOn;
|
|
110
118
|
const onElement = (el) => {
|
|
111
119
|
if (el instanceof HTMLSelectElement && el.multiple) {
|
|
112
|
-
const stop =
|
|
120
|
+
const stop = domBinding(() => {
|
|
113
121
|
const v = field.value();
|
|
114
122
|
const selected = Array.isArray(v) ? v.map(String) : [];
|
|
115
123
|
for (const opt of Array.from(el.options)) {
|
|
116
124
|
opt.selected = selected.includes(opt.value);
|
|
117
125
|
}
|
|
118
|
-
});
|
|
126
|
+
}, el);
|
|
119
127
|
registerDisposer(el, stop);
|
|
120
128
|
}
|
|
121
129
|
if (typeof extraOnElement === "function") {
|
|
@@ -135,6 +143,7 @@ function form(config) {
|
|
|
135
143
|
const fieldEntries = Object.entries(config);
|
|
136
144
|
const fieldMap = {};
|
|
137
145
|
const [manualErrors, setManualErrors] = signal({});
|
|
146
|
+
const fieldErrors = [];
|
|
138
147
|
for (const [name, cfg] of fieldEntries) {
|
|
139
148
|
const [value, setValue] = signal(cfg.initial);
|
|
140
149
|
const [isTouched, setTouched] = signal(false);
|
|
@@ -149,6 +158,7 @@ function form(config) {
|
|
|
149
158
|
}
|
|
150
159
|
return null;
|
|
151
160
|
});
|
|
161
|
+
fieldErrors.push(error);
|
|
152
162
|
const wrappedSet = (next) => {
|
|
153
163
|
setValue(next);
|
|
154
164
|
setManualErrors((prev) => {
|
|
@@ -225,12 +235,22 @@ function form(config) {
|
|
|
225
235
|
field.touch();
|
|
226
236
|
}
|
|
227
237
|
if (isValid()) {
|
|
228
|
-
const
|
|
229
|
-
|
|
238
|
+
const report = (error) => reportError(error, { phase: "async", name: "form.handleSubmit" });
|
|
239
|
+
let pending;
|
|
240
|
+
try {
|
|
241
|
+
pending = adoptThenable(onSubmit(values()));
|
|
242
|
+
} catch (error) {
|
|
243
|
+
report(error);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (pending) {
|
|
230
247
|
setSubmitting(true);
|
|
231
|
-
|
|
248
|
+
pending.then(
|
|
232
249
|
() => setSubmitting(false),
|
|
233
|
-
() =>
|
|
250
|
+
(error) => {
|
|
251
|
+
setSubmitting(false);
|
|
252
|
+
report(error);
|
|
253
|
+
}
|
|
234
254
|
);
|
|
235
255
|
}
|
|
236
256
|
}
|
|
@@ -244,6 +264,17 @@ function form(config) {
|
|
|
244
264
|
function setError(field, message) {
|
|
245
265
|
setManualErrors((prev) => ({ ...prev, [field]: message }));
|
|
246
266
|
}
|
|
267
|
+
let disposed = false;
|
|
268
|
+
function dispose2() {
|
|
269
|
+
if (disposed) return;
|
|
270
|
+
disposed = true;
|
|
271
|
+
errors.dispose();
|
|
272
|
+
isValid.dispose();
|
|
273
|
+
isDirty.dispose();
|
|
274
|
+
touchedState.dispose();
|
|
275
|
+
values.dispose();
|
|
276
|
+
for (const error of fieldErrors) error.dispose();
|
|
277
|
+
}
|
|
247
278
|
return {
|
|
248
279
|
fields: fieldMap,
|
|
249
280
|
errors,
|
|
@@ -254,7 +285,8 @@ function form(config) {
|
|
|
254
285
|
values,
|
|
255
286
|
handleSubmit,
|
|
256
287
|
reset,
|
|
257
|
-
setError
|
|
288
|
+
setError,
|
|
289
|
+
dispose: dispose2
|
|
258
290
|
};
|
|
259
291
|
}
|
|
260
292
|
|
|
@@ -298,7 +330,7 @@ function VirtualList(props) {
|
|
|
298
330
|
content.appendChild(itemEl);
|
|
299
331
|
}
|
|
300
332
|
};
|
|
301
|
-
registerDisposer(container,
|
|
333
|
+
registerDisposer(container, domBinding(update, container));
|
|
302
334
|
return container;
|
|
303
335
|
}
|
|
304
336
|
|
|
@@ -308,11 +340,14 @@ function intersection(options) {
|
|
|
308
340
|
const [ratio, setRatio] = signal(0);
|
|
309
341
|
let observer = null;
|
|
310
342
|
let currentElement = null;
|
|
343
|
+
let generation = 0;
|
|
311
344
|
function observe(element) {
|
|
312
345
|
if (typeof IntersectionObserver === "undefined") return;
|
|
313
346
|
unobserve();
|
|
314
347
|
currentElement = element;
|
|
348
|
+
const observation = ++generation;
|
|
315
349
|
observer = new IntersectionObserver((entries) => {
|
|
350
|
+
if (observation !== generation) return;
|
|
316
351
|
const entry = entries[0];
|
|
317
352
|
if (entry) {
|
|
318
353
|
setIsIntersecting(entry.isIntersecting);
|
|
@@ -322,8 +357,10 @@ function intersection(options) {
|
|
|
322
357
|
observer.observe(element);
|
|
323
358
|
}
|
|
324
359
|
function unobserve() {
|
|
360
|
+
generation++;
|
|
325
361
|
if (observer) {
|
|
326
362
|
if (currentElement) observer.unobserve(currentElement);
|
|
363
|
+
observer.takeRecords();
|
|
327
364
|
observer.disconnect();
|
|
328
365
|
observer = null;
|
|
329
366
|
currentElement = null;
|
|
@@ -342,17 +379,26 @@ function lazyLoad(element, loader, options) {
|
|
|
342
379
|
return () => {
|
|
343
380
|
};
|
|
344
381
|
}
|
|
382
|
+
let done = false;
|
|
383
|
+
const stop = () => {
|
|
384
|
+
done = true;
|
|
385
|
+
observer.takeRecords();
|
|
386
|
+
observer.disconnect();
|
|
387
|
+
};
|
|
345
388
|
const observer = new IntersectionObserver((entries) => {
|
|
389
|
+
if (done) return;
|
|
346
390
|
for (const entry of entries) {
|
|
347
391
|
if (entry.isIntersecting) {
|
|
392
|
+
stop();
|
|
348
393
|
loader();
|
|
349
|
-
observer.disconnect();
|
|
350
394
|
break;
|
|
351
395
|
}
|
|
352
396
|
}
|
|
353
397
|
}, options);
|
|
354
398
|
observer.observe(element);
|
|
355
|
-
return () =>
|
|
399
|
+
return () => {
|
|
400
|
+
if (!done) stop();
|
|
401
|
+
};
|
|
356
402
|
}
|
|
357
403
|
|
|
358
404
|
// src/ui/inputMask.ts
|
|
@@ -501,6 +547,7 @@ function focus() {
|
|
|
501
547
|
const dispose2 = () => {
|
|
502
548
|
if (disposed) return;
|
|
503
549
|
disposed = true;
|
|
550
|
+
unregisterDisposer(element, dispose2);
|
|
504
551
|
element.removeEventListener("focus", onFocus);
|
|
505
552
|
element.removeEventListener("blur", onBlur);
|
|
506
553
|
if (currentElement === element) currentElement = null;
|
|
@@ -569,14 +616,19 @@ function FocusTrap(nodes, options = {}) {
|
|
|
569
616
|
}
|
|
570
617
|
};
|
|
571
618
|
container.addEventListener("keydown", onTrapKeydown);
|
|
619
|
+
let disposed = false;
|
|
572
620
|
if (options.autoFocus !== false) {
|
|
573
621
|
queueMicrotask(() => {
|
|
622
|
+
if (disposed || !container.isConnected) return;
|
|
574
623
|
const first = getFocusable()[0];
|
|
575
624
|
first?.focus();
|
|
576
625
|
});
|
|
577
626
|
}
|
|
578
627
|
let trapObserver = null;
|
|
579
628
|
function restoreFocusAndCleanup() {
|
|
629
|
+
if (disposed) return;
|
|
630
|
+
disposed = true;
|
|
631
|
+
unregisterDisposer(container, restoreFocusAndCleanup);
|
|
580
632
|
if (options.restoreFocus !== false) previouslyFocused?.focus();
|
|
581
633
|
container.removeEventListener("keydown", onTrapKeydown);
|
|
582
634
|
if (trapObserver) {
|
|
@@ -591,29 +643,45 @@ function FocusTrap(nodes, options = {}) {
|
|
|
591
643
|
}
|
|
592
644
|
});
|
|
593
645
|
queueMicrotask(() => {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
646
|
+
const observer = trapObserver;
|
|
647
|
+
if (disposed || !container.isConnected || !observer) return;
|
|
648
|
+
observer.observe(container, { childList: true, subtree: true });
|
|
597
649
|
});
|
|
598
650
|
}
|
|
599
651
|
registerDisposer(container, restoreFocusAndCleanup);
|
|
600
652
|
return container;
|
|
601
653
|
}
|
|
654
|
+
function isApplePlatform() {
|
|
655
|
+
if (typeof navigator === "undefined") return false;
|
|
656
|
+
const nav = navigator;
|
|
657
|
+
return /mac|iphone|ipad|ipod/i.test(nav.userAgentData?.platform || nav.platform || "");
|
|
658
|
+
}
|
|
602
659
|
function hotkey(combo, handler, options = {}) {
|
|
603
660
|
let key = combo;
|
|
604
661
|
let needCtrl = options.ctrl ?? false;
|
|
605
662
|
let needShift = options.shift ?? false;
|
|
606
663
|
let needAlt = options.alt ?? false;
|
|
607
664
|
let needMeta = options.meta ?? false;
|
|
608
|
-
if (combo
|
|
609
|
-
|
|
665
|
+
if (combo === "+") {
|
|
666
|
+
key = "+";
|
|
667
|
+
} else if (combo.includes("+")) {
|
|
668
|
+
const literalPlus = combo.endsWith("++");
|
|
669
|
+
const parts = (literalPlus ? combo.slice(0, -2) : combo).toLowerCase().split("+");
|
|
670
|
+
if (literalPlus) parts.push("+");
|
|
610
671
|
key = parts[parts.length - 1];
|
|
672
|
+
if (key === "") throw new Error(`hotkey("${combo}"): missing key`);
|
|
611
673
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
612
674
|
const mod = parts[i];
|
|
613
675
|
if (mod === "ctrl" || mod === "control") needCtrl = true;
|
|
614
676
|
else if (mod === "shift") needShift = true;
|
|
615
|
-
else if (mod === "alt") needAlt = true;
|
|
677
|
+
else if (mod === "alt" || mod === "option") needAlt = true;
|
|
616
678
|
else if (mod === "meta" || mod === "cmd" || mod === "command") needMeta = true;
|
|
679
|
+
else if (mod === "mod") {
|
|
680
|
+
if (isApplePlatform()) needMeta = true;
|
|
681
|
+
else needCtrl = true;
|
|
682
|
+
} else {
|
|
683
|
+
throw new Error(`hotkey("${combo}"): unknown modifier "${mod}"`);
|
|
684
|
+
}
|
|
617
685
|
}
|
|
618
686
|
}
|
|
619
687
|
const listener = (e) => {
|
|
@@ -968,6 +1036,8 @@ function removeGlobalListenerIfIdle() {
|
|
|
968
1036
|
function dialog() {
|
|
969
1037
|
const [isOpen, setIsOpen] = signal(false);
|
|
970
1038
|
const entry = { close: () => close() };
|
|
1039
|
+
let opened = false;
|
|
1040
|
+
let disposed = false;
|
|
971
1041
|
function pushOnStack() {
|
|
972
1042
|
if (dialogStack.indexOf(entry) !== -1) return;
|
|
973
1043
|
dialogStack.push(entry);
|
|
@@ -979,23 +1049,27 @@ function dialog() {
|
|
|
979
1049
|
removeGlobalListenerIfIdle();
|
|
980
1050
|
}
|
|
981
1051
|
function open() {
|
|
982
|
-
if (
|
|
983
|
-
|
|
1052
|
+
if (disposed || opened) return;
|
|
1053
|
+
opened = true;
|
|
984
1054
|
pushOnStack();
|
|
1055
|
+
setIsOpen(true);
|
|
985
1056
|
}
|
|
986
1057
|
function close() {
|
|
987
|
-
if (!
|
|
1058
|
+
if (!opened) {
|
|
988
1059
|
removeFromStack();
|
|
989
1060
|
return;
|
|
990
1061
|
}
|
|
991
|
-
|
|
1062
|
+
opened = false;
|
|
992
1063
|
removeFromStack();
|
|
1064
|
+
setIsOpen(false);
|
|
993
1065
|
}
|
|
994
1066
|
function toggle() {
|
|
995
|
-
if (
|
|
1067
|
+
if (opened) close();
|
|
996
1068
|
else open();
|
|
997
1069
|
}
|
|
998
1070
|
function dispose2() {
|
|
1071
|
+
disposed = true;
|
|
1072
|
+
opened = false;
|
|
999
1073
|
removeFromStack();
|
|
1000
1074
|
setIsOpen(false);
|
|
1001
1075
|
}
|
|
@@ -1200,32 +1274,49 @@ function pagination(options) {
|
|
|
1200
1274
|
function eventBus() {
|
|
1201
1275
|
const listeners = /* @__PURE__ */ new Map();
|
|
1202
1276
|
function on(event, handler) {
|
|
1203
|
-
let
|
|
1204
|
-
if (!
|
|
1205
|
-
|
|
1206
|
-
listeners.set(event,
|
|
1277
|
+
let subs = listeners.get(event);
|
|
1278
|
+
if (!subs) {
|
|
1279
|
+
subs = /* @__PURE__ */ new Map();
|
|
1280
|
+
listeners.set(event, subs);
|
|
1281
|
+
}
|
|
1282
|
+
let subscription = subs.get(handler);
|
|
1283
|
+
if (!subscription) {
|
|
1284
|
+
subscription = { handler, active: true };
|
|
1285
|
+
subs.set(handler, subscription);
|
|
1207
1286
|
}
|
|
1208
|
-
|
|
1209
|
-
return () =>
|
|
1287
|
+
const own = subscription;
|
|
1288
|
+
return () => {
|
|
1289
|
+
if (listeners.get(event)?.get(handler) !== own) return;
|
|
1290
|
+
off(event, handler);
|
|
1291
|
+
};
|
|
1210
1292
|
}
|
|
1211
1293
|
function emit(event, data) {
|
|
1212
|
-
const
|
|
1213
|
-
if (
|
|
1214
|
-
|
|
1215
|
-
|
|
1294
|
+
const subs = listeners.get(event);
|
|
1295
|
+
if (!subs || subs.size === 0) return;
|
|
1296
|
+
for (const subscription of Array.from(subs.values())) {
|
|
1297
|
+
if (!subscription.active) continue;
|
|
1298
|
+
try {
|
|
1299
|
+
subscription.handler(data);
|
|
1300
|
+
} catch (err) {
|
|
1301
|
+
reportError(err, { phase: "event", name: `eventBus(${String(event)})` });
|
|
1216
1302
|
}
|
|
1217
1303
|
}
|
|
1218
1304
|
}
|
|
1219
1305
|
function off(event, handler) {
|
|
1220
|
-
const
|
|
1221
|
-
if (
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1306
|
+
const subs = listeners.get(event);
|
|
1307
|
+
if (!subs) return;
|
|
1308
|
+
const subscription = subs.get(handler);
|
|
1309
|
+
if (!subscription) return;
|
|
1310
|
+
subscription.active = false;
|
|
1311
|
+
subs.delete(handler);
|
|
1312
|
+
if (subs.size === 0) {
|
|
1313
|
+
listeners.delete(event);
|
|
1226
1314
|
}
|
|
1227
1315
|
}
|
|
1228
1316
|
function clear() {
|
|
1317
|
+
for (const subs of listeners.values()) {
|
|
1318
|
+
for (const subscription of subs.values()) subscription.active = false;
|
|
1319
|
+
}
|
|
1229
1320
|
listeners.clear();
|
|
1230
1321
|
}
|
|
1231
1322
|
return { on, emit, off, clear };
|
|
@@ -1233,13 +1324,33 @@ function eventBus() {
|
|
|
1233
1324
|
|
|
1234
1325
|
// src/platform/customElement.ts
|
|
1235
1326
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
1327
|
+
var MAX_RENDER_PASSES = 10;
|
|
1328
|
+
var ABANDONED_RENDER = /* @__PURE__ */ Symbol("sibujs.defineElement.abandonedRender");
|
|
1236
1329
|
function defineElement(name, component, options = {}) {
|
|
1237
1330
|
if (customElements.get(name)) return;
|
|
1238
1331
|
const observed = options.observedAttributes || [];
|
|
1239
1332
|
class SibuElement extends HTMLElement {
|
|
1240
1333
|
constructor() {
|
|
1241
1334
|
super();
|
|
1242
|
-
|
|
1335
|
+
// Re-rendering is keyed on connection, not on a previous render: a first render
|
|
1336
|
+
// that throws leaves nothing rendered, and the element must still re-render
|
|
1337
|
+
// when a later attribute change fixes the input.
|
|
1338
|
+
this._connected = false;
|
|
1339
|
+
// A render in progress, and whether an attribute changed during it. The
|
|
1340
|
+
// component may write its host's observed attributes while rendering; with
|
|
1341
|
+
// the old subtree kept during the build, rendering again from inside the
|
|
1342
|
+
// callback recursed until the stack overflowed.
|
|
1343
|
+
this._rendering = false;
|
|
1344
|
+
this._dirty = false;
|
|
1345
|
+
// How many teardowns are draining. A host teardown may reconnect the element
|
|
1346
|
+
// (and a nested disconnect may run inside it), and rendering the next
|
|
1347
|
+
// generation from inside that drain would let the drain tear down what the
|
|
1348
|
+
// new render registers. A counter, not a flag: a nested teardown returning
|
|
1349
|
+
// must not declare the outer one finished.
|
|
1350
|
+
this._teardownDepth = 0;
|
|
1351
|
+
// Disconnects so far. A render compares it across its old-subtree teardown
|
|
1352
|
+
// to detect a disconnect that happened inside it.
|
|
1353
|
+
this._disconnects = 0;
|
|
1243
1354
|
if (options.shadow !== false) {
|
|
1244
1355
|
this._root = this.attachShadow({ mode: options.mode || "open" });
|
|
1245
1356
|
} else {
|
|
@@ -1249,32 +1360,106 @@ function defineElement(name, component, options = {}) {
|
|
|
1249
1360
|
static get observedAttributes() {
|
|
1250
1361
|
return observed;
|
|
1251
1362
|
}
|
|
1363
|
+
/** Whether rendering must wait for work already in progress. */
|
|
1364
|
+
get _busy() {
|
|
1365
|
+
return this._rendering || this._teardownDepth > 0;
|
|
1366
|
+
}
|
|
1252
1367
|
connectedCallback() {
|
|
1368
|
+
this._connected = true;
|
|
1369
|
+
if (this._busy) {
|
|
1370
|
+
this._dirty = true;
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1253
1373
|
this._render();
|
|
1254
1374
|
}
|
|
1255
1375
|
disconnectedCallback() {
|
|
1256
|
-
this.
|
|
1376
|
+
this._connected = false;
|
|
1377
|
+
this._disconnects++;
|
|
1378
|
+
this._teardownDepth++;
|
|
1379
|
+
try {
|
|
1380
|
+
this._teardown();
|
|
1381
|
+
} finally {
|
|
1382
|
+
this._teardownDepth--;
|
|
1383
|
+
if (this._teardownDepth === 0 && this._connected && this._dirty) {
|
|
1384
|
+
this._dirty = false;
|
|
1385
|
+
if (!this._rendering) this._render();
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1257
1388
|
}
|
|
1258
|
-
attributeChangedCallback() {
|
|
1259
|
-
if (this.
|
|
1260
|
-
|
|
1389
|
+
attributeChangedCallback(_name, oldValue, newValue) {
|
|
1390
|
+
if (oldValue === newValue || !this._connected) return;
|
|
1391
|
+
if (this._busy) {
|
|
1392
|
+
this._dirty = true;
|
|
1393
|
+
return;
|
|
1261
1394
|
}
|
|
1395
|
+
this._render();
|
|
1262
1396
|
}
|
|
1263
1397
|
_teardown() {
|
|
1264
|
-
this._rendered = null;
|
|
1265
1398
|
replaceChildrenSafely(this._root);
|
|
1266
|
-
|
|
1399
|
+
disposeNodeOwn(this);
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Render as a transaction: build the replacement first, commit only if that
|
|
1403
|
+
* succeeds. Tearing the current subtree down before calling the factory
|
|
1404
|
+
* meant a throwing rerender (an invalid attribute, say) left the element
|
|
1405
|
+
* blank with its live state already disposed. Now a failure keeps the
|
|
1406
|
+
* working subtree, releases whatever the failed attempt registered, and is
|
|
1407
|
+
* reported with this element as its node so an enclosing ErrorBoundary can
|
|
1408
|
+
* claim it.
|
|
1409
|
+
*/
|
|
1267
1410
|
_render() {
|
|
1268
|
-
this.
|
|
1411
|
+
this._rendering = true;
|
|
1412
|
+
try {
|
|
1413
|
+
let passes = 0;
|
|
1414
|
+
do {
|
|
1415
|
+
this._dirty = false;
|
|
1416
|
+
if (++passes > MAX_RENDER_PASSES) {
|
|
1417
|
+
reportError(
|
|
1418
|
+
new Error(
|
|
1419
|
+
`[SibuJS] defineElement(${name}): the component changed its own observed attributes on ${MAX_RENDER_PASSES} consecutive renders; stopped re-rendering.`
|
|
1420
|
+
),
|
|
1421
|
+
{ phase: "render", name: `defineElement(${name})`, node: this }
|
|
1422
|
+
);
|
|
1423
|
+
break;
|
|
1424
|
+
}
|
|
1425
|
+
this._renderOnce();
|
|
1426
|
+
} while (this._dirty && this._connected);
|
|
1427
|
+
} finally {
|
|
1428
|
+
this._rendering = false;
|
|
1429
|
+
this._dirty = false;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
_renderOnce() {
|
|
1269
1433
|
const props = this._getProps();
|
|
1434
|
+
let el;
|
|
1435
|
+
try {
|
|
1436
|
+
el = withDisposerRollback(() => {
|
|
1437
|
+
const built = component(props, this);
|
|
1438
|
+
if (!this._connected) throw ABANDONED_RENDER;
|
|
1439
|
+
return built;
|
|
1440
|
+
});
|
|
1441
|
+
} catch (err) {
|
|
1442
|
+
if (err !== ABANDONED_RENDER) {
|
|
1443
|
+
reportError(err, { phase: "render", name: `defineElement(${name})`, node: this });
|
|
1444
|
+
}
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
const next = [];
|
|
1270
1448
|
if (options.styles && this._root instanceof ShadowRoot) {
|
|
1271
1449
|
const styleEl = document.createElement("style");
|
|
1272
1450
|
styleEl.textContent = options.styles;
|
|
1273
|
-
|
|
1451
|
+
next.push(styleEl);
|
|
1452
|
+
}
|
|
1453
|
+
next.push(el);
|
|
1454
|
+
const disconnects = this._disconnects;
|
|
1455
|
+
for (const node of next) node.parentNode?.removeChild(node);
|
|
1456
|
+
replaceChildrenSafely(this._root);
|
|
1457
|
+
if (this._disconnects !== disconnects) {
|
|
1458
|
+
for (const node of next) dispose(node);
|
|
1459
|
+
if (this._connected) this._dirty = true;
|
|
1460
|
+
return;
|
|
1274
1461
|
}
|
|
1275
|
-
|
|
1276
|
-
this._root.appendChild(el);
|
|
1277
|
-
this._rendered = el;
|
|
1462
|
+
this._root.replaceChildren(...next);
|
|
1278
1463
|
}
|
|
1279
1464
|
_getProps() {
|
|
1280
1465
|
const props = {};
|