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.
- package/dist/browser.cjs +71 -18
- package/dist/browser.js +4 -4
- package/dist/build.cjs +6618 -6178
- package/dist/build.d.cts +554 -415
- package/dist/build.d.ts +554 -415
- package/dist/build.js +2091 -1735
- package/dist/cdn.dev.global.js +12 -12
- package/dist/cdn.full.dev.global.js +13 -13
- package/dist/cdn.full.global.js +12 -12
- package/dist/cdn.global.js +12 -12
- package/dist/{chunk-WFRQ6D7H.js → chunk-22OPSWMF.js} +1 -1
- package/dist/{chunk-ZJCHY2IB.js → chunk-2NC56RGY.js} +1 -1
- package/dist/{chunk-NB7TFSJE.js → chunk-2O6JJCUR.js} +4 -4
- package/dist/{chunk-B2Y5BJSP.js → chunk-4UTGDVSA.js} +70 -16
- package/dist/{chunk-NAVLCP4S.js → chunk-5RIBLY2N.js} +4 -4
- package/dist/{chunk-T5O2CUHV.js → chunk-5XUGJM3J.js} +1 -1
- package/dist/{chunk-HXT2RNH5.js → chunk-ADZHPN2B.js} +1 -1
- package/dist/chunk-B36ADEIR.js +21 -0
- package/dist/{chunk-DFZL3AX7.js → chunk-C73VBPEF.js} +9 -5
- package/dist/{chunk-WH4U2VUE.js → chunk-CPQKWCJ2.js} +13 -5
- package/dist/{chunk-LEUEKK4G.js → chunk-EU7T7EB3.js} +2 -2
- package/dist/{chunk-QLG5IK7F.js → chunk-G3Z2FEBR.js} +4 -4
- package/dist/{chunk-ZPY2UAKM.js → chunk-J4X7ICH4.js} +3 -3
- package/dist/{chunk-XX4BRMWF.js → chunk-K4LNUT5L.js} +1 -1
- package/dist/{chunk-CI4W6XAU.js → chunk-KC62VURU.js} +3 -3
- package/dist/chunk-N5TBYXHW.js +17 -0
- package/dist/{chunk-653KGQS2.js → chunk-OIKD7YDY.js} +2 -2
- package/dist/{chunk-LYTUTXQP.js → chunk-PBJYCE2Z.js} +46 -4
- package/dist/{chunk-EMB5PG7V.js → chunk-QC3NP47A.js} +1 -1
- package/dist/{chunk-IHA62NGJ.js → chunk-QKFVMIAV.js} +1 -1
- package/dist/{chunk-LXBMVGVB.js → chunk-QZ47OZ25.js} +3 -3
- package/dist/{chunk-ZTYCCBU3.js → chunk-VFPLWUT7.js} +44 -26
- package/dist/{chunk-RK7EPQPG.js → chunk-YFTBCCVH.js} +1 -1
- package/dist/data.cjs +71 -18
- package/dist/data.js +5 -5
- package/dist/datePicker-DxuxWpgF.d.cts +285 -0
- package/dist/datePicker-DxuxWpgF.d.ts +285 -0
- package/dist/devtools.cjs +71 -18
- package/dist/devtools.js +4 -4
- package/dist/ecosystem.cjs +82 -21
- package/dist/ecosystem.js +7 -7
- package/dist/extras.cjs +131 -23
- package/dist/extras.d.cts +2 -2
- package/dist/extras.d.ts +2 -2
- package/dist/extras.js +21 -20
- package/dist/index.cjs +115 -37
- package/dist/index.d.cts +59 -5
- package/dist/index.d.ts +59 -5
- package/dist/index.js +10 -10
- package/dist/motion.cjs +69 -16
- package/dist/motion.js +3 -3
- package/dist/patterns.cjs +71 -18
- package/dist/patterns.js +5 -5
- package/dist/performance.cjs +69 -16
- package/dist/performance.js +4 -4
- package/dist/plugins.cjs +1421 -996
- package/dist/plugins.d.cts +132 -3
- package/dist/plugins.d.ts +132 -3
- package/dist/plugins.js +262 -11
- package/dist/routeMiddleware-B1sp4P7a.d.ts +688 -0
- package/dist/routeMiddleware-CedQTsLK.d.cts +688 -0
- package/dist/ssr.cjs +3607 -3524
- package/dist/ssr.d.cts +20 -671
- package/dist/ssr.d.ts +20 -671
- package/dist/ssr.js +15 -7
- package/dist/testing.cjs +71 -18
- package/dist/testing.js +2 -2
- package/dist/ui.cjs +78 -18
- package/dist/ui.js +8 -7
- package/dist/widgets.cjs +1352 -952
- package/dist/widgets.d.cts +100 -268
- package/dist/widgets.d.ts +100 -268
- package/dist/widgets.js +316 -7
- 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-
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
18
|
-
|
|
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.
|
|
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",
|