sibujs 4.12.0 → 4.13.1

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.
Files changed (74) hide show
  1. package/dist/browser.cjs +71 -18
  2. package/dist/browser.js +4 -4
  3. package/dist/build.cjs +6618 -6178
  4. package/dist/build.d.cts +554 -415
  5. package/dist/build.d.ts +554 -415
  6. package/dist/build.js +2091 -1735
  7. package/dist/cdn.dev.global.js +12 -12
  8. package/dist/cdn.full.dev.global.js +13 -13
  9. package/dist/cdn.full.global.js +12 -12
  10. package/dist/cdn.global.js +12 -12
  11. package/dist/{chunk-WFRQ6D7H.js → chunk-22OPSWMF.js} +1 -1
  12. package/dist/{chunk-ZJCHY2IB.js → chunk-2NC56RGY.js} +1 -1
  13. package/dist/{chunk-NB7TFSJE.js → chunk-2O6JJCUR.js} +4 -4
  14. package/dist/{chunk-B2Y5BJSP.js → chunk-4UTGDVSA.js} +70 -16
  15. package/dist/{chunk-NAVLCP4S.js → chunk-5RIBLY2N.js} +4 -4
  16. package/dist/{chunk-T5O2CUHV.js → chunk-5XUGJM3J.js} +1 -1
  17. package/dist/{chunk-HXT2RNH5.js → chunk-ADZHPN2B.js} +1 -1
  18. package/dist/chunk-B36ADEIR.js +21 -0
  19. package/dist/{chunk-DFZL3AX7.js → chunk-C73VBPEF.js} +9 -5
  20. package/dist/{chunk-WH4U2VUE.js → chunk-CPQKWCJ2.js} +13 -5
  21. package/dist/{chunk-LEUEKK4G.js → chunk-EU7T7EB3.js} +2 -2
  22. package/dist/{chunk-QLG5IK7F.js → chunk-G3Z2FEBR.js} +4 -4
  23. package/dist/{chunk-ZPY2UAKM.js → chunk-J4X7ICH4.js} +3 -3
  24. package/dist/{chunk-XX4BRMWF.js → chunk-K4LNUT5L.js} +1 -1
  25. package/dist/{chunk-CI4W6XAU.js → chunk-KC62VURU.js} +3 -3
  26. package/dist/chunk-N5TBYXHW.js +17 -0
  27. package/dist/{chunk-653KGQS2.js → chunk-OIKD7YDY.js} +2 -2
  28. package/dist/{chunk-LYTUTXQP.js → chunk-PBJYCE2Z.js} +46 -4
  29. package/dist/{chunk-EMB5PG7V.js → chunk-QC3NP47A.js} +1 -1
  30. package/dist/{chunk-IHA62NGJ.js → chunk-QKFVMIAV.js} +1 -1
  31. package/dist/{chunk-LXBMVGVB.js → chunk-QZ47OZ25.js} +3 -3
  32. package/dist/{chunk-ZTYCCBU3.js → chunk-VFPLWUT7.js} +44 -26
  33. package/dist/{chunk-RK7EPQPG.js → chunk-YFTBCCVH.js} +1 -1
  34. package/dist/data.cjs +71 -18
  35. package/dist/data.js +5 -5
  36. package/dist/datePicker-DxuxWpgF.d.cts +285 -0
  37. package/dist/datePicker-DxuxWpgF.d.ts +285 -0
  38. package/dist/devtools.cjs +71 -18
  39. package/dist/devtools.js +4 -4
  40. package/dist/ecosystem.cjs +82 -21
  41. package/dist/ecosystem.js +7 -7
  42. package/dist/extras.cjs +131 -23
  43. package/dist/extras.d.cts +2 -2
  44. package/dist/extras.d.ts +2 -2
  45. package/dist/extras.js +21 -20
  46. package/dist/index.cjs +115 -37
  47. package/dist/index.d.cts +59 -5
  48. package/dist/index.d.ts +59 -5
  49. package/dist/index.js +10 -10
  50. package/dist/motion.cjs +69 -16
  51. package/dist/motion.js +3 -3
  52. package/dist/patterns.cjs +71 -18
  53. package/dist/patterns.js +5 -5
  54. package/dist/performance.cjs +69 -16
  55. package/dist/performance.js +4 -4
  56. package/dist/plugins.cjs +1421 -996
  57. package/dist/plugins.d.cts +132 -3
  58. package/dist/plugins.d.ts +132 -3
  59. package/dist/plugins.js +262 -11
  60. package/dist/routeMiddleware-B1sp4P7a.d.ts +688 -0
  61. package/dist/routeMiddleware-CedQTsLK.d.cts +688 -0
  62. package/dist/ssr.cjs +3607 -3524
  63. package/dist/ssr.d.cts +20 -671
  64. package/dist/ssr.d.ts +20 -671
  65. package/dist/ssr.js +15 -7
  66. package/dist/testing.cjs +71 -18
  67. package/dist/testing.js +2 -2
  68. package/dist/ui.cjs +78 -18
  69. package/dist/ui.js +8 -7
  70. package/dist/widgets.cjs +1352 -952
  71. package/dist/widgets.d.cts +100 -268
  72. package/dist/widgets.d.ts +100 -268
  73. package/dist/widgets.js +316 -7
  74. package/package.json +9 -1
package/dist/widgets.js CHANGED
@@ -6,25 +6,334 @@ import {
6
6
  fileUpload,
7
7
  popover,
8
8
  select,
9
+ snapshotAttributes,
9
10
  tabs,
10
11
  tooltip
11
- } from "./chunk-LYTUTXQP.js";
12
- import "./chunk-HXT2RNH5.js";
13
- import "./chunk-WFRQ6D7H.js";
14
- import "./chunk-RK7EPQPG.js";
12
+ } from "./chunk-PBJYCE2Z.js";
13
+ import {
14
+ claimWidgetKey,
15
+ isWidgetKeyEvent,
16
+ widgetKeyOwner
17
+ } from "./chunk-N5TBYXHW.js";
18
+ import "./chunk-ADZHPN2B.js";
19
+ import {
20
+ createId,
21
+ domBinding
22
+ } from "./chunk-22OPSWMF.js";
23
+ import "./chunk-YFTBCCVH.js";
15
24
  import "./chunk-EXXSN3BW.js";
16
25
  import "./chunk-7XHATCIH.js";
17
- import "./chunk-IHA62NGJ.js";
18
- import "./chunk-B2Y5BJSP.js";
26
+ import {
27
+ batch,
28
+ signal
29
+ } from "./chunk-QKFVMIAV.js";
30
+ import {
31
+ untracked
32
+ } from "./chunk-4UTGDVSA.js";
19
33
  import "./chunk-NYNYSPK7.js";
34
+
35
+ // src/widgets/selectMenu.ts
36
+ var boundMenus = /* @__PURE__ */ new WeakMap();
37
+ var OPTION_ATTRS = ["id", "role", "aria-selected", "aria-disabled"];
38
+ function selectMenu(options) {
39
+ const getItems = typeof options.items === "function" ? options.items : () => options.items;
40
+ const toStr = options.itemToString ?? ((item) => String(item));
41
+ const isDisabled = options.isDisabled ?? (() => false);
42
+ const isEqual = options.isEqual ?? Object.is;
43
+ const pageSize = Math.max(1, options.pageSize ?? 10);
44
+ const [ownValue, setOwnValue] = signal(options.defaultValue ?? null);
45
+ const readValue = options.value;
46
+ const value = () => readValue ? readValue() ?? null : ownValue();
47
+ const [isOpen, setIsOpen] = signal(false);
48
+ const [active, setActiveIndex] = signal(-1);
49
+ const enabledAt = (items, i) => i >= 0 && i < items.length && !isDisabled(items[i]);
50
+ function enabledFrom(items, from, dir) {
51
+ for (let i = from; i >= 0 && i < items.length; i += dir) if (!isDisabled(items[i])) return i;
52
+ return -1;
53
+ }
54
+ function selectedIndex(items) {
55
+ const v = value();
56
+ if (v === null) return -1;
57
+ return items.findIndex((it) => isEqual(it, v));
58
+ }
59
+ function open(activeAt = "selected") {
60
+ untracked(() => {
61
+ const items = getItems();
62
+ let idx = -1;
63
+ if (activeAt === "last") idx = enabledFrom(items, items.length - 1, -1);
64
+ else if (activeAt === "first") idx = enabledFrom(items, 0, 1);
65
+ else {
66
+ const sel = selectedIndex(items);
67
+ idx = enabledAt(items, sel) ? sel : enabledFrom(items, 0, 1);
68
+ }
69
+ batch(() => {
70
+ setActiveIndex(idx);
71
+ setIsOpen(true);
72
+ });
73
+ });
74
+ }
75
+ function close() {
76
+ setIsOpen(false);
77
+ }
78
+ function toggle() {
79
+ if (untracked(isOpen)) close();
80
+ else open();
81
+ }
82
+ function setActive(index) {
83
+ if (enabledAt(untracked(getItems), index)) setActiveIndex(index);
84
+ }
85
+ function select2(item) {
86
+ if (isDisabled(item)) return;
87
+ batch(() => {
88
+ if (!readValue) setOwnValue(() => item);
89
+ setIsOpen(false);
90
+ });
91
+ options.onChange?.(item);
92
+ }
93
+ function activeItem() {
94
+ const items = getItems();
95
+ const i = active();
96
+ return i >= 0 && i < items.length ? items[i] : null;
97
+ }
98
+ function bind(els) {
99
+ const existing = boundMenus.get(els.trigger);
100
+ if (existing) return existing;
101
+ const { trigger, listbox } = els;
102
+ const restoreTrigger = snapshotAttributes(trigger, [
103
+ "role",
104
+ "aria-haspopup",
105
+ "aria-expanded",
106
+ "aria-controls",
107
+ "aria-activedescendant",
108
+ "tabindex"
109
+ ]);
110
+ const restoreListbox = snapshotAttributes(listbox, ["id", "role", "tabindex"]);
111
+ const prevHidden = listbox.hidden;
112
+ const optionRestores = /* @__PURE__ */ new Map();
113
+ const generatedIds = /* @__PURE__ */ new WeakMap();
114
+ let optionIndex = /* @__PURE__ */ new Map();
115
+ const listboxId = listbox.id || createId("sibu-select-menu");
116
+ listbox.id = listboxId;
117
+ listbox.setAttribute("role", "listbox");
118
+ listbox.tabIndex = -1;
119
+ trigger.setAttribute("role", "combobox");
120
+ trigger.setAttribute("aria-haspopup", "listbox");
121
+ trigger.setAttribute("aria-controls", listboxId);
122
+ if (trigger.tabIndex < 0) trigger.tabIndex = 0;
123
+ const generatedIdFor = (el, taken) => {
124
+ let id = generatedIds.get(el);
125
+ while (id === void 0 || taken.has(id) || el.ownerDocument.getElementById(id) !== null) {
126
+ id = createId(`${listboxId}-opt`);
127
+ }
128
+ generatedIds.set(el, id);
129
+ return id;
130
+ };
131
+ let lastScrolled = null;
132
+ const stopBinding = domBinding(() => {
133
+ const items = getItems();
134
+ const openNow = isOpen();
135
+ const idx = active();
136
+ const sel = value();
137
+ trigger.setAttribute("aria-expanded", openNow ? "true" : "false");
138
+ listbox.hidden = !openNow;
139
+ const nextIndex = /* @__PURE__ */ new Map();
140
+ let activeEl = null;
141
+ const optionEls = [];
142
+ const taken = /* @__PURE__ */ new Set();
143
+ for (let i = 0; i < items.length; i++) {
144
+ const el = els.option(items[i], i);
145
+ optionEls.push(el);
146
+ if (el?.id) taken.add(el.id);
147
+ }
148
+ for (let i = 0; i < items.length; i++) {
149
+ const el = optionEls[i];
150
+ if (!el) continue;
151
+ if (!optionRestores.has(el)) optionRestores.set(el, snapshotAttributes(el, OPTION_ATTRS));
152
+ nextIndex.set(el, i);
153
+ if (!el.id) {
154
+ el.id = generatedIdFor(el, taken);
155
+ taken.add(el.id);
156
+ }
157
+ el.setAttribute("role", "option");
158
+ el.setAttribute("aria-selected", sel !== null && isEqual(items[i], sel) ? "true" : "false");
159
+ const disabled = isDisabled(items[i]);
160
+ if (disabled) el.setAttribute("aria-disabled", "true");
161
+ else el.removeAttribute("aria-disabled");
162
+ if (i === idx && !disabled) activeEl = el;
163
+ }
164
+ for (const [el, restore] of optionRestores) {
165
+ if (nextIndex.has(el)) continue;
166
+ restore();
167
+ optionRestores.delete(el);
168
+ if (lastScrolled === el) lastScrolled = null;
169
+ }
170
+ optionIndex = nextIndex;
171
+ if (openNow && activeEl) {
172
+ trigger.setAttribute("aria-activedescendant", activeEl.id);
173
+ if (activeEl !== lastScrolled) {
174
+ activeEl.scrollIntoView?.({
175
+ block: "nearest"
176
+ });
177
+ lastScrolled = activeEl;
178
+ }
179
+ } else {
180
+ trigger.removeAttribute("aria-activedescendant");
181
+ if (!openNow) lastScrolled = null;
182
+ }
183
+ }, trigger);
184
+ let typeBuffer = "";
185
+ let typeTimer = null;
186
+ const typeahead = (char) => {
187
+ typeBuffer += char.toLowerCase();
188
+ if (typeTimer !== null) clearTimeout(typeTimer);
189
+ typeTimer = setTimeout(() => {
190
+ typeBuffer = "";
191
+ typeTimer = null;
192
+ }, 500);
193
+ const items = getItems();
194
+ const n = items.length;
195
+ if (n === 0) return;
196
+ const start = untracked(active);
197
+ const repeated = typeBuffer.length > 1 && [...typeBuffer].every((c) => c === typeBuffer[0]);
198
+ const needle = repeated ? typeBuffer[0] : typeBuffer;
199
+ const offset = typeBuffer.length === 1 || repeated ? 1 : 0;
200
+ for (let k = 0; k < n; k++) {
201
+ const i = (Math.max(start, 0) + offset + k) % n;
202
+ if (!isDisabled(items[i]) && toStr(items[i]).toLowerCase().startsWith(needle)) {
203
+ setActiveIndex(i);
204
+ return;
205
+ }
206
+ }
207
+ };
208
+ const commitActive = () => {
209
+ const item = untracked(activeItem);
210
+ if (item !== null && !isDisabled(item)) select2(item);
211
+ else close();
212
+ };
213
+ const move = (delta) => {
214
+ const items = getItems();
215
+ const cur = untracked(active);
216
+ const dir = delta > 0 ? 1 : -1;
217
+ const target = Math.min(items.length - 1, Math.max(0, cur + delta));
218
+ let next = enabledFrom(items, target, dir);
219
+ if (next === -1) next = enabledFrom(items, target, dir === 1 ? -1 : 1);
220
+ if (next !== -1) setActiveIndex(next);
221
+ };
222
+ const onKey = (e) => {
223
+ if (isWidgetKeyEvent(e) || e.ctrlKey || e.metaKey) return;
224
+ const openNow = untracked(isOpen);
225
+ const key = e.key;
226
+ let handled = true;
227
+ if (!openNow) {
228
+ if (key === "ArrowDown" || key === "ArrowUp" || key === "Enter" || key === " ") open();
229
+ else if (key === "Home") open("first");
230
+ else if (key === "End") open("last");
231
+ else if (key.length === 1 && !e.altKey) {
232
+ open();
233
+ typeahead(key);
234
+ } else handled = false;
235
+ } else if (key === "ArrowDown" && !e.altKey) move(1);
236
+ else if (key === "ArrowUp" && e.altKey) commitActive();
237
+ else if (key === "ArrowUp") move(-1);
238
+ else if (key === "Home") setActive(enabledFrom(getItems(), 0, 1));
239
+ else if (key === "End") {
240
+ const items = getItems();
241
+ setActive(enabledFrom(items, items.length - 1, -1));
242
+ } else if (key === "PageDown") move(pageSize);
243
+ else if (key === "PageUp") move(-pageSize);
244
+ else if (key === "Escape") close();
245
+ else if (key === " " && typeBuffer !== "") typeahead(key);
246
+ else if (key === "Enter" || key === " ") commitActive();
247
+ else if (key === "Tab") {
248
+ commitActive();
249
+ handled = false;
250
+ } else if (key.length === 1 && !e.altKey) typeahead(key);
251
+ else handled = false;
252
+ if (handled) {
253
+ e.preventDefault();
254
+ claimWidgetKey(e, "selectMenu");
255
+ }
256
+ };
257
+ const onTriggerClick = () => toggle();
258
+ const onListboxPointerDown = (e) => e.preventDefault();
259
+ const optionFromEvent = (e) => {
260
+ let node = e.target;
261
+ while (node && node !== listbox) {
262
+ const i = optionIndex.get(node);
263
+ if (i !== void 0) return i;
264
+ node = node.parentNode;
265
+ }
266
+ return -1;
267
+ };
268
+ const onListboxClick = (e) => {
269
+ const i = optionFromEvent(e);
270
+ if (i === -1) return;
271
+ const item = getItems()[i];
272
+ if (isDisabled(item)) return;
273
+ select2(item);
274
+ trigger.focus();
275
+ };
276
+ const onListboxPointerMove = (e) => {
277
+ const i = optionFromEvent(e);
278
+ if (i !== -1 && i !== untracked(active)) setActive(i);
279
+ };
280
+ const onDocPointerDown = (e) => {
281
+ if (!untracked(isOpen)) return;
282
+ const target = e.target;
283
+ if (target && (trigger.contains(target) || listbox.contains(target))) return;
284
+ close();
285
+ };
286
+ const onTriggerBlur = (e) => {
287
+ if (!untracked(isOpen)) return;
288
+ const next = e.relatedTarget;
289
+ if (next && listbox.contains(next)) return;
290
+ close();
291
+ };
292
+ trigger.addEventListener("keydown", onKey);
293
+ trigger.addEventListener("click", onTriggerClick);
294
+ trigger.addEventListener("blur", onTriggerBlur);
295
+ listbox.addEventListener("mousedown", onListboxPointerDown);
296
+ listbox.addEventListener("click", onListboxClick);
297
+ listbox.addEventListener("pointermove", onListboxPointerMove);
298
+ const doc = trigger.ownerDocument;
299
+ doc.addEventListener("pointerdown", onDocPointerDown, true);
300
+ let tornDown = false;
301
+ const teardown = () => {
302
+ if (tornDown) return;
303
+ tornDown = true;
304
+ if (boundMenus.get(trigger) === teardown) boundMenus.delete(trigger);
305
+ stopBinding();
306
+ trigger.removeEventListener("keydown", onKey);
307
+ trigger.removeEventListener("click", onTriggerClick);
308
+ trigger.removeEventListener("blur", onTriggerBlur);
309
+ listbox.removeEventListener("mousedown", onListboxPointerDown);
310
+ listbox.removeEventListener("click", onListboxClick);
311
+ listbox.removeEventListener("pointermove", onListboxPointerMove);
312
+ doc.removeEventListener("pointerdown", onDocPointerDown, true);
313
+ if (typeTimer !== null) clearTimeout(typeTimer);
314
+ for (const restore of optionRestores.values()) restore();
315
+ optionRestores.clear();
316
+ optionIndex.clear();
317
+ restoreTrigger();
318
+ restoreListbox();
319
+ listbox.hidden = prevHidden;
320
+ };
321
+ boundMenus.set(trigger, teardown);
322
+ return teardown;
323
+ }
324
+ return { value, isOpen, open, close, toggle, active, activeItem, setActive, select: select2, bind };
325
+ }
20
326
  export {
21
327
  accordion,
22
328
  combobox,
23
329
  contentEditable,
24
330
  datePicker,
25
331
  fileUpload,
332
+ isWidgetKeyEvent,
26
333
  popover,
27
334
  select,
335
+ selectMenu,
28
336
  tabs,
29
- tooltip
337
+ tooltip,
338
+ widgetKeyOwner
30
339
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sibujs",
3
- "version": "4.12.0",
3
+ "version": "4.13.1",
4
4
  "description": "A function-based frontend framework with fine-grained reactivity and no virtual DOM. Tag factories return live DOM nodes you can hold and mutate, signals update only what changed, and nothing needs a compiler — it runs from a script tag or a bundler.",
5
5
  "keywords": [
6
6
  "frontend",
@@ -155,6 +155,14 @@
155
155
  "engines": {
156
156
  "node": ">=22.3.0"
157
157
  },
158
+ "peerDependencies": {
159
+ "jsdom": ">=22"
160
+ },
161
+ "peerDependenciesMeta": {
162
+ "jsdom": {
163
+ "optional": true
164
+ }
165
+ },
158
166
  "devDependencies": {
159
167
  "@biomejs/biome": "2.4.7",
160
168
  "@playwright/test": "^1.61.1",