sibujs 4.4.0 → 4.6.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 +11 -0
- package/dist/browser.cjs +339 -130
- package/dist/browser.d.cts +61 -16
- package/dist/browser.d.ts +61 -16
- package/dist/browser.js +8 -6
- package/dist/build.cjs +273 -82
- package/dist/build.js +15 -16
- package/dist/cdn.dev.global.js +12 -12
- package/dist/cdn.full.dev.global.js +11 -11
- package/dist/cdn.full.global.js +10 -10
- package/dist/cdn.global.js +12 -12
- package/dist/{chunk-IXKSNWV5.js → chunk-2INLLLMZ.js} +1 -1
- package/dist/chunk-3ISI6ACU.js +44 -0
- package/dist/{chunk-VCTAEPSB.js → chunk-47M47FOM.js} +11 -3
- package/dist/chunk-5HZXGZ6T.js +24 -0
- package/dist/{chunk-KKLW7YWL.js → chunk-5MT6SJ3P.js} +275 -144
- package/dist/{chunk-3BTTCZ5J.js → chunk-5Q4R7HCL.js} +42 -4
- package/dist/{chunk-ADM46X22.js → chunk-7GQCWFOE.js} +157 -61
- package/dist/{chunk-4Z3SPQ67.js → chunk-7NCARGJW.js} +16 -8
- package/dist/{chunk-5DXA2J44.js → chunk-7XHATCIH.js} +5 -2
- package/dist/{chunk-LQFGQNMV.js → chunk-AIF3Z2T7.js} +302 -39
- package/dist/{chunk-UCALUKB5.js → chunk-BAAG6ZTI.js} +98 -29
- package/dist/{chunk-NVNJH22U.js → chunk-D33YTSX5.js} +2 -2
- package/dist/{chunk-M2F7TZHH.js → chunk-DKGBBKOF.js} +136 -80
- package/dist/{chunk-NIOYEGBQ.js → chunk-HURREPU2.js} +27 -13
- package/dist/{chunk-HFCOH2GN.js → chunk-HYXDKS4N.js} +153 -110
- package/dist/chunk-J6FW5TV6.js +233 -0
- package/dist/{chunk-36S2YPP4.js → chunk-JSXPZCET.js} +1 -1
- package/dist/chunk-NYNYSPK7.js +318 -0
- package/dist/{chunk-HQSEH5F6.js → chunk-ORMZXBKQ.js} +108 -45
- package/dist/{chunk-LYVUX7NT.js → chunk-OXUY2A6L.js} +287 -130
- package/dist/{chunk-6GSIXTWX.js → chunk-PK6FK2G2.js} +187 -66
- package/dist/{chunk-W2EQ7X2L.js → chunk-QE4TTDU3.js} +47 -9
- package/dist/{chunk-XLO7SLIX.js → chunk-SXXVZMKZ.js} +262 -70
- package/dist/{chunk-R25EFXXC.js → chunk-TUCPL2HB.js} +3 -3
- package/dist/{chunk-RR7M3FHM.js → chunk-UZQ6ALFS.js} +3 -3
- package/dist/{chunk-PNIRUQ4C.js → chunk-VVWPJ543.js} +6 -8
- package/dist/{chunk-7GIHSAWB.js → chunk-XC4MEKGA.js} +3 -3
- package/dist/{chunk-SKPTERHP.js → chunk-XRRZKZYX.js} +55 -31
- package/dist/{contracts-DBdg9J_a.d.cts → contracts-DRIuclVT.d.cts} +10 -2
- package/dist/{contracts-DBdg9J_a.d.ts → contracts-DRIuclVT.d.ts} +10 -2
- package/dist/{customElement-CNZxEB9G.d.ts → customElement-MmInOW1U.d.cts} +59 -10
- package/dist/{customElement-CNZxEB9G.d.cts → customElement-MmInOW1U.d.ts} +59 -10
- package/dist/data.cjs +293 -125
- package/dist/data.d.cts +47 -9
- package/dist/data.d.ts +47 -9
- package/dist/data.js +12 -9
- package/dist/devtools.cjs +121 -58
- package/dist/devtools.js +7 -8
- package/dist/dispose-GEIG2KOF.js +28 -0
- package/dist/ecosystem.cjs +391 -112
- package/dist/ecosystem.d.cts +31 -7
- package/dist/ecosystem.d.ts +31 -7
- package/dist/ecosystem.js +12 -12
- package/dist/extras.cjs +2250 -879
- package/dist/extras.d.cts +10 -9
- package/dist/extras.d.ts +10 -9
- package/dist/extras.js +39 -28
- package/dist/index.cjs +273 -82
- package/dist/index.d.cts +44 -154
- package/dist/index.d.ts +44 -154
- package/dist/index.js +24 -27
- package/dist/motion.cjs +137 -42
- package/dist/motion.js +5 -5
- package/dist/patterns.cjs +397 -48
- package/dist/patterns.d.cts +32 -9
- package/dist/patterns.d.ts +32 -9
- package/dist/patterns.js +8 -8
- package/dist/performance.cjs +338 -217
- 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 +587 -203
- package/dist/plugins.d.cts +3 -3
- package/dist/plugins.d.ts +3 -3
- package/dist/plugins.js +35 -23
- 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 +214 -73
- 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-DFstCLQV.d.cts +117 -0
- package/dist/tagFactory-DkaNVUNV.d.ts +117 -0
- package/dist/testing.cjs +2481 -2153
- package/dist/testing.d.cts +56 -5
- package/dist/testing.d.ts +56 -5
- package/dist/testing.js +580 -307
- package/dist/ui.cjs +798 -328
- package/dist/ui.d.cts +40 -7
- package/dist/ui.d.ts +40 -7
- package/dist/ui.js +151 -56
- package/dist/widgets.cjs +319 -285
- package/dist/widgets.js +9 -10
- package/package.json +2 -2
- package/dist/chunk-2WLZ6757.js +0 -149
- package/dist/chunk-7NBDXVHS.js +0 -60
- package/dist/chunk-CCSJMTRN.js +0 -15
- package/dist/chunk-QKRPLZ2V.js +0 -108
- 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-Bzupt4Pj.d.cts +0 -55
- package/dist/tagFactory-Bzupt4Pj.d.ts +0 -55
package/dist/ui.cjs
CHANGED
|
@@ -85,28 +85,6 @@ __export(ui_exports, {
|
|
|
85
85
|
});
|
|
86
86
|
module.exports = __toCommonJS(ui_exports);
|
|
87
87
|
|
|
88
|
-
// src/core/dev.ts
|
|
89
|
-
var DEV = typeof __SIBU_DEV__ !== "undefined" ? !!__SIBU_DEV__ : typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
|
|
90
|
-
// safe default: off in browser, on in test/dev Node
|
|
91
|
-
typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
|
|
92
|
-
);
|
|
93
|
-
function devAssert(condition, message) {
|
|
94
|
-
if ((typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) && !condition) {
|
|
95
|
-
throw new Error(`[SibuJS] ${message}`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
function devWarnLazy(build) {
|
|
99
|
-
if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
|
|
100
|
-
const message = build();
|
|
101
|
-
if (message) console.warn(`[SibuJS] ${message}`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function devWarn(message) {
|
|
105
|
-
if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
|
|
106
|
-
console.warn(`[SibuJS] ${message}`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
88
|
// src/core/errors.ts
|
|
111
89
|
var ERROR_STATE_KEY = /* @__PURE__ */ Symbol.for("sibujs.runtime-errors.v1");
|
|
112
90
|
function state() {
|
|
@@ -195,6 +173,28 @@ function reportError(error, context) {
|
|
|
195
173
|
});
|
|
196
174
|
}
|
|
197
175
|
|
|
176
|
+
// src/core/dev.ts
|
|
177
|
+
var DEV = typeof __SIBU_DEV__ !== "undefined" ? !!__SIBU_DEV__ : typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
|
|
178
|
+
// safe default: off in browser, on in test/dev Node
|
|
179
|
+
typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
|
|
180
|
+
);
|
|
181
|
+
function devAssert(condition, message) {
|
|
182
|
+
if ((typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) && !condition) {
|
|
183
|
+
throw new Error(`[SibuJS] ${message}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function devWarnLazy(build) {
|
|
187
|
+
if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
|
|
188
|
+
const message = build();
|
|
189
|
+
if (message) console.warn(`[SibuJS] ${message}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function devWarn(message) {
|
|
193
|
+
if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
|
|
194
|
+
console.warn(`[SibuJS] ${message}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
198
|
// src/core/rendering/dispose.ts
|
|
199
199
|
var elementDisposers = /* @__PURE__ */ new WeakMap();
|
|
200
200
|
var MAX_DRAIN_TEARDOWNS = 1e4;
|
|
@@ -206,6 +206,23 @@ function reportDrainRunaway(label, executed, remaining) {
|
|
|
206
206
|
);
|
|
207
207
|
}
|
|
208
208
|
var activeBindingCount = 0;
|
|
209
|
+
function resolveCapture(capture) {
|
|
210
|
+
let frame = capture;
|
|
211
|
+
while (frame?.closed) frame = frame.forwardTo;
|
|
212
|
+
return frame ?? null;
|
|
213
|
+
}
|
|
214
|
+
var registrationCaptures = [];
|
|
215
|
+
function currentDisposerCapture() {
|
|
216
|
+
return resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
|
|
217
|
+
}
|
|
218
|
+
function beginDisposerCapture(capture) {
|
|
219
|
+
if (capture === null && registrationCaptures.length === 0) return false;
|
|
220
|
+
registrationCaptures.push(resolveCapture(capture));
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
function endDisposerCapture() {
|
|
224
|
+
registrationCaptures.pop();
|
|
225
|
+
}
|
|
209
226
|
function registerDisposer(node, teardown) {
|
|
210
227
|
let disposers = elementDisposers.get(node);
|
|
211
228
|
if (!disposers) {
|
|
@@ -214,6 +231,61 @@ function registerDisposer(node, teardown) {
|
|
|
214
231
|
}
|
|
215
232
|
disposers.push(teardown);
|
|
216
233
|
if (DEV) activeBindingCount++;
|
|
234
|
+
const capture = resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
|
|
235
|
+
if (capture) capture.entries.push([node, teardown]);
|
|
236
|
+
}
|
|
237
|
+
function withDisposerRollback(build) {
|
|
238
|
+
const frame = { entries: [], closed: false };
|
|
239
|
+
const captured = frame.entries;
|
|
240
|
+
registrationCaptures.push(frame);
|
|
241
|
+
let result;
|
|
242
|
+
try {
|
|
243
|
+
result = build();
|
|
244
|
+
} catch (err) {
|
|
245
|
+
let executed = 0;
|
|
246
|
+
try {
|
|
247
|
+
while (captured.length > 0) {
|
|
248
|
+
if (executed >= MAX_DRAIN_TEARDOWNS) {
|
|
249
|
+
reportDrainRunaway("rollback", executed, captured.length);
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
const [node, teardown] = captured.pop();
|
|
253
|
+
if (!unregisterDisposer(node, teardown)) continue;
|
|
254
|
+
executed++;
|
|
255
|
+
try {
|
|
256
|
+
teardown();
|
|
257
|
+
} catch (cleanupErr) {
|
|
258
|
+
reportError(cleanupErr, { phase: "cleanup", name: "disposer" });
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
} finally {
|
|
262
|
+
frame.closed = true;
|
|
263
|
+
captured.length = 0;
|
|
264
|
+
registrationCaptures.pop();
|
|
265
|
+
}
|
|
266
|
+
throw err;
|
|
267
|
+
}
|
|
268
|
+
frame.closed = true;
|
|
269
|
+
registrationCaptures.pop();
|
|
270
|
+
const parent = resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
|
|
271
|
+
if (parent) {
|
|
272
|
+
for (const entry of captured) {
|
|
273
|
+
if (elementDisposers.get(entry[0])?.includes(entry[1])) parent.entries.push(entry);
|
|
274
|
+
}
|
|
275
|
+
frame.forwardTo = parent;
|
|
276
|
+
}
|
|
277
|
+
captured.length = 0;
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
function unregisterDisposer(node, teardown) {
|
|
281
|
+
const disposers = elementDisposers.get(node);
|
|
282
|
+
if (!disposers) return false;
|
|
283
|
+
const index = disposers.indexOf(teardown);
|
|
284
|
+
if (index === -1) return false;
|
|
285
|
+
disposers.splice(index, 1);
|
|
286
|
+
if (DEV) activeBindingCount--;
|
|
287
|
+
if (disposers.length === 0) elementDisposers.delete(node);
|
|
288
|
+
return true;
|
|
217
289
|
}
|
|
218
290
|
function dispose(node) {
|
|
219
291
|
const stack = [node];
|
|
@@ -227,33 +299,34 @@ function dispose(node) {
|
|
|
227
299
|
}
|
|
228
300
|
}
|
|
229
301
|
for (let i = order.length - 1; i >= 0; i--) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
302
|
+
disposeNodeOwn(order[i]);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function disposeNodeOwn(node) {
|
|
306
|
+
if (!elementDisposers.has(node)) return;
|
|
307
|
+
let executed = 0;
|
|
308
|
+
let runaway = false;
|
|
309
|
+
while (!runaway) {
|
|
310
|
+
const pending = elementDisposers.get(node);
|
|
311
|
+
if (!pending || pending.length === 0) break;
|
|
312
|
+
const snapshot = pending.slice();
|
|
313
|
+
elementDisposers.delete(node);
|
|
314
|
+
if (DEV) activeBindingCount -= snapshot.length;
|
|
315
|
+
for (let i = 0; i < snapshot.length; i++) {
|
|
316
|
+
if (executed >= MAX_DRAIN_TEARDOWNS) {
|
|
317
|
+
const rest = snapshot.slice(i);
|
|
318
|
+
const added = elementDisposers.get(node);
|
|
319
|
+
elementDisposers.set(node, added ? rest.concat(added) : rest);
|
|
320
|
+
if (DEV) activeBindingCount += rest.length;
|
|
321
|
+
reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
|
|
322
|
+
runaway = true;
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
executed++;
|
|
326
|
+
try {
|
|
327
|
+
snapshot[i]();
|
|
328
|
+
} catch (err) {
|
|
329
|
+
reportError(err, { phase: "cleanup", name: "disposer" });
|
|
257
330
|
}
|
|
258
331
|
}
|
|
259
332
|
}
|
|
@@ -417,8 +490,18 @@ function untracked(fn) {
|
|
|
417
490
|
var subscriberEpochCounter = 0;
|
|
418
491
|
function retrack(effectFn, subscriber) {
|
|
419
492
|
const prev = currentSubscriber;
|
|
493
|
+
let savedDepth = 0;
|
|
494
|
+
let savedSuspendSub = null;
|
|
495
|
+
if (trackingSuspended) {
|
|
496
|
+
savedDepth = suspendDepth;
|
|
497
|
+
savedSuspendSub = suspendSavedSub;
|
|
498
|
+
suspendDepth = 0;
|
|
499
|
+
suspendSavedSub = null;
|
|
500
|
+
trackingSuspended = false;
|
|
501
|
+
}
|
|
420
502
|
currentSubscriber = subscriber;
|
|
421
503
|
const sub = subscriber;
|
|
504
|
+
if (sub._cap === void 0) sub._cap = currentDisposerCapture();
|
|
422
505
|
const epoch = ++subscriberEpochCounter;
|
|
423
506
|
sub._epoch = epoch;
|
|
424
507
|
sub._structDirty = false;
|
|
@@ -431,6 +514,11 @@ function retrack(effectFn, subscriber) {
|
|
|
431
514
|
effectFn();
|
|
432
515
|
} finally {
|
|
433
516
|
currentSubscriber = prev;
|
|
517
|
+
if (savedDepth !== 0) {
|
|
518
|
+
suspendDepth = savedDepth;
|
|
519
|
+
suspendSavedSub = savedSuspendSub;
|
|
520
|
+
trackingSuspended = true;
|
|
521
|
+
}
|
|
434
522
|
let node = sub.depsHead ?? null;
|
|
435
523
|
while (node !== null) {
|
|
436
524
|
const next = node.subNext;
|
|
@@ -450,11 +538,25 @@ function track(effectFn, subscriber) {
|
|
|
450
538
|
if (!subscriber) return reactiveBinding(effectFn);
|
|
451
539
|
cleanup(subscriber);
|
|
452
540
|
const prev = currentSubscriber;
|
|
541
|
+
let savedDepth = 0;
|
|
542
|
+
let savedSuspendSub = null;
|
|
543
|
+
if (trackingSuspended) {
|
|
544
|
+
savedDepth = suspendDepth;
|
|
545
|
+
savedSuspendSub = suspendSavedSub;
|
|
546
|
+
suspendDepth = 0;
|
|
547
|
+
suspendSavedSub = null;
|
|
548
|
+
trackingSuspended = false;
|
|
549
|
+
}
|
|
453
550
|
currentSubscriber = subscriber;
|
|
454
551
|
try {
|
|
455
552
|
effectFn();
|
|
456
553
|
} finally {
|
|
457
554
|
currentSubscriber = prev;
|
|
555
|
+
if (savedDepth !== 0) {
|
|
556
|
+
suspendDepth = savedDepth;
|
|
557
|
+
suspendSavedSub = savedSuspendSub;
|
|
558
|
+
trackingSuspended = true;
|
|
559
|
+
}
|
|
458
560
|
const sub2 = subscriber;
|
|
459
561
|
for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
|
|
460
562
|
const sig = n.sig;
|
|
@@ -591,7 +693,12 @@ function drainQueue() {
|
|
|
591
693
|
quarantined.add(sub);
|
|
592
694
|
continue;
|
|
593
695
|
}
|
|
594
|
-
|
|
696
|
+
const pushed = beginDisposerCapture(sub._cap ?? null);
|
|
697
|
+
try {
|
|
698
|
+
safeInvoke(sub);
|
|
699
|
+
} finally {
|
|
700
|
+
if (pushed) endDisposerCapture();
|
|
701
|
+
}
|
|
595
702
|
}
|
|
596
703
|
}
|
|
597
704
|
function drainNotificationQueue() {
|
|
@@ -625,8 +732,9 @@ function propagateDirty(sub) {
|
|
|
625
732
|
if (s._c) {
|
|
626
733
|
const nSig = s._sig;
|
|
627
734
|
if (nSig) {
|
|
628
|
-
if (!nSig._d) {
|
|
735
|
+
if (!nSig._d || nSig._f === true) {
|
|
629
736
|
nSig._d = true;
|
|
737
|
+
nSig._f = false;
|
|
630
738
|
stack.push(nSig);
|
|
631
739
|
}
|
|
632
740
|
} else {
|
|
@@ -726,7 +834,7 @@ function forEachSubscriber(signal2, visit) {
|
|
|
726
834
|
}
|
|
727
835
|
|
|
728
836
|
// src/reactivity/track.ts
|
|
729
|
-
var _runtimeVersion = true ? "4.
|
|
837
|
+
var _runtimeVersion = true ? "4.6.0" : "dev";
|
|
730
838
|
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
731
839
|
function resolveReactiveApi() {
|
|
732
840
|
const g = globalThis;
|
|
@@ -790,10 +898,14 @@ function derived(getter, options) {
|
|
|
790
898
|
const cs = {};
|
|
791
899
|
cs._d = false;
|
|
792
900
|
cs._init = false;
|
|
901
|
+
cs._f = false;
|
|
793
902
|
cs._g = getter;
|
|
794
903
|
cs.__v = 0;
|
|
904
|
+
let evaluating = false;
|
|
905
|
+
let disposed = false;
|
|
906
|
+
let pendingError;
|
|
795
907
|
const markDirty = () => {
|
|
796
|
-
if (cs._d) return;
|
|
908
|
+
if (cs._d || disposed) return;
|
|
797
909
|
cs._d = true;
|
|
798
910
|
};
|
|
799
911
|
markDirty._c = 1;
|
|
@@ -816,33 +928,53 @@ function derived(getter, options) {
|
|
|
816
928
|
}
|
|
817
929
|
}, markDirty);
|
|
818
930
|
const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
819
|
-
let evaluating = false;
|
|
820
931
|
const validate = () => {
|
|
821
|
-
if (!cs._d) return;
|
|
932
|
+
if (!cs._d || disposed || pendingError !== void 0) return;
|
|
822
933
|
const oldValue = cs._v;
|
|
823
934
|
evaluating = true;
|
|
824
935
|
try {
|
|
825
936
|
retrack2(recompute, markDirty);
|
|
826
937
|
if (!Object.is(oldValue, cs._v)) cs.__v++;
|
|
938
|
+
} catch (err) {
|
|
939
|
+
pendingError = { error: err };
|
|
940
|
+
cs.__v++;
|
|
941
|
+
if (disposed) cs._d = true;
|
|
942
|
+
else cs._f = true;
|
|
827
943
|
} finally {
|
|
828
944
|
evaluating = false;
|
|
945
|
+
if (disposed) cleanup2(markDirty);
|
|
829
946
|
}
|
|
830
|
-
if (hook && !Object.is(oldValue, cs._v)) {
|
|
947
|
+
if (hook && !disposed && !Object.is(oldValue, cs._v)) {
|
|
831
948
|
hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
|
|
832
949
|
}
|
|
833
950
|
};
|
|
834
951
|
cs._validate = validate;
|
|
952
|
+
const throwPending = () => {
|
|
953
|
+
const { error } = pendingError;
|
|
954
|
+
pendingError = void 0;
|
|
955
|
+
if (disposed) cs._d = false;
|
|
956
|
+
throw error;
|
|
957
|
+
};
|
|
835
958
|
function computedGetter() {
|
|
836
959
|
if (evaluating) {
|
|
837
960
|
throw new Error(
|
|
838
961
|
`[SibuJS] Circular dependency detected in derived${debugName ? ` "${debugName}"` : ""}. A derived signal cannot read itself (directly or through a chain).`
|
|
839
962
|
);
|
|
840
963
|
}
|
|
841
|
-
if (isTrackingSuspended2()) {
|
|
842
|
-
if (cs._d)
|
|
964
|
+
if (isTrackingSuspended2() || disposed) {
|
|
965
|
+
if (cs._d) {
|
|
966
|
+
validate();
|
|
967
|
+
if (pendingError !== void 0) throwPending();
|
|
968
|
+
}
|
|
843
969
|
return cs._v;
|
|
844
970
|
}
|
|
845
|
-
if (cs._d)
|
|
971
|
+
if (cs._d) {
|
|
972
|
+
validate();
|
|
973
|
+
if (pendingError !== void 0) {
|
|
974
|
+
recordDependency2(cs);
|
|
975
|
+
throwPending();
|
|
976
|
+
}
|
|
977
|
+
}
|
|
846
978
|
recordDependency2(cs);
|
|
847
979
|
return cs._v;
|
|
848
980
|
}
|
|
@@ -851,171 +983,21 @@ function derived(getter, options) {
|
|
|
851
983
|
cs.__name = debugName;
|
|
852
984
|
}
|
|
853
985
|
computedGetter.__signal = cs;
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
let mod = null;
|
|
865
|
-
const getBuiltin = process.getBuiltinModule;
|
|
866
|
-
if (typeof getBuiltin === "function") {
|
|
867
|
-
mod = getBuiltin("node:async_hooks");
|
|
868
|
-
} else if (typeof require === "function") {
|
|
869
|
-
mod = require("async_hooks");
|
|
986
|
+
computedGetter.dispose = () => {
|
|
987
|
+
if (disposed) return;
|
|
988
|
+
disposed = true;
|
|
989
|
+
cs._d = pendingError !== void 0;
|
|
990
|
+
cleanup2(markDirty);
|
|
991
|
+
const h = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
992
|
+
if (h) {
|
|
993
|
+
try {
|
|
994
|
+
h.emit("computed:destroy", { signal: cs, getter: computedGetter });
|
|
995
|
+
} catch {
|
|
870
996
|
}
|
|
871
|
-
if (mod) detected = new mod.AsyncLocalStorage();
|
|
872
|
-
}
|
|
873
|
-
} catch {
|
|
874
|
-
detected = null;
|
|
875
|
-
}
|
|
876
|
-
return {
|
|
877
|
-
als: detected,
|
|
878
|
-
fallbackStore: { ssr: false, suspenseIdCounter: 0, locale: void 0 },
|
|
879
|
-
fallbackDepth: 0
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
|
|
883
|
-
var als = _shared.als;
|
|
884
|
-
var fallbackStore = _shared.fallbackStore;
|
|
885
|
-
_shared.fallbackDepth ?? (_shared.fallbackDepth = 0);
|
|
886
|
-
function getSSRStore() {
|
|
887
|
-
if (als) {
|
|
888
|
-
const s = als.getStore();
|
|
889
|
-
if (s) return s;
|
|
890
|
-
}
|
|
891
|
-
return fallbackStore;
|
|
892
|
-
}
|
|
893
|
-
function isSSR() {
|
|
894
|
-
return getSSRStore().ssr;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
// src/core/signals/effect.ts
|
|
898
|
-
var _g = globalThis;
|
|
899
|
-
var MAX_RERUNS = 100;
|
|
900
|
-
function flushUserCleanups(ctx) {
|
|
901
|
-
const list = ctx.userCleanups;
|
|
902
|
-
if (list.length === 0) return;
|
|
903
|
-
ctx.userCleanups = [];
|
|
904
|
-
for (let i = list.length - 1; i >= 0; i--) {
|
|
905
|
-
try {
|
|
906
|
-
list[i]();
|
|
907
|
-
} catch (err) {
|
|
908
|
-
reportError(err, { phase: "cleanup" });
|
|
909
997
|
}
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
function drainReruns(ctx) {
|
|
913
|
-
let reruns = 1;
|
|
914
|
-
do {
|
|
915
|
-
ctx.rerunPending = false;
|
|
916
|
-
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
917
|
-
retrack2(ctx.bodyFn, ctx.subscriber);
|
|
918
|
-
} while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
|
|
919
|
-
if (ctx.rerunPending) {
|
|
920
|
-
ctx.rerunPending = false;
|
|
921
|
-
reportError(
|
|
922
|
-
new Error(
|
|
923
|
-
`effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
|
|
924
|
-
),
|
|
925
|
-
{ phase: "effect" }
|
|
926
|
-
);
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
function disposeEffect(ctx) {
|
|
930
|
-
if (ctx.disposed) return;
|
|
931
|
-
ctx.disposed = true;
|
|
932
|
-
const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
933
|
-
if (h) {
|
|
934
|
-
try {
|
|
935
|
-
h.emit("effect:destroy", { effectFn: ctx.fn });
|
|
936
|
-
} catch {
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
try {
|
|
940
|
-
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
941
|
-
} catch (err) {
|
|
942
|
-
if (typeof console !== "undefined") {
|
|
943
|
-
console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
try {
|
|
947
|
-
cleanup2(ctx.subscriber);
|
|
948
|
-
} catch (err) {
|
|
949
|
-
if (typeof console !== "undefined") {
|
|
950
|
-
console.warn("[SibuJS effect] dispose threw:", err);
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
function effect(effectFn, options) {
|
|
955
|
-
devAssert(typeof effectFn === "function", "effect: argument must be a function.");
|
|
956
|
-
if (isSSR()) return () => {
|
|
957
998
|
};
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
onError: options?.onError,
|
|
961
|
-
userCleanups: [],
|
|
962
|
-
running: false,
|
|
963
|
-
rerunPending: false,
|
|
964
|
-
disposed: false,
|
|
965
|
-
onCleanup: null,
|
|
966
|
-
subscriber: null,
|
|
967
|
-
bodyFn: null
|
|
968
|
-
};
|
|
969
|
-
ctx.onCleanup = (fn) => {
|
|
970
|
-
ctx.userCleanups.push(fn);
|
|
971
|
-
};
|
|
972
|
-
const onErrorCaptured = ctx.onError;
|
|
973
|
-
ctx.bodyFn = onErrorCaptured ? () => {
|
|
974
|
-
try {
|
|
975
|
-
ctx.fn(ctx.onCleanup);
|
|
976
|
-
} catch (err) {
|
|
977
|
-
onErrorCaptured(err);
|
|
978
|
-
}
|
|
979
|
-
} : () => {
|
|
980
|
-
ctx.fn(ctx.onCleanup);
|
|
981
|
-
};
|
|
982
|
-
const sub = (() => {
|
|
983
|
-
if (ctx.disposed) return;
|
|
984
|
-
if (ctx.running) {
|
|
985
|
-
ctx.rerunPending = true;
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
ctx.running = true;
|
|
989
|
-
try {
|
|
990
|
-
ctx.rerunPending = false;
|
|
991
|
-
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
992
|
-
retrack2(ctx.bodyFn, sub);
|
|
993
|
-
if (ctx.rerunPending) drainReruns(ctx);
|
|
994
|
-
} finally {
|
|
995
|
-
ctx.running = false;
|
|
996
|
-
ctx.rerunPending = false;
|
|
997
|
-
}
|
|
998
|
-
});
|
|
999
|
-
sub.depsHead = null;
|
|
1000
|
-
sub.depsTail = null;
|
|
1001
|
-
sub._epoch = 0;
|
|
1002
|
-
sub._structDirty = false;
|
|
1003
|
-
sub._hasComputedDep = false;
|
|
1004
|
-
sub._errorPhase = "effect";
|
|
1005
|
-
sub._runEpoch = 0;
|
|
1006
|
-
sub._runs = 0;
|
|
1007
|
-
ctx.subscriber = sub;
|
|
1008
|
-
ctx.running = true;
|
|
1009
|
-
try {
|
|
1010
|
-
retrack2(ctx.bodyFn, ctx.subscriber);
|
|
1011
|
-
if (ctx.rerunPending) drainReruns(ctx);
|
|
1012
|
-
} finally {
|
|
1013
|
-
ctx.running = false;
|
|
1014
|
-
ctx.rerunPending = false;
|
|
1015
|
-
}
|
|
1016
|
-
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1017
|
-
if (hook) hook.emit("effect:create", { effectFn });
|
|
1018
|
-
return () => disposeEffect(ctx);
|
|
999
|
+
if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
|
|
1000
|
+
return computedGetter;
|
|
1019
1001
|
}
|
|
1020
1002
|
|
|
1021
1003
|
// src/reactivity/batch.ts
|
|
@@ -1069,7 +1051,7 @@ var enqueueBatchedSignal = API2.enqueueBatchedSignal;
|
|
|
1069
1051
|
var isBatching = API2.isBatching;
|
|
1070
1052
|
|
|
1071
1053
|
// src/core/signals/signal.ts
|
|
1072
|
-
var
|
|
1054
|
+
var _g = globalThis;
|
|
1073
1055
|
function signal(initial, options) {
|
|
1074
1056
|
const state2 = {
|
|
1075
1057
|
value: initial,
|
|
@@ -1100,7 +1082,7 @@ function signal(initial, options) {
|
|
|
1100
1082
|
state2.value = newValue;
|
|
1101
1083
|
state2.__v++;
|
|
1102
1084
|
if (DEV) {
|
|
1103
|
-
const hook =
|
|
1085
|
+
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1104
1086
|
if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1105
1087
|
}
|
|
1106
1088
|
if (!enqueueBatchedSignal(state2)) {
|
|
@@ -1114,7 +1096,7 @@ function signal(initial, options) {
|
|
|
1114
1096
|
if (Object.is(newValue, prev)) return;
|
|
1115
1097
|
state2.value = newValue;
|
|
1116
1098
|
state2.__v++;
|
|
1117
|
-
const hook =
|
|
1099
|
+
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1118
1100
|
if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1119
1101
|
if (!enqueueBatchedSignal(state2)) {
|
|
1120
1102
|
notifySubscribers2(state2);
|
|
@@ -1132,11 +1114,82 @@ function signal(initial, options) {
|
|
|
1132
1114
|
}
|
|
1133
1115
|
};
|
|
1134
1116
|
}
|
|
1135
|
-
if (DEV) {
|
|
1136
|
-
const hook =
|
|
1137
|
-
if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
|
|
1117
|
+
if (DEV) {
|
|
1118
|
+
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1119
|
+
if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
|
|
1120
|
+
}
|
|
1121
|
+
return [get, set];
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// src/core/ssr-context.ts
|
|
1125
|
+
var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
|
|
1126
|
+
function detectSSRShared() {
|
|
1127
|
+
let detected = null;
|
|
1128
|
+
try {
|
|
1129
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
1130
|
+
let mod = null;
|
|
1131
|
+
const getBuiltin = process.getBuiltinModule;
|
|
1132
|
+
if (typeof getBuiltin === "function") {
|
|
1133
|
+
mod = getBuiltin("node:async_hooks");
|
|
1134
|
+
} else if (typeof require === "function") {
|
|
1135
|
+
mod = require("async_hooks");
|
|
1136
|
+
}
|
|
1137
|
+
if (mod) detected = new mod.AsyncLocalStorage();
|
|
1138
|
+
}
|
|
1139
|
+
} catch {
|
|
1140
|
+
detected = null;
|
|
1141
|
+
}
|
|
1142
|
+
return {
|
|
1143
|
+
als: detected,
|
|
1144
|
+
fallbackStore: { ssr: false, suspenseIdCounter: 0, locale: void 0 },
|
|
1145
|
+
fallbackDepth: 0
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
|
|
1149
|
+
var als = _shared.als;
|
|
1150
|
+
var fallbackStore = _shared.fallbackStore;
|
|
1151
|
+
_shared.fallbackDepth ?? (_shared.fallbackDepth = 0);
|
|
1152
|
+
function getSSRStore() {
|
|
1153
|
+
if (als) {
|
|
1154
|
+
const s = als.getStore();
|
|
1155
|
+
if (s) return s;
|
|
1156
|
+
}
|
|
1157
|
+
return fallbackStore;
|
|
1158
|
+
}
|
|
1159
|
+
function getRequestStore() {
|
|
1160
|
+
if (als) return als.getStore() ?? null;
|
|
1161
|
+
return _shared.fallbackDepth > 0 ? fallbackStore : null;
|
|
1162
|
+
}
|
|
1163
|
+
function isSSR() {
|
|
1164
|
+
return getSSRStore().ssr;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// src/reactivity/domBinding.ts
|
|
1168
|
+
function domBinding(commit, ownerNode) {
|
|
1169
|
+
if (isSSR()) return () => {
|
|
1170
|
+
};
|
|
1171
|
+
return reactiveBinding2(commit, ownerNode);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
// src/utils/adoptThenable.ts
|
|
1175
|
+
function adoptThenable(value) {
|
|
1176
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return null;
|
|
1177
|
+
let then;
|
|
1178
|
+
try {
|
|
1179
|
+
then = value.then;
|
|
1180
|
+
} catch (error) {
|
|
1181
|
+
return Promise.reject(error);
|
|
1138
1182
|
}
|
|
1139
|
-
return
|
|
1183
|
+
if (typeof then !== "function") return null;
|
|
1184
|
+
return new Promise((resolve, reject) => {
|
|
1185
|
+
queueMicrotask(() => {
|
|
1186
|
+
try {
|
|
1187
|
+
Reflect.apply(then, value, [resolve, reject]);
|
|
1188
|
+
} catch (error) {
|
|
1189
|
+
reject(error);
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
});
|
|
1140
1193
|
}
|
|
1141
1194
|
|
|
1142
1195
|
// src/ui/form.ts
|
|
@@ -1166,10 +1219,14 @@ function maxLength(max2, message) {
|
|
|
1166
1219
|
}
|
|
1167
1220
|
function matchesPattern(regex, message = "Invalid format") {
|
|
1168
1221
|
return (value) => {
|
|
1169
|
-
if (
|
|
1170
|
-
|
|
1222
|
+
if (!value) return null;
|
|
1223
|
+
const savedLastIndex = regex.lastIndex;
|
|
1224
|
+
regex.lastIndex = 0;
|
|
1225
|
+
try {
|
|
1226
|
+
return regex.test(value) ? null : message;
|
|
1227
|
+
} finally {
|
|
1228
|
+
regex.lastIndex = savedLastIndex;
|
|
1171
1229
|
}
|
|
1172
|
-
return null;
|
|
1173
1230
|
};
|
|
1174
1231
|
}
|
|
1175
1232
|
function email(message = "Invalid email address") {
|
|
@@ -1218,13 +1275,13 @@ function bindField(field, extras) {
|
|
|
1218
1275
|
const mergedOn = extraOn && typeof extraOn === "object" ? { ...extraOn, ...fieldOn } : fieldOn;
|
|
1219
1276
|
const onElement = (el) => {
|
|
1220
1277
|
if (el instanceof HTMLSelectElement && el.multiple) {
|
|
1221
|
-
const stop =
|
|
1278
|
+
const stop = domBinding(() => {
|
|
1222
1279
|
const v = field.value();
|
|
1223
1280
|
const selected = Array.isArray(v) ? v.map(String) : [];
|
|
1224
1281
|
for (const opt of Array.from(el.options)) {
|
|
1225
1282
|
opt.selected = selected.includes(opt.value);
|
|
1226
1283
|
}
|
|
1227
|
-
});
|
|
1284
|
+
}, el);
|
|
1228
1285
|
registerDisposer(el, stop);
|
|
1229
1286
|
}
|
|
1230
1287
|
if (typeof extraOnElement === "function") {
|
|
@@ -1244,6 +1301,7 @@ function form(config) {
|
|
|
1244
1301
|
const fieldEntries = Object.entries(config);
|
|
1245
1302
|
const fieldMap = {};
|
|
1246
1303
|
const [manualErrors, setManualErrors] = signal({});
|
|
1304
|
+
const fieldErrors = [];
|
|
1247
1305
|
for (const [name, cfg] of fieldEntries) {
|
|
1248
1306
|
const [value, setValue] = signal(cfg.initial);
|
|
1249
1307
|
const [isTouched, setTouched] = signal(false);
|
|
@@ -1258,6 +1316,7 @@ function form(config) {
|
|
|
1258
1316
|
}
|
|
1259
1317
|
return null;
|
|
1260
1318
|
});
|
|
1319
|
+
fieldErrors.push(error);
|
|
1261
1320
|
const wrappedSet = (next) => {
|
|
1262
1321
|
setValue(next);
|
|
1263
1322
|
setManualErrors((prev) => {
|
|
@@ -1334,12 +1393,22 @@ function form(config) {
|
|
|
1334
1393
|
field.touch();
|
|
1335
1394
|
}
|
|
1336
1395
|
if (isValid()) {
|
|
1337
|
-
const
|
|
1338
|
-
|
|
1396
|
+
const report = (error) => reportError(error, { phase: "async", name: "form.handleSubmit" });
|
|
1397
|
+
let pending;
|
|
1398
|
+
try {
|
|
1399
|
+
pending = adoptThenable(onSubmit(values()));
|
|
1400
|
+
} catch (error) {
|
|
1401
|
+
report(error);
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
if (pending) {
|
|
1339
1405
|
setSubmitting(true);
|
|
1340
|
-
|
|
1406
|
+
pending.then(
|
|
1341
1407
|
() => setSubmitting(false),
|
|
1342
|
-
() =>
|
|
1408
|
+
(error) => {
|
|
1409
|
+
setSubmitting(false);
|
|
1410
|
+
report(error);
|
|
1411
|
+
}
|
|
1343
1412
|
);
|
|
1344
1413
|
}
|
|
1345
1414
|
}
|
|
@@ -1353,6 +1422,17 @@ function form(config) {
|
|
|
1353
1422
|
function setError(field, message) {
|
|
1354
1423
|
setManualErrors((prev) => ({ ...prev, [field]: message }));
|
|
1355
1424
|
}
|
|
1425
|
+
let disposed = false;
|
|
1426
|
+
function dispose2() {
|
|
1427
|
+
if (disposed) return;
|
|
1428
|
+
disposed = true;
|
|
1429
|
+
errors.dispose();
|
|
1430
|
+
isValid.dispose();
|
|
1431
|
+
isDirty.dispose();
|
|
1432
|
+
touchedState.dispose();
|
|
1433
|
+
values.dispose();
|
|
1434
|
+
for (const error of fieldErrors) error.dispose();
|
|
1435
|
+
}
|
|
1356
1436
|
return {
|
|
1357
1437
|
fields: fieldMap,
|
|
1358
1438
|
errors,
|
|
@@ -1363,7 +1443,8 @@ function form(config) {
|
|
|
1363
1443
|
values,
|
|
1364
1444
|
handleSubmit,
|
|
1365
1445
|
reset,
|
|
1366
|
-
setError
|
|
1446
|
+
setError,
|
|
1447
|
+
dispose: dispose2
|
|
1367
1448
|
};
|
|
1368
1449
|
}
|
|
1369
1450
|
|
|
@@ -1442,7 +1523,7 @@ function VirtualList(props) {
|
|
|
1442
1523
|
content.appendChild(itemEl);
|
|
1443
1524
|
}
|
|
1444
1525
|
};
|
|
1445
|
-
registerDisposer(container,
|
|
1526
|
+
registerDisposer(container, domBinding(update, container));
|
|
1446
1527
|
return container;
|
|
1447
1528
|
}
|
|
1448
1529
|
|
|
@@ -1452,11 +1533,14 @@ function intersection(options) {
|
|
|
1452
1533
|
const [ratio, setRatio] = signal(0);
|
|
1453
1534
|
let observer = null;
|
|
1454
1535
|
let currentElement = null;
|
|
1536
|
+
let generation = 0;
|
|
1455
1537
|
function observe(element) {
|
|
1456
1538
|
if (typeof IntersectionObserver === "undefined") return;
|
|
1457
1539
|
unobserve();
|
|
1458
1540
|
currentElement = element;
|
|
1541
|
+
const observation = ++generation;
|
|
1459
1542
|
observer = new IntersectionObserver((entries) => {
|
|
1543
|
+
if (observation !== generation) return;
|
|
1460
1544
|
const entry = entries[0];
|
|
1461
1545
|
if (entry) {
|
|
1462
1546
|
setIsIntersecting(entry.isIntersecting);
|
|
@@ -1466,8 +1550,10 @@ function intersection(options) {
|
|
|
1466
1550
|
observer.observe(element);
|
|
1467
1551
|
}
|
|
1468
1552
|
function unobserve() {
|
|
1553
|
+
generation++;
|
|
1469
1554
|
if (observer) {
|
|
1470
1555
|
if (currentElement) observer.unobserve(currentElement);
|
|
1556
|
+
observer.takeRecords();
|
|
1471
1557
|
observer.disconnect();
|
|
1472
1558
|
observer = null;
|
|
1473
1559
|
currentElement = null;
|
|
@@ -1486,17 +1572,26 @@ function lazyLoad(element, loader, options) {
|
|
|
1486
1572
|
return () => {
|
|
1487
1573
|
};
|
|
1488
1574
|
}
|
|
1575
|
+
let done = false;
|
|
1576
|
+
const stop = () => {
|
|
1577
|
+
done = true;
|
|
1578
|
+
observer.takeRecords();
|
|
1579
|
+
observer.disconnect();
|
|
1580
|
+
};
|
|
1489
1581
|
const observer = new IntersectionObserver((entries) => {
|
|
1582
|
+
if (done) return;
|
|
1490
1583
|
for (const entry of entries) {
|
|
1491
1584
|
if (entry.isIntersecting) {
|
|
1585
|
+
stop();
|
|
1492
1586
|
loader();
|
|
1493
|
-
observer.disconnect();
|
|
1494
1587
|
break;
|
|
1495
1588
|
}
|
|
1496
1589
|
}
|
|
1497
1590
|
}, options);
|
|
1498
1591
|
observer.observe(element);
|
|
1499
|
-
return () =>
|
|
1592
|
+
return () => {
|
|
1593
|
+
if (!done) stop();
|
|
1594
|
+
};
|
|
1500
1595
|
}
|
|
1501
1596
|
|
|
1502
1597
|
// src/ui/inputMask.ts
|
|
@@ -1651,6 +1746,7 @@ function focus() {
|
|
|
1651
1746
|
const dispose2 = () => {
|
|
1652
1747
|
if (disposed) return;
|
|
1653
1748
|
disposed = true;
|
|
1749
|
+
unregisterDisposer(element, dispose2);
|
|
1654
1750
|
element.removeEventListener("focus", onFocus);
|
|
1655
1751
|
element.removeEventListener("blur", onBlur);
|
|
1656
1752
|
if (currentElement === element) currentElement = null;
|
|
@@ -1719,14 +1815,19 @@ function FocusTrap(nodes, options = {}) {
|
|
|
1719
1815
|
}
|
|
1720
1816
|
};
|
|
1721
1817
|
container.addEventListener("keydown", onTrapKeydown);
|
|
1818
|
+
let disposed = false;
|
|
1722
1819
|
if (options.autoFocus !== false) {
|
|
1723
1820
|
queueMicrotask(() => {
|
|
1821
|
+
if (disposed || !container.isConnected) return;
|
|
1724
1822
|
const first = getFocusable()[0];
|
|
1725
1823
|
first?.focus();
|
|
1726
1824
|
});
|
|
1727
1825
|
}
|
|
1728
1826
|
let trapObserver = null;
|
|
1729
1827
|
function restoreFocusAndCleanup() {
|
|
1828
|
+
if (disposed) return;
|
|
1829
|
+
disposed = true;
|
|
1830
|
+
unregisterDisposer(container, restoreFocusAndCleanup);
|
|
1730
1831
|
if (options.restoreFocus !== false) previouslyFocused?.focus();
|
|
1731
1832
|
container.removeEventListener("keydown", onTrapKeydown);
|
|
1732
1833
|
if (trapObserver) {
|
|
@@ -1741,29 +1842,45 @@ function FocusTrap(nodes, options = {}) {
|
|
|
1741
1842
|
}
|
|
1742
1843
|
});
|
|
1743
1844
|
queueMicrotask(() => {
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
}
|
|
1845
|
+
const observer = trapObserver;
|
|
1846
|
+
if (disposed || !container.isConnected || !observer) return;
|
|
1847
|
+
observer.observe(container, { childList: true, subtree: true });
|
|
1747
1848
|
});
|
|
1748
1849
|
}
|
|
1749
1850
|
registerDisposer(container, restoreFocusAndCleanup);
|
|
1750
1851
|
return container;
|
|
1751
1852
|
}
|
|
1853
|
+
function isApplePlatform() {
|
|
1854
|
+
if (typeof navigator === "undefined") return false;
|
|
1855
|
+
const nav = navigator;
|
|
1856
|
+
return /mac|iphone|ipad|ipod/i.test(nav.userAgentData?.platform || nav.platform || "");
|
|
1857
|
+
}
|
|
1752
1858
|
function hotkey(combo, handler, options = {}) {
|
|
1753
1859
|
let key = combo;
|
|
1754
1860
|
let needCtrl = options.ctrl ?? false;
|
|
1755
1861
|
let needShift = options.shift ?? false;
|
|
1756
1862
|
let needAlt = options.alt ?? false;
|
|
1757
1863
|
let needMeta = options.meta ?? false;
|
|
1758
|
-
if (combo
|
|
1759
|
-
|
|
1864
|
+
if (combo === "+") {
|
|
1865
|
+
key = "+";
|
|
1866
|
+
} else if (combo.includes("+")) {
|
|
1867
|
+
const literalPlus = combo.endsWith("++");
|
|
1868
|
+
const parts = (literalPlus ? combo.slice(0, -2) : combo).toLowerCase().split("+");
|
|
1869
|
+
if (literalPlus) parts.push("+");
|
|
1760
1870
|
key = parts[parts.length - 1];
|
|
1871
|
+
if (key === "") throw new Error(`hotkey("${combo}"): missing key`);
|
|
1761
1872
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
1762
1873
|
const mod = parts[i];
|
|
1763
1874
|
if (mod === "ctrl" || mod === "control") needCtrl = true;
|
|
1764
1875
|
else if (mod === "shift") needShift = true;
|
|
1765
|
-
else if (mod === "alt") needAlt = true;
|
|
1876
|
+
else if (mod === "alt" || mod === "option") needAlt = true;
|
|
1766
1877
|
else if (mod === "meta" || mod === "cmd" || mod === "command") needMeta = true;
|
|
1878
|
+
else if (mod === "mod") {
|
|
1879
|
+
if (isApplePlatform()) needMeta = true;
|
|
1880
|
+
else needCtrl = true;
|
|
1881
|
+
} else {
|
|
1882
|
+
throw new Error(`hotkey("${combo}"): unknown modifier "${mod}"`);
|
|
1883
|
+
}
|
|
1767
1884
|
}
|
|
1768
1885
|
}
|
|
1769
1886
|
const listener = (e) => {
|
|
@@ -1829,6 +1946,11 @@ function announce(message, priority = "polite") {
|
|
|
1829
1946
|
var COUNTER_KEY = /* @__PURE__ */ Symbol.for("sibujs.createId.v1");
|
|
1830
1947
|
var _counter = globalThis[COUNTER_KEY] ?? (globalThis[COUNTER_KEY] = { n: 0 });
|
|
1831
1948
|
function createId(prefix = "sibu") {
|
|
1949
|
+
const request = getRequestStore();
|
|
1950
|
+
if (request) {
|
|
1951
|
+
request.idCounter = (request.idCounter ?? 0) + 1;
|
|
1952
|
+
return `${prefix}-${request.idCounter}`;
|
|
1953
|
+
}
|
|
1832
1954
|
_counter.n++;
|
|
1833
1955
|
return `${prefix}-${_counter.n}`;
|
|
1834
1956
|
}
|
|
@@ -1888,22 +2010,43 @@ function createListbox(container, options = {}) {
|
|
|
1888
2010
|
if (multiple) container.setAttribute("aria-multiselectable", "true");
|
|
1889
2011
|
if (!container.hasAttribute("tabindex")) container.setAttribute("tabindex", "0");
|
|
1890
2012
|
const [activeValue, setActiveValue] = signal(null);
|
|
2013
|
+
const [selectedValues, setSelectedValues] = signal([]);
|
|
1891
2014
|
const [selectedValue, setSelectedValue] = signal(null);
|
|
1892
2015
|
const [activeDescendantId, setActiveDescendantId] = signal(null);
|
|
1893
|
-
|
|
2016
|
+
let activeValueRef = null;
|
|
2017
|
+
let currentSelection = [];
|
|
2018
|
+
function reconcileOptions() {
|
|
1894
2019
|
const opts = Array.from(container.querySelectorAll(optionSelector));
|
|
2020
|
+
const selectedSet = new Set(currentSelection);
|
|
2021
|
+
let activeStillPresent = false;
|
|
2022
|
+
const active = activeValueRef;
|
|
1895
2023
|
for (const opt of opts) {
|
|
1896
2024
|
if (!opt.id) opt.id = createId("listbox-option");
|
|
1897
|
-
|
|
2025
|
+
const ov = opt.dataset.value;
|
|
2026
|
+
if (!opt.hasAttribute("aria-selected")) {
|
|
2027
|
+
opt.setAttribute("aria-selected", ov !== void 0 && selectedSet.has(ov) ? "true" : "false");
|
|
2028
|
+
}
|
|
2029
|
+
if (active !== null && ov === active) activeStillPresent = true;
|
|
1898
2030
|
}
|
|
2031
|
+
if (active !== null && !activeStillPresent) setActive(null, opts);
|
|
2032
|
+
return opts;
|
|
1899
2033
|
}
|
|
1900
|
-
stampIds();
|
|
1901
2034
|
function getOptions() {
|
|
1902
|
-
return
|
|
2035
|
+
return reconcileOptions();
|
|
2036
|
+
}
|
|
2037
|
+
function isDisabled(opt) {
|
|
2038
|
+
return opt.getAttribute("aria-disabled") === "true" || opt.hasAttribute("disabled");
|
|
2039
|
+
}
|
|
2040
|
+
function enabledOptions() {
|
|
2041
|
+
return getOptions().filter((opt) => !isDisabled(opt) && opt.dataset.value !== void 0);
|
|
2042
|
+
}
|
|
2043
|
+
function optionByValue(value) {
|
|
2044
|
+
return getOptions().find((opt) => opt.dataset.value === value);
|
|
1903
2045
|
}
|
|
1904
|
-
function setActive(value) {
|
|
2046
|
+
function setActive(value, known) {
|
|
2047
|
+
activeValueRef = value;
|
|
1905
2048
|
setActiveValue(value);
|
|
1906
|
-
const opts = getOptions();
|
|
2049
|
+
const opts = known ?? getOptions();
|
|
1907
2050
|
for (const opt of opts) {
|
|
1908
2051
|
if (opt.dataset.value === value) {
|
|
1909
2052
|
opt.setAttribute("data-highlighted", "");
|
|
@@ -1919,29 +2062,33 @@ function createListbox(container, options = {}) {
|
|
|
1919
2062
|
}
|
|
1920
2063
|
}
|
|
1921
2064
|
function select(value) {
|
|
1922
|
-
const
|
|
1923
|
-
|
|
2065
|
+
const target = optionByValue(value);
|
|
2066
|
+
if (!target || isDisabled(target)) return;
|
|
2067
|
+
const previous = selectedValues();
|
|
2068
|
+
let next;
|
|
1924
2069
|
if (multiple) {
|
|
1925
|
-
|
|
1926
|
-
if (nextSelectedSet.has(value)) nextSelectedSet.delete(value);
|
|
1927
|
-
else nextSelectedSet.add(value);
|
|
1928
|
-
setSelectedValue(Array.from(nextSelectedSet).join(","));
|
|
2070
|
+
next = previous.includes(value) ? previous.filter((v) => v !== value) : [...previous, value];
|
|
1929
2071
|
} else {
|
|
1930
|
-
|
|
1931
|
-
setSelectedValue(value);
|
|
2072
|
+
next = [value];
|
|
1932
2073
|
}
|
|
2074
|
+
currentSelection = next;
|
|
2075
|
+
batch(() => {
|
|
2076
|
+
setSelectedValues(next);
|
|
2077
|
+
setSelectedValue(multiple ? next.join(",") : value);
|
|
2078
|
+
});
|
|
1933
2079
|
options.onSelect?.(value);
|
|
2080
|
+
const nextSelectedSet = new Set(next);
|
|
1934
2081
|
const opts = getOptions();
|
|
1935
2082
|
for (const opt of opts) {
|
|
1936
|
-
const ov = opt.dataset.value
|
|
1937
|
-
opt.setAttribute("aria-selected", nextSelectedSet.has(ov) ? "true" : "false");
|
|
2083
|
+
const ov = opt.dataset.value;
|
|
2084
|
+
opt.setAttribute("aria-selected", ov !== void 0 && nextSelectedSet.has(ov) ? "true" : "false");
|
|
1938
2085
|
}
|
|
1939
2086
|
}
|
|
1940
2087
|
function moveActive(delta) {
|
|
1941
|
-
const opts =
|
|
2088
|
+
const opts = enabledOptions();
|
|
1942
2089
|
if (opts.length === 0) return;
|
|
1943
|
-
const currentIdx = opts.findIndex((o) => o.dataset.value ===
|
|
1944
|
-
let next = currentIdx + delta;
|
|
2090
|
+
const currentIdx = opts.findIndex((o) => o.dataset.value === activeValueRef);
|
|
2091
|
+
let next = currentIdx === -1 ? delta > 0 ? 0 : opts.length - 1 : currentIdx + delta;
|
|
1945
2092
|
if (next < 0) next = opts.length - 1;
|
|
1946
2093
|
if (next >= opts.length) next = 0;
|
|
1947
2094
|
const nextValue = opts[next].dataset.value ?? null;
|
|
@@ -1962,13 +2109,13 @@ function createListbox(container, options = {}) {
|
|
|
1962
2109
|
break;
|
|
1963
2110
|
case "Home": {
|
|
1964
2111
|
e.preventDefault();
|
|
1965
|
-
const opts =
|
|
2112
|
+
const opts = enabledOptions();
|
|
1966
2113
|
if (opts.length > 0) setActive(opts[0].dataset.value ?? null);
|
|
1967
2114
|
break;
|
|
1968
2115
|
}
|
|
1969
2116
|
case "End": {
|
|
1970
2117
|
e.preventDefault();
|
|
1971
|
-
const opts =
|
|
2118
|
+
const opts = enabledOptions();
|
|
1972
2119
|
if (opts.length > 0) setActive(opts[opts.length - 1].dataset.value ?? null);
|
|
1973
2120
|
break;
|
|
1974
2121
|
}
|
|
@@ -1983,7 +2130,7 @@ function createListbox(container, options = {}) {
|
|
|
1983
2130
|
}
|
|
1984
2131
|
function onClick(e) {
|
|
1985
2132
|
const target = e.target.closest(optionSelector);
|
|
1986
|
-
if (!target || !container.contains(target)) return;
|
|
2133
|
+
if (!target || !container.contains(target) || isDisabled(target)) return;
|
|
1987
2134
|
const value = target.dataset.value ?? null;
|
|
1988
2135
|
if (value !== null) {
|
|
1989
2136
|
setActive(value);
|
|
@@ -1992,12 +2139,26 @@ function createListbox(container, options = {}) {
|
|
|
1992
2139
|
}
|
|
1993
2140
|
container.addEventListener("keydown", onKeyDown);
|
|
1994
2141
|
container.addEventListener("click", onClick);
|
|
2142
|
+
reconcileOptions();
|
|
2143
|
+
let observer = null;
|
|
2144
|
+
if (typeof MutationObserver !== "undefined") {
|
|
2145
|
+
observer = new MutationObserver(() => {
|
|
2146
|
+
if (!disposed) reconcileOptions();
|
|
2147
|
+
});
|
|
2148
|
+
observer.observe(container, { childList: true, subtree: true });
|
|
2149
|
+
}
|
|
2150
|
+
let disposed = false;
|
|
1995
2151
|
function dispose2() {
|
|
2152
|
+
if (disposed) return;
|
|
2153
|
+
disposed = true;
|
|
2154
|
+
unregisterDisposer(container, dispose2);
|
|
2155
|
+
observer?.disconnect();
|
|
2156
|
+
observer = null;
|
|
1996
2157
|
container.removeEventListener("keydown", onKeyDown);
|
|
1997
2158
|
container.removeEventListener("click", onClick);
|
|
1998
2159
|
}
|
|
1999
2160
|
registerDisposer(container, dispose2);
|
|
2000
|
-
return { activeValue, selectedValue, activeDescendantId, dispose: dispose2 };
|
|
2161
|
+
return { activeValue, selectedValues, selectedValue, activeDescendantId, refresh: reconcileOptions, dispose: dispose2 };
|
|
2001
2162
|
}
|
|
2002
2163
|
function createDialogAria(element, options = {}) {
|
|
2003
2164
|
const titleId = options.labelledBy ?? createId("dialog-title");
|
|
@@ -2488,6 +2649,14 @@ function setSafeAttribute(el, name, value, options = {}) {
|
|
|
2488
2649
|
const html = isHtmlElement(el);
|
|
2489
2650
|
const idlName = html ? name.toLowerCase() : name;
|
|
2490
2651
|
if (typeof value === "boolean") {
|
|
2652
|
+
if (name.length > 5 && name.slice(0, 5).toLowerCase() === "aria-") {
|
|
2653
|
+
const token = value ? "true" : "false";
|
|
2654
|
+
if ((ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name)) !== token) {
|
|
2655
|
+
if (ns) el.setAttributeNS(ns, name, token);
|
|
2656
|
+
else el.setAttribute(name, token);
|
|
2657
|
+
}
|
|
2658
|
+
return true;
|
|
2659
|
+
}
|
|
2491
2660
|
if (html && BOOLEAN_IDL_ATTRS.has(idlName) && idlName in el) {
|
|
2492
2661
|
setProp(el, idlName, value);
|
|
2493
2662
|
} else if (value) {
|
|
@@ -2561,13 +2730,13 @@ function bindBoolAttr(el, attr, getter) {
|
|
|
2561
2730
|
let value;
|
|
2562
2731
|
try {
|
|
2563
2732
|
value = reactiveGetter();
|
|
2564
|
-
} catch {
|
|
2733
|
+
} catch (err) {
|
|
2734
|
+
reportError(err, { phase: "binding", name: "bindBoolAttr", node: el });
|
|
2565
2735
|
return;
|
|
2566
2736
|
}
|
|
2567
2737
|
setSafeAttribute(el, attr, Boolean(value), { label: "bindBoolAttr" });
|
|
2568
2738
|
}
|
|
2569
|
-
|
|
2570
|
-
return teardown;
|
|
2739
|
+
return reactiveBinding2(commit, el);
|
|
2571
2740
|
}
|
|
2572
2741
|
function bindData(el, key, getter) {
|
|
2573
2742
|
const dataAttr = `data-${key}`;
|
|
@@ -2612,6 +2781,8 @@ function removeGlobalListenerIfIdle() {
|
|
|
2612
2781
|
function dialog() {
|
|
2613
2782
|
const [isOpen, setIsOpen] = signal(false);
|
|
2614
2783
|
const entry = { close: () => close() };
|
|
2784
|
+
let opened = false;
|
|
2785
|
+
let disposed = false;
|
|
2615
2786
|
function pushOnStack() {
|
|
2616
2787
|
if (dialogStack.indexOf(entry) !== -1) return;
|
|
2617
2788
|
dialogStack.push(entry);
|
|
@@ -2623,23 +2794,27 @@ function dialog() {
|
|
|
2623
2794
|
removeGlobalListenerIfIdle();
|
|
2624
2795
|
}
|
|
2625
2796
|
function open() {
|
|
2626
|
-
if (
|
|
2627
|
-
|
|
2797
|
+
if (disposed || opened) return;
|
|
2798
|
+
opened = true;
|
|
2628
2799
|
pushOnStack();
|
|
2800
|
+
setIsOpen(true);
|
|
2629
2801
|
}
|
|
2630
2802
|
function close() {
|
|
2631
|
-
if (!
|
|
2803
|
+
if (!opened) {
|
|
2632
2804
|
removeFromStack();
|
|
2633
2805
|
return;
|
|
2634
2806
|
}
|
|
2635
|
-
|
|
2807
|
+
opened = false;
|
|
2636
2808
|
removeFromStack();
|
|
2809
|
+
setIsOpen(false);
|
|
2637
2810
|
}
|
|
2638
2811
|
function toggle() {
|
|
2639
|
-
if (
|
|
2812
|
+
if (opened) close();
|
|
2640
2813
|
else open();
|
|
2641
2814
|
}
|
|
2642
2815
|
function dispose2() {
|
|
2816
|
+
disposed = true;
|
|
2817
|
+
opened = false;
|
|
2643
2818
|
removeFromStack();
|
|
2644
2819
|
setIsOpen(false);
|
|
2645
2820
|
}
|
|
@@ -2831,44 +3006,191 @@ function pagination(options) {
|
|
|
2831
3006
|
const clamped = Math.max(1, Math.min(target, totalPages()));
|
|
2832
3007
|
setPage(clamped);
|
|
2833
3008
|
}
|
|
2834
|
-
|
|
3009
|
+
function dispose2() {
|
|
3010
|
+
startIndex.dispose();
|
|
3011
|
+
endIndex.dispose();
|
|
3012
|
+
currentPage.dispose();
|
|
3013
|
+
totalPages.dispose();
|
|
3014
|
+
}
|
|
3015
|
+
return { page: currentPage, pageSize, totalPages, next, prev, goTo, startIndex, endIndex, dispose: dispose2 };
|
|
2835
3016
|
}
|
|
2836
3017
|
|
|
2837
3018
|
// src/ui/eventBus.ts
|
|
2838
3019
|
function eventBus() {
|
|
2839
3020
|
const listeners = /* @__PURE__ */ new Map();
|
|
2840
3021
|
function on(event, handler) {
|
|
2841
|
-
let
|
|
2842
|
-
if (!
|
|
2843
|
-
|
|
2844
|
-
listeners.set(event,
|
|
3022
|
+
let subs = listeners.get(event);
|
|
3023
|
+
if (!subs) {
|
|
3024
|
+
subs = /* @__PURE__ */ new Map();
|
|
3025
|
+
listeners.set(event, subs);
|
|
3026
|
+
}
|
|
3027
|
+
let subscription = subs.get(handler);
|
|
3028
|
+
if (!subscription) {
|
|
3029
|
+
subscription = { handler, active: true };
|
|
3030
|
+
subs.set(handler, subscription);
|
|
2845
3031
|
}
|
|
2846
|
-
|
|
2847
|
-
return () =>
|
|
3032
|
+
const own = subscription;
|
|
3033
|
+
return () => {
|
|
3034
|
+
if (listeners.get(event)?.get(handler) !== own) return;
|
|
3035
|
+
off(event, handler);
|
|
3036
|
+
};
|
|
2848
3037
|
}
|
|
2849
3038
|
function emit(event, data) {
|
|
2850
|
-
const
|
|
2851
|
-
if (
|
|
2852
|
-
|
|
2853
|
-
|
|
3039
|
+
const subs = listeners.get(event);
|
|
3040
|
+
if (!subs || subs.size === 0) return;
|
|
3041
|
+
for (const subscription of Array.from(subs.values())) {
|
|
3042
|
+
if (!subscription.active) continue;
|
|
3043
|
+
try {
|
|
3044
|
+
subscription.handler(data);
|
|
3045
|
+
} catch (err) {
|
|
3046
|
+
reportError(err, { phase: "event", name: `eventBus(${String(event)})` });
|
|
2854
3047
|
}
|
|
2855
3048
|
}
|
|
2856
3049
|
}
|
|
2857
3050
|
function off(event, handler) {
|
|
2858
|
-
const
|
|
2859
|
-
if (
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
3051
|
+
const subs = listeners.get(event);
|
|
3052
|
+
if (!subs) return;
|
|
3053
|
+
const subscription = subs.get(handler);
|
|
3054
|
+
if (!subscription) return;
|
|
3055
|
+
subscription.active = false;
|
|
3056
|
+
subs.delete(handler);
|
|
3057
|
+
if (subs.size === 0) {
|
|
3058
|
+
listeners.delete(event);
|
|
2864
3059
|
}
|
|
2865
3060
|
}
|
|
2866
3061
|
function clear() {
|
|
3062
|
+
for (const subs of listeners.values()) {
|
|
3063
|
+
for (const subscription of subs.values()) subscription.active = false;
|
|
3064
|
+
}
|
|
2867
3065
|
listeners.clear();
|
|
2868
3066
|
}
|
|
2869
3067
|
return { on, emit, off, clear };
|
|
2870
3068
|
}
|
|
2871
3069
|
|
|
3070
|
+
// src/core/signals/effect.ts
|
|
3071
|
+
var _g2 = globalThis;
|
|
3072
|
+
var MAX_RERUNS = 100;
|
|
3073
|
+
function flushUserCleanups(ctx) {
|
|
3074
|
+
const list = ctx.userCleanups;
|
|
3075
|
+
if (list.length === 0) return;
|
|
3076
|
+
ctx.userCleanups = [];
|
|
3077
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
3078
|
+
try {
|
|
3079
|
+
list[i]();
|
|
3080
|
+
} catch (err) {
|
|
3081
|
+
reportError(err, { phase: "cleanup" });
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
function drainReruns(ctx) {
|
|
3086
|
+
let reruns = 1;
|
|
3087
|
+
do {
|
|
3088
|
+
ctx.rerunPending = false;
|
|
3089
|
+
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
3090
|
+
retrack2(ctx.bodyFn, ctx.subscriber);
|
|
3091
|
+
} while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
|
|
3092
|
+
if (ctx.rerunPending) {
|
|
3093
|
+
ctx.rerunPending = false;
|
|
3094
|
+
reportError(
|
|
3095
|
+
new Error(
|
|
3096
|
+
`effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
|
|
3097
|
+
),
|
|
3098
|
+
{ phase: "effect" }
|
|
3099
|
+
);
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
function disposeEffect(ctx) {
|
|
3103
|
+
if (ctx.disposed) return;
|
|
3104
|
+
ctx.disposed = true;
|
|
3105
|
+
const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
3106
|
+
if (h) {
|
|
3107
|
+
try {
|
|
3108
|
+
h.emit("effect:destroy", { effectFn: ctx.fn });
|
|
3109
|
+
} catch {
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
try {
|
|
3113
|
+
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
3114
|
+
} catch (err) {
|
|
3115
|
+
if (typeof console !== "undefined") {
|
|
3116
|
+
console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
try {
|
|
3120
|
+
cleanup2(ctx.subscriber);
|
|
3121
|
+
} catch (err) {
|
|
3122
|
+
if (typeof console !== "undefined") {
|
|
3123
|
+
console.warn("[SibuJS effect] dispose threw:", err);
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
function effect(effectFn, options) {
|
|
3128
|
+
devAssert(typeof effectFn === "function", "effect: argument must be a function.");
|
|
3129
|
+
if (isSSR()) return () => {
|
|
3130
|
+
};
|
|
3131
|
+
const ctx = {
|
|
3132
|
+
fn: effectFn,
|
|
3133
|
+
onError: options?.onError,
|
|
3134
|
+
userCleanups: [],
|
|
3135
|
+
running: false,
|
|
3136
|
+
rerunPending: false,
|
|
3137
|
+
disposed: false,
|
|
3138
|
+
onCleanup: null,
|
|
3139
|
+
subscriber: null,
|
|
3140
|
+
bodyFn: null
|
|
3141
|
+
};
|
|
3142
|
+
ctx.onCleanup = (fn) => {
|
|
3143
|
+
ctx.userCleanups.push(fn);
|
|
3144
|
+
};
|
|
3145
|
+
const onErrorCaptured = ctx.onError;
|
|
3146
|
+
ctx.bodyFn = onErrorCaptured ? () => {
|
|
3147
|
+
try {
|
|
3148
|
+
ctx.fn(ctx.onCleanup);
|
|
3149
|
+
} catch (err) {
|
|
3150
|
+
onErrorCaptured(err);
|
|
3151
|
+
}
|
|
3152
|
+
} : () => {
|
|
3153
|
+
ctx.fn(ctx.onCleanup);
|
|
3154
|
+
};
|
|
3155
|
+
const sub = (() => {
|
|
3156
|
+
if (ctx.disposed) return;
|
|
3157
|
+
if (ctx.running) {
|
|
3158
|
+
ctx.rerunPending = true;
|
|
3159
|
+
return;
|
|
3160
|
+
}
|
|
3161
|
+
ctx.running = true;
|
|
3162
|
+
try {
|
|
3163
|
+
ctx.rerunPending = false;
|
|
3164
|
+
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
3165
|
+
retrack2(ctx.bodyFn, sub);
|
|
3166
|
+
if (ctx.rerunPending) drainReruns(ctx);
|
|
3167
|
+
} finally {
|
|
3168
|
+
ctx.running = false;
|
|
3169
|
+
ctx.rerunPending = false;
|
|
3170
|
+
}
|
|
3171
|
+
});
|
|
3172
|
+
sub.depsHead = null;
|
|
3173
|
+
sub.depsTail = null;
|
|
3174
|
+
sub._epoch = 0;
|
|
3175
|
+
sub._structDirty = false;
|
|
3176
|
+
sub._hasComputedDep = false;
|
|
3177
|
+
sub._errorPhase = "effect";
|
|
3178
|
+
sub._runEpoch = 0;
|
|
3179
|
+
sub._runs = 0;
|
|
3180
|
+
ctx.subscriber = sub;
|
|
3181
|
+
ctx.running = true;
|
|
3182
|
+
try {
|
|
3183
|
+
retrack2(ctx.bodyFn, ctx.subscriber);
|
|
3184
|
+
if (ctx.rerunPending) drainReruns(ctx);
|
|
3185
|
+
} finally {
|
|
3186
|
+
ctx.running = false;
|
|
3187
|
+
ctx.rerunPending = false;
|
|
3188
|
+
}
|
|
3189
|
+
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
3190
|
+
if (hook) hook.emit("effect:create", { effectFn });
|
|
3191
|
+
return () => disposeEffect(ctx);
|
|
3192
|
+
}
|
|
3193
|
+
|
|
2872
3194
|
// src/ui/lazyEffect.ts
|
|
2873
3195
|
function lazyEffect(element, effectFn, options) {
|
|
2874
3196
|
if (typeof IntersectionObserver === "undefined") {
|
|
@@ -2904,25 +3226,59 @@ function lazyEffect(element, effectFn, options) {
|
|
|
2904
3226
|
|
|
2905
3227
|
// src/ui/timers.ts
|
|
2906
3228
|
function interval(fn, ms) {
|
|
2907
|
-
let
|
|
3229
|
+
let intervalId = null;
|
|
3230
|
+
let resumeId = null;
|
|
2908
3231
|
let running = false;
|
|
2909
|
-
|
|
3232
|
+
let nextDue = 0;
|
|
3233
|
+
let remaining = ms;
|
|
3234
|
+
function tick() {
|
|
3235
|
+
nextDue = Date.now() + ms;
|
|
3236
|
+
fn();
|
|
3237
|
+
}
|
|
3238
|
+
function clearTimers() {
|
|
3239
|
+
if (intervalId !== null) {
|
|
3240
|
+
clearInterval(intervalId);
|
|
3241
|
+
intervalId = null;
|
|
3242
|
+
}
|
|
3243
|
+
if (resumeId !== null) {
|
|
3244
|
+
clearTimeout(resumeId);
|
|
3245
|
+
resumeId = null;
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
function startRegular() {
|
|
3249
|
+
nextDue = Date.now() + ms;
|
|
3250
|
+
intervalId = setInterval(tick, ms);
|
|
3251
|
+
}
|
|
3252
|
+
function resume() {
|
|
2910
3253
|
if (running) return;
|
|
2911
|
-
id = setInterval(fn, ms);
|
|
2912
3254
|
running = true;
|
|
3255
|
+
if (remaining >= ms) {
|
|
3256
|
+
startRegular();
|
|
3257
|
+
return;
|
|
3258
|
+
}
|
|
3259
|
+
nextDue = Date.now() + remaining;
|
|
3260
|
+
resumeId = setTimeout(() => {
|
|
3261
|
+
resumeId = null;
|
|
3262
|
+
startRegular();
|
|
3263
|
+
fn();
|
|
3264
|
+
}, remaining);
|
|
3265
|
+
}
|
|
3266
|
+
function pause() {
|
|
3267
|
+
if (!running) return;
|
|
3268
|
+
remaining = Math.min(ms, Math.max(0, nextDue - Date.now()));
|
|
3269
|
+
clearTimers();
|
|
3270
|
+
running = false;
|
|
2913
3271
|
}
|
|
2914
3272
|
function stop() {
|
|
2915
|
-
|
|
2916
|
-
clearInterval(id);
|
|
2917
|
-
id = null;
|
|
2918
|
-
}
|
|
3273
|
+
clearTimers();
|
|
2919
3274
|
running = false;
|
|
3275
|
+
remaining = ms;
|
|
2920
3276
|
}
|
|
2921
|
-
|
|
3277
|
+
resume();
|
|
2922
3278
|
return {
|
|
2923
3279
|
stop,
|
|
2924
|
-
pause
|
|
2925
|
-
resume
|
|
3280
|
+
pause,
|
|
3281
|
+
resume,
|
|
2926
3282
|
isRunning: () => running
|
|
2927
3283
|
};
|
|
2928
3284
|
}
|
|
@@ -2954,7 +3310,11 @@ function hover(target) {
|
|
|
2954
3310
|
const onLeave = () => setHovered(false);
|
|
2955
3311
|
target.addEventListener("pointerenter", onEnter);
|
|
2956
3312
|
target.addEventListener("pointerleave", onLeave);
|
|
3313
|
+
let disposed = false;
|
|
2957
3314
|
function dispose2() {
|
|
3315
|
+
if (disposed) return;
|
|
3316
|
+
disposed = true;
|
|
3317
|
+
unregisterDisposer(target, dispose2);
|
|
2958
3318
|
target.removeEventListener("pointerenter", onEnter);
|
|
2959
3319
|
target.removeEventListener("pointerleave", onLeave);
|
|
2960
3320
|
}
|
|
@@ -2968,21 +3328,37 @@ var _lock = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.scrollLock.v1"),
|
|
|
2968
3328
|
savedOverflow: null,
|
|
2969
3329
|
savedPaddingRight: null
|
|
2970
3330
|
}));
|
|
3331
|
+
function applyBodyLock() {
|
|
3332
|
+
const body = document.body;
|
|
3333
|
+
if (!body) throw new Error("[scrollLock] document.body is not available yet");
|
|
3334
|
+
const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
3335
|
+
const savedOverflow = body.style.overflow;
|
|
3336
|
+
const savedPaddingRight = body.style.paddingRight;
|
|
3337
|
+
try {
|
|
3338
|
+
body.style.overflow = "hidden";
|
|
3339
|
+
if (scrollBarWidth > 0) {
|
|
3340
|
+
body.style.paddingRight = `${scrollBarWidth}px`;
|
|
3341
|
+
}
|
|
3342
|
+
} catch (err) {
|
|
3343
|
+
try {
|
|
3344
|
+
body.style.overflow = savedOverflow;
|
|
3345
|
+
body.style.paddingRight = savedPaddingRight;
|
|
3346
|
+
} catch {
|
|
3347
|
+
}
|
|
3348
|
+
throw err;
|
|
3349
|
+
}
|
|
3350
|
+
_lock.savedOverflow = savedOverflow;
|
|
3351
|
+
_lock.savedPaddingRight = savedPaddingRight;
|
|
3352
|
+
}
|
|
2971
3353
|
function scrollLock() {
|
|
2972
3354
|
let owned = false;
|
|
2973
3355
|
function lock() {
|
|
2974
3356
|
if (owned) return;
|
|
3357
|
+
if (_lock.count === 0 && typeof document !== "undefined") {
|
|
3358
|
+
applyBodyLock();
|
|
3359
|
+
}
|
|
2975
3360
|
owned = true;
|
|
2976
3361
|
_lock.count++;
|
|
2977
|
-
if (_lock.count !== 1 || typeof document === "undefined") return;
|
|
2978
|
-
const body = document.body;
|
|
2979
|
-
const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
2980
|
-
_lock.savedOverflow = body.style.overflow;
|
|
2981
|
-
_lock.savedPaddingRight = body.style.paddingRight;
|
|
2982
|
-
body.style.overflow = "hidden";
|
|
2983
|
-
if (scrollBarWidth > 0) {
|
|
2984
|
-
body.style.paddingRight = `${scrollBarWidth}px`;
|
|
2985
|
-
}
|
|
2986
3362
|
}
|
|
2987
3363
|
function unlock() {
|
|
2988
3364
|
if (!owned) return;
|
|
@@ -3000,13 +3376,33 @@ function scrollLock() {
|
|
|
3000
3376
|
|
|
3001
3377
|
// src/platform/customElement.ts
|
|
3002
3378
|
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
3379
|
+
var MAX_RENDER_PASSES = 10;
|
|
3380
|
+
var ABANDONED_RENDER = /* @__PURE__ */ Symbol("sibujs.defineElement.abandonedRender");
|
|
3003
3381
|
function defineElement(name, component, options = {}) {
|
|
3004
3382
|
if (customElements.get(name)) return;
|
|
3005
3383
|
const observed = options.observedAttributes || [];
|
|
3006
3384
|
class SibuElement extends HTMLElement {
|
|
3007
3385
|
constructor() {
|
|
3008
3386
|
super();
|
|
3009
|
-
|
|
3387
|
+
// Re-rendering is keyed on connection, not on a previous render: a first render
|
|
3388
|
+
// that throws leaves nothing rendered, and the element must still re-render
|
|
3389
|
+
// when a later attribute change fixes the input.
|
|
3390
|
+
this._connected = false;
|
|
3391
|
+
// A render in progress, and whether an attribute changed during it. The
|
|
3392
|
+
// component may write its host's observed attributes while rendering; with
|
|
3393
|
+
// the old subtree kept during the build, rendering again from inside the
|
|
3394
|
+
// callback recursed until the stack overflowed.
|
|
3395
|
+
this._rendering = false;
|
|
3396
|
+
this._dirty = false;
|
|
3397
|
+
// How many teardowns are draining. A host teardown may reconnect the element
|
|
3398
|
+
// (and a nested disconnect may run inside it), and rendering the next
|
|
3399
|
+
// generation from inside that drain would let the drain tear down what the
|
|
3400
|
+
// new render registers. A counter, not a flag: a nested teardown returning
|
|
3401
|
+
// must not declare the outer one finished.
|
|
3402
|
+
this._teardownDepth = 0;
|
|
3403
|
+
// Disconnects so far. A render compares it across its old-subtree teardown
|
|
3404
|
+
// to detect a disconnect that happened inside it.
|
|
3405
|
+
this._disconnects = 0;
|
|
3010
3406
|
if (options.shadow !== false) {
|
|
3011
3407
|
this._root = this.attachShadow({ mode: options.mode || "open" });
|
|
3012
3408
|
} else {
|
|
@@ -3016,32 +3412,106 @@ function defineElement(name, component, options = {}) {
|
|
|
3016
3412
|
static get observedAttributes() {
|
|
3017
3413
|
return observed;
|
|
3018
3414
|
}
|
|
3415
|
+
/** Whether rendering must wait for work already in progress. */
|
|
3416
|
+
get _busy() {
|
|
3417
|
+
return this._rendering || this._teardownDepth > 0;
|
|
3418
|
+
}
|
|
3019
3419
|
connectedCallback() {
|
|
3420
|
+
this._connected = true;
|
|
3421
|
+
if (this._busy) {
|
|
3422
|
+
this._dirty = true;
|
|
3423
|
+
return;
|
|
3424
|
+
}
|
|
3020
3425
|
this._render();
|
|
3021
3426
|
}
|
|
3022
3427
|
disconnectedCallback() {
|
|
3023
|
-
this.
|
|
3428
|
+
this._connected = false;
|
|
3429
|
+
this._disconnects++;
|
|
3430
|
+
this._teardownDepth++;
|
|
3431
|
+
try {
|
|
3432
|
+
this._teardown();
|
|
3433
|
+
} finally {
|
|
3434
|
+
this._teardownDepth--;
|
|
3435
|
+
if (this._teardownDepth === 0 && this._connected && this._dirty) {
|
|
3436
|
+
this._dirty = false;
|
|
3437
|
+
if (!this._rendering) this._render();
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3024
3440
|
}
|
|
3025
|
-
attributeChangedCallback() {
|
|
3026
|
-
if (this.
|
|
3027
|
-
|
|
3441
|
+
attributeChangedCallback(_name, oldValue, newValue) {
|
|
3442
|
+
if (oldValue === newValue || !this._connected) return;
|
|
3443
|
+
if (this._busy) {
|
|
3444
|
+
this._dirty = true;
|
|
3445
|
+
return;
|
|
3028
3446
|
}
|
|
3447
|
+
this._render();
|
|
3029
3448
|
}
|
|
3030
3449
|
_teardown() {
|
|
3031
|
-
this._rendered = null;
|
|
3032
3450
|
replaceChildrenSafely(this._root);
|
|
3033
|
-
|
|
3451
|
+
disposeNodeOwn(this);
|
|
3452
|
+
}
|
|
3453
|
+
/**
|
|
3454
|
+
* Render as a transaction: build the replacement first, commit only if that
|
|
3455
|
+
* succeeds. Tearing the current subtree down before calling the factory
|
|
3456
|
+
* meant a throwing rerender (an invalid attribute, say) left the element
|
|
3457
|
+
* blank with its live state already disposed. Now a failure keeps the
|
|
3458
|
+
* working subtree, releases whatever the failed attempt registered, and is
|
|
3459
|
+
* reported with this element as its node so an enclosing ErrorBoundary can
|
|
3460
|
+
* claim it.
|
|
3461
|
+
*/
|
|
3034
3462
|
_render() {
|
|
3035
|
-
this.
|
|
3463
|
+
this._rendering = true;
|
|
3464
|
+
try {
|
|
3465
|
+
let passes = 0;
|
|
3466
|
+
do {
|
|
3467
|
+
this._dirty = false;
|
|
3468
|
+
if (++passes > MAX_RENDER_PASSES) {
|
|
3469
|
+
reportError(
|
|
3470
|
+
new Error(
|
|
3471
|
+
`[SibuJS] defineElement(${name}): the component changed its own observed attributes on ${MAX_RENDER_PASSES} consecutive renders; stopped re-rendering.`
|
|
3472
|
+
),
|
|
3473
|
+
{ phase: "render", name: `defineElement(${name})`, node: this }
|
|
3474
|
+
);
|
|
3475
|
+
break;
|
|
3476
|
+
}
|
|
3477
|
+
this._renderOnce();
|
|
3478
|
+
} while (this._dirty && this._connected);
|
|
3479
|
+
} finally {
|
|
3480
|
+
this._rendering = false;
|
|
3481
|
+
this._dirty = false;
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
_renderOnce() {
|
|
3036
3485
|
const props = this._getProps();
|
|
3486
|
+
let el;
|
|
3487
|
+
try {
|
|
3488
|
+
el = withDisposerRollback(() => {
|
|
3489
|
+
const built = component(props, this);
|
|
3490
|
+
if (!this._connected) throw ABANDONED_RENDER;
|
|
3491
|
+
return built;
|
|
3492
|
+
});
|
|
3493
|
+
} catch (err) {
|
|
3494
|
+
if (err !== ABANDONED_RENDER) {
|
|
3495
|
+
reportError(err, { phase: "render", name: `defineElement(${name})`, node: this });
|
|
3496
|
+
}
|
|
3497
|
+
return;
|
|
3498
|
+
}
|
|
3499
|
+
const next = [];
|
|
3037
3500
|
if (options.styles && this._root instanceof ShadowRoot) {
|
|
3038
3501
|
const styleEl = document.createElement("style");
|
|
3039
3502
|
styleEl.textContent = options.styles;
|
|
3040
|
-
|
|
3503
|
+
next.push(styleEl);
|
|
3504
|
+
}
|
|
3505
|
+
next.push(el);
|
|
3506
|
+
const disconnects = this._disconnects;
|
|
3507
|
+
for (const node of next) node.parentNode?.removeChild(node);
|
|
3508
|
+
replaceChildrenSafely(this._root);
|
|
3509
|
+
if (this._disconnects !== disconnects) {
|
|
3510
|
+
for (const node of next) dispose(node);
|
|
3511
|
+
if (this._connected) this._dirty = true;
|
|
3512
|
+
return;
|
|
3041
3513
|
}
|
|
3042
|
-
|
|
3043
|
-
this._root.appendChild(el);
|
|
3044
|
-
this._rendered = el;
|
|
3514
|
+
this._root.replaceChildren(...next);
|
|
3045
3515
|
}
|
|
3046
3516
|
_getProps() {
|
|
3047
3517
|
const props = {};
|
|
@@ -3192,7 +3662,7 @@ function validateProps(props, schema) {
|
|
|
3192
3662
|
const errors = [];
|
|
3193
3663
|
for (const [key, def] of Object.entries(schema)) {
|
|
3194
3664
|
const propDef = typeof def === "function" ? { type: def } : def;
|
|
3195
|
-
if (result[key]
|
|
3665
|
+
if (result[key] === void 0 && propDef.default !== void 0) {
|
|
3196
3666
|
result[key] = typeof propDef.default === "function" ? propDef.default() : propDef.default;
|
|
3197
3667
|
}
|
|
3198
3668
|
if (propDef.required && result[key] == null) {
|
|
@@ -3218,7 +3688,7 @@ ${errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
|
3218
3688
|
for (const [key, def] of Object.entries(schema)) {
|
|
3219
3689
|
if (typeof def === "function") continue;
|
|
3220
3690
|
const fallback = def.default;
|
|
3221
|
-
if (result[key]
|
|
3691
|
+
if (result[key] === void 0 && fallback !== void 0) {
|
|
3222
3692
|
result[key] = typeof fallback === "function" ? fallback() : fallback;
|
|
3223
3693
|
}
|
|
3224
3694
|
}
|