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
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
|
}
|
|
@@ -428,6 +501,7 @@ function retrack(effectFn, subscriber) {
|
|
|
428
501
|
}
|
|
429
502
|
currentSubscriber = subscriber;
|
|
430
503
|
const sub = subscriber;
|
|
504
|
+
if (sub._cap === void 0) sub._cap = currentDisposerCapture();
|
|
431
505
|
const epoch = ++subscriberEpochCounter;
|
|
432
506
|
sub._epoch = epoch;
|
|
433
507
|
sub._structDirty = false;
|
|
@@ -619,7 +693,12 @@ function drainQueue() {
|
|
|
619
693
|
quarantined.add(sub);
|
|
620
694
|
continue;
|
|
621
695
|
}
|
|
622
|
-
|
|
696
|
+
const pushed = beginDisposerCapture(sub._cap ?? null);
|
|
697
|
+
try {
|
|
698
|
+
safeInvoke(sub);
|
|
699
|
+
} finally {
|
|
700
|
+
if (pushed) endDisposerCapture();
|
|
701
|
+
}
|
|
623
702
|
}
|
|
624
703
|
}
|
|
625
704
|
function drainNotificationQueue() {
|
|
@@ -755,7 +834,7 @@ function forEachSubscriber(signal2, visit) {
|
|
|
755
834
|
}
|
|
756
835
|
|
|
757
836
|
// src/reactivity/track.ts
|
|
758
|
-
var _runtimeVersion = true ? "4.
|
|
837
|
+
var _runtimeVersion = true ? "4.7.0" : "dev";
|
|
759
838
|
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
760
839
|
function resolveReactiveApi() {
|
|
761
840
|
const g = globalThis;
|
|
@@ -811,6 +890,14 @@ var getSubscriberCount2 = API.getSubscriberCount;
|
|
|
811
890
|
var getSubscriberDeps2 = API.getSubscriberDeps;
|
|
812
891
|
var forEachSubscriber2 = API.forEachSubscriber;
|
|
813
892
|
|
|
893
|
+
// src/core/devtoolsHook.ts
|
|
894
|
+
function emitDevtools(hook, event, payload) {
|
|
895
|
+
try {
|
|
896
|
+
hook.emit(event, payload);
|
|
897
|
+
} catch {
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
814
901
|
// src/core/signals/derived.ts
|
|
815
902
|
function derived(getter, options) {
|
|
816
903
|
devAssert(typeof getter === "function", "derived: argument must be a getter function.");
|
|
@@ -837,17 +924,17 @@ function derived(getter, options) {
|
|
|
837
924
|
cs._d = false;
|
|
838
925
|
cs._init = true;
|
|
839
926
|
};
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
try {
|
|
927
|
+
try {
|
|
928
|
+
track2(() => {
|
|
843
929
|
cs._v = getter();
|
|
844
930
|
cs._d = false;
|
|
845
931
|
cs._init = true;
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
932
|
+
}, markDirty);
|
|
933
|
+
} catch (err) {
|
|
934
|
+
disposed = true;
|
|
935
|
+
cleanup2(markDirty);
|
|
936
|
+
throw err;
|
|
937
|
+
}
|
|
851
938
|
const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
852
939
|
const validate = () => {
|
|
853
940
|
if (!cs._d || disposed || pendingError !== void 0) return;
|
|
@@ -866,7 +953,7 @@ function derived(getter, options) {
|
|
|
866
953
|
if (disposed) cleanup2(markDirty);
|
|
867
954
|
}
|
|
868
955
|
if (hook && !disposed && !Object.is(oldValue, cs._v)) {
|
|
869
|
-
hook
|
|
956
|
+
emitDevtools(hook, "computed:update", { signal: cs, oldValue, newValue: cs._v });
|
|
870
957
|
}
|
|
871
958
|
};
|
|
872
959
|
cs._validate = validate;
|
|
@@ -917,173 +1004,10 @@ function derived(getter, options) {
|
|
|
917
1004
|
}
|
|
918
1005
|
}
|
|
919
1006
|
};
|
|
920
|
-
if (hook) hook
|
|
1007
|
+
if (hook) emitDevtools(hook, "computed:create", { signal: cs, name: debugName, getter: computedGetter });
|
|
921
1008
|
return computedGetter;
|
|
922
1009
|
}
|
|
923
1010
|
|
|
924
|
-
// src/core/ssr-context.ts
|
|
925
|
-
var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
|
|
926
|
-
function detectSSRShared() {
|
|
927
|
-
let detected = null;
|
|
928
|
-
try {
|
|
929
|
-
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
930
|
-
let mod = null;
|
|
931
|
-
const getBuiltin = process.getBuiltinModule;
|
|
932
|
-
if (typeof getBuiltin === "function") {
|
|
933
|
-
mod = getBuiltin("node:async_hooks");
|
|
934
|
-
} else if (typeof require === "function") {
|
|
935
|
-
mod = require("async_hooks");
|
|
936
|
-
}
|
|
937
|
-
if (mod) detected = new mod.AsyncLocalStorage();
|
|
938
|
-
}
|
|
939
|
-
} catch {
|
|
940
|
-
detected = null;
|
|
941
|
-
}
|
|
942
|
-
return {
|
|
943
|
-
als: detected,
|
|
944
|
-
fallbackStore: { ssr: false, suspenseIdCounter: 0, locale: void 0 },
|
|
945
|
-
fallbackDepth: 0
|
|
946
|
-
};
|
|
947
|
-
}
|
|
948
|
-
var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
|
|
949
|
-
var als = _shared.als;
|
|
950
|
-
var fallbackStore = _shared.fallbackStore;
|
|
951
|
-
_shared.fallbackDepth ?? (_shared.fallbackDepth = 0);
|
|
952
|
-
function getSSRStore() {
|
|
953
|
-
if (als) {
|
|
954
|
-
const s = als.getStore();
|
|
955
|
-
if (s) return s;
|
|
956
|
-
}
|
|
957
|
-
return fallbackStore;
|
|
958
|
-
}
|
|
959
|
-
function isSSR() {
|
|
960
|
-
return getSSRStore().ssr;
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
// src/core/signals/effect.ts
|
|
964
|
-
var _g = globalThis;
|
|
965
|
-
var MAX_RERUNS = 100;
|
|
966
|
-
function flushUserCleanups(ctx) {
|
|
967
|
-
const list = ctx.userCleanups;
|
|
968
|
-
if (list.length === 0) return;
|
|
969
|
-
ctx.userCleanups = [];
|
|
970
|
-
for (let i = list.length - 1; i >= 0; i--) {
|
|
971
|
-
try {
|
|
972
|
-
list[i]();
|
|
973
|
-
} catch (err) {
|
|
974
|
-
reportError(err, { phase: "cleanup" });
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
function drainReruns(ctx) {
|
|
979
|
-
let reruns = 1;
|
|
980
|
-
do {
|
|
981
|
-
ctx.rerunPending = false;
|
|
982
|
-
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
983
|
-
retrack2(ctx.bodyFn, ctx.subscriber);
|
|
984
|
-
} while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
|
|
985
|
-
if (ctx.rerunPending) {
|
|
986
|
-
ctx.rerunPending = false;
|
|
987
|
-
reportError(
|
|
988
|
-
new Error(
|
|
989
|
-
`effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
|
|
990
|
-
),
|
|
991
|
-
{ phase: "effect" }
|
|
992
|
-
);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
function disposeEffect(ctx) {
|
|
996
|
-
if (ctx.disposed) return;
|
|
997
|
-
ctx.disposed = true;
|
|
998
|
-
const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
999
|
-
if (h) {
|
|
1000
|
-
try {
|
|
1001
|
-
h.emit("effect:destroy", { effectFn: ctx.fn });
|
|
1002
|
-
} catch {
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
try {
|
|
1006
|
-
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
1007
|
-
} catch (err) {
|
|
1008
|
-
if (typeof console !== "undefined") {
|
|
1009
|
-
console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
try {
|
|
1013
|
-
cleanup2(ctx.subscriber);
|
|
1014
|
-
} catch (err) {
|
|
1015
|
-
if (typeof console !== "undefined") {
|
|
1016
|
-
console.warn("[SibuJS effect] dispose threw:", err);
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
function effect(effectFn, options) {
|
|
1021
|
-
devAssert(typeof effectFn === "function", "effect: argument must be a function.");
|
|
1022
|
-
if (isSSR()) return () => {
|
|
1023
|
-
};
|
|
1024
|
-
const ctx = {
|
|
1025
|
-
fn: effectFn,
|
|
1026
|
-
onError: options?.onError,
|
|
1027
|
-
userCleanups: [],
|
|
1028
|
-
running: false,
|
|
1029
|
-
rerunPending: false,
|
|
1030
|
-
disposed: false,
|
|
1031
|
-
onCleanup: null,
|
|
1032
|
-
subscriber: null,
|
|
1033
|
-
bodyFn: null
|
|
1034
|
-
};
|
|
1035
|
-
ctx.onCleanup = (fn) => {
|
|
1036
|
-
ctx.userCleanups.push(fn);
|
|
1037
|
-
};
|
|
1038
|
-
const onErrorCaptured = ctx.onError;
|
|
1039
|
-
ctx.bodyFn = onErrorCaptured ? () => {
|
|
1040
|
-
try {
|
|
1041
|
-
ctx.fn(ctx.onCleanup);
|
|
1042
|
-
} catch (err) {
|
|
1043
|
-
onErrorCaptured(err);
|
|
1044
|
-
}
|
|
1045
|
-
} : () => {
|
|
1046
|
-
ctx.fn(ctx.onCleanup);
|
|
1047
|
-
};
|
|
1048
|
-
const sub = (() => {
|
|
1049
|
-
if (ctx.disposed) return;
|
|
1050
|
-
if (ctx.running) {
|
|
1051
|
-
ctx.rerunPending = true;
|
|
1052
|
-
return;
|
|
1053
|
-
}
|
|
1054
|
-
ctx.running = true;
|
|
1055
|
-
try {
|
|
1056
|
-
ctx.rerunPending = false;
|
|
1057
|
-
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
1058
|
-
retrack2(ctx.bodyFn, sub);
|
|
1059
|
-
if (ctx.rerunPending) drainReruns(ctx);
|
|
1060
|
-
} finally {
|
|
1061
|
-
ctx.running = false;
|
|
1062
|
-
ctx.rerunPending = false;
|
|
1063
|
-
}
|
|
1064
|
-
});
|
|
1065
|
-
sub.depsHead = null;
|
|
1066
|
-
sub.depsTail = null;
|
|
1067
|
-
sub._epoch = 0;
|
|
1068
|
-
sub._structDirty = false;
|
|
1069
|
-
sub._hasComputedDep = false;
|
|
1070
|
-
sub._errorPhase = "effect";
|
|
1071
|
-
sub._runEpoch = 0;
|
|
1072
|
-
sub._runs = 0;
|
|
1073
|
-
ctx.subscriber = sub;
|
|
1074
|
-
ctx.running = true;
|
|
1075
|
-
try {
|
|
1076
|
-
retrack2(ctx.bodyFn, ctx.subscriber);
|
|
1077
|
-
if (ctx.rerunPending) drainReruns(ctx);
|
|
1078
|
-
} finally {
|
|
1079
|
-
ctx.running = false;
|
|
1080
|
-
ctx.rerunPending = false;
|
|
1081
|
-
}
|
|
1082
|
-
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1083
|
-
if (hook) hook.emit("effect:create", { effectFn });
|
|
1084
|
-
return () => disposeEffect(ctx);
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
1011
|
// src/reactivity/batch.ts
|
|
1088
1012
|
var batchDepth = 0;
|
|
1089
1013
|
var pendingSignals = /* @__PURE__ */ new Set();
|
|
@@ -1135,7 +1059,7 @@ var enqueueBatchedSignal = API2.enqueueBatchedSignal;
|
|
|
1135
1059
|
var isBatching = API2.isBatching;
|
|
1136
1060
|
|
|
1137
1061
|
// src/core/signals/signal.ts
|
|
1138
|
-
var
|
|
1062
|
+
var _g = globalThis;
|
|
1139
1063
|
function signal(initial, options) {
|
|
1140
1064
|
const state2 = {
|
|
1141
1065
|
value: initial,
|
|
@@ -1166,8 +1090,8 @@ function signal(initial, options) {
|
|
|
1166
1090
|
state2.value = newValue;
|
|
1167
1091
|
state2.__v++;
|
|
1168
1092
|
if (DEV) {
|
|
1169
|
-
const hook =
|
|
1170
|
-
if (hook) hook
|
|
1093
|
+
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1094
|
+
if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1171
1095
|
}
|
|
1172
1096
|
if (!enqueueBatchedSignal(state2)) {
|
|
1173
1097
|
notifySubscribers2(state2);
|
|
@@ -1180,8 +1104,8 @@ function signal(initial, options) {
|
|
|
1180
1104
|
if (Object.is(newValue, prev)) return;
|
|
1181
1105
|
state2.value = newValue;
|
|
1182
1106
|
state2.__v++;
|
|
1183
|
-
const hook =
|
|
1184
|
-
if (hook) hook
|
|
1107
|
+
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1108
|
+
if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1185
1109
|
if (!enqueueBatchedSignal(state2)) {
|
|
1186
1110
|
notifySubscribers2(state2);
|
|
1187
1111
|
}
|
|
@@ -1198,11 +1122,82 @@ function signal(initial, options) {
|
|
|
1198
1122
|
}
|
|
1199
1123
|
};
|
|
1200
1124
|
}
|
|
1201
|
-
if (DEV) {
|
|
1202
|
-
const hook =
|
|
1203
|
-
if (hook) hook
|
|
1125
|
+
if (DEV) {
|
|
1126
|
+
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1127
|
+
if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
|
|
1128
|
+
}
|
|
1129
|
+
return [get, set];
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
// src/core/ssr-context.ts
|
|
1133
|
+
var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
|
|
1134
|
+
function detectSSRShared() {
|
|
1135
|
+
let detected = null;
|
|
1136
|
+
try {
|
|
1137
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
1138
|
+
let mod = null;
|
|
1139
|
+
const getBuiltin = process.getBuiltinModule;
|
|
1140
|
+
if (typeof getBuiltin === "function") {
|
|
1141
|
+
mod = getBuiltin("node:async_hooks");
|
|
1142
|
+
} else if (typeof require === "function") {
|
|
1143
|
+
mod = require("async_hooks");
|
|
1144
|
+
}
|
|
1145
|
+
if (mod) detected = new mod.AsyncLocalStorage();
|
|
1146
|
+
}
|
|
1147
|
+
} catch {
|
|
1148
|
+
detected = null;
|
|
1149
|
+
}
|
|
1150
|
+
return {
|
|
1151
|
+
als: detected,
|
|
1152
|
+
fallbackStore: { ssr: false, suspenseIdCounter: 0, locale: void 0 },
|
|
1153
|
+
fallbackDepth: 0
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
|
|
1157
|
+
var als = _shared.als;
|
|
1158
|
+
var fallbackStore = _shared.fallbackStore;
|
|
1159
|
+
_shared.fallbackDepth ?? (_shared.fallbackDepth = 0);
|
|
1160
|
+
function getSSRStore() {
|
|
1161
|
+
if (als) {
|
|
1162
|
+
const s = als.getStore();
|
|
1163
|
+
if (s) return s;
|
|
1164
|
+
}
|
|
1165
|
+
return fallbackStore;
|
|
1166
|
+
}
|
|
1167
|
+
function getRequestStore() {
|
|
1168
|
+
if (als) return als.getStore() ?? null;
|
|
1169
|
+
return _shared.fallbackDepth > 0 ? fallbackStore : null;
|
|
1170
|
+
}
|
|
1171
|
+
function isSSR() {
|
|
1172
|
+
return getSSRStore().ssr;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// src/reactivity/domBinding.ts
|
|
1176
|
+
function domBinding(commit, ownerNode) {
|
|
1177
|
+
if (isSSR()) return () => {
|
|
1178
|
+
};
|
|
1179
|
+
return reactiveBinding2(commit, ownerNode);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// src/utils/adoptThenable.ts
|
|
1183
|
+
function adoptThenable(value) {
|
|
1184
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return null;
|
|
1185
|
+
let then;
|
|
1186
|
+
try {
|
|
1187
|
+
then = value.then;
|
|
1188
|
+
} catch (error) {
|
|
1189
|
+
return Promise.reject(error);
|
|
1204
1190
|
}
|
|
1205
|
-
return
|
|
1191
|
+
if (typeof then !== "function") return null;
|
|
1192
|
+
return new Promise((resolve, reject) => {
|
|
1193
|
+
queueMicrotask(() => {
|
|
1194
|
+
try {
|
|
1195
|
+
Reflect.apply(then, value, [resolve, reject]);
|
|
1196
|
+
} catch (error) {
|
|
1197
|
+
reject(error);
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
});
|
|
1206
1201
|
}
|
|
1207
1202
|
|
|
1208
1203
|
// src/ui/form.ts
|
|
@@ -1232,10 +1227,14 @@ function maxLength(max2, message) {
|
|
|
1232
1227
|
}
|
|
1233
1228
|
function matchesPattern(regex, message = "Invalid format") {
|
|
1234
1229
|
return (value) => {
|
|
1235
|
-
if (
|
|
1236
|
-
|
|
1230
|
+
if (!value) return null;
|
|
1231
|
+
const savedLastIndex = regex.lastIndex;
|
|
1232
|
+
regex.lastIndex = 0;
|
|
1233
|
+
try {
|
|
1234
|
+
return regex.test(value) ? null : message;
|
|
1235
|
+
} finally {
|
|
1236
|
+
regex.lastIndex = savedLastIndex;
|
|
1237
1237
|
}
|
|
1238
|
-
return null;
|
|
1239
1238
|
};
|
|
1240
1239
|
}
|
|
1241
1240
|
function email(message = "Invalid email address") {
|
|
@@ -1284,13 +1283,13 @@ function bindField(field, extras) {
|
|
|
1284
1283
|
const mergedOn = extraOn && typeof extraOn === "object" ? { ...extraOn, ...fieldOn } : fieldOn;
|
|
1285
1284
|
const onElement = (el) => {
|
|
1286
1285
|
if (el instanceof HTMLSelectElement && el.multiple) {
|
|
1287
|
-
const stop =
|
|
1286
|
+
const stop = domBinding(() => {
|
|
1288
1287
|
const v = field.value();
|
|
1289
1288
|
const selected = Array.isArray(v) ? v.map(String) : [];
|
|
1290
1289
|
for (const opt of Array.from(el.options)) {
|
|
1291
1290
|
opt.selected = selected.includes(opt.value);
|
|
1292
1291
|
}
|
|
1293
|
-
});
|
|
1292
|
+
}, el);
|
|
1294
1293
|
registerDisposer(el, stop);
|
|
1295
1294
|
}
|
|
1296
1295
|
if (typeof extraOnElement === "function") {
|
|
@@ -1310,6 +1309,7 @@ function form(config) {
|
|
|
1310
1309
|
const fieldEntries = Object.entries(config);
|
|
1311
1310
|
const fieldMap = {};
|
|
1312
1311
|
const [manualErrors, setManualErrors] = signal({});
|
|
1312
|
+
const fieldErrors = [];
|
|
1313
1313
|
for (const [name, cfg] of fieldEntries) {
|
|
1314
1314
|
const [value, setValue] = signal(cfg.initial);
|
|
1315
1315
|
const [isTouched, setTouched] = signal(false);
|
|
@@ -1324,6 +1324,7 @@ function form(config) {
|
|
|
1324
1324
|
}
|
|
1325
1325
|
return null;
|
|
1326
1326
|
});
|
|
1327
|
+
fieldErrors.push(error);
|
|
1327
1328
|
const wrappedSet = (next) => {
|
|
1328
1329
|
setValue(next);
|
|
1329
1330
|
setManualErrors((prev) => {
|
|
@@ -1400,12 +1401,22 @@ function form(config) {
|
|
|
1400
1401
|
field.touch();
|
|
1401
1402
|
}
|
|
1402
1403
|
if (isValid()) {
|
|
1403
|
-
const
|
|
1404
|
-
|
|
1404
|
+
const report = (error) => reportError(error, { phase: "async", name: "form.handleSubmit" });
|
|
1405
|
+
let pending;
|
|
1406
|
+
try {
|
|
1407
|
+
pending = adoptThenable(onSubmit(values()));
|
|
1408
|
+
} catch (error) {
|
|
1409
|
+
report(error);
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
if (pending) {
|
|
1405
1413
|
setSubmitting(true);
|
|
1406
|
-
|
|
1414
|
+
pending.then(
|
|
1407
1415
|
() => setSubmitting(false),
|
|
1408
|
-
() =>
|
|
1416
|
+
(error) => {
|
|
1417
|
+
setSubmitting(false);
|
|
1418
|
+
report(error);
|
|
1419
|
+
}
|
|
1409
1420
|
);
|
|
1410
1421
|
}
|
|
1411
1422
|
}
|
|
@@ -1419,6 +1430,17 @@ function form(config) {
|
|
|
1419
1430
|
function setError(field, message) {
|
|
1420
1431
|
setManualErrors((prev) => ({ ...prev, [field]: message }));
|
|
1421
1432
|
}
|
|
1433
|
+
let disposed = false;
|
|
1434
|
+
function dispose2() {
|
|
1435
|
+
if (disposed) return;
|
|
1436
|
+
disposed = true;
|
|
1437
|
+
errors.dispose();
|
|
1438
|
+
isValid.dispose();
|
|
1439
|
+
isDirty.dispose();
|
|
1440
|
+
touchedState.dispose();
|
|
1441
|
+
values.dispose();
|
|
1442
|
+
for (const error of fieldErrors) error.dispose();
|
|
1443
|
+
}
|
|
1422
1444
|
return {
|
|
1423
1445
|
fields: fieldMap,
|
|
1424
1446
|
errors,
|
|
@@ -1429,7 +1451,8 @@ function form(config) {
|
|
|
1429
1451
|
values,
|
|
1430
1452
|
handleSubmit,
|
|
1431
1453
|
reset,
|
|
1432
|
-
setError
|
|
1454
|
+
setError,
|
|
1455
|
+
dispose: dispose2
|
|
1433
1456
|
};
|
|
1434
1457
|
}
|
|
1435
1458
|
|
|
@@ -1508,7 +1531,7 @@ function VirtualList(props) {
|
|
|
1508
1531
|
content.appendChild(itemEl);
|
|
1509
1532
|
}
|
|
1510
1533
|
};
|
|
1511
|
-
registerDisposer(container,
|
|
1534
|
+
registerDisposer(container, domBinding(update, container));
|
|
1512
1535
|
return container;
|
|
1513
1536
|
}
|
|
1514
1537
|
|
|
@@ -1518,11 +1541,14 @@ function intersection(options) {
|
|
|
1518
1541
|
const [ratio, setRatio] = signal(0);
|
|
1519
1542
|
let observer = null;
|
|
1520
1543
|
let currentElement = null;
|
|
1544
|
+
let generation = 0;
|
|
1521
1545
|
function observe(element) {
|
|
1522
1546
|
if (typeof IntersectionObserver === "undefined") return;
|
|
1523
1547
|
unobserve();
|
|
1524
1548
|
currentElement = element;
|
|
1549
|
+
const observation = ++generation;
|
|
1525
1550
|
observer = new IntersectionObserver((entries) => {
|
|
1551
|
+
if (observation !== generation) return;
|
|
1526
1552
|
const entry = entries[0];
|
|
1527
1553
|
if (entry) {
|
|
1528
1554
|
setIsIntersecting(entry.isIntersecting);
|
|
@@ -1532,8 +1558,10 @@ function intersection(options) {
|
|
|
1532
1558
|
observer.observe(element);
|
|
1533
1559
|
}
|
|
1534
1560
|
function unobserve() {
|
|
1561
|
+
generation++;
|
|
1535
1562
|
if (observer) {
|
|
1536
1563
|
if (currentElement) observer.unobserve(currentElement);
|
|
1564
|
+
observer.takeRecords();
|
|
1537
1565
|
observer.disconnect();
|
|
1538
1566
|
observer = null;
|
|
1539
1567
|
currentElement = null;
|
|
@@ -1552,17 +1580,26 @@ function lazyLoad(element, loader, options) {
|
|
|
1552
1580
|
return () => {
|
|
1553
1581
|
};
|
|
1554
1582
|
}
|
|
1583
|
+
let done = false;
|
|
1584
|
+
const stop = () => {
|
|
1585
|
+
done = true;
|
|
1586
|
+
observer.takeRecords();
|
|
1587
|
+
observer.disconnect();
|
|
1588
|
+
};
|
|
1555
1589
|
const observer = new IntersectionObserver((entries) => {
|
|
1590
|
+
if (done) return;
|
|
1556
1591
|
for (const entry of entries) {
|
|
1557
1592
|
if (entry.isIntersecting) {
|
|
1593
|
+
stop();
|
|
1558
1594
|
loader();
|
|
1559
|
-
observer.disconnect();
|
|
1560
1595
|
break;
|
|
1561
1596
|
}
|
|
1562
1597
|
}
|
|
1563
1598
|
}, options);
|
|
1564
1599
|
observer.observe(element);
|
|
1565
|
-
return () =>
|
|
1600
|
+
return () => {
|
|
1601
|
+
if (!done) stop();
|
|
1602
|
+
};
|
|
1566
1603
|
}
|
|
1567
1604
|
|
|
1568
1605
|
// src/ui/inputMask.ts
|
|
@@ -1717,6 +1754,7 @@ function focus() {
|
|
|
1717
1754
|
const dispose2 = () => {
|
|
1718
1755
|
if (disposed) return;
|
|
1719
1756
|
disposed = true;
|
|
1757
|
+
unregisterDisposer(element, dispose2);
|
|
1720
1758
|
element.removeEventListener("focus", onFocus);
|
|
1721
1759
|
element.removeEventListener("blur", onBlur);
|
|
1722
1760
|
if (currentElement === element) currentElement = null;
|
|
@@ -1785,14 +1823,19 @@ function FocusTrap(nodes, options = {}) {
|
|
|
1785
1823
|
}
|
|
1786
1824
|
};
|
|
1787
1825
|
container.addEventListener("keydown", onTrapKeydown);
|
|
1826
|
+
let disposed = false;
|
|
1788
1827
|
if (options.autoFocus !== false) {
|
|
1789
1828
|
queueMicrotask(() => {
|
|
1829
|
+
if (disposed || !container.isConnected) return;
|
|
1790
1830
|
const first = getFocusable()[0];
|
|
1791
1831
|
first?.focus();
|
|
1792
1832
|
});
|
|
1793
1833
|
}
|
|
1794
1834
|
let trapObserver = null;
|
|
1795
1835
|
function restoreFocusAndCleanup() {
|
|
1836
|
+
if (disposed) return;
|
|
1837
|
+
disposed = true;
|
|
1838
|
+
unregisterDisposer(container, restoreFocusAndCleanup);
|
|
1796
1839
|
if (options.restoreFocus !== false) previouslyFocused?.focus();
|
|
1797
1840
|
container.removeEventListener("keydown", onTrapKeydown);
|
|
1798
1841
|
if (trapObserver) {
|
|
@@ -1807,29 +1850,45 @@ function FocusTrap(nodes, options = {}) {
|
|
|
1807
1850
|
}
|
|
1808
1851
|
});
|
|
1809
1852
|
queueMicrotask(() => {
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
}
|
|
1853
|
+
const observer = trapObserver;
|
|
1854
|
+
if (disposed || !container.isConnected || !observer) return;
|
|
1855
|
+
observer.observe(container, { childList: true, subtree: true });
|
|
1813
1856
|
});
|
|
1814
1857
|
}
|
|
1815
1858
|
registerDisposer(container, restoreFocusAndCleanup);
|
|
1816
1859
|
return container;
|
|
1817
1860
|
}
|
|
1861
|
+
function isApplePlatform() {
|
|
1862
|
+
if (typeof navigator === "undefined") return false;
|
|
1863
|
+
const nav = navigator;
|
|
1864
|
+
return /mac|iphone|ipad|ipod/i.test(nav.userAgentData?.platform || nav.platform || "");
|
|
1865
|
+
}
|
|
1818
1866
|
function hotkey(combo, handler, options = {}) {
|
|
1819
1867
|
let key = combo;
|
|
1820
1868
|
let needCtrl = options.ctrl ?? false;
|
|
1821
1869
|
let needShift = options.shift ?? false;
|
|
1822
1870
|
let needAlt = options.alt ?? false;
|
|
1823
1871
|
let needMeta = options.meta ?? false;
|
|
1824
|
-
if (combo
|
|
1825
|
-
|
|
1872
|
+
if (combo === "+") {
|
|
1873
|
+
key = "+";
|
|
1874
|
+
} else if (combo.includes("+")) {
|
|
1875
|
+
const literalPlus = combo.endsWith("++");
|
|
1876
|
+
const parts = (literalPlus ? combo.slice(0, -2) : combo).toLowerCase().split("+");
|
|
1877
|
+
if (literalPlus) parts.push("+");
|
|
1826
1878
|
key = parts[parts.length - 1];
|
|
1879
|
+
if (key === "") throw new Error(`hotkey("${combo}"): missing key`);
|
|
1827
1880
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
1828
1881
|
const mod = parts[i];
|
|
1829
1882
|
if (mod === "ctrl" || mod === "control") needCtrl = true;
|
|
1830
1883
|
else if (mod === "shift") needShift = true;
|
|
1831
|
-
else if (mod === "alt") needAlt = true;
|
|
1884
|
+
else if (mod === "alt" || mod === "option") needAlt = true;
|
|
1832
1885
|
else if (mod === "meta" || mod === "cmd" || mod === "command") needMeta = true;
|
|
1886
|
+
else if (mod === "mod") {
|
|
1887
|
+
if (isApplePlatform()) needMeta = true;
|
|
1888
|
+
else needCtrl = true;
|
|
1889
|
+
} else {
|
|
1890
|
+
throw new Error(`hotkey("${combo}"): unknown modifier "${mod}"`);
|
|
1891
|
+
}
|
|
1833
1892
|
}
|
|
1834
1893
|
}
|
|
1835
1894
|
const listener = (e) => {
|
|
@@ -1895,6 +1954,11 @@ function announce(message, priority = "polite") {
|
|
|
1895
1954
|
var COUNTER_KEY = /* @__PURE__ */ Symbol.for("sibujs.createId.v1");
|
|
1896
1955
|
var _counter = globalThis[COUNTER_KEY] ?? (globalThis[COUNTER_KEY] = { n: 0 });
|
|
1897
1956
|
function createId(prefix = "sibu") {
|
|
1957
|
+
const request = getRequestStore();
|
|
1958
|
+
if (request) {
|
|
1959
|
+
request.idCounter = (request.idCounter ?? 0) + 1;
|
|
1960
|
+
return `${prefix}-${request.idCounter}`;
|
|
1961
|
+
}
|
|
1898
1962
|
_counter.n++;
|
|
1899
1963
|
return `${prefix}-${_counter.n}`;
|
|
1900
1964
|
}
|
|
@@ -1954,22 +2018,43 @@ function createListbox(container, options = {}) {
|
|
|
1954
2018
|
if (multiple) container.setAttribute("aria-multiselectable", "true");
|
|
1955
2019
|
if (!container.hasAttribute("tabindex")) container.setAttribute("tabindex", "0");
|
|
1956
2020
|
const [activeValue, setActiveValue] = signal(null);
|
|
2021
|
+
const [selectedValues, setSelectedValues] = signal([]);
|
|
1957
2022
|
const [selectedValue, setSelectedValue] = signal(null);
|
|
1958
2023
|
const [activeDescendantId, setActiveDescendantId] = signal(null);
|
|
1959
|
-
|
|
2024
|
+
let activeValueRef = null;
|
|
2025
|
+
let currentSelection = [];
|
|
2026
|
+
function reconcileOptions() {
|
|
1960
2027
|
const opts = Array.from(container.querySelectorAll(optionSelector));
|
|
2028
|
+
const selectedSet = new Set(currentSelection);
|
|
2029
|
+
let activeStillPresent = false;
|
|
2030
|
+
const active = activeValueRef;
|
|
1961
2031
|
for (const opt of opts) {
|
|
1962
2032
|
if (!opt.id) opt.id = createId("listbox-option");
|
|
1963
|
-
|
|
2033
|
+
const ov = opt.dataset.value;
|
|
2034
|
+
if (!opt.hasAttribute("aria-selected")) {
|
|
2035
|
+
opt.setAttribute("aria-selected", ov !== void 0 && selectedSet.has(ov) ? "true" : "false");
|
|
2036
|
+
}
|
|
2037
|
+
if (active !== null && ov === active) activeStillPresent = true;
|
|
1964
2038
|
}
|
|
2039
|
+
if (active !== null && !activeStillPresent) setActive(null, opts);
|
|
2040
|
+
return opts;
|
|
1965
2041
|
}
|
|
1966
|
-
stampIds();
|
|
1967
2042
|
function getOptions() {
|
|
1968
|
-
return
|
|
2043
|
+
return reconcileOptions();
|
|
2044
|
+
}
|
|
2045
|
+
function isDisabled(opt) {
|
|
2046
|
+
return opt.getAttribute("aria-disabled") === "true" || opt.hasAttribute("disabled");
|
|
1969
2047
|
}
|
|
1970
|
-
function
|
|
2048
|
+
function enabledOptions() {
|
|
2049
|
+
return getOptions().filter((opt) => !isDisabled(opt) && opt.dataset.value !== void 0);
|
|
2050
|
+
}
|
|
2051
|
+
function optionByValue(value) {
|
|
2052
|
+
return getOptions().find((opt) => opt.dataset.value === value);
|
|
2053
|
+
}
|
|
2054
|
+
function setActive(value, known) {
|
|
2055
|
+
activeValueRef = value;
|
|
1971
2056
|
setActiveValue(value);
|
|
1972
|
-
const opts = getOptions();
|
|
2057
|
+
const opts = known ?? getOptions();
|
|
1973
2058
|
for (const opt of opts) {
|
|
1974
2059
|
if (opt.dataset.value === value) {
|
|
1975
2060
|
opt.setAttribute("data-highlighted", "");
|
|
@@ -1985,29 +2070,33 @@ function createListbox(container, options = {}) {
|
|
|
1985
2070
|
}
|
|
1986
2071
|
}
|
|
1987
2072
|
function select(value) {
|
|
1988
|
-
const
|
|
1989
|
-
|
|
2073
|
+
const target = optionByValue(value);
|
|
2074
|
+
if (!target || isDisabled(target)) return;
|
|
2075
|
+
const previous = selectedValues();
|
|
2076
|
+
let next;
|
|
1990
2077
|
if (multiple) {
|
|
1991
|
-
|
|
1992
|
-
if (nextSelectedSet.has(value)) nextSelectedSet.delete(value);
|
|
1993
|
-
else nextSelectedSet.add(value);
|
|
1994
|
-
setSelectedValue(Array.from(nextSelectedSet).join(","));
|
|
2078
|
+
next = previous.includes(value) ? previous.filter((v) => v !== value) : [...previous, value];
|
|
1995
2079
|
} else {
|
|
1996
|
-
|
|
1997
|
-
setSelectedValue(value);
|
|
2080
|
+
next = [value];
|
|
1998
2081
|
}
|
|
2082
|
+
currentSelection = next;
|
|
2083
|
+
batch(() => {
|
|
2084
|
+
setSelectedValues(next);
|
|
2085
|
+
setSelectedValue(multiple ? next.join(",") : value);
|
|
2086
|
+
});
|
|
1999
2087
|
options.onSelect?.(value);
|
|
2088
|
+
const nextSelectedSet = new Set(next);
|
|
2000
2089
|
const opts = getOptions();
|
|
2001
2090
|
for (const opt of opts) {
|
|
2002
|
-
const ov = opt.dataset.value
|
|
2003
|
-
opt.setAttribute("aria-selected", nextSelectedSet.has(ov) ? "true" : "false");
|
|
2091
|
+
const ov = opt.dataset.value;
|
|
2092
|
+
opt.setAttribute("aria-selected", ov !== void 0 && nextSelectedSet.has(ov) ? "true" : "false");
|
|
2004
2093
|
}
|
|
2005
2094
|
}
|
|
2006
2095
|
function moveActive(delta) {
|
|
2007
|
-
const opts =
|
|
2096
|
+
const opts = enabledOptions();
|
|
2008
2097
|
if (opts.length === 0) return;
|
|
2009
|
-
const currentIdx = opts.findIndex((o) => o.dataset.value ===
|
|
2010
|
-
let next = currentIdx + delta;
|
|
2098
|
+
const currentIdx = opts.findIndex((o) => o.dataset.value === activeValueRef);
|
|
2099
|
+
let next = currentIdx === -1 ? delta > 0 ? 0 : opts.length - 1 : currentIdx + delta;
|
|
2011
2100
|
if (next < 0) next = opts.length - 1;
|
|
2012
2101
|
if (next >= opts.length) next = 0;
|
|
2013
2102
|
const nextValue = opts[next].dataset.value ?? null;
|
|
@@ -2028,13 +2117,13 @@ function createListbox(container, options = {}) {
|
|
|
2028
2117
|
break;
|
|
2029
2118
|
case "Home": {
|
|
2030
2119
|
e.preventDefault();
|
|
2031
|
-
const opts =
|
|
2120
|
+
const opts = enabledOptions();
|
|
2032
2121
|
if (opts.length > 0) setActive(opts[0].dataset.value ?? null);
|
|
2033
2122
|
break;
|
|
2034
2123
|
}
|
|
2035
2124
|
case "End": {
|
|
2036
2125
|
e.preventDefault();
|
|
2037
|
-
const opts =
|
|
2126
|
+
const opts = enabledOptions();
|
|
2038
2127
|
if (opts.length > 0) setActive(opts[opts.length - 1].dataset.value ?? null);
|
|
2039
2128
|
break;
|
|
2040
2129
|
}
|
|
@@ -2049,7 +2138,7 @@ function createListbox(container, options = {}) {
|
|
|
2049
2138
|
}
|
|
2050
2139
|
function onClick(e) {
|
|
2051
2140
|
const target = e.target.closest(optionSelector);
|
|
2052
|
-
if (!target || !container.contains(target)) return;
|
|
2141
|
+
if (!target || !container.contains(target) || isDisabled(target)) return;
|
|
2053
2142
|
const value = target.dataset.value ?? null;
|
|
2054
2143
|
if (value !== null) {
|
|
2055
2144
|
setActive(value);
|
|
@@ -2058,12 +2147,26 @@ function createListbox(container, options = {}) {
|
|
|
2058
2147
|
}
|
|
2059
2148
|
container.addEventListener("keydown", onKeyDown);
|
|
2060
2149
|
container.addEventListener("click", onClick);
|
|
2150
|
+
reconcileOptions();
|
|
2151
|
+
let observer = null;
|
|
2152
|
+
if (typeof MutationObserver !== "undefined") {
|
|
2153
|
+
observer = new MutationObserver(() => {
|
|
2154
|
+
if (!disposed) reconcileOptions();
|
|
2155
|
+
});
|
|
2156
|
+
observer.observe(container, { childList: true, subtree: true });
|
|
2157
|
+
}
|
|
2158
|
+
let disposed = false;
|
|
2061
2159
|
function dispose2() {
|
|
2160
|
+
if (disposed) return;
|
|
2161
|
+
disposed = true;
|
|
2162
|
+
unregisterDisposer(container, dispose2);
|
|
2163
|
+
observer?.disconnect();
|
|
2164
|
+
observer = null;
|
|
2062
2165
|
container.removeEventListener("keydown", onKeyDown);
|
|
2063
2166
|
container.removeEventListener("click", onClick);
|
|
2064
2167
|
}
|
|
2065
2168
|
registerDisposer(container, dispose2);
|
|
2066
|
-
return { activeValue, selectedValue, activeDescendantId, dispose: dispose2 };
|
|
2169
|
+
return { activeValue, selectedValues, selectedValue, activeDescendantId, refresh: reconcileOptions, dispose: dispose2 };
|
|
2067
2170
|
}
|
|
2068
2171
|
function createDialogAria(element, options = {}) {
|
|
2069
2172
|
const titleId = options.labelledBy ?? createId("dialog-title");
|
|
@@ -2686,6 +2789,8 @@ function removeGlobalListenerIfIdle() {
|
|
|
2686
2789
|
function dialog() {
|
|
2687
2790
|
const [isOpen, setIsOpen] = signal(false);
|
|
2688
2791
|
const entry = { close: () => close() };
|
|
2792
|
+
let opened = false;
|
|
2793
|
+
let disposed = false;
|
|
2689
2794
|
function pushOnStack() {
|
|
2690
2795
|
if (dialogStack.indexOf(entry) !== -1) return;
|
|
2691
2796
|
dialogStack.push(entry);
|
|
@@ -2697,23 +2802,27 @@ function dialog() {
|
|
|
2697
2802
|
removeGlobalListenerIfIdle();
|
|
2698
2803
|
}
|
|
2699
2804
|
function open() {
|
|
2700
|
-
if (
|
|
2701
|
-
|
|
2805
|
+
if (disposed || opened) return;
|
|
2806
|
+
opened = true;
|
|
2702
2807
|
pushOnStack();
|
|
2808
|
+
setIsOpen(true);
|
|
2703
2809
|
}
|
|
2704
2810
|
function close() {
|
|
2705
|
-
if (!
|
|
2811
|
+
if (!opened) {
|
|
2706
2812
|
removeFromStack();
|
|
2707
2813
|
return;
|
|
2708
2814
|
}
|
|
2709
|
-
|
|
2815
|
+
opened = false;
|
|
2710
2816
|
removeFromStack();
|
|
2817
|
+
setIsOpen(false);
|
|
2711
2818
|
}
|
|
2712
2819
|
function toggle() {
|
|
2713
|
-
if (
|
|
2820
|
+
if (opened) close();
|
|
2714
2821
|
else open();
|
|
2715
2822
|
}
|
|
2716
2823
|
function dispose2() {
|
|
2824
|
+
disposed = true;
|
|
2825
|
+
opened = false;
|
|
2717
2826
|
removeFromStack();
|
|
2718
2827
|
setIsOpen(false);
|
|
2719
2828
|
}
|
|
@@ -2918,37 +3027,183 @@ function pagination(options) {
|
|
|
2918
3027
|
function eventBus() {
|
|
2919
3028
|
const listeners = /* @__PURE__ */ new Map();
|
|
2920
3029
|
function on(event, handler) {
|
|
2921
|
-
let
|
|
2922
|
-
if (!
|
|
2923
|
-
|
|
2924
|
-
listeners.set(event,
|
|
3030
|
+
let subs = listeners.get(event);
|
|
3031
|
+
if (!subs) {
|
|
3032
|
+
subs = /* @__PURE__ */ new Map();
|
|
3033
|
+
listeners.set(event, subs);
|
|
3034
|
+
}
|
|
3035
|
+
let subscription = subs.get(handler);
|
|
3036
|
+
if (!subscription) {
|
|
3037
|
+
subscription = { handler, active: true };
|
|
3038
|
+
subs.set(handler, subscription);
|
|
2925
3039
|
}
|
|
2926
|
-
|
|
2927
|
-
return () =>
|
|
3040
|
+
const own = subscription;
|
|
3041
|
+
return () => {
|
|
3042
|
+
if (listeners.get(event)?.get(handler) !== own) return;
|
|
3043
|
+
off(event, handler);
|
|
3044
|
+
};
|
|
2928
3045
|
}
|
|
2929
3046
|
function emit(event, data) {
|
|
2930
|
-
const
|
|
2931
|
-
if (
|
|
2932
|
-
|
|
2933
|
-
|
|
3047
|
+
const subs = listeners.get(event);
|
|
3048
|
+
if (!subs || subs.size === 0) return;
|
|
3049
|
+
for (const subscription of Array.from(subs.values())) {
|
|
3050
|
+
if (!subscription.active) continue;
|
|
3051
|
+
try {
|
|
3052
|
+
subscription.handler(data);
|
|
3053
|
+
} catch (err) {
|
|
3054
|
+
reportError(err, { phase: "event", name: `eventBus(${String(event)})` });
|
|
2934
3055
|
}
|
|
2935
3056
|
}
|
|
2936
3057
|
}
|
|
2937
3058
|
function off(event, handler) {
|
|
2938
|
-
const
|
|
2939
|
-
if (
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
3059
|
+
const subs = listeners.get(event);
|
|
3060
|
+
if (!subs) return;
|
|
3061
|
+
const subscription = subs.get(handler);
|
|
3062
|
+
if (!subscription) return;
|
|
3063
|
+
subscription.active = false;
|
|
3064
|
+
subs.delete(handler);
|
|
3065
|
+
if (subs.size === 0) {
|
|
3066
|
+
listeners.delete(event);
|
|
2944
3067
|
}
|
|
2945
3068
|
}
|
|
2946
3069
|
function clear() {
|
|
3070
|
+
for (const subs of listeners.values()) {
|
|
3071
|
+
for (const subscription of subs.values()) subscription.active = false;
|
|
3072
|
+
}
|
|
2947
3073
|
listeners.clear();
|
|
2948
3074
|
}
|
|
2949
3075
|
return { on, emit, off, clear };
|
|
2950
3076
|
}
|
|
2951
3077
|
|
|
3078
|
+
// src/core/signals/effect.ts
|
|
3079
|
+
var _g2 = globalThis;
|
|
3080
|
+
var MAX_RERUNS = 100;
|
|
3081
|
+
function flushUserCleanups(ctx) {
|
|
3082
|
+
const list = ctx.userCleanups;
|
|
3083
|
+
if (list.length === 0) return;
|
|
3084
|
+
ctx.userCleanups = [];
|
|
3085
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
3086
|
+
try {
|
|
3087
|
+
list[i]();
|
|
3088
|
+
} catch (err) {
|
|
3089
|
+
reportError(err, { phase: "cleanup" });
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
function drainReruns(ctx) {
|
|
3094
|
+
let reruns = 1;
|
|
3095
|
+
do {
|
|
3096
|
+
ctx.rerunPending = false;
|
|
3097
|
+
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
3098
|
+
retrack2(ctx.bodyFn, ctx.subscriber);
|
|
3099
|
+
} while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
|
|
3100
|
+
if (ctx.rerunPending) {
|
|
3101
|
+
ctx.rerunPending = false;
|
|
3102
|
+
reportError(
|
|
3103
|
+
new Error(
|
|
3104
|
+
`effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
|
|
3105
|
+
),
|
|
3106
|
+
{ phase: "effect" }
|
|
3107
|
+
);
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
function disposeEffect(ctx) {
|
|
3111
|
+
if (ctx.disposed) return;
|
|
3112
|
+
ctx.disposed = true;
|
|
3113
|
+
const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
3114
|
+
if (h) {
|
|
3115
|
+
try {
|
|
3116
|
+
h.emit("effect:destroy", { effectFn: ctx.fn });
|
|
3117
|
+
} catch {
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
try {
|
|
3121
|
+
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
3122
|
+
} catch (err) {
|
|
3123
|
+
if (typeof console !== "undefined") {
|
|
3124
|
+
console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
try {
|
|
3128
|
+
cleanup2(ctx.subscriber);
|
|
3129
|
+
} catch (err) {
|
|
3130
|
+
if (typeof console !== "undefined") {
|
|
3131
|
+
console.warn("[SibuJS effect] dispose threw:", err);
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
function effect(effectFn, options) {
|
|
3136
|
+
devAssert(typeof effectFn === "function", "effect: argument must be a function.");
|
|
3137
|
+
if (isSSR()) return () => {
|
|
3138
|
+
};
|
|
3139
|
+
const ctx = {
|
|
3140
|
+
fn: effectFn,
|
|
3141
|
+
onError: options?.onError,
|
|
3142
|
+
userCleanups: [],
|
|
3143
|
+
running: false,
|
|
3144
|
+
rerunPending: false,
|
|
3145
|
+
disposed: false,
|
|
3146
|
+
onCleanup: null,
|
|
3147
|
+
subscriber: null,
|
|
3148
|
+
bodyFn: null
|
|
3149
|
+
};
|
|
3150
|
+
ctx.onCleanup = (fn) => {
|
|
3151
|
+
ctx.userCleanups.push(fn);
|
|
3152
|
+
};
|
|
3153
|
+
const onErrorCaptured = ctx.onError;
|
|
3154
|
+
ctx.bodyFn = onErrorCaptured ? () => {
|
|
3155
|
+
try {
|
|
3156
|
+
ctx.fn(ctx.onCleanup);
|
|
3157
|
+
} catch (err) {
|
|
3158
|
+
onErrorCaptured(err);
|
|
3159
|
+
}
|
|
3160
|
+
} : () => {
|
|
3161
|
+
ctx.fn(ctx.onCleanup);
|
|
3162
|
+
};
|
|
3163
|
+
const sub = (() => {
|
|
3164
|
+
if (ctx.disposed) return;
|
|
3165
|
+
if (ctx.running) {
|
|
3166
|
+
ctx.rerunPending = true;
|
|
3167
|
+
return;
|
|
3168
|
+
}
|
|
3169
|
+
ctx.running = true;
|
|
3170
|
+
try {
|
|
3171
|
+
ctx.rerunPending = false;
|
|
3172
|
+
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
3173
|
+
retrack2(ctx.bodyFn, sub);
|
|
3174
|
+
if (ctx.rerunPending) drainReruns(ctx);
|
|
3175
|
+
} finally {
|
|
3176
|
+
ctx.running = false;
|
|
3177
|
+
ctx.rerunPending = false;
|
|
3178
|
+
}
|
|
3179
|
+
});
|
|
3180
|
+
sub.depsHead = null;
|
|
3181
|
+
sub.depsTail = null;
|
|
3182
|
+
sub._epoch = 0;
|
|
3183
|
+
sub._structDirty = false;
|
|
3184
|
+
sub._hasComputedDep = false;
|
|
3185
|
+
sub._errorPhase = "effect";
|
|
3186
|
+
sub._runEpoch = 0;
|
|
3187
|
+
sub._runs = 0;
|
|
3188
|
+
ctx.subscriber = sub;
|
|
3189
|
+
ctx.running = true;
|
|
3190
|
+
try {
|
|
3191
|
+
retrack2(ctx.bodyFn, ctx.subscriber);
|
|
3192
|
+
if (ctx.rerunPending) drainReruns(ctx);
|
|
3193
|
+
} catch (err) {
|
|
3194
|
+
ctx.disposed = true;
|
|
3195
|
+
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
3196
|
+
cleanup2(ctx.subscriber);
|
|
3197
|
+
throw err;
|
|
3198
|
+
} finally {
|
|
3199
|
+
ctx.running = false;
|
|
3200
|
+
ctx.rerunPending = false;
|
|
3201
|
+
}
|
|
3202
|
+
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
3203
|
+
if (hook) emitDevtools(hook, "effect:create", { effectFn });
|
|
3204
|
+
return () => disposeEffect(ctx);
|
|
3205
|
+
}
|
|
3206
|
+
|
|
2952
3207
|
// src/ui/lazyEffect.ts
|
|
2953
3208
|
function lazyEffect(element, effectFn, options) {
|
|
2954
3209
|
if (typeof IntersectionObserver === "undefined") {
|
|
@@ -2984,25 +3239,59 @@ function lazyEffect(element, effectFn, options) {
|
|
|
2984
3239
|
|
|
2985
3240
|
// src/ui/timers.ts
|
|
2986
3241
|
function interval(fn, ms) {
|
|
2987
|
-
let
|
|
3242
|
+
let intervalId = null;
|
|
3243
|
+
let resumeId = null;
|
|
2988
3244
|
let running = false;
|
|
2989
|
-
|
|
3245
|
+
let nextDue = 0;
|
|
3246
|
+
let remaining = ms;
|
|
3247
|
+
function tick() {
|
|
3248
|
+
nextDue = Date.now() + ms;
|
|
3249
|
+
fn();
|
|
3250
|
+
}
|
|
3251
|
+
function clearTimers() {
|
|
3252
|
+
if (intervalId !== null) {
|
|
3253
|
+
clearInterval(intervalId);
|
|
3254
|
+
intervalId = null;
|
|
3255
|
+
}
|
|
3256
|
+
if (resumeId !== null) {
|
|
3257
|
+
clearTimeout(resumeId);
|
|
3258
|
+
resumeId = null;
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
function startRegular() {
|
|
3262
|
+
nextDue = Date.now() + ms;
|
|
3263
|
+
intervalId = setInterval(tick, ms);
|
|
3264
|
+
}
|
|
3265
|
+
function resume() {
|
|
2990
3266
|
if (running) return;
|
|
2991
|
-
id = setInterval(fn, ms);
|
|
2992
3267
|
running = true;
|
|
3268
|
+
if (remaining >= ms) {
|
|
3269
|
+
startRegular();
|
|
3270
|
+
return;
|
|
3271
|
+
}
|
|
3272
|
+
nextDue = Date.now() + remaining;
|
|
3273
|
+
resumeId = setTimeout(() => {
|
|
3274
|
+
resumeId = null;
|
|
3275
|
+
startRegular();
|
|
3276
|
+
fn();
|
|
3277
|
+
}, remaining);
|
|
3278
|
+
}
|
|
3279
|
+
function pause() {
|
|
3280
|
+
if (!running) return;
|
|
3281
|
+
remaining = Math.min(ms, Math.max(0, nextDue - Date.now()));
|
|
3282
|
+
clearTimers();
|
|
3283
|
+
running = false;
|
|
2993
3284
|
}
|
|
2994
3285
|
function stop() {
|
|
2995
|
-
|
|
2996
|
-
clearInterval(id);
|
|
2997
|
-
id = null;
|
|
2998
|
-
}
|
|
3286
|
+
clearTimers();
|
|
2999
3287
|
running = false;
|
|
3288
|
+
remaining = ms;
|
|
3000
3289
|
}
|
|
3001
|
-
|
|
3290
|
+
resume();
|
|
3002
3291
|
return {
|
|
3003
3292
|
stop,
|
|
3004
|
-
pause
|
|
3005
|
-
resume
|
|
3293
|
+
pause,
|
|
3294
|
+
resume,
|
|
3006
3295
|
isRunning: () => running
|
|
3007
3296
|
};
|
|
3008
3297
|
}
|
|
@@ -3034,7 +3323,11 @@ function hover(target) {
|
|
|
3034
3323
|
const onLeave = () => setHovered(false);
|
|
3035
3324
|
target.addEventListener("pointerenter", onEnter);
|
|
3036
3325
|
target.addEventListener("pointerleave", onLeave);
|
|
3326
|
+
let disposed = false;
|
|
3037
3327
|
function dispose2() {
|
|
3328
|
+
if (disposed) return;
|
|
3329
|
+
disposed = true;
|
|
3330
|
+
unregisterDisposer(target, dispose2);
|
|
3038
3331
|
target.removeEventListener("pointerenter", onEnter);
|
|
3039
3332
|
target.removeEventListener("pointerleave", onLeave);
|
|
3040
3333
|
}
|
|
@@ -3048,21 +3341,37 @@ var _lock = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.scrollLock.v1"),
|
|
|
3048
3341
|
savedOverflow: null,
|
|
3049
3342
|
savedPaddingRight: null
|
|
3050
3343
|
}));
|
|
3344
|
+
function applyBodyLock() {
|
|
3345
|
+
const body = document.body;
|
|
3346
|
+
if (!body) throw new Error("[scrollLock] document.body is not available yet");
|
|
3347
|
+
const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
3348
|
+
const savedOverflow = body.style.overflow;
|
|
3349
|
+
const savedPaddingRight = body.style.paddingRight;
|
|
3350
|
+
try {
|
|
3351
|
+
body.style.overflow = "hidden";
|
|
3352
|
+
if (scrollBarWidth > 0) {
|
|
3353
|
+
body.style.paddingRight = `${scrollBarWidth}px`;
|
|
3354
|
+
}
|
|
3355
|
+
} catch (err) {
|
|
3356
|
+
try {
|
|
3357
|
+
body.style.overflow = savedOverflow;
|
|
3358
|
+
body.style.paddingRight = savedPaddingRight;
|
|
3359
|
+
} catch {
|
|
3360
|
+
}
|
|
3361
|
+
throw err;
|
|
3362
|
+
}
|
|
3363
|
+
_lock.savedOverflow = savedOverflow;
|
|
3364
|
+
_lock.savedPaddingRight = savedPaddingRight;
|
|
3365
|
+
}
|
|
3051
3366
|
function scrollLock() {
|
|
3052
3367
|
let owned = false;
|
|
3053
3368
|
function lock() {
|
|
3054
3369
|
if (owned) return;
|
|
3370
|
+
if (_lock.count === 0 && typeof document !== "undefined") {
|
|
3371
|
+
applyBodyLock();
|
|
3372
|
+
}
|
|
3055
3373
|
owned = true;
|
|
3056
3374
|
_lock.count++;
|
|
3057
|
-
if (_lock.count !== 1 || typeof document === "undefined") return;
|
|
3058
|
-
const body = document.body;
|
|
3059
|
-
const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
3060
|
-
_lock.savedOverflow = body.style.overflow;
|
|
3061
|
-
_lock.savedPaddingRight = body.style.paddingRight;
|
|
3062
|
-
body.style.overflow = "hidden";
|
|
3063
|
-
if (scrollBarWidth > 0) {
|
|
3064
|
-
body.style.paddingRight = `${scrollBarWidth}px`;
|
|
3065
|
-
}
|
|
3066
3375
|
}
|
|
3067
3376
|
function unlock() {
|
|
3068
3377
|
if (!owned) return;
|
|
@@ -3080,13 +3389,33 @@ function scrollLock() {
|
|
|
3080
3389
|
|
|
3081
3390
|
// src/platform/customElement.ts
|
|
3082
3391
|
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
3392
|
+
var MAX_RENDER_PASSES = 10;
|
|
3393
|
+
var ABANDONED_RENDER = /* @__PURE__ */ Symbol("sibujs.defineElement.abandonedRender");
|
|
3083
3394
|
function defineElement(name, component, options = {}) {
|
|
3084
3395
|
if (customElements.get(name)) return;
|
|
3085
3396
|
const observed = options.observedAttributes || [];
|
|
3086
3397
|
class SibuElement extends HTMLElement {
|
|
3087
3398
|
constructor() {
|
|
3088
3399
|
super();
|
|
3089
|
-
|
|
3400
|
+
// Re-rendering is keyed on connection, not on a previous render: a first render
|
|
3401
|
+
// that throws leaves nothing rendered, and the element must still re-render
|
|
3402
|
+
// when a later attribute change fixes the input.
|
|
3403
|
+
this._connected = false;
|
|
3404
|
+
// A render in progress, and whether an attribute changed during it. The
|
|
3405
|
+
// component may write its host's observed attributes while rendering; with
|
|
3406
|
+
// the old subtree kept during the build, rendering again from inside the
|
|
3407
|
+
// callback recursed until the stack overflowed.
|
|
3408
|
+
this._rendering = false;
|
|
3409
|
+
this._dirty = false;
|
|
3410
|
+
// How many teardowns are draining. A host teardown may reconnect the element
|
|
3411
|
+
// (and a nested disconnect may run inside it), and rendering the next
|
|
3412
|
+
// generation from inside that drain would let the drain tear down what the
|
|
3413
|
+
// new render registers. A counter, not a flag: a nested teardown returning
|
|
3414
|
+
// must not declare the outer one finished.
|
|
3415
|
+
this._teardownDepth = 0;
|
|
3416
|
+
// Disconnects so far. A render compares it across its old-subtree teardown
|
|
3417
|
+
// to detect a disconnect that happened inside it.
|
|
3418
|
+
this._disconnects = 0;
|
|
3090
3419
|
if (options.shadow !== false) {
|
|
3091
3420
|
this._root = this.attachShadow({ mode: options.mode || "open" });
|
|
3092
3421
|
} else {
|
|
@@ -3096,32 +3425,106 @@ function defineElement(name, component, options = {}) {
|
|
|
3096
3425
|
static get observedAttributes() {
|
|
3097
3426
|
return observed;
|
|
3098
3427
|
}
|
|
3428
|
+
/** Whether rendering must wait for work already in progress. */
|
|
3429
|
+
get _busy() {
|
|
3430
|
+
return this._rendering || this._teardownDepth > 0;
|
|
3431
|
+
}
|
|
3099
3432
|
connectedCallback() {
|
|
3433
|
+
this._connected = true;
|
|
3434
|
+
if (this._busy) {
|
|
3435
|
+
this._dirty = true;
|
|
3436
|
+
return;
|
|
3437
|
+
}
|
|
3100
3438
|
this._render();
|
|
3101
3439
|
}
|
|
3102
3440
|
disconnectedCallback() {
|
|
3103
|
-
this.
|
|
3441
|
+
this._connected = false;
|
|
3442
|
+
this._disconnects++;
|
|
3443
|
+
this._teardownDepth++;
|
|
3444
|
+
try {
|
|
3445
|
+
this._teardown();
|
|
3446
|
+
} finally {
|
|
3447
|
+
this._teardownDepth--;
|
|
3448
|
+
if (this._teardownDepth === 0 && this._connected && this._dirty) {
|
|
3449
|
+
this._dirty = false;
|
|
3450
|
+
if (!this._rendering) this._render();
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3104
3453
|
}
|
|
3105
|
-
attributeChangedCallback() {
|
|
3106
|
-
if (this.
|
|
3107
|
-
|
|
3454
|
+
attributeChangedCallback(_name, oldValue, newValue) {
|
|
3455
|
+
if (oldValue === newValue || !this._connected) return;
|
|
3456
|
+
if (this._busy) {
|
|
3457
|
+
this._dirty = true;
|
|
3458
|
+
return;
|
|
3108
3459
|
}
|
|
3460
|
+
this._render();
|
|
3109
3461
|
}
|
|
3110
3462
|
_teardown() {
|
|
3111
|
-
this._rendered = null;
|
|
3112
3463
|
replaceChildrenSafely(this._root);
|
|
3113
|
-
|
|
3464
|
+
disposeNodeOwn(this);
|
|
3465
|
+
}
|
|
3466
|
+
/**
|
|
3467
|
+
* Render as a transaction: build the replacement first, commit only if that
|
|
3468
|
+
* succeeds. Tearing the current subtree down before calling the factory
|
|
3469
|
+
* meant a throwing rerender (an invalid attribute, say) left the element
|
|
3470
|
+
* blank with its live state already disposed. Now a failure keeps the
|
|
3471
|
+
* working subtree, releases whatever the failed attempt registered, and is
|
|
3472
|
+
* reported with this element as its node so an enclosing ErrorBoundary can
|
|
3473
|
+
* claim it.
|
|
3474
|
+
*/
|
|
3114
3475
|
_render() {
|
|
3115
|
-
this.
|
|
3476
|
+
this._rendering = true;
|
|
3477
|
+
try {
|
|
3478
|
+
let passes = 0;
|
|
3479
|
+
do {
|
|
3480
|
+
this._dirty = false;
|
|
3481
|
+
if (++passes > MAX_RENDER_PASSES) {
|
|
3482
|
+
reportError(
|
|
3483
|
+
new Error(
|
|
3484
|
+
`[SibuJS] defineElement(${name}): the component changed its own observed attributes on ${MAX_RENDER_PASSES} consecutive renders; stopped re-rendering.`
|
|
3485
|
+
),
|
|
3486
|
+
{ phase: "render", name: `defineElement(${name})`, node: this }
|
|
3487
|
+
);
|
|
3488
|
+
break;
|
|
3489
|
+
}
|
|
3490
|
+
this._renderOnce();
|
|
3491
|
+
} while (this._dirty && this._connected);
|
|
3492
|
+
} finally {
|
|
3493
|
+
this._rendering = false;
|
|
3494
|
+
this._dirty = false;
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
_renderOnce() {
|
|
3116
3498
|
const props = this._getProps();
|
|
3499
|
+
let el;
|
|
3500
|
+
try {
|
|
3501
|
+
el = withDisposerRollback(() => {
|
|
3502
|
+
const built = component(props, this);
|
|
3503
|
+
if (!this._connected) throw ABANDONED_RENDER;
|
|
3504
|
+
return built;
|
|
3505
|
+
});
|
|
3506
|
+
} catch (err) {
|
|
3507
|
+
if (err !== ABANDONED_RENDER) {
|
|
3508
|
+
reportError(err, { phase: "render", name: `defineElement(${name})`, node: this });
|
|
3509
|
+
}
|
|
3510
|
+
return;
|
|
3511
|
+
}
|
|
3512
|
+
const next = [];
|
|
3117
3513
|
if (options.styles && this._root instanceof ShadowRoot) {
|
|
3118
3514
|
const styleEl = document.createElement("style");
|
|
3119
3515
|
styleEl.textContent = options.styles;
|
|
3120
|
-
|
|
3516
|
+
next.push(styleEl);
|
|
3517
|
+
}
|
|
3518
|
+
next.push(el);
|
|
3519
|
+
const disconnects = this._disconnects;
|
|
3520
|
+
for (const node of next) node.parentNode?.removeChild(node);
|
|
3521
|
+
replaceChildrenSafely(this._root);
|
|
3522
|
+
if (this._disconnects !== disconnects) {
|
|
3523
|
+
for (const node of next) dispose(node);
|
|
3524
|
+
if (this._connected) this._dirty = true;
|
|
3525
|
+
return;
|
|
3121
3526
|
}
|
|
3122
|
-
|
|
3123
|
-
this._root.appendChild(el);
|
|
3124
|
-
this._rendered = el;
|
|
3527
|
+
this._root.replaceChildren(...next);
|
|
3125
3528
|
}
|
|
3126
3529
|
_getProps() {
|
|
3127
3530
|
const props = {};
|
|
@@ -3272,7 +3675,7 @@ function validateProps(props, schema) {
|
|
|
3272
3675
|
const errors = [];
|
|
3273
3676
|
for (const [key, def] of Object.entries(schema)) {
|
|
3274
3677
|
const propDef = typeof def === "function" ? { type: def } : def;
|
|
3275
|
-
if (result[key]
|
|
3678
|
+
if (result[key] === void 0 && propDef.default !== void 0) {
|
|
3276
3679
|
result[key] = typeof propDef.default === "function" ? propDef.default() : propDef.default;
|
|
3277
3680
|
}
|
|
3278
3681
|
if (propDef.required && result[key] == null) {
|
|
@@ -3298,7 +3701,7 @@ ${errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
|
3298
3701
|
for (const [key, def] of Object.entries(schema)) {
|
|
3299
3702
|
if (typeof def === "function") continue;
|
|
3300
3703
|
const fallback = def.default;
|
|
3301
|
-
if (result[key]
|
|
3704
|
+
if (result[key] === void 0 && fallback !== void 0) {
|
|
3302
3705
|
result[key] = typeof fallback === "function" ? fallback() : fallback;
|
|
3303
3706
|
}
|
|
3304
3707
|
}
|