sibujs 4.5.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/dist/browser.cjs +309 -129
- package/dist/browser.d.cts +46 -14
- package/dist/browser.d.ts +46 -14
- package/dist/browser.js +8 -6
- package/dist/build.cjs +184 -72
- 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-ONOHFDLG.js → chunk-47M47FOM.js} +3 -3
- package/dist/chunk-5HZXGZ6T.js +24 -0
- package/dist/{chunk-KKLW7YWL.js → chunk-5MT6SJ3P.js} +275 -144
- package/dist/{chunk-UGRX3S57.js → chunk-5Q4R7HCL.js} +12 -3
- package/dist/{chunk-S373NSMK.js → chunk-7GQCWFOE.js} +150 -59
- package/dist/{chunk-FTIR4QW2.js → chunk-7NCARGJW.js} +6 -8
- package/dist/{chunk-5DXA2J44.js → chunk-7XHATCIH.js} +5 -2
- package/dist/{chunk-VZKNK2V7.js → chunk-AIF3Z2T7.js} +296 -38
- package/dist/{chunk-4PMLNECI.js → chunk-BAAG6ZTI.js} +98 -29
- package/dist/{chunk-XZZOBQAY.js → chunk-D33YTSX5.js} +2 -2
- package/dist/{chunk-TBYTO6BS.js → chunk-DKGBBKOF.js} +136 -80
- package/dist/{chunk-NIOYEGBQ.js → chunk-HURREPU2.js} +27 -13
- package/dist/{chunk-GW3SCCZG.js → chunk-HYXDKS4N.js} +153 -110
- package/dist/chunk-J6FW5TV6.js +233 -0
- package/dist/{chunk-PCT43HW3.js → chunk-JSXPZCET.js} +1 -1
- package/dist/chunk-NYNYSPK7.js +318 -0
- package/dist/{chunk-RJE2BNI4.js → chunk-ORMZXBKQ.js} +108 -45
- package/dist/{chunk-OMJJM3KM.js → chunk-OXUY2A6L.js} +287 -130
- package/dist/{chunk-3JZ4L5TJ.js → chunk-PK6FK2G2.js} +181 -66
- package/dist/{chunk-RBTPLM32.js → chunk-QE4TTDU3.js} +2 -2
- package/dist/{chunk-2DCGACUU.js → chunk-SXXVZMKZ.js} +251 -66
- package/dist/{chunk-R25EFXXC.js → chunk-TUCPL2HB.js} +3 -3
- package/dist/{chunk-7LN645I6.js → chunk-UZQ6ALFS.js} +3 -3
- package/dist/{chunk-KEISJXBU.js → chunk-VVWPJ543.js} +6 -8
- package/dist/{chunk-RIXRAYIU.js → chunk-XC4MEKGA.js} +3 -3
- package/dist/{chunk-B3WHI2QA.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-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 +213 -117
- package/dist/data.d.cts +47 -9
- package/dist/data.d.ts +47 -9
- package/dist/data.js +12 -9
- package/dist/devtools.cjs +81 -57
- package/dist/devtools.js +7 -8
- package/dist/dispose-GEIG2KOF.js +28 -0
- package/dist/ecosystem.cjs +309 -104
- package/dist/ecosystem.d.cts +19 -3
- package/dist/ecosystem.d.ts +19 -3
- package/dist/ecosystem.js +12 -12
- package/dist/extras.cjs +2133 -863
- package/dist/extras.d.cts +10 -9
- package/dist/extras.d.ts +10 -9
- package/dist/extras.js +39 -28
- package/dist/index.cjs +184 -72
- package/dist/index.d.cts +22 -5
- package/dist/index.d.ts +22 -5
- package/dist/index.js +24 -27
- package/dist/motion.cjs +107 -41
- package/dist/motion.js +5 -5
- package/dist/patterns.cjs +317 -39
- package/dist/patterns.d.cts +27 -9
- package/dist/patterns.d.ts +27 -9
- package/dist/patterns.js +8 -8
- package/dist/performance.cjs +308 -216
- 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 +549 -202
- 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 +176 -72
- 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-DVoDpHye.d.ts → tagFactory-DFstCLQV.d.cts} +2 -100
- package/dist/{tagFactory-DVoDpHye.d.cts → tagFactory-DkaNVUNV.d.ts} +2 -100
- package/dist/testing.cjs +2484 -2185
- package/dist/testing.d.cts +56 -5
- package/dist/testing.d.ts +56 -5
- package/dist/testing.js +580 -307
- package/dist/ui.cjs +707 -317
- package/dist/ui.d.cts +40 -7
- package/dist/ui.d.ts +40 -7
- package/dist/ui.js +151 -56
- package/dist/widgets.cjs +246 -278
- package/dist/widgets.js +9 -10
- package/package.json +1 -1
- 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/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.6.0" : "dev";
|
|
759
838
|
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
760
839
|
function resolveReactiveApi() {
|
|
761
840
|
const g = globalThis;
|
|
@@ -921,169 +1000,6 @@ function derived(getter, options) {
|
|
|
921
1000
|
return computedGetter;
|
|
922
1001
|
}
|
|
923
1002
|
|
|
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
1003
|
// src/reactivity/batch.ts
|
|
1088
1004
|
var batchDepth = 0;
|
|
1089
1005
|
var pendingSignals = /* @__PURE__ */ new Set();
|
|
@@ -1135,7 +1051,7 @@ var enqueueBatchedSignal = API2.enqueueBatchedSignal;
|
|
|
1135
1051
|
var isBatching = API2.isBatching;
|
|
1136
1052
|
|
|
1137
1053
|
// src/core/signals/signal.ts
|
|
1138
|
-
var
|
|
1054
|
+
var _g = globalThis;
|
|
1139
1055
|
function signal(initial, options) {
|
|
1140
1056
|
const state2 = {
|
|
1141
1057
|
value: initial,
|
|
@@ -1166,7 +1082,7 @@ function signal(initial, options) {
|
|
|
1166
1082
|
state2.value = newValue;
|
|
1167
1083
|
state2.__v++;
|
|
1168
1084
|
if (DEV) {
|
|
1169
|
-
const hook =
|
|
1085
|
+
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1170
1086
|
if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1171
1087
|
}
|
|
1172
1088
|
if (!enqueueBatchedSignal(state2)) {
|
|
@@ -1180,7 +1096,7 @@ function signal(initial, options) {
|
|
|
1180
1096
|
if (Object.is(newValue, prev)) return;
|
|
1181
1097
|
state2.value = newValue;
|
|
1182
1098
|
state2.__v++;
|
|
1183
|
-
const hook =
|
|
1099
|
+
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1184
1100
|
if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1185
1101
|
if (!enqueueBatchedSignal(state2)) {
|
|
1186
1102
|
notifySubscribers2(state2);
|
|
@@ -1198,11 +1114,82 @@ function signal(initial, options) {
|
|
|
1198
1114
|
}
|
|
1199
1115
|
};
|
|
1200
1116
|
}
|
|
1201
|
-
if (DEV) {
|
|
1202
|
-
const hook =
|
|
1203
|
-
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);
|
|
1204
1182
|
}
|
|
1205
|
-
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
|
+
});
|
|
1206
1193
|
}
|
|
1207
1194
|
|
|
1208
1195
|
// src/ui/form.ts
|
|
@@ -1232,10 +1219,14 @@ function maxLength(max2, message) {
|
|
|
1232
1219
|
}
|
|
1233
1220
|
function matchesPattern(regex, message = "Invalid format") {
|
|
1234
1221
|
return (value) => {
|
|
1235
|
-
if (
|
|
1236
|
-
|
|
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;
|
|
1237
1229
|
}
|
|
1238
|
-
return null;
|
|
1239
1230
|
};
|
|
1240
1231
|
}
|
|
1241
1232
|
function email(message = "Invalid email address") {
|
|
@@ -1284,13 +1275,13 @@ function bindField(field, extras) {
|
|
|
1284
1275
|
const mergedOn = extraOn && typeof extraOn === "object" ? { ...extraOn, ...fieldOn } : fieldOn;
|
|
1285
1276
|
const onElement = (el) => {
|
|
1286
1277
|
if (el instanceof HTMLSelectElement && el.multiple) {
|
|
1287
|
-
const stop =
|
|
1278
|
+
const stop = domBinding(() => {
|
|
1288
1279
|
const v = field.value();
|
|
1289
1280
|
const selected = Array.isArray(v) ? v.map(String) : [];
|
|
1290
1281
|
for (const opt of Array.from(el.options)) {
|
|
1291
1282
|
opt.selected = selected.includes(opt.value);
|
|
1292
1283
|
}
|
|
1293
|
-
});
|
|
1284
|
+
}, el);
|
|
1294
1285
|
registerDisposer(el, stop);
|
|
1295
1286
|
}
|
|
1296
1287
|
if (typeof extraOnElement === "function") {
|
|
@@ -1310,6 +1301,7 @@ function form(config) {
|
|
|
1310
1301
|
const fieldEntries = Object.entries(config);
|
|
1311
1302
|
const fieldMap = {};
|
|
1312
1303
|
const [manualErrors, setManualErrors] = signal({});
|
|
1304
|
+
const fieldErrors = [];
|
|
1313
1305
|
for (const [name, cfg] of fieldEntries) {
|
|
1314
1306
|
const [value, setValue] = signal(cfg.initial);
|
|
1315
1307
|
const [isTouched, setTouched] = signal(false);
|
|
@@ -1324,6 +1316,7 @@ function form(config) {
|
|
|
1324
1316
|
}
|
|
1325
1317
|
return null;
|
|
1326
1318
|
});
|
|
1319
|
+
fieldErrors.push(error);
|
|
1327
1320
|
const wrappedSet = (next) => {
|
|
1328
1321
|
setValue(next);
|
|
1329
1322
|
setManualErrors((prev) => {
|
|
@@ -1400,12 +1393,22 @@ function form(config) {
|
|
|
1400
1393
|
field.touch();
|
|
1401
1394
|
}
|
|
1402
1395
|
if (isValid()) {
|
|
1403
|
-
const
|
|
1404
|
-
|
|
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) {
|
|
1405
1405
|
setSubmitting(true);
|
|
1406
|
-
|
|
1406
|
+
pending.then(
|
|
1407
1407
|
() => setSubmitting(false),
|
|
1408
|
-
() =>
|
|
1408
|
+
(error) => {
|
|
1409
|
+
setSubmitting(false);
|
|
1410
|
+
report(error);
|
|
1411
|
+
}
|
|
1409
1412
|
);
|
|
1410
1413
|
}
|
|
1411
1414
|
}
|
|
@@ -1419,6 +1422,17 @@ function form(config) {
|
|
|
1419
1422
|
function setError(field, message) {
|
|
1420
1423
|
setManualErrors((prev) => ({ ...prev, [field]: message }));
|
|
1421
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
|
+
}
|
|
1422
1436
|
return {
|
|
1423
1437
|
fields: fieldMap,
|
|
1424
1438
|
errors,
|
|
@@ -1429,7 +1443,8 @@ function form(config) {
|
|
|
1429
1443
|
values,
|
|
1430
1444
|
handleSubmit,
|
|
1431
1445
|
reset,
|
|
1432
|
-
setError
|
|
1446
|
+
setError,
|
|
1447
|
+
dispose: dispose2
|
|
1433
1448
|
};
|
|
1434
1449
|
}
|
|
1435
1450
|
|
|
@@ -1508,7 +1523,7 @@ function VirtualList(props) {
|
|
|
1508
1523
|
content.appendChild(itemEl);
|
|
1509
1524
|
}
|
|
1510
1525
|
};
|
|
1511
|
-
registerDisposer(container,
|
|
1526
|
+
registerDisposer(container, domBinding(update, container));
|
|
1512
1527
|
return container;
|
|
1513
1528
|
}
|
|
1514
1529
|
|
|
@@ -1518,11 +1533,14 @@ function intersection(options) {
|
|
|
1518
1533
|
const [ratio, setRatio] = signal(0);
|
|
1519
1534
|
let observer = null;
|
|
1520
1535
|
let currentElement = null;
|
|
1536
|
+
let generation = 0;
|
|
1521
1537
|
function observe(element) {
|
|
1522
1538
|
if (typeof IntersectionObserver === "undefined") return;
|
|
1523
1539
|
unobserve();
|
|
1524
1540
|
currentElement = element;
|
|
1541
|
+
const observation = ++generation;
|
|
1525
1542
|
observer = new IntersectionObserver((entries) => {
|
|
1543
|
+
if (observation !== generation) return;
|
|
1526
1544
|
const entry = entries[0];
|
|
1527
1545
|
if (entry) {
|
|
1528
1546
|
setIsIntersecting(entry.isIntersecting);
|
|
@@ -1532,8 +1550,10 @@ function intersection(options) {
|
|
|
1532
1550
|
observer.observe(element);
|
|
1533
1551
|
}
|
|
1534
1552
|
function unobserve() {
|
|
1553
|
+
generation++;
|
|
1535
1554
|
if (observer) {
|
|
1536
1555
|
if (currentElement) observer.unobserve(currentElement);
|
|
1556
|
+
observer.takeRecords();
|
|
1537
1557
|
observer.disconnect();
|
|
1538
1558
|
observer = null;
|
|
1539
1559
|
currentElement = null;
|
|
@@ -1552,17 +1572,26 @@ function lazyLoad(element, loader, options) {
|
|
|
1552
1572
|
return () => {
|
|
1553
1573
|
};
|
|
1554
1574
|
}
|
|
1575
|
+
let done = false;
|
|
1576
|
+
const stop = () => {
|
|
1577
|
+
done = true;
|
|
1578
|
+
observer.takeRecords();
|
|
1579
|
+
observer.disconnect();
|
|
1580
|
+
};
|
|
1555
1581
|
const observer = new IntersectionObserver((entries) => {
|
|
1582
|
+
if (done) return;
|
|
1556
1583
|
for (const entry of entries) {
|
|
1557
1584
|
if (entry.isIntersecting) {
|
|
1585
|
+
stop();
|
|
1558
1586
|
loader();
|
|
1559
|
-
observer.disconnect();
|
|
1560
1587
|
break;
|
|
1561
1588
|
}
|
|
1562
1589
|
}
|
|
1563
1590
|
}, options);
|
|
1564
1591
|
observer.observe(element);
|
|
1565
|
-
return () =>
|
|
1592
|
+
return () => {
|
|
1593
|
+
if (!done) stop();
|
|
1594
|
+
};
|
|
1566
1595
|
}
|
|
1567
1596
|
|
|
1568
1597
|
// src/ui/inputMask.ts
|
|
@@ -1717,6 +1746,7 @@ function focus() {
|
|
|
1717
1746
|
const dispose2 = () => {
|
|
1718
1747
|
if (disposed) return;
|
|
1719
1748
|
disposed = true;
|
|
1749
|
+
unregisterDisposer(element, dispose2);
|
|
1720
1750
|
element.removeEventListener("focus", onFocus);
|
|
1721
1751
|
element.removeEventListener("blur", onBlur);
|
|
1722
1752
|
if (currentElement === element) currentElement = null;
|
|
@@ -1785,14 +1815,19 @@ function FocusTrap(nodes, options = {}) {
|
|
|
1785
1815
|
}
|
|
1786
1816
|
};
|
|
1787
1817
|
container.addEventListener("keydown", onTrapKeydown);
|
|
1818
|
+
let disposed = false;
|
|
1788
1819
|
if (options.autoFocus !== false) {
|
|
1789
1820
|
queueMicrotask(() => {
|
|
1821
|
+
if (disposed || !container.isConnected) return;
|
|
1790
1822
|
const first = getFocusable()[0];
|
|
1791
1823
|
first?.focus();
|
|
1792
1824
|
});
|
|
1793
1825
|
}
|
|
1794
1826
|
let trapObserver = null;
|
|
1795
1827
|
function restoreFocusAndCleanup() {
|
|
1828
|
+
if (disposed) return;
|
|
1829
|
+
disposed = true;
|
|
1830
|
+
unregisterDisposer(container, restoreFocusAndCleanup);
|
|
1796
1831
|
if (options.restoreFocus !== false) previouslyFocused?.focus();
|
|
1797
1832
|
container.removeEventListener("keydown", onTrapKeydown);
|
|
1798
1833
|
if (trapObserver) {
|
|
@@ -1807,29 +1842,45 @@ function FocusTrap(nodes, options = {}) {
|
|
|
1807
1842
|
}
|
|
1808
1843
|
});
|
|
1809
1844
|
queueMicrotask(() => {
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
}
|
|
1845
|
+
const observer = trapObserver;
|
|
1846
|
+
if (disposed || !container.isConnected || !observer) return;
|
|
1847
|
+
observer.observe(container, { childList: true, subtree: true });
|
|
1813
1848
|
});
|
|
1814
1849
|
}
|
|
1815
1850
|
registerDisposer(container, restoreFocusAndCleanup);
|
|
1816
1851
|
return container;
|
|
1817
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
|
+
}
|
|
1818
1858
|
function hotkey(combo, handler, options = {}) {
|
|
1819
1859
|
let key = combo;
|
|
1820
1860
|
let needCtrl = options.ctrl ?? false;
|
|
1821
1861
|
let needShift = options.shift ?? false;
|
|
1822
1862
|
let needAlt = options.alt ?? false;
|
|
1823
1863
|
let needMeta = options.meta ?? false;
|
|
1824
|
-
if (combo
|
|
1825
|
-
|
|
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("+");
|
|
1826
1870
|
key = parts[parts.length - 1];
|
|
1871
|
+
if (key === "") throw new Error(`hotkey("${combo}"): missing key`);
|
|
1827
1872
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
1828
1873
|
const mod = parts[i];
|
|
1829
1874
|
if (mod === "ctrl" || mod === "control") needCtrl = true;
|
|
1830
1875
|
else if (mod === "shift") needShift = true;
|
|
1831
|
-
else if (mod === "alt") needAlt = true;
|
|
1876
|
+
else if (mod === "alt" || mod === "option") needAlt = true;
|
|
1832
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
|
+
}
|
|
1833
1884
|
}
|
|
1834
1885
|
}
|
|
1835
1886
|
const listener = (e) => {
|
|
@@ -1895,6 +1946,11 @@ function announce(message, priority = "polite") {
|
|
|
1895
1946
|
var COUNTER_KEY = /* @__PURE__ */ Symbol.for("sibujs.createId.v1");
|
|
1896
1947
|
var _counter = globalThis[COUNTER_KEY] ?? (globalThis[COUNTER_KEY] = { n: 0 });
|
|
1897
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
|
+
}
|
|
1898
1954
|
_counter.n++;
|
|
1899
1955
|
return `${prefix}-${_counter.n}`;
|
|
1900
1956
|
}
|
|
@@ -1954,22 +2010,43 @@ function createListbox(container, options = {}) {
|
|
|
1954
2010
|
if (multiple) container.setAttribute("aria-multiselectable", "true");
|
|
1955
2011
|
if (!container.hasAttribute("tabindex")) container.setAttribute("tabindex", "0");
|
|
1956
2012
|
const [activeValue, setActiveValue] = signal(null);
|
|
2013
|
+
const [selectedValues, setSelectedValues] = signal([]);
|
|
1957
2014
|
const [selectedValue, setSelectedValue] = signal(null);
|
|
1958
2015
|
const [activeDescendantId, setActiveDescendantId] = signal(null);
|
|
1959
|
-
|
|
2016
|
+
let activeValueRef = null;
|
|
2017
|
+
let currentSelection = [];
|
|
2018
|
+
function reconcileOptions() {
|
|
1960
2019
|
const opts = Array.from(container.querySelectorAll(optionSelector));
|
|
2020
|
+
const selectedSet = new Set(currentSelection);
|
|
2021
|
+
let activeStillPresent = false;
|
|
2022
|
+
const active = activeValueRef;
|
|
1961
2023
|
for (const opt of opts) {
|
|
1962
2024
|
if (!opt.id) opt.id = createId("listbox-option");
|
|
1963
|
-
|
|
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;
|
|
1964
2030
|
}
|
|
2031
|
+
if (active !== null && !activeStillPresent) setActive(null, opts);
|
|
2032
|
+
return opts;
|
|
1965
2033
|
}
|
|
1966
|
-
stampIds();
|
|
1967
2034
|
function getOptions() {
|
|
1968
|
-
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);
|
|
1969
2042
|
}
|
|
1970
|
-
function
|
|
2043
|
+
function optionByValue(value) {
|
|
2044
|
+
return getOptions().find((opt) => opt.dataset.value === value);
|
|
2045
|
+
}
|
|
2046
|
+
function setActive(value, known) {
|
|
2047
|
+
activeValueRef = value;
|
|
1971
2048
|
setActiveValue(value);
|
|
1972
|
-
const opts = getOptions();
|
|
2049
|
+
const opts = known ?? getOptions();
|
|
1973
2050
|
for (const opt of opts) {
|
|
1974
2051
|
if (opt.dataset.value === value) {
|
|
1975
2052
|
opt.setAttribute("data-highlighted", "");
|
|
@@ -1985,29 +2062,33 @@ function createListbox(container, options = {}) {
|
|
|
1985
2062
|
}
|
|
1986
2063
|
}
|
|
1987
2064
|
function select(value) {
|
|
1988
|
-
const
|
|
1989
|
-
|
|
2065
|
+
const target = optionByValue(value);
|
|
2066
|
+
if (!target || isDisabled(target)) return;
|
|
2067
|
+
const previous = selectedValues();
|
|
2068
|
+
let next;
|
|
1990
2069
|
if (multiple) {
|
|
1991
|
-
|
|
1992
|
-
if (nextSelectedSet.has(value)) nextSelectedSet.delete(value);
|
|
1993
|
-
else nextSelectedSet.add(value);
|
|
1994
|
-
setSelectedValue(Array.from(nextSelectedSet).join(","));
|
|
2070
|
+
next = previous.includes(value) ? previous.filter((v) => v !== value) : [...previous, value];
|
|
1995
2071
|
} else {
|
|
1996
|
-
|
|
1997
|
-
setSelectedValue(value);
|
|
2072
|
+
next = [value];
|
|
1998
2073
|
}
|
|
2074
|
+
currentSelection = next;
|
|
2075
|
+
batch(() => {
|
|
2076
|
+
setSelectedValues(next);
|
|
2077
|
+
setSelectedValue(multiple ? next.join(",") : value);
|
|
2078
|
+
});
|
|
1999
2079
|
options.onSelect?.(value);
|
|
2080
|
+
const nextSelectedSet = new Set(next);
|
|
2000
2081
|
const opts = getOptions();
|
|
2001
2082
|
for (const opt of opts) {
|
|
2002
|
-
const ov = opt.dataset.value
|
|
2003
|
-
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");
|
|
2004
2085
|
}
|
|
2005
2086
|
}
|
|
2006
2087
|
function moveActive(delta) {
|
|
2007
|
-
const opts =
|
|
2088
|
+
const opts = enabledOptions();
|
|
2008
2089
|
if (opts.length === 0) return;
|
|
2009
|
-
const currentIdx = opts.findIndex((o) => o.dataset.value ===
|
|
2010
|
-
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;
|
|
2011
2092
|
if (next < 0) next = opts.length - 1;
|
|
2012
2093
|
if (next >= opts.length) next = 0;
|
|
2013
2094
|
const nextValue = opts[next].dataset.value ?? null;
|
|
@@ -2028,13 +2109,13 @@ function createListbox(container, options = {}) {
|
|
|
2028
2109
|
break;
|
|
2029
2110
|
case "Home": {
|
|
2030
2111
|
e.preventDefault();
|
|
2031
|
-
const opts =
|
|
2112
|
+
const opts = enabledOptions();
|
|
2032
2113
|
if (opts.length > 0) setActive(opts[0].dataset.value ?? null);
|
|
2033
2114
|
break;
|
|
2034
2115
|
}
|
|
2035
2116
|
case "End": {
|
|
2036
2117
|
e.preventDefault();
|
|
2037
|
-
const opts =
|
|
2118
|
+
const opts = enabledOptions();
|
|
2038
2119
|
if (opts.length > 0) setActive(opts[opts.length - 1].dataset.value ?? null);
|
|
2039
2120
|
break;
|
|
2040
2121
|
}
|
|
@@ -2049,7 +2130,7 @@ function createListbox(container, options = {}) {
|
|
|
2049
2130
|
}
|
|
2050
2131
|
function onClick(e) {
|
|
2051
2132
|
const target = e.target.closest(optionSelector);
|
|
2052
|
-
if (!target || !container.contains(target)) return;
|
|
2133
|
+
if (!target || !container.contains(target) || isDisabled(target)) return;
|
|
2053
2134
|
const value = target.dataset.value ?? null;
|
|
2054
2135
|
if (value !== null) {
|
|
2055
2136
|
setActive(value);
|
|
@@ -2058,12 +2139,26 @@ function createListbox(container, options = {}) {
|
|
|
2058
2139
|
}
|
|
2059
2140
|
container.addEventListener("keydown", onKeyDown);
|
|
2060
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;
|
|
2061
2151
|
function dispose2() {
|
|
2152
|
+
if (disposed) return;
|
|
2153
|
+
disposed = true;
|
|
2154
|
+
unregisterDisposer(container, dispose2);
|
|
2155
|
+
observer?.disconnect();
|
|
2156
|
+
observer = null;
|
|
2062
2157
|
container.removeEventListener("keydown", onKeyDown);
|
|
2063
2158
|
container.removeEventListener("click", onClick);
|
|
2064
2159
|
}
|
|
2065
2160
|
registerDisposer(container, dispose2);
|
|
2066
|
-
return { activeValue, selectedValue, activeDescendantId, dispose: dispose2 };
|
|
2161
|
+
return { activeValue, selectedValues, selectedValue, activeDescendantId, refresh: reconcileOptions, dispose: dispose2 };
|
|
2067
2162
|
}
|
|
2068
2163
|
function createDialogAria(element, options = {}) {
|
|
2069
2164
|
const titleId = options.labelledBy ?? createId("dialog-title");
|
|
@@ -2686,6 +2781,8 @@ function removeGlobalListenerIfIdle() {
|
|
|
2686
2781
|
function dialog() {
|
|
2687
2782
|
const [isOpen, setIsOpen] = signal(false);
|
|
2688
2783
|
const entry = { close: () => close() };
|
|
2784
|
+
let opened = false;
|
|
2785
|
+
let disposed = false;
|
|
2689
2786
|
function pushOnStack() {
|
|
2690
2787
|
if (dialogStack.indexOf(entry) !== -1) return;
|
|
2691
2788
|
dialogStack.push(entry);
|
|
@@ -2697,23 +2794,27 @@ function dialog() {
|
|
|
2697
2794
|
removeGlobalListenerIfIdle();
|
|
2698
2795
|
}
|
|
2699
2796
|
function open() {
|
|
2700
|
-
if (
|
|
2701
|
-
|
|
2797
|
+
if (disposed || opened) return;
|
|
2798
|
+
opened = true;
|
|
2702
2799
|
pushOnStack();
|
|
2800
|
+
setIsOpen(true);
|
|
2703
2801
|
}
|
|
2704
2802
|
function close() {
|
|
2705
|
-
if (!
|
|
2803
|
+
if (!opened) {
|
|
2706
2804
|
removeFromStack();
|
|
2707
2805
|
return;
|
|
2708
2806
|
}
|
|
2709
|
-
|
|
2807
|
+
opened = false;
|
|
2710
2808
|
removeFromStack();
|
|
2809
|
+
setIsOpen(false);
|
|
2711
2810
|
}
|
|
2712
2811
|
function toggle() {
|
|
2713
|
-
if (
|
|
2812
|
+
if (opened) close();
|
|
2714
2813
|
else open();
|
|
2715
2814
|
}
|
|
2716
2815
|
function dispose2() {
|
|
2816
|
+
disposed = true;
|
|
2817
|
+
opened = false;
|
|
2717
2818
|
removeFromStack();
|
|
2718
2819
|
setIsOpen(false);
|
|
2719
2820
|
}
|
|
@@ -2918,37 +3019,178 @@ function pagination(options) {
|
|
|
2918
3019
|
function eventBus() {
|
|
2919
3020
|
const listeners = /* @__PURE__ */ new Map();
|
|
2920
3021
|
function on(event, handler) {
|
|
2921
|
-
let
|
|
2922
|
-
if (!
|
|
2923
|
-
|
|
2924
|
-
listeners.set(event,
|
|
3022
|
+
let subs = listeners.get(event);
|
|
3023
|
+
if (!subs) {
|
|
3024
|
+
subs = /* @__PURE__ */ new Map();
|
|
3025
|
+
listeners.set(event, subs);
|
|
2925
3026
|
}
|
|
2926
|
-
|
|
2927
|
-
|
|
3027
|
+
let subscription = subs.get(handler);
|
|
3028
|
+
if (!subscription) {
|
|
3029
|
+
subscription = { handler, active: true };
|
|
3030
|
+
subs.set(handler, subscription);
|
|
3031
|
+
}
|
|
3032
|
+
const own = subscription;
|
|
3033
|
+
return () => {
|
|
3034
|
+
if (listeners.get(event)?.get(handler) !== own) return;
|
|
3035
|
+
off(event, handler);
|
|
3036
|
+
};
|
|
2928
3037
|
}
|
|
2929
3038
|
function emit(event, data) {
|
|
2930
|
-
const
|
|
2931
|
-
if (
|
|
2932
|
-
|
|
2933
|
-
|
|
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)})` });
|
|
2934
3047
|
}
|
|
2935
3048
|
}
|
|
2936
3049
|
}
|
|
2937
3050
|
function off(event, handler) {
|
|
2938
|
-
const
|
|
2939
|
-
if (
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
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);
|
|
2944
3059
|
}
|
|
2945
3060
|
}
|
|
2946
3061
|
function clear() {
|
|
3062
|
+
for (const subs of listeners.values()) {
|
|
3063
|
+
for (const subscription of subs.values()) subscription.active = false;
|
|
3064
|
+
}
|
|
2947
3065
|
listeners.clear();
|
|
2948
3066
|
}
|
|
2949
3067
|
return { on, emit, off, clear };
|
|
2950
3068
|
}
|
|
2951
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
|
+
|
|
2952
3194
|
// src/ui/lazyEffect.ts
|
|
2953
3195
|
function lazyEffect(element, effectFn, options) {
|
|
2954
3196
|
if (typeof IntersectionObserver === "undefined") {
|
|
@@ -2984,25 +3226,59 @@ function lazyEffect(element, effectFn, options) {
|
|
|
2984
3226
|
|
|
2985
3227
|
// src/ui/timers.ts
|
|
2986
3228
|
function interval(fn, ms) {
|
|
2987
|
-
let
|
|
3229
|
+
let intervalId = null;
|
|
3230
|
+
let resumeId = null;
|
|
2988
3231
|
let running = false;
|
|
2989
|
-
|
|
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() {
|
|
2990
3253
|
if (running) return;
|
|
2991
|
-
id = setInterval(fn, ms);
|
|
2992
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;
|
|
2993
3271
|
}
|
|
2994
3272
|
function stop() {
|
|
2995
|
-
|
|
2996
|
-
clearInterval(id);
|
|
2997
|
-
id = null;
|
|
2998
|
-
}
|
|
3273
|
+
clearTimers();
|
|
2999
3274
|
running = false;
|
|
3275
|
+
remaining = ms;
|
|
3000
3276
|
}
|
|
3001
|
-
|
|
3277
|
+
resume();
|
|
3002
3278
|
return {
|
|
3003
3279
|
stop,
|
|
3004
|
-
pause
|
|
3005
|
-
resume
|
|
3280
|
+
pause,
|
|
3281
|
+
resume,
|
|
3006
3282
|
isRunning: () => running
|
|
3007
3283
|
};
|
|
3008
3284
|
}
|
|
@@ -3034,7 +3310,11 @@ function hover(target) {
|
|
|
3034
3310
|
const onLeave = () => setHovered(false);
|
|
3035
3311
|
target.addEventListener("pointerenter", onEnter);
|
|
3036
3312
|
target.addEventListener("pointerleave", onLeave);
|
|
3313
|
+
let disposed = false;
|
|
3037
3314
|
function dispose2() {
|
|
3315
|
+
if (disposed) return;
|
|
3316
|
+
disposed = true;
|
|
3317
|
+
unregisterDisposer(target, dispose2);
|
|
3038
3318
|
target.removeEventListener("pointerenter", onEnter);
|
|
3039
3319
|
target.removeEventListener("pointerleave", onLeave);
|
|
3040
3320
|
}
|
|
@@ -3048,21 +3328,37 @@ var _lock = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.scrollLock.v1"),
|
|
|
3048
3328
|
savedOverflow: null,
|
|
3049
3329
|
savedPaddingRight: null
|
|
3050
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
|
+
}
|
|
3051
3353
|
function scrollLock() {
|
|
3052
3354
|
let owned = false;
|
|
3053
3355
|
function lock() {
|
|
3054
3356
|
if (owned) return;
|
|
3357
|
+
if (_lock.count === 0 && typeof document !== "undefined") {
|
|
3358
|
+
applyBodyLock();
|
|
3359
|
+
}
|
|
3055
3360
|
owned = true;
|
|
3056
3361
|
_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
3362
|
}
|
|
3067
3363
|
function unlock() {
|
|
3068
3364
|
if (!owned) return;
|
|
@@ -3080,13 +3376,33 @@ function scrollLock() {
|
|
|
3080
3376
|
|
|
3081
3377
|
// src/platform/customElement.ts
|
|
3082
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");
|
|
3083
3381
|
function defineElement(name, component, options = {}) {
|
|
3084
3382
|
if (customElements.get(name)) return;
|
|
3085
3383
|
const observed = options.observedAttributes || [];
|
|
3086
3384
|
class SibuElement extends HTMLElement {
|
|
3087
3385
|
constructor() {
|
|
3088
3386
|
super();
|
|
3089
|
-
|
|
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;
|
|
3090
3406
|
if (options.shadow !== false) {
|
|
3091
3407
|
this._root = this.attachShadow({ mode: options.mode || "open" });
|
|
3092
3408
|
} else {
|
|
@@ -3096,32 +3412,106 @@ function defineElement(name, component, options = {}) {
|
|
|
3096
3412
|
static get observedAttributes() {
|
|
3097
3413
|
return observed;
|
|
3098
3414
|
}
|
|
3415
|
+
/** Whether rendering must wait for work already in progress. */
|
|
3416
|
+
get _busy() {
|
|
3417
|
+
return this._rendering || this._teardownDepth > 0;
|
|
3418
|
+
}
|
|
3099
3419
|
connectedCallback() {
|
|
3420
|
+
this._connected = true;
|
|
3421
|
+
if (this._busy) {
|
|
3422
|
+
this._dirty = true;
|
|
3423
|
+
return;
|
|
3424
|
+
}
|
|
3100
3425
|
this._render();
|
|
3101
3426
|
}
|
|
3102
3427
|
disconnectedCallback() {
|
|
3103
|
-
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
|
+
}
|
|
3104
3440
|
}
|
|
3105
|
-
attributeChangedCallback() {
|
|
3106
|
-
if (this.
|
|
3107
|
-
|
|
3441
|
+
attributeChangedCallback(_name, oldValue, newValue) {
|
|
3442
|
+
if (oldValue === newValue || !this._connected) return;
|
|
3443
|
+
if (this._busy) {
|
|
3444
|
+
this._dirty = true;
|
|
3445
|
+
return;
|
|
3108
3446
|
}
|
|
3447
|
+
this._render();
|
|
3109
3448
|
}
|
|
3110
3449
|
_teardown() {
|
|
3111
|
-
this._rendered = null;
|
|
3112
3450
|
replaceChildrenSafely(this._root);
|
|
3113
|
-
|
|
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
|
+
*/
|
|
3114
3462
|
_render() {
|
|
3115
|
-
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() {
|
|
3116
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 = [];
|
|
3117
3500
|
if (options.styles && this._root instanceof ShadowRoot) {
|
|
3118
3501
|
const styleEl = document.createElement("style");
|
|
3119
3502
|
styleEl.textContent = options.styles;
|
|
3120
|
-
|
|
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;
|
|
3121
3513
|
}
|
|
3122
|
-
|
|
3123
|
-
this._root.appendChild(el);
|
|
3124
|
-
this._rendered = el;
|
|
3514
|
+
this._root.replaceChildren(...next);
|
|
3125
3515
|
}
|
|
3126
3516
|
_getProps() {
|
|
3127
3517
|
const props = {};
|
|
@@ -3272,7 +3662,7 @@ function validateProps(props, schema) {
|
|
|
3272
3662
|
const errors = [];
|
|
3273
3663
|
for (const [key, def] of Object.entries(schema)) {
|
|
3274
3664
|
const propDef = typeof def === "function" ? { type: def } : def;
|
|
3275
|
-
if (result[key]
|
|
3665
|
+
if (result[key] === void 0 && propDef.default !== void 0) {
|
|
3276
3666
|
result[key] = typeof propDef.default === "function" ? propDef.default() : propDef.default;
|
|
3277
3667
|
}
|
|
3278
3668
|
if (propDef.required && result[key] == null) {
|
|
@@ -3298,7 +3688,7 @@ ${errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
|
3298
3688
|
for (const [key, def] of Object.entries(schema)) {
|
|
3299
3689
|
if (typeof def === "function") continue;
|
|
3300
3690
|
const fallback = def.default;
|
|
3301
|
-
if (result[key]
|
|
3691
|
+
if (result[key] === void 0 && fallback !== void 0) {
|
|
3302
3692
|
result[key] = typeof fallback === "function" ? fallback() : fallback;
|
|
3303
3693
|
}
|
|
3304
3694
|
}
|