sibujs 1.4.0 → 2.0.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 +105 -119
- package/dist/browser.cjs +288 -80
- package/dist/browser.d.cts +19 -9
- package/dist/browser.d.ts +19 -9
- package/dist/browser.js +6 -6
- package/dist/build.cjs +1019 -313
- package/dist/build.d.cts +1 -1
- package/dist/build.d.ts +1 -1
- package/dist/build.js +15 -13
- package/dist/cdn.global.js +17 -16
- package/dist/chunk-2RA7SHDA.js +65 -0
- package/dist/chunk-2UPRY23K.js +80 -0
- package/dist/chunk-3JHCYHWN.js +125 -0
- package/dist/{chunk-ZWKZCBO6.js → chunk-3LR7GLWQ.js} +154 -33
- package/dist/{chunk-3AIRKM3B.js → chunk-3NSGB5JN.js} +115 -34
- package/dist/{chunk-3ARAQO7B.js → chunk-52YJLLRO.js} +29 -6
- package/dist/chunk-54EDRCEF.js +93 -0
- package/dist/chunk-7JDB7I65.js +1327 -0
- package/dist/{chunk-WZSPOOER.js → chunk-CC65Y57T.js} +8 -5
- package/dist/{chunk-23VV7YD3.js → chunk-DFPFITST.js} +25 -30
- package/dist/{chunk-WR5D4EGH.js → chunk-GTBNNBJ6.js} +14 -2
- package/dist/chunk-HB24TBAF.js +121 -0
- package/dist/{chunk-CZUGLNJS.js → chunk-ITX6OO3F.js} +3 -3
- package/dist/{chunk-JAKHTMQU.js → chunk-JA6667UN.js} +206 -46
- package/dist/{chunk-77L6NL3X.js → chunk-JXMMDLBY.js} +306 -183
- package/dist/{chunk-3X2YG6YM.js → chunk-JYD2PWXH.js} +59 -28
- package/dist/{chunk-F3FA4F32.js → chunk-KLRMB5ZS.js} +135 -79
- package/dist/{chunk-5X6PP2UK.js → chunk-LMLD24FC.js} +2 -2
- package/dist/{chunk-M4NLBH4I.js → chunk-LYTCUZ7H.js} +3 -2
- package/dist/{chunk-TSOKIX5Z.js → chunk-MIUAXB7K.js} +126 -74
- package/dist/{chunk-QWZG56ET.js → chunk-ND2664SF.js} +558 -190
- package/dist/{chunk-JCI5M6U6.js → chunk-O2MNQFLP.js} +261 -79
- package/dist/{chunk-EWFVA3TJ.js → chunk-R73P76YZ.js} +1 -1
- package/dist/{chunk-2BYQDGN3.js → chunk-SAHNHTFC.js} +234 -63
- package/dist/chunk-UCS6AMJ7.js +79 -0
- package/dist/{chunk-ZD6OAMTH.js → chunk-VLPPXTYG.js} +90 -35
- package/dist/{chunk-OUZZEE4S.js → chunk-WOMYAHHI.js} +17 -11
- package/dist/{contracts-xo5ckdRP.d.cts → contracts-ey_Qh8ef.d.cts} +7 -8
- package/dist/{contracts-xo5ckdRP.d.ts → contracts-ey_Qh8ef.d.ts} +7 -8
- package/dist/{customElement-D2DJp_xn.d.cts → customElement-CPfIrbvg.d.cts} +18 -9
- package/dist/{customElement-D2DJp_xn.d.ts → customElement-CPfIrbvg.d.ts} +18 -9
- package/dist/data.cjs +452 -100
- package/dist/data.d.cts +20 -2
- package/dist/data.d.ts +20 -2
- package/dist/data.js +11 -9
- package/dist/devtools.cjs +535 -247
- package/dist/devtools.d.cts +1 -1
- package/dist/devtools.d.ts +1 -1
- package/dist/devtools.js +34 -30
- package/dist/ecosystem.cjs +499 -143
- package/dist/ecosystem.d.cts +13 -11
- package/dist/ecosystem.d.ts +13 -11
- package/dist/ecosystem.js +12 -11
- package/dist/extras.cjs +3639 -1629
- package/dist/extras.d.cts +11 -11
- package/dist/extras.d.ts +11 -11
- package/dist/extras.js +58 -45
- package/dist/index.cjs +1023 -313
- package/dist/index.d.cts +128 -55
- package/dist/index.d.ts +128 -55
- package/dist/index.js +28 -16
- package/dist/{introspect-BumjnBKr.d.cts → introspect-BWNjNw64.d.cts} +22 -2
- package/dist/{introspect-CZrlcaYy.d.ts → introspect-cY2pg9pW.d.ts} +22 -2
- package/dist/motion.cjs +90 -36
- package/dist/motion.d.cts +1 -1
- package/dist/motion.d.ts +1 -1
- package/dist/motion.js +4 -4
- package/dist/patterns.cjs +414 -81
- package/dist/patterns.d.cts +53 -20
- package/dist/patterns.d.ts +53 -20
- package/dist/patterns.js +7 -7
- package/dist/performance.cjs +364 -108
- package/dist/performance.d.cts +29 -17
- package/dist/performance.d.ts +29 -17
- package/dist/performance.js +13 -6
- package/dist/plugin-D30wlGW5.d.cts +71 -0
- package/dist/plugin-D30wlGW5.d.ts +71 -0
- package/dist/plugins.cjs +652 -271
- package/dist/plugins.d.cts +13 -6
- package/dist/plugins.d.ts +13 -6
- package/dist/plugins.js +116 -50
- package/dist/{ssr-Do_SiVoL.d.cts → ssr-CrVNy6Pa.d.cts} +9 -15
- package/dist/{ssr-Do_SiVoL.d.ts → ssr-CrVNy6Pa.d.ts} +9 -15
- package/dist/{ssr-4PBXAOO3.js → ssr-FXD2PPMC.js} +4 -3
- package/dist/ssr.cjs +648 -219
- package/dist/ssr.d.cts +27 -7
- package/dist/ssr.d.ts +27 -7
- package/dist/ssr.js +12 -11
- package/dist/{tagFactory-DaJ0YWX6.d.ts → tagFactory-S17H2qxu.d.cts} +9 -1
- package/dist/{tagFactory-DaJ0YWX6.d.cts → tagFactory-S17H2qxu.d.ts} +9 -1
- package/dist/testing.cjs +252 -63
- package/dist/testing.d.cts +17 -4
- package/dist/testing.d.ts +17 -4
- package/dist/testing.js +100 -44
- package/dist/ui.cjs +576 -168
- package/dist/ui.d.cts +13 -16
- package/dist/ui.d.ts +13 -16
- package/dist/ui.js +20 -17
- package/dist/widgets.cjs +1001 -93
- package/dist/widgets.d.cts +104 -2
- package/dist/widgets.d.ts +104 -2
- package/dist/widgets.js +9 -7
- package/package.json +8 -2
- package/dist/chunk-32DY64NT.js +0 -282
- package/dist/chunk-3CRQALYP.js +0 -877
- package/dist/chunk-4EI4AG32.js +0 -482
- package/dist/chunk-4MYMUBRS.js +0 -21
- package/dist/chunk-6HLLIF3K.js +0 -398
- package/dist/chunk-6LSNVCS2.js +0 -937
- package/dist/chunk-6SA3QQES.js +0 -61
- package/dist/chunk-7BF6TK55.js +0 -1097
- package/dist/chunk-7TQKR4PP.js +0 -294
- package/dist/chunk-7V26P53V.js +0 -712
- package/dist/chunk-AZ3ISID5.js +0 -298
- package/dist/chunk-B7SWRFUT.js +0 -332
- package/dist/chunk-BGN5ZMP4.js +0 -26
- package/dist/chunk-BTU3TJDS.js +0 -365
- package/dist/chunk-BW3WT46K.js +0 -937
- package/dist/chunk-C6KFWOFV.js +0 -616
- package/dist/chunk-CHF5OHIA.js +0 -61
- package/dist/chunk-CHJ27IGK.js +0 -26
- package/dist/chunk-CMBFNA7L.js +0 -27
- package/dist/chunk-DAHRH4ON.js +0 -331
- package/dist/chunk-DKOHBI74.js +0 -924
- package/dist/chunk-DTCOOBMX.js +0 -725
- package/dist/chunk-EBGIRKQY.js +0 -616
- package/dist/chunk-EUZND3CB.js +0 -27
- package/dist/chunk-EVCZO745.js +0 -365
- package/dist/chunk-FGOEVHY3.js +0 -60
- package/dist/chunk-G3BOQPVO.js +0 -365
- package/dist/chunk-GCOK2LC3.js +0 -282
- package/dist/chunk-HGMJFBC7.js +0 -654
- package/dist/chunk-K5ZUMYVS.js +0 -89
- package/dist/chunk-KQPDEVVS.js +0 -398
- package/dist/chunk-L6JRBDNS.js +0 -60
- package/dist/chunk-LA6KQEDU.js +0 -712
- package/dist/chunk-MDVXJWFN.js +0 -304
- package/dist/chunk-MEZVEBPN.js +0 -2008
- package/dist/chunk-MK4ERFYL.js +0 -2249
- package/dist/chunk-MLKGABMK.js +0 -9
- package/dist/chunk-MQ5GOYPH.js +0 -2249
- package/dist/chunk-N6IZB6KJ.js +0 -567
- package/dist/chunk-NEKUBFPT.js +0 -60
- package/dist/chunk-NHUC2QWH.js +0 -282
- package/dist/chunk-NMRUZALC.js +0 -1097
- package/dist/chunk-NYVAC6P5.js +0 -37
- package/dist/chunk-OF7UZIVB.js +0 -725
- package/dist/chunk-P6W3STU4.js +0 -2249
- package/dist/chunk-PBHF5WKN.js +0 -616
- package/dist/chunk-PTQJDMRT.js +0 -146
- package/dist/chunk-PZEGYCF5.js +0 -61
- package/dist/chunk-QBMDLBU2.js +0 -975
- package/dist/chunk-RQGQSLQK.js +0 -725
- package/dist/chunk-SDLZDHKP.js +0 -107
- package/dist/chunk-TNQWPPE6.js +0 -37
- package/dist/chunk-UHNL42EF.js +0 -2730
- package/dist/chunk-UNXCEF6S.js +0 -21
- package/dist/chunk-V2XTI523.js +0 -347
- package/dist/chunk-VAU366PN.js +0 -2241
- package/dist/chunk-VMVDTCXB.js +0 -712
- package/dist/chunk-VRW3FULF.js +0 -725
- package/dist/chunk-WADYRCO2.js +0 -304
- package/dist/chunk-WILQZRO4.js +0 -282
- package/dist/chunk-WUHJISPP.js +0 -298
- package/dist/chunk-XYU6TZOW.js +0 -182
- package/dist/chunk-Y6GP4QGG.js +0 -276
- package/dist/chunk-YECR7UIA.js +0 -347
- package/dist/chunk-YUTWTI4B.js +0 -654
- package/dist/chunk-Z65KYU7I.js +0 -26
- package/dist/chunk-Z6POF5YC.js +0 -975
- package/dist/chunk-ZBJP6WFL.js +0 -482
- package/dist/contracts-DDrwxvJ-.d.cts +0 -245
- package/dist/contracts-DDrwxvJ-.d.ts +0 -245
- package/dist/contracts-DOrhwbke.d.cts +0 -245
- package/dist/contracts-DOrhwbke.d.ts +0 -245
- package/dist/customElement-BKQfbSZQ.d.cts +0 -262
- package/dist/customElement-BKQfbSZQ.d.ts +0 -262
- package/dist/customElement-yz8uyk-0.d.cts +0 -308
- package/dist/customElement-yz8uyk-0.d.ts +0 -308
- package/dist/introspect-Cb0zgpi2.d.cts +0 -477
- package/dist/introspect-Y2xNXGSf.d.ts +0 -477
- package/dist/plugin-Bek4RhJY.d.cts +0 -43
- package/dist/plugin-Bek4RhJY.d.ts +0 -43
- package/dist/ssr-3RXHP5ES.js +0 -38
- package/dist/ssr-6GIMY5MX.js +0 -38
- package/dist/ssr-BA6sxxUd.d.cts +0 -135
- package/dist/ssr-BA6sxxUd.d.ts +0 -135
- package/dist/ssr-WKUPVSSK.js +0 -36
- package/dist/tagFactory-Dl8QCLga.d.cts +0 -23
- package/dist/tagFactory-Dl8QCLga.d.ts +0 -23
package/dist/widgets.cjs
CHANGED
|
@@ -39,12 +39,12 @@ function isDev() {
|
|
|
39
39
|
var _isDev = isDev();
|
|
40
40
|
function devAssert(condition, message) {
|
|
41
41
|
if (_isDev && !condition) {
|
|
42
|
-
throw new Error(`[
|
|
42
|
+
throw new Error(`[SibuJS] ${message}`);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
function devWarn(message) {
|
|
46
46
|
if (_isDev) {
|
|
47
|
-
console.warn(`[
|
|
47
|
+
console.warn(`[SibuJS] ${message}`);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -54,11 +54,11 @@ var subscriberStack = new Array(32);
|
|
|
54
54
|
var stackCapacity = 32;
|
|
55
55
|
var stackTop = -1;
|
|
56
56
|
var currentSubscriber = null;
|
|
57
|
-
var signalSubscribers = /* @__PURE__ */ new WeakMap();
|
|
58
57
|
var SUBS = "__s";
|
|
59
58
|
var notifyDepth = 0;
|
|
60
59
|
var pendingQueue = [];
|
|
61
60
|
var pendingSet = /* @__PURE__ */ new Set();
|
|
61
|
+
var propagateStack = [];
|
|
62
62
|
function safeInvoke(sub) {
|
|
63
63
|
try {
|
|
64
64
|
sub();
|
|
@@ -67,6 +67,15 @@ function safeInvoke(sub) {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
var trackingSuspended = false;
|
|
70
|
+
function retrack(effectFn, subscriber) {
|
|
71
|
+
const prev = currentSubscriber;
|
|
72
|
+
currentSubscriber = subscriber;
|
|
73
|
+
try {
|
|
74
|
+
effectFn();
|
|
75
|
+
} finally {
|
|
76
|
+
currentSubscriber = prev;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
70
79
|
function track(effectFn, subscriber) {
|
|
71
80
|
if (!subscriber) subscriber = effectFn;
|
|
72
81
|
cleanup(subscriber);
|
|
@@ -105,7 +114,6 @@ function recordDependency(signal2) {
|
|
|
105
114
|
let subs = signal2[SUBS];
|
|
106
115
|
if (!subs) {
|
|
107
116
|
subs = /* @__PURE__ */ new Set();
|
|
108
|
-
signalSubscribers.set(signal2, subs);
|
|
109
117
|
signal2[SUBS] = subs;
|
|
110
118
|
}
|
|
111
119
|
subs.add(currentSubscriber);
|
|
@@ -127,57 +135,71 @@ function queueSignalNotification(signal2) {
|
|
|
127
135
|
}
|
|
128
136
|
}
|
|
129
137
|
}
|
|
138
|
+
var maxDrainIterations = 1e5;
|
|
130
139
|
function drainNotificationQueue() {
|
|
131
140
|
if (notifyDepth > 0) return;
|
|
132
141
|
notifyDepth++;
|
|
133
142
|
try {
|
|
134
143
|
let i = 0;
|
|
135
144
|
while (i < pendingQueue.length) {
|
|
145
|
+
if (i >= maxDrainIterations) {
|
|
146
|
+
if (typeof console !== "undefined") {
|
|
147
|
+
console.error(
|
|
148
|
+
`[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
136
153
|
safeInvoke(pendingQueue[i]);
|
|
137
154
|
i++;
|
|
138
155
|
}
|
|
139
156
|
} finally {
|
|
140
|
-
pendingQueue.length = 0;
|
|
141
|
-
pendingSet.clear();
|
|
142
157
|
notifyDepth--;
|
|
158
|
+
if (notifyDepth === 0) {
|
|
159
|
+
pendingQueue.length = 0;
|
|
160
|
+
pendingSet.clear();
|
|
161
|
+
}
|
|
143
162
|
}
|
|
144
163
|
}
|
|
145
164
|
function propagateDirty(sub) {
|
|
146
165
|
sub();
|
|
147
|
-
|
|
148
|
-
|
|
166
|
+
const rootSig = sub._sig;
|
|
167
|
+
if (!rootSig) return;
|
|
168
|
+
const stack = propagateStack;
|
|
169
|
+
const baseLen = stack.length;
|
|
170
|
+
stack.push(rootSig);
|
|
171
|
+
while (stack.length > baseLen) {
|
|
172
|
+
const sig = stack.pop();
|
|
149
173
|
const first = sig.__f;
|
|
150
174
|
if (first) {
|
|
151
175
|
if (first._c) {
|
|
152
176
|
const nSig = first._sig;
|
|
153
|
-
nSig._d
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (!pendingSet.has(first)) {
|
|
177
|
+
if (!nSig._d) {
|
|
178
|
+
nSig._d = true;
|
|
179
|
+
stack.push(nSig);
|
|
180
|
+
}
|
|
181
|
+
} else if (!pendingSet.has(first)) {
|
|
158
182
|
pendingSet.add(first);
|
|
159
183
|
pendingQueue.push(first);
|
|
160
184
|
}
|
|
161
|
-
|
|
185
|
+
continue;
|
|
162
186
|
}
|
|
163
187
|
const subs = sig[SUBS];
|
|
164
|
-
if (!subs)
|
|
165
|
-
let nextSig;
|
|
188
|
+
if (!subs) continue;
|
|
166
189
|
for (const s of subs) {
|
|
167
190
|
if (s._c) {
|
|
168
|
-
s();
|
|
169
191
|
const nSig = s._sig;
|
|
170
|
-
if (nSig && !
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
192
|
+
if (nSig && !nSig._d) {
|
|
193
|
+
nSig._d = true;
|
|
194
|
+
stack.push(nSig);
|
|
195
|
+
} else if (!nSig) {
|
|
196
|
+
s();
|
|
174
197
|
}
|
|
175
198
|
} else if (!pendingSet.has(s)) {
|
|
176
199
|
pendingSet.add(s);
|
|
177
200
|
pendingQueue.push(s);
|
|
178
201
|
}
|
|
179
202
|
}
|
|
180
|
-
sig = nextSig;
|
|
181
203
|
}
|
|
182
204
|
}
|
|
183
205
|
function notifySubscribers(signal2) {
|
|
@@ -201,13 +223,23 @@ function notifySubscribers(signal2) {
|
|
|
201
223
|
}
|
|
202
224
|
let i = 0;
|
|
203
225
|
while (i < pendingQueue.length) {
|
|
226
|
+
if (i >= maxDrainIterations) {
|
|
227
|
+
if (typeof console !== "undefined") {
|
|
228
|
+
console.error(
|
|
229
|
+
`[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
204
234
|
safeInvoke(pendingQueue[i]);
|
|
205
235
|
i++;
|
|
206
236
|
}
|
|
207
237
|
} finally {
|
|
208
|
-
pendingQueue.length = 0;
|
|
209
|
-
pendingSet.clear();
|
|
210
238
|
notifyDepth--;
|
|
239
|
+
if (notifyDepth === 0) {
|
|
240
|
+
pendingQueue.length = 0;
|
|
241
|
+
pendingSet.clear();
|
|
242
|
+
}
|
|
211
243
|
}
|
|
212
244
|
return;
|
|
213
245
|
}
|
|
@@ -227,30 +259,48 @@ function notifySubscribers(signal2) {
|
|
|
227
259
|
notifyDepth++;
|
|
228
260
|
try {
|
|
229
261
|
let directCount = 0;
|
|
262
|
+
let hasComputedSub = false;
|
|
230
263
|
for (const sub of subs) {
|
|
264
|
+
if (sub._c) hasComputedSub = true;
|
|
231
265
|
pendingQueue[directCount++] = sub;
|
|
232
266
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
267
|
+
if (!hasComputedSub) {
|
|
268
|
+
for (let i2 = 0; i2 < directCount; i2++) {
|
|
269
|
+
safeInvoke(pendingQueue[i2]);
|
|
236
270
|
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
271
|
+
} else {
|
|
272
|
+
for (let i2 = 0; i2 < directCount; i2++) {
|
|
273
|
+
if (pendingQueue[i2]._c) {
|
|
274
|
+
propagateDirty(pendingQueue[i2]);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
for (let i2 = 0; i2 < directCount; i2++) {
|
|
278
|
+
const sub = pendingQueue[i2];
|
|
279
|
+
if (!sub._c && !pendingSet.has(sub)) {
|
|
280
|
+
pendingSet.add(sub);
|
|
281
|
+
safeInvoke(sub);
|
|
242
282
|
}
|
|
243
283
|
}
|
|
244
284
|
}
|
|
245
285
|
let i = directCount;
|
|
246
286
|
while (i < pendingQueue.length) {
|
|
287
|
+
if (i - directCount >= maxDrainIterations) {
|
|
288
|
+
if (typeof console !== "undefined") {
|
|
289
|
+
console.error(
|
|
290
|
+
`[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
247
295
|
safeInvoke(pendingQueue[i]);
|
|
248
296
|
i++;
|
|
249
297
|
}
|
|
250
298
|
} finally {
|
|
251
|
-
pendingQueue.length = 0;
|
|
252
|
-
pendingSet.clear();
|
|
253
299
|
notifyDepth--;
|
|
300
|
+
if (notifyDepth === 0) {
|
|
301
|
+
pendingQueue.length = 0;
|
|
302
|
+
pendingSet.clear();
|
|
303
|
+
}
|
|
254
304
|
}
|
|
255
305
|
}
|
|
256
306
|
function cleanup(subscriber) {
|
|
@@ -261,7 +311,9 @@ function cleanup(subscriber) {
|
|
|
261
311
|
if (subs) {
|
|
262
312
|
subs.delete(subscriber);
|
|
263
313
|
if (singleDep.__f === subscriber) {
|
|
264
|
-
singleDep.__f = void 0;
|
|
314
|
+
singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
|
|
315
|
+
} else if (subs.size === 1 && singleDep.__f === void 0) {
|
|
316
|
+
singleDep.__f = subs.values().next().value;
|
|
265
317
|
}
|
|
266
318
|
}
|
|
267
319
|
sub._dep = void 0;
|
|
@@ -274,7 +326,9 @@ function cleanup(subscriber) {
|
|
|
274
326
|
if (subs) {
|
|
275
327
|
subs.delete(subscriber);
|
|
276
328
|
if (signal2.__f === subscriber) {
|
|
277
|
-
signal2.__f = void 0;
|
|
329
|
+
signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
|
|
330
|
+
} else if (subs.size === 1 && signal2.__f === void 0) {
|
|
331
|
+
signal2.__f = subs.values().next().value;
|
|
278
332
|
}
|
|
279
333
|
}
|
|
280
334
|
}
|
|
@@ -285,6 +339,7 @@ function cleanup(subscriber) {
|
|
|
285
339
|
function derived(getter, options) {
|
|
286
340
|
devAssert(typeof getter === "function", "derived: argument must be a getter function.");
|
|
287
341
|
const debugName = options?.name;
|
|
342
|
+
const equals = options?.equals;
|
|
288
343
|
const cs = {};
|
|
289
344
|
cs._d = false;
|
|
290
345
|
cs._g = getter;
|
|
@@ -295,25 +350,56 @@ function derived(getter, options) {
|
|
|
295
350
|
markDirty._c = 1;
|
|
296
351
|
markDirty._sig = cs;
|
|
297
352
|
track(() => {
|
|
298
|
-
|
|
299
|
-
|
|
353
|
+
let threw = true;
|
|
354
|
+
try {
|
|
355
|
+
cs._v = getter();
|
|
356
|
+
cs._d = false;
|
|
357
|
+
threw = false;
|
|
358
|
+
} finally {
|
|
359
|
+
if (threw) cs._d = true;
|
|
360
|
+
}
|
|
300
361
|
}, markDirty);
|
|
301
362
|
const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
363
|
+
let evaluating = false;
|
|
302
364
|
function computedGetter() {
|
|
365
|
+
if (evaluating) {
|
|
366
|
+
throw new Error(
|
|
367
|
+
`[SibuJS] Circular dependency detected in derived${debugName ? ` "${debugName}"` : ""}. A derived signal cannot read itself (directly or through a chain).`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
303
370
|
if (trackingSuspended) {
|
|
304
371
|
if (cs._d) {
|
|
305
|
-
|
|
306
|
-
|
|
372
|
+
evaluating = true;
|
|
373
|
+
let threw = true;
|
|
374
|
+
try {
|
|
375
|
+
retrack(() => {
|
|
376
|
+
cs._v = getter();
|
|
377
|
+
cs._d = false;
|
|
378
|
+
threw = false;
|
|
379
|
+
}, markDirty);
|
|
380
|
+
} finally {
|
|
381
|
+
evaluating = false;
|
|
382
|
+
if (threw) cs._d = true;
|
|
383
|
+
}
|
|
307
384
|
}
|
|
308
385
|
return cs._v;
|
|
309
386
|
}
|
|
310
387
|
recordDependency(cs);
|
|
311
388
|
if (cs._d) {
|
|
312
389
|
const oldValue = cs._v;
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
390
|
+
evaluating = true;
|
|
391
|
+
let threw = true;
|
|
392
|
+
try {
|
|
393
|
+
retrack(() => {
|
|
394
|
+
const next = getter();
|
|
395
|
+
cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
|
|
396
|
+
cs._d = false;
|
|
397
|
+
threw = false;
|
|
398
|
+
}, markDirty);
|
|
399
|
+
} finally {
|
|
400
|
+
evaluating = false;
|
|
401
|
+
if (threw) cs._d = true;
|
|
402
|
+
}
|
|
317
403
|
if (hook && oldValue !== cs._v) {
|
|
318
404
|
hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
|
|
319
405
|
}
|
|
@@ -329,6 +415,121 @@ function derived(getter, options) {
|
|
|
329
415
|
return computedGetter;
|
|
330
416
|
}
|
|
331
417
|
|
|
418
|
+
// src/core/ssr-context.ts
|
|
419
|
+
var als = null;
|
|
420
|
+
try {
|
|
421
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
422
|
+
const req = Function("return typeof require==='function'?require:null")();
|
|
423
|
+
if (req) {
|
|
424
|
+
const mod = req("node:async_hooks");
|
|
425
|
+
als = new mod.AsyncLocalStorage();
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
} catch {
|
|
429
|
+
als = null;
|
|
430
|
+
}
|
|
431
|
+
var fallbackStore = { ssr: false, suspenseIdCounter: 0 };
|
|
432
|
+
function getSSRStore() {
|
|
433
|
+
if (als) {
|
|
434
|
+
const s = als.getStore();
|
|
435
|
+
if (s) return s;
|
|
436
|
+
}
|
|
437
|
+
return fallbackStore;
|
|
438
|
+
}
|
|
439
|
+
function isSSR() {
|
|
440
|
+
return getSSRStore().ssr;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// src/core/signals/effect.ts
|
|
444
|
+
var _g = globalThis;
|
|
445
|
+
function effect(effectFn, options) {
|
|
446
|
+
devAssert(typeof effectFn === "function", "effect: argument must be a function.");
|
|
447
|
+
if (isSSR()) return () => {
|
|
448
|
+
};
|
|
449
|
+
const onError = options?.onError;
|
|
450
|
+
let userCleanups = [];
|
|
451
|
+
const onCleanup = (fn) => {
|
|
452
|
+
userCleanups.push(fn);
|
|
453
|
+
};
|
|
454
|
+
const runUserCleanups = () => {
|
|
455
|
+
if (userCleanups.length === 0) return;
|
|
456
|
+
const list = userCleanups;
|
|
457
|
+
userCleanups = [];
|
|
458
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
459
|
+
try {
|
|
460
|
+
list[i]();
|
|
461
|
+
} catch (err) {
|
|
462
|
+
if (typeof console !== "undefined") {
|
|
463
|
+
console.warn("[SibuJS effect] onCleanup threw:", err);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
const invokeBody = () => effectFn(onCleanup);
|
|
469
|
+
const wrappedFn = onError ? () => {
|
|
470
|
+
try {
|
|
471
|
+
invokeBody();
|
|
472
|
+
} catch (err) {
|
|
473
|
+
onError(err);
|
|
474
|
+
}
|
|
475
|
+
} : invokeBody;
|
|
476
|
+
let cleanupHandle = () => {
|
|
477
|
+
};
|
|
478
|
+
let running = false;
|
|
479
|
+
const subscriber = () => {
|
|
480
|
+
if (running) {
|
|
481
|
+
if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
|
|
482
|
+
console.warn(
|
|
483
|
+
"[SibuJS] effect re-entered itself while running \u2014 the triggering update will be ignored. Wrap mutual writes in `batch()` or split the effect to avoid this."
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
running = true;
|
|
489
|
+
try {
|
|
490
|
+
runUserCleanups();
|
|
491
|
+
cleanupHandle();
|
|
492
|
+
cleanupHandle = track(wrappedFn, subscriber);
|
|
493
|
+
} finally {
|
|
494
|
+
running = false;
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
running = true;
|
|
498
|
+
try {
|
|
499
|
+
cleanupHandle = track(wrappedFn, subscriber);
|
|
500
|
+
} finally {
|
|
501
|
+
running = false;
|
|
502
|
+
}
|
|
503
|
+
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
504
|
+
if (hook) hook.emit("effect:create", { effectFn });
|
|
505
|
+
let disposed = false;
|
|
506
|
+
return () => {
|
|
507
|
+
if (disposed) return;
|
|
508
|
+
disposed = true;
|
|
509
|
+
const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
510
|
+
if (h) {
|
|
511
|
+
try {
|
|
512
|
+
h.emit("effect:destroy", { effectFn });
|
|
513
|
+
} catch {
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
try {
|
|
517
|
+
runUserCleanups();
|
|
518
|
+
} catch (err) {
|
|
519
|
+
if (typeof console !== "undefined") {
|
|
520
|
+
console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
cleanupHandle();
|
|
525
|
+
} catch (err) {
|
|
526
|
+
if (typeof console !== "undefined") {
|
|
527
|
+
console.warn("[SibuJS effect] dispose threw:", err);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
332
533
|
// src/reactivity/batch.ts
|
|
333
534
|
var batchDepth = 0;
|
|
334
535
|
var pendingSignals = /* @__PURE__ */ new Set();
|
|
@@ -349,15 +550,18 @@ function enqueueBatchedSignal(signal2) {
|
|
|
349
550
|
return true;
|
|
350
551
|
}
|
|
351
552
|
function flushBatch() {
|
|
352
|
-
|
|
353
|
-
|
|
553
|
+
try {
|
|
554
|
+
for (const signal2 of pendingSignals) {
|
|
555
|
+
queueSignalNotification(signal2);
|
|
556
|
+
}
|
|
557
|
+
} finally {
|
|
558
|
+
pendingSignals.clear();
|
|
354
559
|
}
|
|
355
|
-
pendingSignals.clear();
|
|
356
560
|
drainNotificationQueue();
|
|
357
561
|
}
|
|
358
562
|
|
|
359
563
|
// src/core/signals/signal.ts
|
|
360
|
-
var
|
|
564
|
+
var _g2 = globalThis;
|
|
361
565
|
var _isDev3 = isDev();
|
|
362
566
|
function signal(initial, options) {
|
|
363
567
|
const state = { value: initial };
|
|
@@ -378,7 +582,7 @@ function signal(initial, options) {
|
|
|
378
582
|
if (_isDev3) {
|
|
379
583
|
const oldValue = state.value;
|
|
380
584
|
state.value = newValue;
|
|
381
|
-
const hook =
|
|
585
|
+
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
382
586
|
if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
|
|
383
587
|
} else {
|
|
384
588
|
state.value = newValue;
|
|
@@ -388,18 +592,12 @@ function signal(initial, options) {
|
|
|
388
592
|
}
|
|
389
593
|
}
|
|
390
594
|
if (_isDev3) {
|
|
391
|
-
const hook =
|
|
595
|
+
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
392
596
|
if (hook) hook.emit("signal:create", { signal: state, name: debugName, getter: get, initial });
|
|
393
597
|
}
|
|
394
598
|
return [get, set];
|
|
395
599
|
}
|
|
396
600
|
|
|
397
|
-
// src/core/ssr-context.ts
|
|
398
|
-
var ssrMode = false;
|
|
399
|
-
function isSSR() {
|
|
400
|
-
return ssrMode;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
601
|
// src/core/signals/watch.ts
|
|
404
602
|
function watch(getter, callback) {
|
|
405
603
|
devAssert(typeof getter === "function", "watch: first argument must be a getter function.");
|
|
@@ -425,6 +623,8 @@ function watch(getter, callback) {
|
|
|
425
623
|
}
|
|
426
624
|
|
|
427
625
|
// src/widgets/Combobox.ts
|
|
626
|
+
var comboboxIdCounter = 0;
|
|
627
|
+
var boundComboboxes = /* @__PURE__ */ new WeakMap();
|
|
428
628
|
function combobox(options) {
|
|
429
629
|
const { items, filterFn, itemToString } = options;
|
|
430
630
|
const defaultFilterFn = (item, q) => {
|
|
@@ -481,6 +681,92 @@ function combobox(options) {
|
|
|
481
681
|
function close() {
|
|
482
682
|
setIsOpen(false);
|
|
483
683
|
}
|
|
684
|
+
function bind(els) {
|
|
685
|
+
const existing = boundComboboxes.get(els.input);
|
|
686
|
+
if (existing) return existing;
|
|
687
|
+
const listboxId = `sibu-combobox-listbox-${++comboboxIdCounter}`;
|
|
688
|
+
els.listbox.id = listboxId;
|
|
689
|
+
els.listbox.setAttribute("role", "listbox");
|
|
690
|
+
els.input.setAttribute("role", "combobox");
|
|
691
|
+
els.input.setAttribute("aria-autocomplete", "list");
|
|
692
|
+
els.input.setAttribute("aria-controls", listboxId);
|
|
693
|
+
const fxTeardown = effect(() => {
|
|
694
|
+
const open2 = isOpen();
|
|
695
|
+
els.input.setAttribute("aria-expanded", open2 ? "true" : "false");
|
|
696
|
+
els.listbox.hidden = !open2;
|
|
697
|
+
const idx = highlightedIndex();
|
|
698
|
+
const filtered = filteredItems();
|
|
699
|
+
let activeId = "";
|
|
700
|
+
for (let i = 0; i < filtered.length; i++) {
|
|
701
|
+
const optEl = els.option(filtered[i], i);
|
|
702
|
+
if (!optEl) continue;
|
|
703
|
+
if (!optEl.id) optEl.id = `${listboxId}-opt-${i}`;
|
|
704
|
+
optEl.setAttribute("role", "option");
|
|
705
|
+
const isHighlighted = i === idx;
|
|
706
|
+
optEl.setAttribute("aria-selected", isHighlighted ? "true" : "false");
|
|
707
|
+
if (isHighlighted) activeId = optEl.id;
|
|
708
|
+
}
|
|
709
|
+
if (activeId) els.input.setAttribute("aria-activedescendant", activeId);
|
|
710
|
+
else els.input.removeAttribute("aria-activedescendant");
|
|
711
|
+
});
|
|
712
|
+
const onInput = () => {
|
|
713
|
+
setQuery(els.input.value);
|
|
714
|
+
open();
|
|
715
|
+
};
|
|
716
|
+
const onKey = (e) => {
|
|
717
|
+
if (e.key === "ArrowDown") {
|
|
718
|
+
e.preventDefault();
|
|
719
|
+
if (!isOpen()) open();
|
|
720
|
+
highlightNext();
|
|
721
|
+
} else if (e.key === "ArrowUp") {
|
|
722
|
+
e.preventDefault();
|
|
723
|
+
if (!isOpen()) open();
|
|
724
|
+
highlightPrev();
|
|
725
|
+
} else if (e.key === "Enter") {
|
|
726
|
+
if (highlightedIndex() >= 0) {
|
|
727
|
+
e.preventDefault();
|
|
728
|
+
selectHighlighted();
|
|
729
|
+
}
|
|
730
|
+
} else if (e.key === "Escape") {
|
|
731
|
+
e.preventDefault();
|
|
732
|
+
close();
|
|
733
|
+
} else if (e.key === "Home") {
|
|
734
|
+
e.preventDefault();
|
|
735
|
+
if (filteredItems().length > 0) setHighlightedIndex(0);
|
|
736
|
+
} else if (e.key === "End") {
|
|
737
|
+
e.preventDefault();
|
|
738
|
+
const len = filteredItems().length;
|
|
739
|
+
if (len > 0) setHighlightedIndex(len - 1);
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
let blurTimer = null;
|
|
743
|
+
const onFocus = () => open();
|
|
744
|
+
const onBlur = () => {
|
|
745
|
+
if (blurTimer !== null) clearTimeout(blurTimer);
|
|
746
|
+
blurTimer = setTimeout(() => {
|
|
747
|
+
blurTimer = null;
|
|
748
|
+
if (document.activeElement !== els.input) close();
|
|
749
|
+
}, 100);
|
|
750
|
+
};
|
|
751
|
+
els.input.addEventListener("input", onInput);
|
|
752
|
+
els.input.addEventListener("keydown", onKey);
|
|
753
|
+
els.input.addEventListener("focus", onFocus);
|
|
754
|
+
els.input.addEventListener("blur", onBlur);
|
|
755
|
+
const teardown = () => {
|
|
756
|
+
boundComboboxes.delete(els.input);
|
|
757
|
+
fxTeardown();
|
|
758
|
+
els.input.removeEventListener("input", onInput);
|
|
759
|
+
els.input.removeEventListener("keydown", onKey);
|
|
760
|
+
els.input.removeEventListener("focus", onFocus);
|
|
761
|
+
els.input.removeEventListener("blur", onBlur);
|
|
762
|
+
if (blurTimer !== null) {
|
|
763
|
+
clearTimeout(blurTimer);
|
|
764
|
+
blurTimer = null;
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
boundComboboxes.set(els.input, teardown);
|
|
768
|
+
return teardown;
|
|
769
|
+
}
|
|
484
770
|
return {
|
|
485
771
|
query,
|
|
486
772
|
setQuery,
|
|
@@ -493,11 +779,13 @@ function combobox(options) {
|
|
|
493
779
|
selectHighlighted,
|
|
494
780
|
isOpen,
|
|
495
781
|
open,
|
|
496
|
-
close
|
|
782
|
+
close,
|
|
783
|
+
bind
|
|
497
784
|
};
|
|
498
785
|
}
|
|
499
786
|
|
|
500
787
|
// src/widgets/Tabs.ts
|
|
788
|
+
var boundTablists = /* @__PURE__ */ new WeakMap();
|
|
501
789
|
function tabs(options) {
|
|
502
790
|
const { tabs: tabDefs, defaultTab } = options;
|
|
503
791
|
const initialTab = defaultTab ?? tabDefs.find((t) => !t.disabled)?.id ?? tabDefs[0]?.id ?? "";
|
|
@@ -544,6 +832,118 @@ function tabs(options) {
|
|
|
544
832
|
function isActive(id) {
|
|
545
833
|
return activeTab() === id;
|
|
546
834
|
}
|
|
835
|
+
function bind(els) {
|
|
836
|
+
const existing = boundTablists.get(els.tablist);
|
|
837
|
+
if (existing) return existing;
|
|
838
|
+
const restore = [];
|
|
839
|
+
const prevTablistRole = els.tablist.getAttribute("role");
|
|
840
|
+
els.tablist.setAttribute("role", "tablist");
|
|
841
|
+
restore.push(() => {
|
|
842
|
+
if (prevTablistRole === null) els.tablist.removeAttribute("role");
|
|
843
|
+
else els.tablist.setAttribute("role", prevTablistRole);
|
|
844
|
+
});
|
|
845
|
+
for (const def of tabDefs) {
|
|
846
|
+
const tabEl = els.tabs[def.id];
|
|
847
|
+
if (!tabEl) continue;
|
|
848
|
+
const prevRole = tabEl.getAttribute("role");
|
|
849
|
+
const prevId = tabEl.id;
|
|
850
|
+
const prevDisabled = tabEl.getAttribute("aria-disabled");
|
|
851
|
+
const prevControls = tabEl.getAttribute("aria-controls");
|
|
852
|
+
tabEl.setAttribute("role", "tab");
|
|
853
|
+
tabEl.setAttribute("id", `sibu-tab-${def.id}`);
|
|
854
|
+
if (def.disabled) tabEl.setAttribute("aria-disabled", "true");
|
|
855
|
+
const panelEl = els.panels?.[def.id];
|
|
856
|
+
let prevPanelRole = null;
|
|
857
|
+
let prevPanelId = "";
|
|
858
|
+
let prevPanelLabelledBy = null;
|
|
859
|
+
if (panelEl) {
|
|
860
|
+
prevPanelRole = panelEl.getAttribute("role");
|
|
861
|
+
prevPanelId = panelEl.id;
|
|
862
|
+
prevPanelLabelledBy = panelEl.getAttribute("aria-labelledby");
|
|
863
|
+
panelEl.setAttribute("role", "tabpanel");
|
|
864
|
+
panelEl.setAttribute("id", `sibu-tabpanel-${def.id}`);
|
|
865
|
+
panelEl.setAttribute("aria-labelledby", `sibu-tab-${def.id}`);
|
|
866
|
+
tabEl.setAttribute("aria-controls", `sibu-tabpanel-${def.id}`);
|
|
867
|
+
}
|
|
868
|
+
restore.push(() => {
|
|
869
|
+
if (prevRole === null) tabEl.removeAttribute("role");
|
|
870
|
+
else tabEl.setAttribute("role", prevRole);
|
|
871
|
+
if (prevId === "") tabEl.removeAttribute("id");
|
|
872
|
+
else tabEl.id = prevId;
|
|
873
|
+
if (prevDisabled === null) tabEl.removeAttribute("aria-disabled");
|
|
874
|
+
else tabEl.setAttribute("aria-disabled", prevDisabled);
|
|
875
|
+
if (prevControls === null) tabEl.removeAttribute("aria-controls");
|
|
876
|
+
else tabEl.setAttribute("aria-controls", prevControls);
|
|
877
|
+
tabEl.removeAttribute("aria-selected");
|
|
878
|
+
tabEl.removeAttribute("tabindex");
|
|
879
|
+
if (panelEl) {
|
|
880
|
+
if (prevPanelRole === null) panelEl.removeAttribute("role");
|
|
881
|
+
else panelEl.setAttribute("role", prevPanelRole);
|
|
882
|
+
if (prevPanelId === "") panelEl.removeAttribute("id");
|
|
883
|
+
else panelEl.id = prevPanelId;
|
|
884
|
+
if (prevPanelLabelledBy === null) panelEl.removeAttribute("aria-labelledby");
|
|
885
|
+
else panelEl.setAttribute("aria-labelledby", prevPanelLabelledBy);
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
const fxTeardown = effect(() => {
|
|
890
|
+
const active = activeTab();
|
|
891
|
+
for (const def of tabDefs) {
|
|
892
|
+
const tabEl = els.tabs[def.id];
|
|
893
|
+
if (!tabEl) continue;
|
|
894
|
+
const isAct = def.id === active;
|
|
895
|
+
tabEl.setAttribute("aria-selected", isAct ? "true" : "false");
|
|
896
|
+
tabEl.tabIndex = isAct ? 0 : -1;
|
|
897
|
+
const panelEl = els.panels?.[def.id];
|
|
898
|
+
if (panelEl) panelEl.hidden = !isAct;
|
|
899
|
+
}
|
|
900
|
+
});
|
|
901
|
+
const onKey = (e) => {
|
|
902
|
+
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
|
|
903
|
+
e.preventDefault();
|
|
904
|
+
nextTab();
|
|
905
|
+
els.tabs[activeTab()]?.focus();
|
|
906
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
|
|
907
|
+
e.preventDefault();
|
|
908
|
+
prevTab();
|
|
909
|
+
els.tabs[activeTab()]?.focus();
|
|
910
|
+
} else if (e.key === "Home") {
|
|
911
|
+
e.preventDefault();
|
|
912
|
+
const first = tabDefs.find((t) => !t.disabled);
|
|
913
|
+
if (first) {
|
|
914
|
+
setActiveTabState(first.id);
|
|
915
|
+
els.tabs[first.id]?.focus();
|
|
916
|
+
}
|
|
917
|
+
} else if (e.key === "End") {
|
|
918
|
+
e.preventDefault();
|
|
919
|
+
for (let i = tabDefs.length - 1; i >= 0; i--) {
|
|
920
|
+
if (!tabDefs[i].disabled) {
|
|
921
|
+
setActiveTabState(tabDefs[i].id);
|
|
922
|
+
els.tabs[tabDefs[i].id]?.focus();
|
|
923
|
+
break;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
els.tablist.addEventListener("keydown", onKey);
|
|
929
|
+
const clickHandlers = [];
|
|
930
|
+
for (const def of tabDefs) {
|
|
931
|
+
const tabEl = els.tabs[def.id];
|
|
932
|
+
if (!tabEl) continue;
|
|
933
|
+
const fn = () => setActiveTab(def.id);
|
|
934
|
+
tabEl.addEventListener("click", fn);
|
|
935
|
+
clickHandlers.push({ el: tabEl, fn });
|
|
936
|
+
}
|
|
937
|
+
const teardown = () => {
|
|
938
|
+
boundTablists.delete(els.tablist);
|
|
939
|
+
fxTeardown();
|
|
940
|
+
els.tablist.removeEventListener("keydown", onKey);
|
|
941
|
+
for (const { el, fn } of clickHandlers) el.removeEventListener("click", fn);
|
|
942
|
+
for (const r of restore) r();
|
|
943
|
+
};
|
|
944
|
+
boundTablists.set(els.tablist, teardown);
|
|
945
|
+
return teardown;
|
|
946
|
+
}
|
|
547
947
|
return {
|
|
548
948
|
activeTab,
|
|
549
949
|
setActiveTab,
|
|
@@ -551,11 +951,13 @@ function tabs(options) {
|
|
|
551
951
|
nextTab,
|
|
552
952
|
prevTab,
|
|
553
953
|
/** Reactive check — use inside class/nodes bindings for per-tab reactivity */
|
|
554
|
-
isActive
|
|
954
|
+
isActive,
|
|
955
|
+
bind
|
|
555
956
|
};
|
|
556
957
|
}
|
|
557
958
|
|
|
558
959
|
// src/widgets/Accordion.ts
|
|
960
|
+
var boundAccordions = /* @__PURE__ */ new WeakMap();
|
|
559
961
|
function accordion(options) {
|
|
560
962
|
const { items: itemDefs, multiple = false, defaultExpanded = [] } = options;
|
|
561
963
|
const [expandedIds, setExpandedIds] = signal(new Set(defaultExpanded));
|
|
@@ -602,6 +1004,86 @@ function accordion(options) {
|
|
|
602
1004
|
function isExpanded(id) {
|
|
603
1005
|
return expandedIds().has(id);
|
|
604
1006
|
}
|
|
1007
|
+
function bind(els) {
|
|
1008
|
+
const idempotencyKey = els.root ?? (itemDefs.length > 0 ? els.triggers[itemDefs[0].id] : void 0);
|
|
1009
|
+
if (idempotencyKey) {
|
|
1010
|
+
const existing = boundAccordions.get(idempotencyKey);
|
|
1011
|
+
if (existing) return existing;
|
|
1012
|
+
}
|
|
1013
|
+
const restore = [];
|
|
1014
|
+
for (const item of itemDefs) {
|
|
1015
|
+
const trig = els.triggers[item.id];
|
|
1016
|
+
const panel = els.panels[item.id];
|
|
1017
|
+
if (!trig) continue;
|
|
1018
|
+
const prevTrigId = trig.id;
|
|
1019
|
+
const prevTrigControls = trig.getAttribute("aria-controls");
|
|
1020
|
+
trig.id = `sibu-accordion-trigger-${item.id}`;
|
|
1021
|
+
let prevPanelRole = null;
|
|
1022
|
+
let prevPanelId = "";
|
|
1023
|
+
let prevPanelLabelledBy = null;
|
|
1024
|
+
if (panel) {
|
|
1025
|
+
prevPanelRole = panel.getAttribute("role");
|
|
1026
|
+
prevPanelId = panel.id;
|
|
1027
|
+
prevPanelLabelledBy = panel.getAttribute("aria-labelledby");
|
|
1028
|
+
panel.setAttribute("role", "region");
|
|
1029
|
+
panel.id = `sibu-accordion-panel-${item.id}`;
|
|
1030
|
+
panel.setAttribute("aria-labelledby", trig.id);
|
|
1031
|
+
trig.setAttribute("aria-controls", panel.id);
|
|
1032
|
+
}
|
|
1033
|
+
restore.push(() => {
|
|
1034
|
+
if (prevTrigId === "") trig.removeAttribute("id");
|
|
1035
|
+
else trig.id = prevTrigId;
|
|
1036
|
+
if (prevTrigControls === null) trig.removeAttribute("aria-controls");
|
|
1037
|
+
else trig.setAttribute("aria-controls", prevTrigControls);
|
|
1038
|
+
trig.removeAttribute("aria-expanded");
|
|
1039
|
+
if (panel) {
|
|
1040
|
+
if (prevPanelRole === null) panel.removeAttribute("role");
|
|
1041
|
+
else panel.setAttribute("role", prevPanelRole);
|
|
1042
|
+
if (prevPanelId === "") panel.removeAttribute("id");
|
|
1043
|
+
else panel.id = prevPanelId;
|
|
1044
|
+
if (prevPanelLabelledBy === null) panel.removeAttribute("aria-labelledby");
|
|
1045
|
+
else panel.setAttribute("aria-labelledby", prevPanelLabelledBy);
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
const fxTeardown = effect(() => {
|
|
1050
|
+
const ids = expandedIds();
|
|
1051
|
+
for (const item of itemDefs) {
|
|
1052
|
+
const trig = els.triggers[item.id];
|
|
1053
|
+
const panel = els.panels[item.id];
|
|
1054
|
+
if (!trig) continue;
|
|
1055
|
+
const expanded = ids.has(item.id);
|
|
1056
|
+
trig.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
1057
|
+
if (panel) panel.hidden = !expanded;
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
const handlers = [];
|
|
1061
|
+
for (const item of itemDefs) {
|
|
1062
|
+
const trig = els.triggers[item.id];
|
|
1063
|
+
if (!trig) continue;
|
|
1064
|
+
const click = () => toggle(item.id);
|
|
1065
|
+
const key = (e) => {
|
|
1066
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1067
|
+
e.preventDefault();
|
|
1068
|
+
toggle(item.id);
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
trig.addEventListener("click", click);
|
|
1072
|
+
trig.addEventListener("keydown", key);
|
|
1073
|
+
handlers.push({ el: trig, click, key });
|
|
1074
|
+
}
|
|
1075
|
+
const teardown = () => {
|
|
1076
|
+
if (idempotencyKey) boundAccordions.delete(idempotencyKey);
|
|
1077
|
+
fxTeardown();
|
|
1078
|
+
for (const { el, click, key } of handlers) {
|
|
1079
|
+
el.removeEventListener("click", click);
|
|
1080
|
+
el.removeEventListener("keydown", key);
|
|
1081
|
+
}
|
|
1082
|
+
for (const r of restore) r();
|
|
1083
|
+
};
|
|
1084
|
+
if (idempotencyKey) boundAccordions.set(idempotencyKey, teardown);
|
|
1085
|
+
return teardown;
|
|
1086
|
+
}
|
|
605
1087
|
return {
|
|
606
1088
|
items,
|
|
607
1089
|
toggle,
|
|
@@ -610,11 +1092,14 @@ function accordion(options) {
|
|
|
610
1092
|
expandAll,
|
|
611
1093
|
collapseAll,
|
|
612
1094
|
/** Reactive check — use inside class/nodes bindings for per-item reactivity */
|
|
613
|
-
isExpanded
|
|
1095
|
+
isExpanded,
|
|
1096
|
+
bind
|
|
614
1097
|
};
|
|
615
1098
|
}
|
|
616
1099
|
|
|
617
1100
|
// src/widgets/Popover.ts
|
|
1101
|
+
var popoverIdCounter = 0;
|
|
1102
|
+
var boundPopovers = /* @__PURE__ */ new WeakMap();
|
|
618
1103
|
function popover() {
|
|
619
1104
|
const [isOpen, setIsOpen] = signal(false);
|
|
620
1105
|
function open() {
|
|
@@ -626,12 +1111,88 @@ function popover() {
|
|
|
626
1111
|
function toggle() {
|
|
627
1112
|
setIsOpen((prev) => !prev);
|
|
628
1113
|
}
|
|
629
|
-
|
|
1114
|
+
function bind(els) {
|
|
1115
|
+
const existing = boundPopovers.get(els.trigger);
|
|
1116
|
+
if (existing) return existing;
|
|
1117
|
+
const id = `sibu-popover-${++popoverIdCounter}`;
|
|
1118
|
+
const prevPopoverRole = els.popover.getAttribute("role");
|
|
1119
|
+
const prevPopoverId = els.popover.id;
|
|
1120
|
+
const prevLabelledBy = els.popover.getAttribute("aria-labelledby");
|
|
1121
|
+
const prevTriggerHaspopup = els.trigger.getAttribute("aria-haspopup");
|
|
1122
|
+
const prevTriggerControls = els.trigger.getAttribute("aria-controls");
|
|
1123
|
+
els.popover.setAttribute("role", "dialog");
|
|
1124
|
+
els.popover.id = id;
|
|
1125
|
+
els.trigger.setAttribute("aria-haspopup", "dialog");
|
|
1126
|
+
els.trigger.setAttribute("aria-controls", id);
|
|
1127
|
+
let assignedLabelId = null;
|
|
1128
|
+
if (els.labelledBy) {
|
|
1129
|
+
if (!els.labelledBy.id) {
|
|
1130
|
+
els.labelledBy.id = `${id}-label`;
|
|
1131
|
+
assignedLabelId = els.labelledBy.id;
|
|
1132
|
+
}
|
|
1133
|
+
els.popover.setAttribute("aria-labelledby", els.labelledBy.id);
|
|
1134
|
+
}
|
|
1135
|
+
const fxTeardown = effect(() => {
|
|
1136
|
+
const open2 = isOpen();
|
|
1137
|
+
els.trigger.setAttribute("aria-expanded", open2 ? "true" : "false");
|
|
1138
|
+
els.popover.hidden = !open2;
|
|
1139
|
+
});
|
|
1140
|
+
const onTriggerClick = (e) => {
|
|
1141
|
+
e.preventDefault();
|
|
1142
|
+
toggle();
|
|
1143
|
+
};
|
|
1144
|
+
const onKey = (e) => {
|
|
1145
|
+
if (e.key === "Escape" && isOpen()) {
|
|
1146
|
+
e.stopPropagation();
|
|
1147
|
+
close();
|
|
1148
|
+
els.trigger.focus();
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
1151
|
+
const onDocPointer = (e) => {
|
|
1152
|
+
if (!isOpen()) return;
|
|
1153
|
+
const t = e.target;
|
|
1154
|
+
if (!t) return;
|
|
1155
|
+
if (els.trigger.contains(t) || els.popover.contains(t)) return;
|
|
1156
|
+
close();
|
|
1157
|
+
};
|
|
1158
|
+
els.trigger.addEventListener("click", onTriggerClick);
|
|
1159
|
+
els.popover.addEventListener("keydown", onKey);
|
|
1160
|
+
els.trigger.addEventListener("keydown", onKey);
|
|
1161
|
+
document.addEventListener("pointerdown", onDocPointer);
|
|
1162
|
+
const teardown = () => {
|
|
1163
|
+
boundPopovers.delete(els.trigger);
|
|
1164
|
+
fxTeardown();
|
|
1165
|
+
els.trigger.removeEventListener("click", onTriggerClick);
|
|
1166
|
+
els.popover.removeEventListener("keydown", onKey);
|
|
1167
|
+
els.trigger.removeEventListener("keydown", onKey);
|
|
1168
|
+
document.removeEventListener("pointerdown", onDocPointer);
|
|
1169
|
+
if (prevPopoverRole === null) els.popover.removeAttribute("role");
|
|
1170
|
+
else els.popover.setAttribute("role", prevPopoverRole);
|
|
1171
|
+
if (prevPopoverId === "") els.popover.removeAttribute("id");
|
|
1172
|
+
else els.popover.id = prevPopoverId;
|
|
1173
|
+
if (prevLabelledBy === null) els.popover.removeAttribute("aria-labelledby");
|
|
1174
|
+
else els.popover.setAttribute("aria-labelledby", prevLabelledBy);
|
|
1175
|
+
if (assignedLabelId && els.labelledBy?.id === assignedLabelId) {
|
|
1176
|
+
els.labelledBy.removeAttribute("id");
|
|
1177
|
+
}
|
|
1178
|
+
if (prevTriggerHaspopup === null) els.trigger.removeAttribute("aria-haspopup");
|
|
1179
|
+
else els.trigger.setAttribute("aria-haspopup", prevTriggerHaspopup);
|
|
1180
|
+
if (prevTriggerControls === null) els.trigger.removeAttribute("aria-controls");
|
|
1181
|
+
else els.trigger.setAttribute("aria-controls", prevTriggerControls);
|
|
1182
|
+
els.trigger.removeAttribute("aria-expanded");
|
|
1183
|
+
};
|
|
1184
|
+
boundPopovers.set(els.trigger, teardown);
|
|
1185
|
+
return teardown;
|
|
1186
|
+
}
|
|
1187
|
+
return { isOpen, open, close, toggle, bind };
|
|
630
1188
|
}
|
|
631
1189
|
|
|
632
1190
|
// src/widgets/Select.ts
|
|
1191
|
+
var selectIdCounter = 0;
|
|
1192
|
+
var boundSelects = /* @__PURE__ */ new WeakMap();
|
|
633
1193
|
function select(options) {
|
|
634
|
-
const { items, multiple = false } = options;
|
|
1194
|
+
const { items, multiple = false, itemToString, isDisabled } = options;
|
|
1195
|
+
const isItemDisabled = isDisabled ?? (() => false);
|
|
635
1196
|
const [selectedItems, setSelectedItems] = signal([]);
|
|
636
1197
|
const [isOpen, setIsOpen] = signal(false);
|
|
637
1198
|
const [highlightedIndex, setHighlightedIndex] = signal(-1);
|
|
@@ -640,6 +1201,7 @@ function select(options) {
|
|
|
640
1201
|
return sel.length > 0 ? sel[sel.length - 1] : null;
|
|
641
1202
|
});
|
|
642
1203
|
function select2(item) {
|
|
1204
|
+
if (isItemDisabled(item)) return;
|
|
643
1205
|
if (multiple) {
|
|
644
1206
|
setSelectedItems((prev) => {
|
|
645
1207
|
if (prev.includes(item)) return prev;
|
|
@@ -671,18 +1233,28 @@ function select(options) {
|
|
|
671
1233
|
function close() {
|
|
672
1234
|
setIsOpen(false);
|
|
673
1235
|
}
|
|
1236
|
+
function nextEnabled(from, dir) {
|
|
1237
|
+
const len = items.length;
|
|
1238
|
+
if (len === 0) return -1;
|
|
1239
|
+
let i = from;
|
|
1240
|
+
for (let n = 0; n < len; n++) {
|
|
1241
|
+
i = (i + dir + len) % len;
|
|
1242
|
+
if (!isItemDisabled(items[i])) return i;
|
|
1243
|
+
}
|
|
1244
|
+
return -1;
|
|
1245
|
+
}
|
|
674
1246
|
function highlightNext() {
|
|
675
1247
|
if (items.length === 0) return;
|
|
676
1248
|
setHighlightedIndex((prev) => {
|
|
677
|
-
const
|
|
678
|
-
return
|
|
1249
|
+
const n = nextEnabled(prev < 0 ? -1 : prev, 1);
|
|
1250
|
+
return n === -1 ? prev : n;
|
|
679
1251
|
});
|
|
680
1252
|
}
|
|
681
1253
|
function highlightPrev() {
|
|
682
1254
|
if (items.length === 0) return;
|
|
683
1255
|
setHighlightedIndex((prev) => {
|
|
684
|
-
const
|
|
685
|
-
return
|
|
1256
|
+
const n = nextEnabled(prev < 0 ? items.length : prev, -1);
|
|
1257
|
+
return n === -1 ? prev : n;
|
|
686
1258
|
});
|
|
687
1259
|
}
|
|
688
1260
|
function selectHighlighted() {
|
|
@@ -694,6 +1266,73 @@ function select(options) {
|
|
|
694
1266
|
function clear() {
|
|
695
1267
|
setSelectedItems([]);
|
|
696
1268
|
}
|
|
1269
|
+
function bind(els) {
|
|
1270
|
+
const existing = boundSelects.get(els.listbox);
|
|
1271
|
+
if (existing) return existing;
|
|
1272
|
+
const listboxId = `sibu-select-${++selectIdCounter}`;
|
|
1273
|
+
els.listbox.id = listboxId;
|
|
1274
|
+
els.listbox.setAttribute("role", "listbox");
|
|
1275
|
+
els.listbox.setAttribute("aria-multiselectable", multiple ? "true" : "false");
|
|
1276
|
+
if (els.listbox.tabIndex < 0) els.listbox.tabIndex = 0;
|
|
1277
|
+
const toStr = els.itemToString ?? itemToString ?? ((it) => String(it));
|
|
1278
|
+
const fxTeardown = effect(() => {
|
|
1279
|
+
const idx = highlightedIndex();
|
|
1280
|
+
const sel = selectedItems();
|
|
1281
|
+
let activeId = "";
|
|
1282
|
+
for (let i = 0; i < items.length; i++) {
|
|
1283
|
+
const optEl = els.option(items[i], i);
|
|
1284
|
+
if (!optEl) continue;
|
|
1285
|
+
if (!optEl.id) optEl.id = `${listboxId}-opt-${i}`;
|
|
1286
|
+
optEl.setAttribute("role", "option");
|
|
1287
|
+
optEl.setAttribute("aria-selected", sel.includes(items[i]) ? "true" : "false");
|
|
1288
|
+
if (isItemDisabled(items[i])) optEl.setAttribute("aria-disabled", "true");
|
|
1289
|
+
else optEl.removeAttribute("aria-disabled");
|
|
1290
|
+
if (i === idx) activeId = optEl.id;
|
|
1291
|
+
}
|
|
1292
|
+
if (activeId) els.listbox.setAttribute("aria-activedescendant", activeId);
|
|
1293
|
+
else els.listbox.removeAttribute("aria-activedescendant");
|
|
1294
|
+
});
|
|
1295
|
+
let typeBuffer = "";
|
|
1296
|
+
let typeTimer = null;
|
|
1297
|
+
const onKey = (e) => {
|
|
1298
|
+
if (e.key === "ArrowDown") {
|
|
1299
|
+
e.preventDefault();
|
|
1300
|
+
highlightNext();
|
|
1301
|
+
} else if (e.key === "ArrowUp") {
|
|
1302
|
+
e.preventDefault();
|
|
1303
|
+
highlightPrev();
|
|
1304
|
+
} else if (e.key === "Home") {
|
|
1305
|
+
e.preventDefault();
|
|
1306
|
+
if (items.length > 0) setHighlightedIndex(0);
|
|
1307
|
+
} else if (e.key === "End") {
|
|
1308
|
+
e.preventDefault();
|
|
1309
|
+
if (items.length > 0) setHighlightedIndex(items.length - 1);
|
|
1310
|
+
} else if (e.key === "Enter" || e.key === " ") {
|
|
1311
|
+
if (highlightedIndex() >= 0) {
|
|
1312
|
+
e.preventDefault();
|
|
1313
|
+
selectHighlighted();
|
|
1314
|
+
}
|
|
1315
|
+
} else if (e.key.length === 1 && /\S/.test(e.key)) {
|
|
1316
|
+
typeBuffer += e.key.toLowerCase();
|
|
1317
|
+
if (typeTimer !== null) clearTimeout(typeTimer);
|
|
1318
|
+
typeTimer = setTimeout(() => {
|
|
1319
|
+
typeBuffer = "";
|
|
1320
|
+
typeTimer = null;
|
|
1321
|
+
}, 500);
|
|
1322
|
+
const found = items.findIndex((it) => !isItemDisabled(it) && toStr(it).toLowerCase().startsWith(typeBuffer));
|
|
1323
|
+
if (found !== -1) setHighlightedIndex(found);
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
els.listbox.addEventListener("keydown", onKey);
|
|
1327
|
+
const teardown = () => {
|
|
1328
|
+
boundSelects.delete(els.listbox);
|
|
1329
|
+
fxTeardown();
|
|
1330
|
+
els.listbox.removeEventListener("keydown", onKey);
|
|
1331
|
+
if (typeTimer !== null) clearTimeout(typeTimer);
|
|
1332
|
+
};
|
|
1333
|
+
boundSelects.set(els.listbox, teardown);
|
|
1334
|
+
return teardown;
|
|
1335
|
+
}
|
|
697
1336
|
return {
|
|
698
1337
|
selectedItems,
|
|
699
1338
|
selectedItem,
|
|
@@ -708,21 +1347,28 @@ function select(options) {
|
|
|
708
1347
|
highlightNext,
|
|
709
1348
|
highlightPrev,
|
|
710
1349
|
selectHighlighted,
|
|
711
|
-
clear
|
|
1350
|
+
clear,
|
|
1351
|
+
bind
|
|
712
1352
|
};
|
|
713
1353
|
}
|
|
714
1354
|
|
|
715
1355
|
// src/widgets/Tooltip.ts
|
|
1356
|
+
var tooltipIdCounter = 0;
|
|
1357
|
+
var boundTriggers = /* @__PURE__ */ new WeakMap();
|
|
716
1358
|
function tooltip(options) {
|
|
717
1359
|
const delay = options?.delay ?? 0;
|
|
1360
|
+
const hideDelay = options?.hideDelay ?? 100;
|
|
718
1361
|
const [isVisible, setIsVisible] = signal(false);
|
|
719
1362
|
const [content, setContent] = signal("");
|
|
720
1363
|
let delayTimer = null;
|
|
1364
|
+
let hideTimer = null;
|
|
721
1365
|
function show() {
|
|
1366
|
+
if (hideTimer !== null) {
|
|
1367
|
+
clearTimeout(hideTimer);
|
|
1368
|
+
hideTimer = null;
|
|
1369
|
+
}
|
|
722
1370
|
if (delay > 0) {
|
|
723
|
-
if (delayTimer !== null)
|
|
724
|
-
clearTimeout(delayTimer);
|
|
725
|
-
}
|
|
1371
|
+
if (delayTimer !== null) clearTimeout(delayTimer);
|
|
726
1372
|
delayTimer = setTimeout(() => {
|
|
727
1373
|
setIsVisible(true);
|
|
728
1374
|
delayTimer = null;
|
|
@@ -738,10 +1384,82 @@ function tooltip(options) {
|
|
|
738
1384
|
}
|
|
739
1385
|
setIsVisible(false);
|
|
740
1386
|
}
|
|
741
|
-
|
|
1387
|
+
function scheduleHide() {
|
|
1388
|
+
if (delayTimer !== null) {
|
|
1389
|
+
clearTimeout(delayTimer);
|
|
1390
|
+
delayTimer = null;
|
|
1391
|
+
}
|
|
1392
|
+
if (hideTimer !== null) clearTimeout(hideTimer);
|
|
1393
|
+
hideTimer = setTimeout(() => {
|
|
1394
|
+
hideTimer = null;
|
|
1395
|
+
setIsVisible(false);
|
|
1396
|
+
}, hideDelay);
|
|
1397
|
+
}
|
|
1398
|
+
function bind(els) {
|
|
1399
|
+
const existing = boundTriggers.get(els.trigger);
|
|
1400
|
+
if (existing) return existing;
|
|
1401
|
+
const id = `sibu-tooltip-${++tooltipIdCounter}`;
|
|
1402
|
+
els.tooltip.setAttribute("role", "tooltip");
|
|
1403
|
+
els.tooltip.id = id;
|
|
1404
|
+
const prevDescribedBy = els.trigger.getAttribute("aria-describedby");
|
|
1405
|
+
els.trigger.setAttribute("aria-describedby", prevDescribedBy ? `${prevDescribedBy} ${id}` : id);
|
|
1406
|
+
const fxTeardown = effect(() => {
|
|
1407
|
+
els.tooltip.hidden = !isVisible();
|
|
1408
|
+
});
|
|
1409
|
+
const onTriggerEnter = () => show();
|
|
1410
|
+
const onTriggerLeave = () => scheduleHide();
|
|
1411
|
+
const onTooltipEnter = () => {
|
|
1412
|
+
if (hideTimer !== null) {
|
|
1413
|
+
clearTimeout(hideTimer);
|
|
1414
|
+
hideTimer = null;
|
|
1415
|
+
}
|
|
1416
|
+
};
|
|
1417
|
+
const onTooltipLeave = () => scheduleHide();
|
|
1418
|
+
const onFocus = () => show();
|
|
1419
|
+
const onBlur = () => hide();
|
|
1420
|
+
const onKey = (e) => {
|
|
1421
|
+
if (e.key === "Escape" && isVisible()) {
|
|
1422
|
+
e.stopPropagation();
|
|
1423
|
+
hide();
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
els.trigger.addEventListener("pointerenter", onTriggerEnter);
|
|
1427
|
+
els.trigger.addEventListener("pointerleave", onTriggerLeave);
|
|
1428
|
+
els.trigger.addEventListener("focus", onFocus);
|
|
1429
|
+
els.trigger.addEventListener("blur", onBlur);
|
|
1430
|
+
els.trigger.addEventListener("keydown", onKey);
|
|
1431
|
+
els.tooltip.addEventListener("pointerenter", onTooltipEnter);
|
|
1432
|
+
els.tooltip.addEventListener("pointerleave", onTooltipLeave);
|
|
1433
|
+
const teardown = () => {
|
|
1434
|
+
boundTriggers.delete(els.trigger);
|
|
1435
|
+
fxTeardown();
|
|
1436
|
+
els.trigger.removeEventListener("pointerenter", onTriggerEnter);
|
|
1437
|
+
els.trigger.removeEventListener("pointerleave", onTriggerLeave);
|
|
1438
|
+
els.trigger.removeEventListener("focus", onFocus);
|
|
1439
|
+
els.trigger.removeEventListener("blur", onBlur);
|
|
1440
|
+
els.trigger.removeEventListener("keydown", onKey);
|
|
1441
|
+
els.tooltip.removeEventListener("pointerenter", onTooltipEnter);
|
|
1442
|
+
els.tooltip.removeEventListener("pointerleave", onTooltipLeave);
|
|
1443
|
+
const cur = els.trigger.getAttribute("aria-describedby");
|
|
1444
|
+
if (cur) {
|
|
1445
|
+
const remaining = cur.split(/\s+/).filter((part) => part && part !== id);
|
|
1446
|
+
if (remaining.length > 0) els.trigger.setAttribute("aria-describedby", remaining.join(" "));
|
|
1447
|
+
else els.trigger.removeAttribute("aria-describedby");
|
|
1448
|
+
} else if (prevDescribedBy) {
|
|
1449
|
+
els.trigger.setAttribute("aria-describedby", prevDescribedBy);
|
|
1450
|
+
}
|
|
1451
|
+
if (delayTimer !== null) clearTimeout(delayTimer);
|
|
1452
|
+
if (hideTimer !== null) clearTimeout(hideTimer);
|
|
1453
|
+
};
|
|
1454
|
+
boundTriggers.set(els.trigger, teardown);
|
|
1455
|
+
return teardown;
|
|
1456
|
+
}
|
|
1457
|
+
return { isVisible, show, hide, content, setContent, bind };
|
|
742
1458
|
}
|
|
743
1459
|
|
|
744
1460
|
// src/widgets/FileUpload.ts
|
|
1461
|
+
var fileUploadIdCounter = 0;
|
|
1462
|
+
var boundFileUploads = /* @__PURE__ */ new WeakMap();
|
|
745
1463
|
function fileUpload(options) {
|
|
746
1464
|
const accept = options?.accept;
|
|
747
1465
|
const multiple = options?.multiple ?? false;
|
|
@@ -809,6 +1527,107 @@ function fileUpload(options) {
|
|
|
809
1527
|
setErrors([]);
|
|
810
1528
|
});
|
|
811
1529
|
}
|
|
1530
|
+
function bind(els) {
|
|
1531
|
+
const existing = boundFileUploads.get(els.input);
|
|
1532
|
+
if (existing) return existing;
|
|
1533
|
+
const id = `sibu-fileupload-${++fileUploadIdCounter}`;
|
|
1534
|
+
const restore = [];
|
|
1535
|
+
if (accept) els.input.accept = accept;
|
|
1536
|
+
els.input.multiple = multiple;
|
|
1537
|
+
let hintId = null;
|
|
1538
|
+
if (els.hint) {
|
|
1539
|
+
const assignedHintId = !els.hint.id;
|
|
1540
|
+
if (assignedHintId) els.hint.id = `${id}-hint`;
|
|
1541
|
+
hintId = els.hint.id;
|
|
1542
|
+
const prev = els.input.getAttribute("aria-describedby");
|
|
1543
|
+
els.input.setAttribute("aria-describedby", prev ? `${prev} ${hintId}` : hintId);
|
|
1544
|
+
restore.push(() => {
|
|
1545
|
+
const cur = els.input.getAttribute("aria-describedby");
|
|
1546
|
+
if (cur) {
|
|
1547
|
+
const parts = cur.split(/\s+/).filter((p) => p && p !== hintId);
|
|
1548
|
+
if (parts.length > 0) els.input.setAttribute("aria-describedby", parts.join(" "));
|
|
1549
|
+
else els.input.removeAttribute("aria-describedby");
|
|
1550
|
+
}
|
|
1551
|
+
if (assignedHintId && els.hint && els.hint.id === hintId) els.hint.removeAttribute("id");
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
if (els.errorRegion) {
|
|
1555
|
+
const prevRole = els.errorRegion.getAttribute("role");
|
|
1556
|
+
const prevLive = els.errorRegion.getAttribute("aria-live");
|
|
1557
|
+
els.errorRegion.setAttribute("role", "status");
|
|
1558
|
+
els.errorRegion.setAttribute("aria-live", "polite");
|
|
1559
|
+
restore.push(() => {
|
|
1560
|
+
if (prevRole === null) els.errorRegion.removeAttribute("role");
|
|
1561
|
+
else els.errorRegion.setAttribute("role", prevRole);
|
|
1562
|
+
if (prevLive === null) els.errorRegion.removeAttribute("aria-live");
|
|
1563
|
+
else els.errorRegion.setAttribute("aria-live", prevLive);
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
if (els.dropZone) {
|
|
1567
|
+
const prevDzRole = els.dropZone.getAttribute("role");
|
|
1568
|
+
const prevDzLabel = els.dropZone.getAttribute("aria-label");
|
|
1569
|
+
const prevDzTabindex = els.dropZone.hasAttribute("tabindex") ? els.dropZone.getAttribute("tabindex") : null;
|
|
1570
|
+
els.dropZone.setAttribute("role", "button");
|
|
1571
|
+
els.dropZone.setAttribute("aria-label", "File drop zone \u2014 click or press Enter to browse");
|
|
1572
|
+
if (els.dropZone.tabIndex < 0) els.dropZone.tabIndex = 0;
|
|
1573
|
+
restore.push(() => {
|
|
1574
|
+
if (prevDzRole === null) els.dropZone.removeAttribute("role");
|
|
1575
|
+
else els.dropZone.setAttribute("role", prevDzRole);
|
|
1576
|
+
if (prevDzLabel === null) els.dropZone.removeAttribute("aria-label");
|
|
1577
|
+
else els.dropZone.setAttribute("aria-label", prevDzLabel);
|
|
1578
|
+
if (prevDzTabindex === null) els.dropZone.removeAttribute("tabindex");
|
|
1579
|
+
else els.dropZone.setAttribute("tabindex", prevDzTabindex);
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
const fxTeardown = effect(() => {
|
|
1583
|
+
const errs = errors();
|
|
1584
|
+
if (els.errorRegion) els.errorRegion.textContent = errs.join(". ");
|
|
1585
|
+
if (els.dropZone) els.dropZone.setAttribute("data-drag-over", isDragOver() ? "true" : "false");
|
|
1586
|
+
});
|
|
1587
|
+
const onChange = () => {
|
|
1588
|
+
if (els.input.files) addFiles(els.input.files);
|
|
1589
|
+
};
|
|
1590
|
+
const onDropClick = () => els.input.click();
|
|
1591
|
+
const onDropKey = (e) => {
|
|
1592
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1593
|
+
e.preventDefault();
|
|
1594
|
+
els.input.click();
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
const onDragOver = (e) => {
|
|
1598
|
+
e.preventDefault();
|
|
1599
|
+
setDragOver(true);
|
|
1600
|
+
};
|
|
1601
|
+
const onDragLeave = () => setDragOver(false);
|
|
1602
|
+
const onDrop = (e) => {
|
|
1603
|
+
e.preventDefault();
|
|
1604
|
+
setDragOver(false);
|
|
1605
|
+
if (e.dataTransfer?.files) addFiles(e.dataTransfer.files);
|
|
1606
|
+
};
|
|
1607
|
+
els.input.addEventListener("change", onChange);
|
|
1608
|
+
if (els.dropZone) {
|
|
1609
|
+
els.dropZone.addEventListener("click", onDropClick);
|
|
1610
|
+
els.dropZone.addEventListener("keydown", onDropKey);
|
|
1611
|
+
els.dropZone.addEventListener("dragover", onDragOver);
|
|
1612
|
+
els.dropZone.addEventListener("dragleave", onDragLeave);
|
|
1613
|
+
els.dropZone.addEventListener("drop", onDrop);
|
|
1614
|
+
}
|
|
1615
|
+
const teardown = () => {
|
|
1616
|
+
boundFileUploads.delete(els.input);
|
|
1617
|
+
fxTeardown();
|
|
1618
|
+
els.input.removeEventListener("change", onChange);
|
|
1619
|
+
if (els.dropZone) {
|
|
1620
|
+
els.dropZone.removeEventListener("click", onDropClick);
|
|
1621
|
+
els.dropZone.removeEventListener("keydown", onDropKey);
|
|
1622
|
+
els.dropZone.removeEventListener("dragover", onDragOver);
|
|
1623
|
+
els.dropZone.removeEventListener("dragleave", onDragLeave);
|
|
1624
|
+
els.dropZone.removeEventListener("drop", onDrop);
|
|
1625
|
+
}
|
|
1626
|
+
for (const r of restore) r();
|
|
1627
|
+
};
|
|
1628
|
+
boundFileUploads.set(els.input, teardown);
|
|
1629
|
+
return teardown;
|
|
1630
|
+
}
|
|
812
1631
|
return {
|
|
813
1632
|
files,
|
|
814
1633
|
addFiles,
|
|
@@ -816,14 +1635,36 @@ function fileUpload(options) {
|
|
|
816
1635
|
clear,
|
|
817
1636
|
errors,
|
|
818
1637
|
isDragOver,
|
|
819
|
-
setDragOver
|
|
1638
|
+
setDragOver,
|
|
1639
|
+
bind
|
|
820
1640
|
};
|
|
821
1641
|
}
|
|
822
1642
|
|
|
1643
|
+
// src/utils/sanitize.ts
|
|
1644
|
+
function stripHtml(html) {
|
|
1645
|
+
return String(html).replace(/<[^>]*>/g, "");
|
|
1646
|
+
}
|
|
1647
|
+
|
|
823
1648
|
// src/widgets/contentEditable.ts
|
|
824
1649
|
function contentEditable() {
|
|
825
|
-
const [content,
|
|
1650
|
+
const [content, setContentInternal] = signal("");
|
|
826
1651
|
const [isFocused, setFocused] = signal(false);
|
|
1652
|
+
function setContent(input) {
|
|
1653
|
+
if (typeof input === "string") {
|
|
1654
|
+
setContentInternal(input);
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
if (typeof input.text === "string") {
|
|
1658
|
+
setContentInternal(input.text);
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
if (typeof input.html === "string") {
|
|
1662
|
+
const shouldSanitize = input.sanitize !== false;
|
|
1663
|
+
setContentInternal(shouldSanitize ? stripHtml(input.html) : input.html);
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
setContentInternal("");
|
|
1667
|
+
}
|
|
827
1668
|
function wrapSelection(tagName) {
|
|
828
1669
|
if (typeof window === "undefined") return;
|
|
829
1670
|
const selection = window.getSelection();
|
|
@@ -900,6 +1741,7 @@ function contentEditable() {
|
|
|
900
1741
|
}
|
|
901
1742
|
|
|
902
1743
|
// src/widgets/datePicker.ts
|
|
1744
|
+
var boundDatePickers = /* @__PURE__ */ new WeakMap();
|
|
903
1745
|
function datePicker(options) {
|
|
904
1746
|
const minDate = options?.minDate;
|
|
905
1747
|
const maxDate = options?.maxDate;
|
|
@@ -923,33 +1765,20 @@ function datePicker(options) {
|
|
|
923
1765
|
setSelectedDate(date);
|
|
924
1766
|
}
|
|
925
1767
|
}
|
|
1768
|
+
function shiftMonth(prev, delta) {
|
|
1769
|
+
return new Date(prev.getFullYear(), prev.getMonth() + delta, 1);
|
|
1770
|
+
}
|
|
926
1771
|
function nextMonth() {
|
|
927
|
-
setViewDate((prev) =>
|
|
928
|
-
const next = new Date(prev);
|
|
929
|
-
next.setMonth(next.getMonth() + 1);
|
|
930
|
-
return next;
|
|
931
|
-
});
|
|
1772
|
+
setViewDate((prev) => shiftMonth(prev, 1));
|
|
932
1773
|
}
|
|
933
1774
|
function prevMonth() {
|
|
934
|
-
setViewDate((prev) =>
|
|
935
|
-
const next = new Date(prev);
|
|
936
|
-
next.setMonth(next.getMonth() - 1);
|
|
937
|
-
return next;
|
|
938
|
-
});
|
|
1775
|
+
setViewDate((prev) => shiftMonth(prev, -1));
|
|
939
1776
|
}
|
|
940
1777
|
function nextYear() {
|
|
941
|
-
setViewDate((prev) =>
|
|
942
|
-
const next = new Date(prev);
|
|
943
|
-
next.setFullYear(next.getFullYear() + 1);
|
|
944
|
-
return next;
|
|
945
|
-
});
|
|
1778
|
+
setViewDate((prev) => new Date(prev.getFullYear() + 1, prev.getMonth(), 1));
|
|
946
1779
|
}
|
|
947
1780
|
function prevYear() {
|
|
948
|
-
setViewDate((prev) =>
|
|
949
|
-
const next = new Date(prev);
|
|
950
|
-
next.setFullYear(next.getFullYear() - 1);
|
|
951
|
-
return next;
|
|
952
|
-
});
|
|
1781
|
+
setViewDate((prev) => new Date(prev.getFullYear() - 1, prev.getMonth(), 1));
|
|
953
1782
|
}
|
|
954
1783
|
const daysInMonth = derived(() => {
|
|
955
1784
|
const vd = viewDate();
|
|
@@ -1014,8 +1843,87 @@ function datePicker(options) {
|
|
|
1014
1843
|
daysInMonth,
|
|
1015
1844
|
isDateDisabled,
|
|
1016
1845
|
/** Reactive check — use inside class bindings for per-day reactivity */
|
|
1017
|
-
isSelected
|
|
1846
|
+
isSelected,
|
|
1847
|
+
bind
|
|
1018
1848
|
};
|
|
1849
|
+
function bind(els) {
|
|
1850
|
+
const existing = boundDatePickers.get(els.grid);
|
|
1851
|
+
if (existing) return existing;
|
|
1852
|
+
els.grid.setAttribute("role", "grid");
|
|
1853
|
+
if (els.grid.tabIndex < 0) els.grid.tabIndex = 0;
|
|
1854
|
+
const fxTeardown = effect(() => {
|
|
1855
|
+
const sel = selectedDate();
|
|
1856
|
+
const view = viewDate();
|
|
1857
|
+
const days = daysInMonth();
|
|
1858
|
+
for (const d of days) {
|
|
1859
|
+
const cell = els.cell(d.date);
|
|
1860
|
+
if (!cell) continue;
|
|
1861
|
+
cell.setAttribute("role", "gridcell");
|
|
1862
|
+
cell.setAttribute("aria-selected", sel && isSameCalendarDay(sel, d.date) ? "true" : "false");
|
|
1863
|
+
if (d.isDisabled) cell.setAttribute("aria-disabled", "true");
|
|
1864
|
+
else cell.removeAttribute("aria-disabled");
|
|
1865
|
+
cell.tabIndex = isSameCalendarDay(view, d.date) ? 0 : -1;
|
|
1866
|
+
}
|
|
1867
|
+
});
|
|
1868
|
+
function isSameCalendarDay(a, b) {
|
|
1869
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
1870
|
+
}
|
|
1871
|
+
function shiftDays(delta) {
|
|
1872
|
+
setViewDate((prev) => new Date(prev.getFullYear(), prev.getMonth(), prev.getDate() + delta));
|
|
1873
|
+
}
|
|
1874
|
+
const onKey = (e) => {
|
|
1875
|
+
switch (e.key) {
|
|
1876
|
+
case "ArrowLeft":
|
|
1877
|
+
e.preventDefault();
|
|
1878
|
+
shiftDays(-1);
|
|
1879
|
+
break;
|
|
1880
|
+
case "ArrowRight":
|
|
1881
|
+
e.preventDefault();
|
|
1882
|
+
shiftDays(1);
|
|
1883
|
+
break;
|
|
1884
|
+
case "ArrowUp":
|
|
1885
|
+
e.preventDefault();
|
|
1886
|
+
shiftDays(-7);
|
|
1887
|
+
break;
|
|
1888
|
+
case "ArrowDown":
|
|
1889
|
+
e.preventDefault();
|
|
1890
|
+
shiftDays(7);
|
|
1891
|
+
break;
|
|
1892
|
+
case "Home":
|
|
1893
|
+
e.preventDefault();
|
|
1894
|
+
setViewDate((p) => new Date(p.getFullYear(), p.getMonth(), p.getDate() - p.getDay()));
|
|
1895
|
+
break;
|
|
1896
|
+
case "End": {
|
|
1897
|
+
e.preventDefault();
|
|
1898
|
+
setViewDate((p) => new Date(p.getFullYear(), p.getMonth(), p.getDate() + (6 - p.getDay())));
|
|
1899
|
+
break;
|
|
1900
|
+
}
|
|
1901
|
+
case "PageUp":
|
|
1902
|
+
e.preventDefault();
|
|
1903
|
+
if (e.shiftKey) prevYear();
|
|
1904
|
+
else prevMonth();
|
|
1905
|
+
break;
|
|
1906
|
+
case "PageDown":
|
|
1907
|
+
e.preventDefault();
|
|
1908
|
+
if (e.shiftKey) nextYear();
|
|
1909
|
+
else nextMonth();
|
|
1910
|
+
break;
|
|
1911
|
+
case "Enter":
|
|
1912
|
+
case " ":
|
|
1913
|
+
e.preventDefault();
|
|
1914
|
+
select2(viewDate());
|
|
1915
|
+
break;
|
|
1916
|
+
}
|
|
1917
|
+
};
|
|
1918
|
+
els.grid.addEventListener("keydown", onKey);
|
|
1919
|
+
const teardown = () => {
|
|
1920
|
+
boundDatePickers.delete(els.grid);
|
|
1921
|
+
fxTeardown();
|
|
1922
|
+
els.grid.removeEventListener("keydown", onKey);
|
|
1923
|
+
};
|
|
1924
|
+
boundDatePickers.set(els.grid, teardown);
|
|
1925
|
+
return teardown;
|
|
1926
|
+
}
|
|
1019
1927
|
}
|
|
1020
1928
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1021
1929
|
0 && (module.exports = {
|