@vertz/ui-primitives 0.1.1 → 0.2.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/README.md +3 -3
- package/dist/index.d.ts +422 -36
- package/dist/index.js +2277 -182
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { signal } from "@vertz/ui";
|
|
3
3
|
|
|
4
4
|
// src/utils/aria.ts
|
|
5
|
+
import { onAnimationsComplete } from "@vertz/ui/internals";
|
|
6
|
+
var hideGeneration = new WeakMap;
|
|
5
7
|
function setExpanded(el, expanded) {
|
|
6
8
|
el.setAttribute("aria-expanded", String(expanded));
|
|
7
9
|
}
|
|
@@ -12,6 +14,21 @@ function setHidden(el, hidden) {
|
|
|
12
14
|
el.setAttribute("aria-hidden", String(hidden));
|
|
13
15
|
el.style.display = hidden ? "none" : "";
|
|
14
16
|
}
|
|
17
|
+
function setHiddenAnimated(el, hidden) {
|
|
18
|
+
const gen = (hideGeneration.get(el) ?? 0) + 1;
|
|
19
|
+
hideGeneration.set(el, gen);
|
|
20
|
+
if (!hidden) {
|
|
21
|
+
el.setAttribute("aria-hidden", "false");
|
|
22
|
+
el.style.display = "";
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
el.setAttribute("aria-hidden", "true");
|
|
26
|
+
onAnimationsComplete(el, () => {
|
|
27
|
+
if (hideGeneration.get(el) === gen) {
|
|
28
|
+
el.style.display = "none";
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
15
32
|
function setChecked(el, checked) {
|
|
16
33
|
el.setAttribute("aria-checked", String(checked));
|
|
17
34
|
}
|
|
@@ -27,6 +44,9 @@ function setLabelledBy(el, labelId) {
|
|
|
27
44
|
function setDescribedBy(el, descriptionId) {
|
|
28
45
|
el.setAttribute("aria-describedby", descriptionId);
|
|
29
46
|
}
|
|
47
|
+
function setPressed(el, pressed) {
|
|
48
|
+
el.setAttribute("aria-pressed", String(pressed));
|
|
49
|
+
}
|
|
30
50
|
function setValueRange(el, now, min, max) {
|
|
31
51
|
el.setAttribute("aria-valuenow", String(now));
|
|
32
52
|
el.setAttribute("aria-valuemin", String(min));
|
|
@@ -111,10 +131,29 @@ var Accordion = {
|
|
|
111
131
|
const { multiple = false, defaultValue = [], onValueChange } = options;
|
|
112
132
|
const state = { value: signal([...defaultValue]) };
|
|
113
133
|
const triggers = [];
|
|
134
|
+
const itemMap = new Map;
|
|
114
135
|
const root = document.createElement("div");
|
|
115
136
|
root.setAttribute("data-orientation", "vertical");
|
|
137
|
+
function updateItemState(val, open) {
|
|
138
|
+
const entry = itemMap.get(val);
|
|
139
|
+
if (!entry)
|
|
140
|
+
return;
|
|
141
|
+
const { trigger: t, content: c } = entry;
|
|
142
|
+
if (open) {
|
|
143
|
+
setHidden(c, false);
|
|
144
|
+
}
|
|
145
|
+
const height = c.scrollHeight;
|
|
146
|
+
c.style.setProperty("--accordion-content-height", `${height}px`);
|
|
147
|
+
setExpanded(t, open);
|
|
148
|
+
setDataState(t, open ? "open" : "closed");
|
|
149
|
+
setDataState(c, open ? "open" : "closed");
|
|
150
|
+
if (!open) {
|
|
151
|
+
setHiddenAnimated(c, true);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
116
154
|
function toggleItem(value) {
|
|
117
|
-
const
|
|
155
|
+
const prev = [...state.value.peek()];
|
|
156
|
+
const current = [...prev];
|
|
118
157
|
const idx = current.indexOf(value);
|
|
119
158
|
if (idx >= 0) {
|
|
120
159
|
current.splice(idx, 1);
|
|
@@ -128,6 +167,16 @@ var Accordion = {
|
|
|
128
167
|
}
|
|
129
168
|
state.value.value = current;
|
|
130
169
|
onValueChange?.(current);
|
|
170
|
+
for (const v of prev) {
|
|
171
|
+
if (!current.includes(v)) {
|
|
172
|
+
updateItemState(v, false);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
for (const v of current) {
|
|
176
|
+
if (!prev.includes(v)) {
|
|
177
|
+
updateItemState(v, true);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
131
180
|
}
|
|
132
181
|
root.addEventListener("keydown", (event) => {
|
|
133
182
|
if (isKey(event, Keys.ArrowUp, Keys.ArrowDown, Keys.Home, Keys.End)) {
|
|
@@ -156,16 +205,18 @@ var Accordion = {
|
|
|
156
205
|
setDataState(content, isOpen ? "open" : "closed");
|
|
157
206
|
trigger.addEventListener("click", () => {
|
|
158
207
|
toggleItem(value);
|
|
159
|
-
const nowOpen = state.value.peek().includes(value);
|
|
160
|
-
setExpanded(trigger, nowOpen);
|
|
161
|
-
setHidden(content, !nowOpen);
|
|
162
|
-
setDataState(trigger, nowOpen ? "open" : "closed");
|
|
163
|
-
setDataState(content, nowOpen ? "open" : "closed");
|
|
164
208
|
});
|
|
209
|
+
itemMap.set(value, { trigger, content });
|
|
165
210
|
triggers.push(trigger);
|
|
166
211
|
item.appendChild(trigger);
|
|
167
212
|
item.appendChild(content);
|
|
168
213
|
root.appendChild(item);
|
|
214
|
+
if (isOpen) {
|
|
215
|
+
requestAnimationFrame(() => {
|
|
216
|
+
const height = content.scrollHeight;
|
|
217
|
+
content.style.setProperty("--accordion-content-height", `${height}px`);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
169
220
|
return { item, trigger, content };
|
|
170
221
|
}
|
|
171
222
|
return { root, state, Item };
|
|
@@ -212,8 +263,363 @@ var Button = {
|
|
|
212
263
|
return { root, state };
|
|
213
264
|
}
|
|
214
265
|
};
|
|
215
|
-
// src/
|
|
266
|
+
// src/calendar/calendar.ts
|
|
216
267
|
import { signal as signal3 } from "@vertz/ui";
|
|
268
|
+
var MONTH_NAMES = [
|
|
269
|
+
"January",
|
|
270
|
+
"February",
|
|
271
|
+
"March",
|
|
272
|
+
"April",
|
|
273
|
+
"May",
|
|
274
|
+
"June",
|
|
275
|
+
"July",
|
|
276
|
+
"August",
|
|
277
|
+
"September",
|
|
278
|
+
"October",
|
|
279
|
+
"November",
|
|
280
|
+
"December"
|
|
281
|
+
];
|
|
282
|
+
var DAY_NAMES = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
283
|
+
function getDaysInMonth(year, month) {
|
|
284
|
+
return new Date(year, month + 1, 0).getDate();
|
|
285
|
+
}
|
|
286
|
+
function isSameDay(a, b) {
|
|
287
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
288
|
+
}
|
|
289
|
+
function addDays(date, days) {
|
|
290
|
+
const result = new Date(date);
|
|
291
|
+
result.setDate(result.getDate() + days);
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
function addMonths(date, months) {
|
|
295
|
+
const result = new Date(date);
|
|
296
|
+
result.setMonth(result.getMonth() + months);
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
var Calendar = {
|
|
300
|
+
Root(options = {}) {
|
|
301
|
+
const now = new Date;
|
|
302
|
+
const defaultMonth = options.defaultMonth ?? now;
|
|
303
|
+
const weekStartsOn = options.weekStartsOn ?? 0;
|
|
304
|
+
const mode = options.mode ?? "single";
|
|
305
|
+
const state = {
|
|
306
|
+
value: signal3(options.defaultValue ?? null),
|
|
307
|
+
focusedDate: signal3(defaultMonth),
|
|
308
|
+
displayMonth: signal3(defaultMonth)
|
|
309
|
+
};
|
|
310
|
+
const root = document.createElement("div");
|
|
311
|
+
const header = document.createElement("div");
|
|
312
|
+
const title = document.createElement("div");
|
|
313
|
+
const prevButton = document.createElement("button");
|
|
314
|
+
prevButton.setAttribute("type", "button");
|
|
315
|
+
const nextButton = document.createElement("button");
|
|
316
|
+
nextButton.setAttribute("type", "button");
|
|
317
|
+
const grid = document.createElement("table");
|
|
318
|
+
grid.setAttribute("role", "grid");
|
|
319
|
+
function updateTitle() {
|
|
320
|
+
const month = state.displayMonth.peek();
|
|
321
|
+
title.textContent = `${MONTH_NAMES[month.getMonth()]} ${month.getFullYear()}`;
|
|
322
|
+
}
|
|
323
|
+
function isDateDisabled(date) {
|
|
324
|
+
if (options.disabled?.(date))
|
|
325
|
+
return true;
|
|
326
|
+
if (options.minDate && date < options.minDate && !isSameDay(date, options.minDate)) {
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
if (options.maxDate && date > options.maxDate && !isSameDay(date, options.maxDate)) {
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
function isSelected(date) {
|
|
335
|
+
const val = state.value.peek();
|
|
336
|
+
if (val === null)
|
|
337
|
+
return false;
|
|
338
|
+
if (val instanceof Date)
|
|
339
|
+
return isSameDay(val, date);
|
|
340
|
+
if (Array.isArray(val))
|
|
341
|
+
return val.some((d) => isSameDay(d, date));
|
|
342
|
+
if ("from" in val && "to" in val) {
|
|
343
|
+
return isSameDay(val.from, date) || isSameDay(val.to, date);
|
|
344
|
+
}
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
function isInRange(date) {
|
|
348
|
+
const val = state.value.peek();
|
|
349
|
+
if (val === null || !("from" in val))
|
|
350
|
+
return false;
|
|
351
|
+
const range = val;
|
|
352
|
+
return date > range.from && date < range.to;
|
|
353
|
+
}
|
|
354
|
+
function selectDate(date) {
|
|
355
|
+
if (isDateDisabled(date))
|
|
356
|
+
return;
|
|
357
|
+
if (mode === "single") {
|
|
358
|
+
state.value.value = date;
|
|
359
|
+
} else if (mode === "multiple") {
|
|
360
|
+
const current = state.value.peek() ?? [];
|
|
361
|
+
const existing = current.findIndex((d) => isSameDay(d, date));
|
|
362
|
+
if (existing >= 0) {
|
|
363
|
+
const next = [...current];
|
|
364
|
+
next.splice(existing, 1);
|
|
365
|
+
state.value.value = next;
|
|
366
|
+
} else {
|
|
367
|
+
state.value.value = [...current, date];
|
|
368
|
+
}
|
|
369
|
+
} else if (mode === "range") {
|
|
370
|
+
const current = state.value.peek();
|
|
371
|
+
if (!current || "to" in current && current.to) {
|
|
372
|
+
state.value.value = { from: date, to: date };
|
|
373
|
+
} else {
|
|
374
|
+
if (date < current.from) {
|
|
375
|
+
state.value.value = { from: date, to: current.from };
|
|
376
|
+
} else {
|
|
377
|
+
state.value.value = { from: current.from, to: date };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
options.onValueChange?.(state.value.peek());
|
|
382
|
+
}
|
|
383
|
+
function buildGrid() {
|
|
384
|
+
grid.innerHTML = "";
|
|
385
|
+
const display = state.displayMonth.peek();
|
|
386
|
+
const year = display.getFullYear();
|
|
387
|
+
const month = display.getMonth();
|
|
388
|
+
const daysInMonth = getDaysInMonth(year, month);
|
|
389
|
+
const thead = document.createElement("thead");
|
|
390
|
+
const headerRow = document.createElement("tr");
|
|
391
|
+
for (let i = 0;i < 7; i++) {
|
|
392
|
+
const dayIndex = (weekStartsOn + i) % 7;
|
|
393
|
+
const th = document.createElement("th");
|
|
394
|
+
th.setAttribute("scope", "col");
|
|
395
|
+
th.textContent = DAY_NAMES[dayIndex] ?? "";
|
|
396
|
+
headerRow.appendChild(th);
|
|
397
|
+
}
|
|
398
|
+
thead.appendChild(headerRow);
|
|
399
|
+
grid.appendChild(thead);
|
|
400
|
+
const tbody = document.createElement("tbody");
|
|
401
|
+
const firstDay = new Date(year, month, 1);
|
|
402
|
+
const firstDayOfWeek = firstDay.getDay();
|
|
403
|
+
const offset = (firstDayOfWeek - weekStartsOn + 7) % 7;
|
|
404
|
+
const startDate = addDays(firstDay, -offset);
|
|
405
|
+
let currentDate = startDate;
|
|
406
|
+
const totalCells = offset + daysInMonth;
|
|
407
|
+
const totalRows = Math.ceil(totalCells / 7);
|
|
408
|
+
for (let row = 0;row < totalRows; row++) {
|
|
409
|
+
const tr = document.createElement("tr");
|
|
410
|
+
for (let col = 0;col < 7; col++) {
|
|
411
|
+
const td = document.createElement("td");
|
|
412
|
+
td.setAttribute("role", "gridcell");
|
|
413
|
+
const btn = document.createElement("button");
|
|
414
|
+
btn.setAttribute("type", "button");
|
|
415
|
+
const cellDate = new Date(currentDate);
|
|
416
|
+
btn.textContent = String(cellDate.getDate());
|
|
417
|
+
btn.setAttribute("data-date", cellDate.toISOString().split("T")[0] ?? "");
|
|
418
|
+
const isOutside = cellDate.getMonth() !== month;
|
|
419
|
+
if (isOutside) {
|
|
420
|
+
btn.setAttribute("data-outside-month", "true");
|
|
421
|
+
}
|
|
422
|
+
if (isSameDay(cellDate, now)) {
|
|
423
|
+
btn.setAttribute("data-today", "true");
|
|
424
|
+
}
|
|
425
|
+
if (isDateDisabled(cellDate)) {
|
|
426
|
+
btn.setAttribute("aria-disabled", "true");
|
|
427
|
+
}
|
|
428
|
+
if (isSelected(cellDate)) {
|
|
429
|
+
btn.setAttribute("aria-selected", "true");
|
|
430
|
+
}
|
|
431
|
+
if (mode === "range") {
|
|
432
|
+
const val = state.value.peek();
|
|
433
|
+
if (val && "from" in val) {
|
|
434
|
+
if (isSameDay(cellDate, val.from)) {
|
|
435
|
+
btn.setAttribute("data-range-start", "true");
|
|
436
|
+
}
|
|
437
|
+
if (isSameDay(cellDate, val.to)) {
|
|
438
|
+
btn.setAttribute("data-range-end", "true");
|
|
439
|
+
}
|
|
440
|
+
if (isInRange(cellDate)) {
|
|
441
|
+
btn.setAttribute("data-in-range", "true");
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
btn.addEventListener("click", () => {
|
|
446
|
+
selectDate(cellDate);
|
|
447
|
+
rebuildGrid();
|
|
448
|
+
});
|
|
449
|
+
td.appendChild(btn);
|
|
450
|
+
tr.appendChild(td);
|
|
451
|
+
currentDate = addDays(currentDate, 1);
|
|
452
|
+
}
|
|
453
|
+
tbody.appendChild(tr);
|
|
454
|
+
}
|
|
455
|
+
grid.appendChild(tbody);
|
|
456
|
+
}
|
|
457
|
+
function rebuildGrid() {
|
|
458
|
+
updateTitle();
|
|
459
|
+
buildGrid();
|
|
460
|
+
}
|
|
461
|
+
function navigateMonth(delta) {
|
|
462
|
+
state.displayMonth.value = addMonths(state.displayMonth.peek(), delta);
|
|
463
|
+
options.onMonthChange?.(state.displayMonth.peek());
|
|
464
|
+
rebuildGrid();
|
|
465
|
+
}
|
|
466
|
+
prevButton.addEventListener("click", () => navigateMonth(-1));
|
|
467
|
+
nextButton.addEventListener("click", () => navigateMonth(1));
|
|
468
|
+
grid.addEventListener("keydown", (event) => {
|
|
469
|
+
const active = document.activeElement;
|
|
470
|
+
if (!active || active.tagName !== "BUTTON")
|
|
471
|
+
return;
|
|
472
|
+
const dateStr = active.getAttribute("data-date");
|
|
473
|
+
if (!dateStr)
|
|
474
|
+
return;
|
|
475
|
+
const focused = new Date(dateStr + "T00:00:00");
|
|
476
|
+
let next = null;
|
|
477
|
+
if (event.key === "ArrowLeft") {
|
|
478
|
+
event.preventDefault();
|
|
479
|
+
next = addDays(focused, -1);
|
|
480
|
+
} else if (event.key === "ArrowRight") {
|
|
481
|
+
event.preventDefault();
|
|
482
|
+
next = addDays(focused, 1);
|
|
483
|
+
} else if (event.key === "ArrowUp") {
|
|
484
|
+
event.preventDefault();
|
|
485
|
+
next = addDays(focused, -7);
|
|
486
|
+
} else if (event.key === "ArrowDown") {
|
|
487
|
+
event.preventDefault();
|
|
488
|
+
next = addDays(focused, 7);
|
|
489
|
+
} else if (event.key === "Home") {
|
|
490
|
+
event.preventDefault();
|
|
491
|
+
const dayOfWeek = (focused.getDay() - weekStartsOn + 7) % 7;
|
|
492
|
+
next = addDays(focused, -dayOfWeek);
|
|
493
|
+
} else if (event.key === "End") {
|
|
494
|
+
event.preventDefault();
|
|
495
|
+
const dayOfWeek = (focused.getDay() - weekStartsOn + 7) % 7;
|
|
496
|
+
next = addDays(focused, 6 - dayOfWeek);
|
|
497
|
+
} else if (event.key === "PageUp") {
|
|
498
|
+
event.preventDefault();
|
|
499
|
+
next = event.shiftKey ? addMonths(focused, -12) : addMonths(focused, -1);
|
|
500
|
+
} else if (event.key === "PageDown") {
|
|
501
|
+
event.preventDefault();
|
|
502
|
+
next = event.shiftKey ? addMonths(focused, 12) : addMonths(focused, 1);
|
|
503
|
+
} else if (event.key === "Enter" || event.key === " ") {
|
|
504
|
+
event.preventDefault();
|
|
505
|
+
selectDate(focused);
|
|
506
|
+
rebuildGrid();
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
if (next) {
|
|
510
|
+
state.focusedDate.value = next;
|
|
511
|
+
if (next.getMonth() !== state.displayMonth.peek().getMonth() || next.getFullYear() !== state.displayMonth.peek().getFullYear()) {
|
|
512
|
+
state.displayMonth.value = new Date(next.getFullYear(), next.getMonth(), 1);
|
|
513
|
+
options.onMonthChange?.(state.displayMonth.peek());
|
|
514
|
+
rebuildGrid();
|
|
515
|
+
}
|
|
516
|
+
const dateKey = next.toISOString().split("T")[0];
|
|
517
|
+
const btn = grid.querySelector(`button[data-date="${dateKey}"]`);
|
|
518
|
+
btn?.focus();
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
updateTitle();
|
|
522
|
+
buildGrid();
|
|
523
|
+
root.appendChild(header);
|
|
524
|
+
root.appendChild(grid);
|
|
525
|
+
header.appendChild(prevButton);
|
|
526
|
+
header.appendChild(title);
|
|
527
|
+
header.appendChild(nextButton);
|
|
528
|
+
return { root, header, title, prevButton, nextButton, grid, state };
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
// src/carousel/carousel.ts
|
|
532
|
+
import { signal as signal4 } from "@vertz/ui";
|
|
533
|
+
var Carousel = {
|
|
534
|
+
Root(options = {}) {
|
|
535
|
+
const { orientation = "horizontal", loop = false, defaultIndex = 0, onSlideChange } = options;
|
|
536
|
+
const state = {
|
|
537
|
+
currentIndex: signal4(defaultIndex),
|
|
538
|
+
slideCount: signal4(0)
|
|
539
|
+
};
|
|
540
|
+
const slides = [];
|
|
541
|
+
const root = document.createElement("div");
|
|
542
|
+
root.setAttribute("role", "region");
|
|
543
|
+
root.setAttribute("aria-roledescription", "carousel");
|
|
544
|
+
root.setAttribute("data-orientation", orientation);
|
|
545
|
+
const viewport = document.createElement("div");
|
|
546
|
+
viewport.style.overflow = "hidden";
|
|
547
|
+
const prevButton = document.createElement("button");
|
|
548
|
+
prevButton.setAttribute("type", "button");
|
|
549
|
+
prevButton.setAttribute("aria-label", "Previous slide");
|
|
550
|
+
const nextButton = document.createElement("button");
|
|
551
|
+
nextButton.setAttribute("type", "button");
|
|
552
|
+
nextButton.setAttribute("aria-label", "Next slide");
|
|
553
|
+
function updateSlideVisibility() {
|
|
554
|
+
const current = state.currentIndex.peek();
|
|
555
|
+
for (let i = 0;i < slides.length; i++) {
|
|
556
|
+
const slide = slides[i];
|
|
557
|
+
if (!slide)
|
|
558
|
+
continue;
|
|
559
|
+
slide.setAttribute("aria-hidden", String(i !== current));
|
|
560
|
+
slide.setAttribute("aria-label", `Slide ${i + 1} of ${slides.length}`);
|
|
561
|
+
setDataState(slide, i === current ? "active" : "inactive");
|
|
562
|
+
}
|
|
563
|
+
if (!loop) {
|
|
564
|
+
prevButton.disabled = current <= 0;
|
|
565
|
+
nextButton.disabled = current >= slides.length - 1;
|
|
566
|
+
}
|
|
567
|
+
const translateProp = orientation === "horizontal" ? "translateX" : "translateY";
|
|
568
|
+
viewport.style.transform = `${translateProp}(-${current * 100}%)`;
|
|
569
|
+
}
|
|
570
|
+
function goTo(index) {
|
|
571
|
+
const total = slides.length;
|
|
572
|
+
if (total === 0)
|
|
573
|
+
return;
|
|
574
|
+
let next = index;
|
|
575
|
+
if (loop) {
|
|
576
|
+
next = (index % total + total) % total;
|
|
577
|
+
} else {
|
|
578
|
+
next = Math.max(0, Math.min(total - 1, index));
|
|
579
|
+
}
|
|
580
|
+
if (next === state.currentIndex.peek())
|
|
581
|
+
return;
|
|
582
|
+
state.currentIndex.value = next;
|
|
583
|
+
updateSlideVisibility();
|
|
584
|
+
onSlideChange?.(next);
|
|
585
|
+
}
|
|
586
|
+
function goNext() {
|
|
587
|
+
goTo(state.currentIndex.peek() + 1);
|
|
588
|
+
}
|
|
589
|
+
function goPrev() {
|
|
590
|
+
goTo(state.currentIndex.peek() - 1);
|
|
591
|
+
}
|
|
592
|
+
prevButton.addEventListener("click", goPrev);
|
|
593
|
+
nextButton.addEventListener("click", goNext);
|
|
594
|
+
root.addEventListener("keydown", (event) => {
|
|
595
|
+
const prevKey = orientation === "horizontal" ? Keys.ArrowLeft : Keys.ArrowUp;
|
|
596
|
+
const nextKey = orientation === "horizontal" ? Keys.ArrowRight : Keys.ArrowDown;
|
|
597
|
+
if (isKey(event, prevKey)) {
|
|
598
|
+
event.preventDefault();
|
|
599
|
+
goPrev();
|
|
600
|
+
}
|
|
601
|
+
if (isKey(event, nextKey)) {
|
|
602
|
+
event.preventDefault();
|
|
603
|
+
goNext();
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
function Slide() {
|
|
607
|
+
const slide = document.createElement("div");
|
|
608
|
+
slide.setAttribute("role", "group");
|
|
609
|
+
slide.setAttribute("aria-roledescription", "slide");
|
|
610
|
+
slides.push(slide);
|
|
611
|
+
state.slideCount.value = slides.length;
|
|
612
|
+
viewport.appendChild(slide);
|
|
613
|
+
updateSlideVisibility();
|
|
614
|
+
return slide;
|
|
615
|
+
}
|
|
616
|
+
root.appendChild(viewport);
|
|
617
|
+
updateSlideVisibility();
|
|
618
|
+
return { root, viewport, prevButton, nextButton, state, Slide, goTo, goNext, goPrev };
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
// src/checkbox/checkbox.ts
|
|
622
|
+
import { signal as signal5 } from "@vertz/ui";
|
|
217
623
|
function dataStateFor(checked) {
|
|
218
624
|
if (checked === "mixed")
|
|
219
625
|
return "indeterminate";
|
|
@@ -223,8 +629,8 @@ var Checkbox = {
|
|
|
223
629
|
Root(options = {}) {
|
|
224
630
|
const { defaultChecked = false, disabled = false, onCheckedChange } = options;
|
|
225
631
|
const state = {
|
|
226
|
-
checked:
|
|
227
|
-
disabled:
|
|
632
|
+
checked: signal5(defaultChecked),
|
|
633
|
+
disabled: signal5(disabled)
|
|
228
634
|
};
|
|
229
635
|
const root = document.createElement("button");
|
|
230
636
|
root.setAttribute("type", "button");
|
|
@@ -256,17 +662,66 @@ var Checkbox = {
|
|
|
256
662
|
return { root, state };
|
|
257
663
|
}
|
|
258
664
|
};
|
|
665
|
+
// src/collapsible/collapsible.ts
|
|
666
|
+
import { signal as signal6 } from "@vertz/ui";
|
|
667
|
+
var Collapsible = {
|
|
668
|
+
Root(options = {}) {
|
|
669
|
+
const { defaultOpen = false, disabled = false, onOpenChange } = options;
|
|
670
|
+
const ids = linkedIds("collapsible");
|
|
671
|
+
const state = {
|
|
672
|
+
open: signal6(defaultOpen),
|
|
673
|
+
disabled: signal6(disabled)
|
|
674
|
+
};
|
|
675
|
+
const root = document.createElement("div");
|
|
676
|
+
const trigger = document.createElement("button");
|
|
677
|
+
trigger.setAttribute("type", "button");
|
|
678
|
+
trigger.id = ids.triggerId;
|
|
679
|
+
trigger.setAttribute("aria-controls", ids.contentId);
|
|
680
|
+
setExpanded(trigger, defaultOpen);
|
|
681
|
+
setDataState(trigger, defaultOpen ? "open" : "closed");
|
|
682
|
+
if (disabled) {
|
|
683
|
+
trigger.disabled = true;
|
|
684
|
+
trigger.setAttribute("aria-disabled", "true");
|
|
685
|
+
}
|
|
686
|
+
const content = document.createElement("div");
|
|
687
|
+
content.id = ids.contentId;
|
|
688
|
+
setHidden(content, !defaultOpen);
|
|
689
|
+
setDataState(content, defaultOpen ? "open" : "closed");
|
|
690
|
+
function toggle() {
|
|
691
|
+
if (state.disabled.peek())
|
|
692
|
+
return;
|
|
693
|
+
const next = !state.open.peek();
|
|
694
|
+
state.open.value = next;
|
|
695
|
+
if (next) {
|
|
696
|
+
setHidden(content, false);
|
|
697
|
+
}
|
|
698
|
+
const height = content.scrollHeight;
|
|
699
|
+
content.style.setProperty("--collapsible-content-height", `${height}px`);
|
|
700
|
+
setExpanded(trigger, next);
|
|
701
|
+
setDataState(trigger, next ? "open" : "closed");
|
|
702
|
+
setDataState(content, next ? "open" : "closed");
|
|
703
|
+
if (!next) {
|
|
704
|
+
setHiddenAnimated(content, true);
|
|
705
|
+
}
|
|
706
|
+
onOpenChange?.(next);
|
|
707
|
+
}
|
|
708
|
+
trigger.addEventListener("click", toggle);
|
|
709
|
+
root.appendChild(trigger);
|
|
710
|
+
root.appendChild(content);
|
|
711
|
+
return { root, trigger, content, state };
|
|
712
|
+
}
|
|
713
|
+
};
|
|
259
714
|
// src/combobox/combobox.ts
|
|
260
|
-
import { signal as
|
|
715
|
+
import { signal as signal7 } from "@vertz/ui";
|
|
261
716
|
var Combobox = {
|
|
262
717
|
Root(options = {}) {
|
|
263
718
|
const { defaultValue = "", onValueChange, onInputChange } = options;
|
|
264
719
|
const ids = linkedIds("combobox");
|
|
265
720
|
const state = {
|
|
266
|
-
open:
|
|
267
|
-
value:
|
|
268
|
-
inputValue:
|
|
269
|
-
activeIndex:
|
|
721
|
+
open: signal7(false),
|
|
722
|
+
value: signal7(defaultValue),
|
|
723
|
+
inputValue: signal7(defaultValue),
|
|
724
|
+
activeIndex: signal7(-1)
|
|
270
725
|
};
|
|
271
726
|
const optionElements = [];
|
|
272
727
|
const input = document.createElement("input");
|
|
@@ -293,8 +748,8 @@ var Combobox = {
|
|
|
293
748
|
state.open.value = false;
|
|
294
749
|
state.activeIndex.value = -1;
|
|
295
750
|
setExpanded(input, false);
|
|
296
|
-
setHidden(listbox, true);
|
|
297
751
|
setDataState(listbox, "closed");
|
|
752
|
+
setHiddenAnimated(listbox, true);
|
|
298
753
|
updateActiveDescendant(-1);
|
|
299
754
|
}
|
|
300
755
|
function selectOption(value) {
|
|
@@ -387,77 +842,678 @@ var Combobox = {
|
|
|
387
842
|
return { input, listbox, state, Option };
|
|
388
843
|
}
|
|
389
844
|
};
|
|
390
|
-
// src/
|
|
391
|
-
import { signal as
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
845
|
+
// src/command/command.ts
|
|
846
|
+
import { signal as signal8 } from "@vertz/ui";
|
|
847
|
+
var Command = {
|
|
848
|
+
Root(options = {}) {
|
|
849
|
+
const { filter: customFilter, onSelect, onInputChange, placeholder } = options;
|
|
850
|
+
const listId = uniqueId("command-list");
|
|
851
|
+
const state = {
|
|
852
|
+
inputValue: signal8(""),
|
|
853
|
+
activeIndex: signal8(0)
|
|
854
|
+
};
|
|
855
|
+
const allItems = [];
|
|
856
|
+
const groups = new Map;
|
|
857
|
+
const root = document.createElement("div");
|
|
858
|
+
const input = document.createElement("input");
|
|
859
|
+
input.setAttribute("type", "text");
|
|
860
|
+
input.setAttribute("role", "combobox");
|
|
861
|
+
input.setAttribute("aria-autocomplete", "list");
|
|
862
|
+
input.setAttribute("aria-expanded", "true");
|
|
863
|
+
input.setAttribute("aria-controls", listId);
|
|
864
|
+
if (placeholder)
|
|
865
|
+
input.placeholder = placeholder;
|
|
866
|
+
const list = document.createElement("div");
|
|
867
|
+
list.setAttribute("role", "listbox");
|
|
868
|
+
list.id = listId;
|
|
869
|
+
const empty = document.createElement("div");
|
|
870
|
+
setHidden(empty, true);
|
|
871
|
+
const defaultFilter = (value, search) => {
|
|
872
|
+
return value.toLowerCase().includes(search.toLowerCase());
|
|
873
|
+
};
|
|
874
|
+
const filterFn = customFilter ?? defaultFilter;
|
|
875
|
+
function getVisibleItems() {
|
|
876
|
+
return allItems.filter((item) => item.getAttribute("aria-hidden") !== "true");
|
|
877
|
+
}
|
|
878
|
+
function updateActiveItem() {
|
|
879
|
+
const visible = getVisibleItems();
|
|
880
|
+
const activeIdx = state.activeIndex.peek();
|
|
881
|
+
for (const item of allItems) {
|
|
882
|
+
item.setAttribute("aria-selected", "false");
|
|
883
|
+
}
|
|
884
|
+
if (visible.length > 0 && activeIdx >= 0 && activeIdx < visible.length) {
|
|
885
|
+
visible[activeIdx]?.setAttribute("aria-selected", "true");
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
function runFilter() {
|
|
889
|
+
const search = state.inputValue.peek();
|
|
890
|
+
let visibleCount = 0;
|
|
891
|
+
for (const item of allItems) {
|
|
892
|
+
const value = item.getAttribute("data-value") ?? "";
|
|
893
|
+
const text = item.textContent ?? "";
|
|
894
|
+
const keywords = item.getAttribute("data-keywords") ?? "";
|
|
895
|
+
const searchable = `${value} ${text} ${keywords}`;
|
|
896
|
+
const matches = search === "" || filterFn(searchable, search);
|
|
897
|
+
setHidden(item, !matches);
|
|
898
|
+
if (matches)
|
|
899
|
+
visibleCount++;
|
|
900
|
+
}
|
|
901
|
+
for (const [groupEl, group] of groups) {
|
|
902
|
+
const hasVisible = group.items.some((item) => item.getAttribute("aria-hidden") !== "true");
|
|
903
|
+
setHidden(group.heading, !hasVisible);
|
|
904
|
+
if (!hasVisible) {
|
|
905
|
+
groupEl.style.display = "none";
|
|
906
|
+
} else {
|
|
907
|
+
groupEl.style.display = "";
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
setHidden(empty, visibleCount > 0);
|
|
911
|
+
state.activeIndex.value = 0;
|
|
912
|
+
updateActiveItem();
|
|
913
|
+
}
|
|
914
|
+
input.addEventListener("input", () => {
|
|
915
|
+
state.inputValue.value = input.value;
|
|
916
|
+
onInputChange?.(input.value);
|
|
917
|
+
runFilter();
|
|
918
|
+
});
|
|
919
|
+
input.addEventListener("keydown", (event) => {
|
|
920
|
+
const visible = getVisibleItems();
|
|
921
|
+
if (isKey(event, Keys.ArrowDown)) {
|
|
419
922
|
event.preventDefault();
|
|
420
|
-
|
|
923
|
+
const next = Math.min(state.activeIndex.peek() + 1, visible.length - 1);
|
|
924
|
+
state.activeIndex.value = next;
|
|
925
|
+
updateActiveItem();
|
|
926
|
+
return;
|
|
421
927
|
}
|
|
422
|
-
|
|
423
|
-
if (document.activeElement === last) {
|
|
928
|
+
if (isKey(event, Keys.ArrowUp)) {
|
|
424
929
|
event.preventDefault();
|
|
425
|
-
|
|
930
|
+
const prev = Math.max(state.activeIndex.peek() - 1, 0);
|
|
931
|
+
state.activeIndex.value = prev;
|
|
932
|
+
updateActiveItem();
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
if (isKey(event, Keys.Enter)) {
|
|
936
|
+
event.preventDefault();
|
|
937
|
+
const active = visible[state.activeIndex.peek()];
|
|
938
|
+
if (active) {
|
|
939
|
+
const val = active.getAttribute("data-value");
|
|
940
|
+
if (val !== null) {
|
|
941
|
+
onSelect?.(val);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
if (isKey(event, Keys.Escape)) {
|
|
947
|
+
event.preventDefault();
|
|
948
|
+
input.value = "";
|
|
949
|
+
state.inputValue.value = "";
|
|
950
|
+
onInputChange?.("");
|
|
951
|
+
runFilter();
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
function createItem(value, label, keywords, parent) {
|
|
955
|
+
const item = document.createElement("div");
|
|
956
|
+
item.setAttribute("role", "option");
|
|
957
|
+
item.setAttribute("data-value", value);
|
|
958
|
+
item.setAttribute("aria-selected", "false");
|
|
959
|
+
item.textContent = label ?? value;
|
|
960
|
+
if (keywords && keywords.length > 0) {
|
|
961
|
+
item.setAttribute("data-keywords", keywords.join(" "));
|
|
426
962
|
}
|
|
963
|
+
item.addEventListener("click", () => {
|
|
964
|
+
onSelect?.(value);
|
|
965
|
+
});
|
|
966
|
+
allItems.push(item);
|
|
967
|
+
(parent ?? list).appendChild(item);
|
|
968
|
+
updateActiveItem();
|
|
969
|
+
return item;
|
|
970
|
+
}
|
|
971
|
+
function Item(value, label, keywords) {
|
|
972
|
+
return createItem(value, label, keywords);
|
|
973
|
+
}
|
|
974
|
+
function Group(label) {
|
|
975
|
+
const headingId = uniqueId("command-group");
|
|
976
|
+
const el = document.createElement("div");
|
|
977
|
+
el.setAttribute("role", "group");
|
|
978
|
+
el.setAttribute("aria-labelledby", headingId);
|
|
979
|
+
const heading = document.createElement("div");
|
|
980
|
+
heading.id = headingId;
|
|
981
|
+
heading.textContent = label;
|
|
982
|
+
el.appendChild(heading);
|
|
983
|
+
const groupItems = [];
|
|
984
|
+
groups.set(el, { heading, items: groupItems });
|
|
985
|
+
list.appendChild(el);
|
|
986
|
+
return {
|
|
987
|
+
el,
|
|
988
|
+
Item: (value, itemLabel, keywords) => {
|
|
989
|
+
const item = createItem(value, itemLabel, keywords, el);
|
|
990
|
+
groupItems.push(item);
|
|
991
|
+
return item;
|
|
992
|
+
}
|
|
993
|
+
};
|
|
427
994
|
}
|
|
995
|
+
function Separator() {
|
|
996
|
+
const hr = document.createElement("hr");
|
|
997
|
+
hr.setAttribute("role", "separator");
|
|
998
|
+
list.appendChild(hr);
|
|
999
|
+
return hr;
|
|
1000
|
+
}
|
|
1001
|
+
root.appendChild(input);
|
|
1002
|
+
root.appendChild(list);
|
|
1003
|
+
root.appendChild(empty);
|
|
1004
|
+
return { root, input, list, empty, state, Item, Group, Separator };
|
|
428
1005
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
1006
|
+
};
|
|
1007
|
+
// src/context-menu/context-menu.ts
|
|
1008
|
+
import { signal as signal9 } from "@vertz/ui";
|
|
1009
|
+
|
|
1010
|
+
// src/utils/dismiss.ts
|
|
1011
|
+
function createDismiss(options) {
|
|
1012
|
+
const { onDismiss, insideElements, escapeKey = true, clickOutside = true } = options;
|
|
1013
|
+
function handlePointerDown(event) {
|
|
1014
|
+
const target = event.target;
|
|
1015
|
+
const isInside = insideElements.some((el) => el.contains(target));
|
|
1016
|
+
if (!isInside) {
|
|
1017
|
+
onDismiss();
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
function handleKeyDown(event) {
|
|
1021
|
+
if (event.key === "Escape") {
|
|
1022
|
+
onDismiss();
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
if (clickOutside) {
|
|
1026
|
+
document.addEventListener("pointerdown", handlePointerDown, true);
|
|
1027
|
+
}
|
|
1028
|
+
if (escapeKey) {
|
|
1029
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1030
|
+
}
|
|
1031
|
+
return function cleanup() {
|
|
1032
|
+
if (clickOutside) {
|
|
1033
|
+
document.removeEventListener("pointerdown", handlePointerDown, true);
|
|
1034
|
+
}
|
|
1035
|
+
if (escapeKey) {
|
|
1036
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
1037
|
+
}
|
|
432
1038
|
};
|
|
433
1039
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
1040
|
+
|
|
1041
|
+
// src/utils/floating.ts
|
|
1042
|
+
import { autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/dom";
|
|
1043
|
+
function createFloatingPosition(reference, floating, options = {}) {
|
|
1044
|
+
const {
|
|
1045
|
+
placement = "bottom-start",
|
|
1046
|
+
strategy = "fixed",
|
|
1047
|
+
offset: offsetValue = 4,
|
|
1048
|
+
flip: enableFlip = true,
|
|
1049
|
+
shift: enableShift = true,
|
|
1050
|
+
middleware: extraMiddleware = [],
|
|
1051
|
+
matchReferenceWidth = false,
|
|
1052
|
+
portal = false
|
|
1053
|
+
} = options;
|
|
1054
|
+
if (portal && floating.parentElement !== document.body) {
|
|
1055
|
+
document.body.appendChild(floating);
|
|
1056
|
+
}
|
|
1057
|
+
const mw = [];
|
|
1058
|
+
mw.push(offset(offsetValue));
|
|
1059
|
+
if (enableFlip)
|
|
1060
|
+
mw.push(flip());
|
|
1061
|
+
if (enableShift)
|
|
1062
|
+
mw.push(shift());
|
|
1063
|
+
if (matchReferenceWidth) {
|
|
1064
|
+
mw.push({
|
|
1065
|
+
name: "matchReferenceWidth",
|
|
1066
|
+
fn({ rects }) {
|
|
1067
|
+
floating.style.minWidth = `${rects.reference.width}px`;
|
|
1068
|
+
return {};
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
mw.push(...extraMiddleware);
|
|
1073
|
+
function updatePosition() {
|
|
1074
|
+
computePosition(reference, floating, {
|
|
1075
|
+
placement,
|
|
1076
|
+
strategy,
|
|
1077
|
+
middleware: mw
|
|
1078
|
+
}).then((result) => {
|
|
1079
|
+
floating.style.position = result.strategy;
|
|
1080
|
+
floating.style.left = `${result.x}px`;
|
|
1081
|
+
floating.style.top = `${result.y}px`;
|
|
1082
|
+
const [side = "bottom", align = "center"] = result.placement.split("-");
|
|
1083
|
+
floating.setAttribute("data-side", side);
|
|
1084
|
+
floating.setAttribute("data-align", align);
|
|
1085
|
+
});
|
|
438
1086
|
}
|
|
1087
|
+
const cleanupAutoUpdate = autoUpdate(reference, floating, updatePosition, {
|
|
1088
|
+
animationFrame: true
|
|
1089
|
+
});
|
|
1090
|
+
return {
|
|
1091
|
+
cleanup: cleanupAutoUpdate,
|
|
1092
|
+
update() {
|
|
1093
|
+
updatePosition();
|
|
1094
|
+
return Promise.resolve();
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
439
1097
|
}
|
|
440
|
-
function
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
1098
|
+
function virtualElement(x, y) {
|
|
1099
|
+
return {
|
|
1100
|
+
getBoundingClientRect() {
|
|
1101
|
+
return {
|
|
1102
|
+
x,
|
|
1103
|
+
y,
|
|
1104
|
+
top: y,
|
|
1105
|
+
left: x,
|
|
1106
|
+
bottom: y,
|
|
1107
|
+
right: x,
|
|
1108
|
+
width: 0,
|
|
1109
|
+
height: 0,
|
|
1110
|
+
toJSON() {
|
|
1111
|
+
return this;
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
445
1114
|
}
|
|
446
1115
|
};
|
|
447
1116
|
}
|
|
448
|
-
|
|
449
|
-
|
|
1117
|
+
|
|
1118
|
+
// src/context-menu/context-menu.ts
|
|
1119
|
+
var ContextMenu = {
|
|
1120
|
+
Root(options = {}) {
|
|
1121
|
+
const { onSelect, positioning } = options;
|
|
1122
|
+
const state = {
|
|
1123
|
+
open: signal9(false),
|
|
1124
|
+
activeIndex: signal9(-1)
|
|
1125
|
+
};
|
|
1126
|
+
const items = [];
|
|
1127
|
+
let floatingCleanup = null;
|
|
1128
|
+
let dismissCleanup = null;
|
|
1129
|
+
const trigger = document.createElement("div");
|
|
1130
|
+
const contentId = uniqueId("ctx-menu");
|
|
1131
|
+
const content = document.createElement("div");
|
|
1132
|
+
content.setAttribute("role", "menu");
|
|
1133
|
+
content.id = contentId;
|
|
1134
|
+
content.style.position = "fixed";
|
|
1135
|
+
setHidden(content, true);
|
|
1136
|
+
setDataState(content, "closed");
|
|
1137
|
+
function handleClickOutside(event) {
|
|
1138
|
+
const target = event.target;
|
|
1139
|
+
if (!trigger.contains(target) && !content.contains(target)) {
|
|
1140
|
+
close();
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
function open(x, y) {
|
|
1144
|
+
state.open.value = true;
|
|
1145
|
+
setHidden(content, false);
|
|
1146
|
+
setDataState(content, "open");
|
|
1147
|
+
if (positioning) {
|
|
1148
|
+
const result = createFloatingPosition(virtualElement(x, y), content, {
|
|
1149
|
+
strategy: "fixed",
|
|
1150
|
+
...positioning
|
|
1151
|
+
});
|
|
1152
|
+
floatingCleanup = result.cleanup;
|
|
1153
|
+
dismissCleanup = createDismiss({
|
|
1154
|
+
onDismiss: close,
|
|
1155
|
+
insideElements: [trigger, content],
|
|
1156
|
+
escapeKey: false
|
|
1157
|
+
});
|
|
1158
|
+
} else {
|
|
1159
|
+
content.style.left = `${x}px`;
|
|
1160
|
+
content.style.top = `${y}px`;
|
|
1161
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1162
|
+
}
|
|
1163
|
+
state.activeIndex.value = 0;
|
|
1164
|
+
updateActiveItem(0);
|
|
1165
|
+
items[0]?.focus();
|
|
1166
|
+
}
|
|
1167
|
+
function close() {
|
|
1168
|
+
state.open.value = false;
|
|
1169
|
+
setDataState(content, "closed");
|
|
1170
|
+
setHiddenAnimated(content, true);
|
|
1171
|
+
if (positioning) {
|
|
1172
|
+
floatingCleanup?.();
|
|
1173
|
+
floatingCleanup = null;
|
|
1174
|
+
dismissCleanup?.();
|
|
1175
|
+
dismissCleanup = null;
|
|
1176
|
+
} else {
|
|
1177
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
function updateActiveItem(index) {
|
|
1181
|
+
for (let i = 0;i < items.length; i++) {
|
|
1182
|
+
items[i]?.setAttribute("tabindex", i === index ? "0" : "-1");
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
trigger.addEventListener("contextmenu", (event) => {
|
|
1186
|
+
event.preventDefault();
|
|
1187
|
+
if (state.open.peek()) {
|
|
1188
|
+
close();
|
|
1189
|
+
}
|
|
1190
|
+
open(event.clientX, event.clientY);
|
|
1191
|
+
});
|
|
1192
|
+
content.addEventListener("keydown", (event) => {
|
|
1193
|
+
if (isKey(event, Keys.Escape)) {
|
|
1194
|
+
event.preventDefault();
|
|
1195
|
+
close();
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
if (isKey(event, Keys.Enter, Keys.Space)) {
|
|
1199
|
+
event.preventDefault();
|
|
1200
|
+
const active = items[state.activeIndex.peek()];
|
|
1201
|
+
if (active) {
|
|
1202
|
+
const val = active.getAttribute("data-value");
|
|
1203
|
+
if (val !== null) {
|
|
1204
|
+
onSelect?.(val);
|
|
1205
|
+
close();
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
const result = handleListNavigation(event, items, { orientation: "vertical" });
|
|
1211
|
+
if (result) {
|
|
1212
|
+
const idx = items.indexOf(result);
|
|
1213
|
+
if (idx >= 0) {
|
|
1214
|
+
state.activeIndex.value = idx;
|
|
1215
|
+
updateActiveItem(idx);
|
|
1216
|
+
}
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
1220
|
+
const char = event.key.toLowerCase();
|
|
1221
|
+
const match = items.find((item) => item.textContent?.toLowerCase().startsWith(char));
|
|
1222
|
+
if (match) {
|
|
1223
|
+
const idx = items.indexOf(match);
|
|
1224
|
+
state.activeIndex.value = idx;
|
|
1225
|
+
updateActiveItem(idx);
|
|
1226
|
+
match.focus();
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
});
|
|
1230
|
+
function createItem(value, label, parent) {
|
|
1231
|
+
const item = document.createElement("div");
|
|
1232
|
+
item.setAttribute("role", "menuitem");
|
|
1233
|
+
item.setAttribute("data-value", value);
|
|
1234
|
+
item.setAttribute("tabindex", "-1");
|
|
1235
|
+
item.textContent = label ?? value;
|
|
1236
|
+
item.addEventListener("click", () => {
|
|
1237
|
+
onSelect?.(value);
|
|
1238
|
+
close();
|
|
1239
|
+
});
|
|
1240
|
+
items.push(item);
|
|
1241
|
+
(parent ?? content).appendChild(item);
|
|
1242
|
+
return item;
|
|
1243
|
+
}
|
|
1244
|
+
function Item(value, label) {
|
|
1245
|
+
return createItem(value, label);
|
|
1246
|
+
}
|
|
1247
|
+
function Group(label) {
|
|
1248
|
+
const el = document.createElement("div");
|
|
1249
|
+
el.setAttribute("role", "group");
|
|
1250
|
+
el.setAttribute("aria-label", label);
|
|
1251
|
+
content.appendChild(el);
|
|
1252
|
+
return {
|
|
1253
|
+
el,
|
|
1254
|
+
Item: (value, itemLabel) => createItem(value, itemLabel, el)
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
function Separator() {
|
|
1258
|
+
const hr = document.createElement("hr");
|
|
1259
|
+
hr.setAttribute("role", "separator");
|
|
1260
|
+
content.appendChild(hr);
|
|
1261
|
+
return hr;
|
|
1262
|
+
}
|
|
1263
|
+
function Label(text) {
|
|
1264
|
+
const el = document.createElement("div");
|
|
1265
|
+
el.setAttribute("role", "none");
|
|
1266
|
+
el.textContent = text;
|
|
1267
|
+
content.appendChild(el);
|
|
1268
|
+
return el;
|
|
1269
|
+
}
|
|
1270
|
+
return { trigger, content, state, Item, Group, Separator, Label };
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
// src/date-picker/date-picker.ts
|
|
1274
|
+
import { signal as signal11 } from "@vertz/ui";
|
|
1275
|
+
|
|
1276
|
+
// src/popover/popover.ts
|
|
1277
|
+
import { signal as signal10 } from "@vertz/ui";
|
|
1278
|
+
|
|
1279
|
+
// src/utils/focus.ts
|
|
1280
|
+
var FOCUSABLE_SELECTOR = [
|
|
1281
|
+
"a[href]",
|
|
1282
|
+
"button:not([disabled])",
|
|
1283
|
+
"input:not([disabled])",
|
|
1284
|
+
"select:not([disabled])",
|
|
1285
|
+
"textarea:not([disabled])",
|
|
1286
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
1287
|
+
"[contenteditable]"
|
|
1288
|
+
].join(", ");
|
|
1289
|
+
function getFocusableElements(container) {
|
|
1290
|
+
return Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));
|
|
1291
|
+
}
|
|
1292
|
+
function trapFocus(container) {
|
|
1293
|
+
function handleKeyDown(event) {
|
|
1294
|
+
if (event.key !== "Tab")
|
|
1295
|
+
return;
|
|
1296
|
+
const focusable = getFocusableElements(container);
|
|
1297
|
+
if (focusable.length === 0)
|
|
1298
|
+
return;
|
|
1299
|
+
const first = focusable[0];
|
|
1300
|
+
const last = focusable[focusable.length - 1];
|
|
1301
|
+
if (!first || !last)
|
|
1302
|
+
return;
|
|
1303
|
+
if (event.shiftKey) {
|
|
1304
|
+
if (document.activeElement === first) {
|
|
1305
|
+
event.preventDefault();
|
|
1306
|
+
last.focus();
|
|
1307
|
+
}
|
|
1308
|
+
} else {
|
|
1309
|
+
if (document.activeElement === last) {
|
|
1310
|
+
event.preventDefault();
|
|
1311
|
+
first.focus();
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
container.addEventListener("keydown", handleKeyDown);
|
|
1316
|
+
return () => {
|
|
1317
|
+
container.removeEventListener("keydown", handleKeyDown);
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
function focusFirst(container) {
|
|
1321
|
+
const focusable = getFocusableElements(container);
|
|
1322
|
+
if (focusable.length > 0) {
|
|
1323
|
+
focusable[0]?.focus();
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
function saveFocus() {
|
|
1327
|
+
const previously = document.activeElement;
|
|
1328
|
+
return () => {
|
|
1329
|
+
if (previously && typeof previously.focus === "function") {
|
|
1330
|
+
previously.focus();
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
function setRovingTabindex(items, activeIndex) {
|
|
1335
|
+
for (let i = 0;i < items.length; i++) {
|
|
450
1336
|
items[i]?.setAttribute("tabindex", i === activeIndex ? "0" : "-1");
|
|
451
1337
|
}
|
|
452
1338
|
}
|
|
453
1339
|
|
|
1340
|
+
// src/popover/popover.ts
|
|
1341
|
+
var Popover = {
|
|
1342
|
+
Root(options = {}) {
|
|
1343
|
+
const { defaultOpen = false, onOpenChange, positioning } = options;
|
|
1344
|
+
const ids = linkedIds("popover");
|
|
1345
|
+
const state = { open: signal10(defaultOpen) };
|
|
1346
|
+
let restoreFocus = null;
|
|
1347
|
+
let floatingCleanup = null;
|
|
1348
|
+
let dismissCleanup = null;
|
|
1349
|
+
const trigger = document.createElement("button");
|
|
1350
|
+
trigger.setAttribute("type", "button");
|
|
1351
|
+
trigger.id = ids.triggerId;
|
|
1352
|
+
trigger.setAttribute("aria-controls", ids.contentId);
|
|
1353
|
+
trigger.setAttribute("aria-haspopup", "dialog");
|
|
1354
|
+
setExpanded(trigger, defaultOpen);
|
|
1355
|
+
setDataState(trigger, defaultOpen ? "open" : "closed");
|
|
1356
|
+
const content = document.createElement("div");
|
|
1357
|
+
content.setAttribute("role", "dialog");
|
|
1358
|
+
content.id = ids.contentId;
|
|
1359
|
+
setHidden(content, !defaultOpen);
|
|
1360
|
+
setDataState(content, defaultOpen ? "open" : "closed");
|
|
1361
|
+
function open() {
|
|
1362
|
+
state.open.value = true;
|
|
1363
|
+
setExpanded(trigger, true);
|
|
1364
|
+
setHidden(content, false);
|
|
1365
|
+
setDataState(trigger, "open");
|
|
1366
|
+
setDataState(content, "open");
|
|
1367
|
+
restoreFocus = saveFocus();
|
|
1368
|
+
queueMicrotask(() => focusFirst(content));
|
|
1369
|
+
if (positioning) {
|
|
1370
|
+
const result = createFloatingPosition(trigger, content, positioning);
|
|
1371
|
+
floatingCleanup = result.cleanup;
|
|
1372
|
+
dismissCleanup = createDismiss({
|
|
1373
|
+
onDismiss: close,
|
|
1374
|
+
insideElements: [trigger, content],
|
|
1375
|
+
escapeKey: false
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
onOpenChange?.(true);
|
|
1379
|
+
}
|
|
1380
|
+
function close() {
|
|
1381
|
+
state.open.value = false;
|
|
1382
|
+
setExpanded(trigger, false);
|
|
1383
|
+
setDataState(trigger, "closed");
|
|
1384
|
+
setDataState(content, "closed");
|
|
1385
|
+
setHiddenAnimated(content, true);
|
|
1386
|
+
floatingCleanup?.();
|
|
1387
|
+
floatingCleanup = null;
|
|
1388
|
+
dismissCleanup?.();
|
|
1389
|
+
dismissCleanup = null;
|
|
1390
|
+
restoreFocus?.();
|
|
1391
|
+
restoreFocus = null;
|
|
1392
|
+
onOpenChange?.(false);
|
|
1393
|
+
}
|
|
1394
|
+
trigger.addEventListener("click", () => {
|
|
1395
|
+
if (state.open.peek()) {
|
|
1396
|
+
close();
|
|
1397
|
+
} else {
|
|
1398
|
+
open();
|
|
1399
|
+
}
|
|
1400
|
+
});
|
|
1401
|
+
content.addEventListener("keydown", (event) => {
|
|
1402
|
+
if (isKey(event, Keys.Escape)) {
|
|
1403
|
+
event.preventDefault();
|
|
1404
|
+
close();
|
|
1405
|
+
}
|
|
1406
|
+
});
|
|
1407
|
+
return { trigger, content, state };
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
|
|
1411
|
+
// src/date-picker/date-picker.ts
|
|
1412
|
+
function defaultFormatDate(date) {
|
|
1413
|
+
return date.toLocaleDateString();
|
|
1414
|
+
}
|
|
1415
|
+
function formatRangeDisplay(value, fmt) {
|
|
1416
|
+
return `${fmt(value.from)} – ${fmt(value.to)}`;
|
|
1417
|
+
}
|
|
1418
|
+
var DatePicker = {
|
|
1419
|
+
Root(options = {}) {
|
|
1420
|
+
const {
|
|
1421
|
+
mode = "single",
|
|
1422
|
+
defaultValue = null,
|
|
1423
|
+
minDate,
|
|
1424
|
+
maxDate,
|
|
1425
|
+
disabled,
|
|
1426
|
+
formatDate = defaultFormatDate,
|
|
1427
|
+
placeholder = "Pick a date",
|
|
1428
|
+
onValueChange,
|
|
1429
|
+
onOpenChange
|
|
1430
|
+
} = options;
|
|
1431
|
+
const defaultMonth = options.defaultMonth ?? (defaultValue instanceof Date ? defaultValue : defaultValue && ("from" in defaultValue) ? defaultValue.from : new Date);
|
|
1432
|
+
const calendarMode = mode === "range" ? "range" : "single";
|
|
1433
|
+
const calendarDefaultValue = defaultValue instanceof Date ? defaultValue : defaultValue && ("from" in defaultValue) ? defaultValue : undefined;
|
|
1434
|
+
const popover = Popover.Root({
|
|
1435
|
+
onOpenChange(open) {
|
|
1436
|
+
state.open.value = open;
|
|
1437
|
+
onOpenChange?.(open);
|
|
1438
|
+
}
|
|
1439
|
+
});
|
|
1440
|
+
const calendarResult = Calendar.Root({
|
|
1441
|
+
mode: calendarMode,
|
|
1442
|
+
defaultValue: calendarDefaultValue,
|
|
1443
|
+
defaultMonth,
|
|
1444
|
+
minDate,
|
|
1445
|
+
maxDate,
|
|
1446
|
+
disabled,
|
|
1447
|
+
onValueChange(value) {
|
|
1448
|
+
if (mode === "single" && value instanceof Date) {
|
|
1449
|
+
state.value.value = value;
|
|
1450
|
+
updateTriggerText();
|
|
1451
|
+
onValueChange?.(value);
|
|
1452
|
+
hide();
|
|
1453
|
+
} else if (mode === "range" && value && "from" in value) {
|
|
1454
|
+
const range = value;
|
|
1455
|
+
state.value.value = range;
|
|
1456
|
+
updateTriggerText();
|
|
1457
|
+
onValueChange?.(range);
|
|
1458
|
+
if (range.from && range.to && range.from.getTime() !== range.to.getTime()) {
|
|
1459
|
+
hide();
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
});
|
|
1464
|
+
popover.content.appendChild(calendarResult.root);
|
|
1465
|
+
const state = {
|
|
1466
|
+
open: signal11(false),
|
|
1467
|
+
value: signal11(defaultValue),
|
|
1468
|
+
displayMonth: calendarResult.state.displayMonth
|
|
1469
|
+
};
|
|
1470
|
+
function updateTriggerText() {
|
|
1471
|
+
const val = state.value.peek();
|
|
1472
|
+
if (val === null) {
|
|
1473
|
+
popover.trigger.textContent = placeholder;
|
|
1474
|
+
popover.trigger.setAttribute("data-placeholder", "true");
|
|
1475
|
+
} else if (val instanceof Date) {
|
|
1476
|
+
popover.trigger.textContent = formatDate(val);
|
|
1477
|
+
popover.trigger.removeAttribute("data-placeholder");
|
|
1478
|
+
} else if ("from" in val) {
|
|
1479
|
+
popover.trigger.textContent = formatRangeDisplay(val, formatDate);
|
|
1480
|
+
popover.trigger.removeAttribute("data-placeholder");
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
function show() {
|
|
1484
|
+
popover.trigger.click();
|
|
1485
|
+
}
|
|
1486
|
+
function hide() {
|
|
1487
|
+
if (state.open.peek()) {
|
|
1488
|
+
popover.trigger.click();
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
updateTriggerText();
|
|
1492
|
+
return {
|
|
1493
|
+
trigger: popover.trigger,
|
|
1494
|
+
content: popover.content,
|
|
1495
|
+
calendar: {
|
|
1496
|
+
root: calendarResult.root,
|
|
1497
|
+
header: calendarResult.header,
|
|
1498
|
+
title: calendarResult.title,
|
|
1499
|
+
prevButton: calendarResult.prevButton,
|
|
1500
|
+
nextButton: calendarResult.nextButton,
|
|
1501
|
+
grid: calendarResult.grid
|
|
1502
|
+
},
|
|
1503
|
+
state,
|
|
1504
|
+
show,
|
|
1505
|
+
hide
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
454
1509
|
// src/dialog/dialog.ts
|
|
1510
|
+
import { signal as signal12 } from "@vertz/ui";
|
|
455
1511
|
var Dialog = {
|
|
456
1512
|
Root(options = {}) {
|
|
457
1513
|
const { modal = true, defaultOpen = false, onOpenChange } = options;
|
|
458
1514
|
const ids = linkedIds("dialog");
|
|
459
1515
|
const titleId = `${ids.contentId}-title`;
|
|
460
|
-
const state = { open:
|
|
1516
|
+
const state = { open: signal12(defaultOpen) };
|
|
461
1517
|
let restoreFocus = null;
|
|
462
1518
|
let removeTrap = null;
|
|
463
1519
|
const trigger = document.createElement("button");
|
|
@@ -468,11 +1524,6 @@ var Dialog = {
|
|
|
468
1524
|
setDataState(trigger, defaultOpen ? "open" : "closed");
|
|
469
1525
|
const overlay = document.createElement("div");
|
|
470
1526
|
overlay.setAttribute("data-dialog-overlay", "");
|
|
471
|
-
if (modal) {
|
|
472
|
-
overlay.style.position = "fixed";
|
|
473
|
-
overlay.style.inset = "0";
|
|
474
|
-
overlay.style.zIndex = "49";
|
|
475
|
-
}
|
|
476
1527
|
setHidden(overlay, !defaultOpen);
|
|
477
1528
|
setDataState(overlay, defaultOpen ? "open" : "closed");
|
|
478
1529
|
const content = document.createElement("div");
|
|
@@ -480,11 +1531,6 @@ var Dialog = {
|
|
|
480
1531
|
content.id = ids.contentId;
|
|
481
1532
|
if (modal) {
|
|
482
1533
|
content.setAttribute("aria-modal", "true");
|
|
483
|
-
content.style.position = "fixed";
|
|
484
|
-
content.style.top = "50%";
|
|
485
|
-
content.style.left = "50%";
|
|
486
|
-
content.style.transform = "translate(-50%, -50%)";
|
|
487
|
-
content.style.zIndex = "50";
|
|
488
1534
|
}
|
|
489
1535
|
setLabelledBy(content, titleId);
|
|
490
1536
|
setHidden(content, !defaultOpen);
|
|
@@ -512,11 +1558,11 @@ var Dialog = {
|
|
|
512
1558
|
function closeDialog() {
|
|
513
1559
|
state.open.value = false;
|
|
514
1560
|
setExpanded(trigger, false);
|
|
515
|
-
setHidden(overlay, true);
|
|
516
|
-
setHidden(content, true);
|
|
517
1561
|
setDataState(trigger, "closed");
|
|
518
1562
|
setDataState(overlay, "closed");
|
|
519
1563
|
setDataState(content, "closed");
|
|
1564
|
+
setHiddenAnimated(overlay, true);
|
|
1565
|
+
setHiddenAnimated(content, true);
|
|
520
1566
|
removeTrap?.();
|
|
521
1567
|
removeTrap = null;
|
|
522
1568
|
restoreFocus?.();
|
|
@@ -543,20 +1589,22 @@ var Dialog = {
|
|
|
543
1589
|
closeDialog();
|
|
544
1590
|
}
|
|
545
1591
|
});
|
|
546
|
-
return { trigger, overlay, content, title, close, state };
|
|
1592
|
+
return { trigger, overlay, content, title, close, state, show: openDialog, hide: closeDialog };
|
|
547
1593
|
}
|
|
548
1594
|
};
|
|
549
1595
|
// src/menu/menu.ts
|
|
550
|
-
import { signal as
|
|
1596
|
+
import { signal as signal13 } from "@vertz/ui";
|
|
551
1597
|
var Menu = {
|
|
552
1598
|
Root(options = {}) {
|
|
553
|
-
const { onSelect } = options;
|
|
1599
|
+
const { onSelect, positioning } = options;
|
|
554
1600
|
const ids = linkedIds("menu");
|
|
555
1601
|
const state = {
|
|
556
|
-
open:
|
|
557
|
-
activeIndex:
|
|
1602
|
+
open: signal13(false),
|
|
1603
|
+
activeIndex: signal13(-1)
|
|
558
1604
|
};
|
|
559
1605
|
const items = [];
|
|
1606
|
+
let floatingCleanup = null;
|
|
1607
|
+
let dismissCleanup = null;
|
|
560
1608
|
const trigger = document.createElement("button");
|
|
561
1609
|
trigger.setAttribute("type", "button");
|
|
562
1610
|
trigger.id = ids.triggerId;
|
|
@@ -566,25 +1614,58 @@ var Menu = {
|
|
|
566
1614
|
setDataState(trigger, "closed");
|
|
567
1615
|
const content = document.createElement("div");
|
|
568
1616
|
content.setAttribute("role", "menu");
|
|
1617
|
+
content.setAttribute("tabindex", "-1");
|
|
569
1618
|
content.id = ids.contentId;
|
|
570
1619
|
setHidden(content, true);
|
|
571
1620
|
setDataState(content, "closed");
|
|
572
|
-
function
|
|
1621
|
+
function handleClickOutside(event) {
|
|
1622
|
+
const target = event.target;
|
|
1623
|
+
if (!trigger.contains(target) && !content.contains(target)) {
|
|
1624
|
+
close();
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
function open(activateFirst = false) {
|
|
573
1628
|
state.open.value = true;
|
|
574
1629
|
setExpanded(trigger, true);
|
|
575
1630
|
setHidden(content, false);
|
|
576
1631
|
setDataState(trigger, "open");
|
|
577
1632
|
setDataState(content, "open");
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
1633
|
+
if (positioning) {
|
|
1634
|
+
const ref = positioning.referenceElement ?? trigger;
|
|
1635
|
+
const result = createFloatingPosition(ref, content, positioning);
|
|
1636
|
+
floatingCleanup = result.cleanup;
|
|
1637
|
+
dismissCleanup = createDismiss({
|
|
1638
|
+
onDismiss: close,
|
|
1639
|
+
insideElements: [ref, trigger, content],
|
|
1640
|
+
escapeKey: false
|
|
1641
|
+
});
|
|
1642
|
+
} else {
|
|
1643
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1644
|
+
}
|
|
1645
|
+
if (activateFirst && items.length > 0) {
|
|
1646
|
+
state.activeIndex.value = 0;
|
|
1647
|
+
updateActiveItem(0);
|
|
1648
|
+
items[0]?.focus();
|
|
1649
|
+
} else {
|
|
1650
|
+
state.activeIndex.value = -1;
|
|
1651
|
+
updateActiveItem(-1);
|
|
1652
|
+
content.focus();
|
|
1653
|
+
}
|
|
581
1654
|
}
|
|
582
1655
|
function close() {
|
|
583
1656
|
state.open.value = false;
|
|
584
1657
|
setExpanded(trigger, false);
|
|
585
|
-
setHidden(content, true);
|
|
586
1658
|
setDataState(trigger, "closed");
|
|
587
1659
|
setDataState(content, "closed");
|
|
1660
|
+
setHiddenAnimated(content, true);
|
|
1661
|
+
if (positioning) {
|
|
1662
|
+
floatingCleanup?.();
|
|
1663
|
+
floatingCleanup = null;
|
|
1664
|
+
dismissCleanup?.();
|
|
1665
|
+
dismissCleanup = null;
|
|
1666
|
+
} else {
|
|
1667
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1668
|
+
}
|
|
588
1669
|
trigger.focus();
|
|
589
1670
|
}
|
|
590
1671
|
function updateActiveItem(index) {
|
|
@@ -603,7 +1684,7 @@ var Menu = {
|
|
|
603
1684
|
if (isKey(event, Keys.ArrowDown, Keys.Enter, Keys.Space)) {
|
|
604
1685
|
event.preventDefault();
|
|
605
1686
|
if (!state.open.peek())
|
|
606
|
-
open();
|
|
1687
|
+
open(true);
|
|
607
1688
|
}
|
|
608
1689
|
});
|
|
609
1690
|
content.addEventListener("keydown", (event) => {
|
|
@@ -624,6 +1705,23 @@ var Menu = {
|
|
|
624
1705
|
}
|
|
625
1706
|
return;
|
|
626
1707
|
}
|
|
1708
|
+
if (state.activeIndex.peek() === -1) {
|
|
1709
|
+
if (isKey(event, Keys.ArrowDown)) {
|
|
1710
|
+
event.preventDefault();
|
|
1711
|
+
state.activeIndex.value = 0;
|
|
1712
|
+
updateActiveItem(0);
|
|
1713
|
+
items[0]?.focus();
|
|
1714
|
+
return;
|
|
1715
|
+
}
|
|
1716
|
+
if (isKey(event, Keys.ArrowUp)) {
|
|
1717
|
+
event.preventDefault();
|
|
1718
|
+
const last = items.length - 1;
|
|
1719
|
+
state.activeIndex.value = last;
|
|
1720
|
+
updateActiveItem(last);
|
|
1721
|
+
items[last]?.focus();
|
|
1722
|
+
return;
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
627
1725
|
const result = handleListNavigation(event, items, { orientation: "vertical" });
|
|
628
1726
|
if (result) {
|
|
629
1727
|
const idx = items.indexOf(result);
|
|
@@ -631,87 +1729,571 @@ var Menu = {
|
|
|
631
1729
|
state.activeIndex.value = idx;
|
|
632
1730
|
updateActiveItem(idx);
|
|
633
1731
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1734
|
+
if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
1735
|
+
const char = event.key.toLowerCase();
|
|
1736
|
+
const match = items.find((item) => item.textContent?.toLowerCase().startsWith(char));
|
|
1737
|
+
if (match) {
|
|
1738
|
+
const idx = items.indexOf(match);
|
|
1739
|
+
state.activeIndex.value = idx;
|
|
1740
|
+
updateActiveItem(idx);
|
|
1741
|
+
match.focus();
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
function createItem(value, label, parent) {
|
|
1746
|
+
const item = document.createElement("div");
|
|
1747
|
+
item.setAttribute("role", "menuitem");
|
|
1748
|
+
item.setAttribute("data-value", value);
|
|
1749
|
+
item.setAttribute("tabindex", "-1");
|
|
1750
|
+
item.textContent = label ?? value;
|
|
1751
|
+
item.addEventListener("click", () => {
|
|
1752
|
+
onSelect?.(value);
|
|
1753
|
+
close();
|
|
1754
|
+
});
|
|
1755
|
+
items.push(item);
|
|
1756
|
+
(parent ?? content).appendChild(item);
|
|
1757
|
+
return item;
|
|
1758
|
+
}
|
|
1759
|
+
function Item(value, label) {
|
|
1760
|
+
return createItem(value, label);
|
|
1761
|
+
}
|
|
1762
|
+
function Group(label) {
|
|
1763
|
+
const el = document.createElement("div");
|
|
1764
|
+
el.setAttribute("role", "group");
|
|
1765
|
+
el.setAttribute("aria-label", label);
|
|
1766
|
+
content.appendChild(el);
|
|
1767
|
+
return {
|
|
1768
|
+
el,
|
|
1769
|
+
Item: (value, itemLabel) => createItem(value, itemLabel, el)
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
function Separator() {
|
|
1773
|
+
const hr = document.createElement("hr");
|
|
1774
|
+
hr.setAttribute("role", "separator");
|
|
1775
|
+
content.appendChild(hr);
|
|
1776
|
+
return hr;
|
|
1777
|
+
}
|
|
1778
|
+
function Label(text) {
|
|
1779
|
+
const el = document.createElement("div");
|
|
1780
|
+
el.setAttribute("role", "none");
|
|
1781
|
+
el.textContent = text;
|
|
1782
|
+
content.appendChild(el);
|
|
1783
|
+
return el;
|
|
1784
|
+
}
|
|
1785
|
+
return { trigger, content, state, Item, Group, Separator, Label };
|
|
1786
|
+
}
|
|
1787
|
+
};
|
|
1788
|
+
|
|
1789
|
+
// src/dropdown-menu/dropdown-menu.ts
|
|
1790
|
+
var DropdownMenu = {
|
|
1791
|
+
Root(options = {}) {
|
|
1792
|
+
const { positioning, ...rest } = options;
|
|
1793
|
+
return Menu.Root({
|
|
1794
|
+
...rest,
|
|
1795
|
+
positioning: {
|
|
1796
|
+
placement: "bottom-start",
|
|
1797
|
+
...positioning
|
|
1798
|
+
}
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
};
|
|
1802
|
+
// src/hover-card/hover-card.ts
|
|
1803
|
+
import { signal as signal14 } from "@vertz/ui";
|
|
1804
|
+
var HoverCard = {
|
|
1805
|
+
Root(options = {}) {
|
|
1806
|
+
const { openDelay = 700, closeDelay = 300, onOpenChange, positioning } = options;
|
|
1807
|
+
const contentId = uniqueId("hovercard");
|
|
1808
|
+
const state = { open: signal14(false) };
|
|
1809
|
+
let openTimeout = null;
|
|
1810
|
+
let closeTimeout = null;
|
|
1811
|
+
let floatingCleanup = null;
|
|
1812
|
+
const trigger = document.createElement("span");
|
|
1813
|
+
trigger.setAttribute("aria-haspopup", "dialog");
|
|
1814
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
1815
|
+
const content = document.createElement("div");
|
|
1816
|
+
content.setAttribute("role", "dialog");
|
|
1817
|
+
content.id = contentId;
|
|
1818
|
+
setHidden(content, true);
|
|
1819
|
+
setDataState(content, "closed");
|
|
1820
|
+
function cancelTimers() {
|
|
1821
|
+
if (openTimeout) {
|
|
1822
|
+
clearTimeout(openTimeout);
|
|
1823
|
+
openTimeout = null;
|
|
1824
|
+
}
|
|
1825
|
+
if (closeTimeout) {
|
|
1826
|
+
clearTimeout(closeTimeout);
|
|
1827
|
+
closeTimeout = null;
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
function show() {
|
|
1831
|
+
cancelTimers();
|
|
1832
|
+
if (state.open.peek())
|
|
1833
|
+
return;
|
|
1834
|
+
openTimeout = setTimeout(() => {
|
|
1835
|
+
state.open.value = true;
|
|
1836
|
+
setExpanded(trigger, true);
|
|
1837
|
+
setHidden(content, false);
|
|
1838
|
+
setDataState(content, "open");
|
|
1839
|
+
if (positioning) {
|
|
1840
|
+
const effectivePlacement = positioning.placement ?? "bottom";
|
|
1841
|
+
const result = createFloatingPosition(trigger, content, {
|
|
1842
|
+
...positioning,
|
|
1843
|
+
placement: effectivePlacement
|
|
1844
|
+
});
|
|
1845
|
+
floatingCleanup = result.cleanup;
|
|
1846
|
+
}
|
|
1847
|
+
onOpenChange?.(true);
|
|
1848
|
+
openTimeout = null;
|
|
1849
|
+
}, openDelay);
|
|
1850
|
+
}
|
|
1851
|
+
function hide() {
|
|
1852
|
+
cancelTimers();
|
|
1853
|
+
if (!state.open.peek())
|
|
1854
|
+
return;
|
|
1855
|
+
closeTimeout = setTimeout(() => {
|
|
1856
|
+
state.open.value = false;
|
|
1857
|
+
setExpanded(trigger, false);
|
|
1858
|
+
setDataState(content, "closed");
|
|
1859
|
+
setHiddenAnimated(content, true);
|
|
1860
|
+
floatingCleanup?.();
|
|
1861
|
+
floatingCleanup = null;
|
|
1862
|
+
onOpenChange?.(false);
|
|
1863
|
+
closeTimeout = null;
|
|
1864
|
+
}, closeDelay);
|
|
1865
|
+
}
|
|
1866
|
+
trigger.addEventListener("mouseenter", show);
|
|
1867
|
+
trigger.addEventListener("mouseleave", hide);
|
|
1868
|
+
trigger.addEventListener("focus", () => {
|
|
1869
|
+
cancelTimers();
|
|
1870
|
+
state.open.value = true;
|
|
1871
|
+
setExpanded(trigger, true);
|
|
1872
|
+
setHidden(content, false);
|
|
1873
|
+
setDataState(content, "open");
|
|
1874
|
+
if (positioning) {
|
|
1875
|
+
floatingCleanup?.();
|
|
1876
|
+
const effectivePlacement = positioning.placement ?? "bottom";
|
|
1877
|
+
const result = createFloatingPosition(trigger, content, {
|
|
1878
|
+
...positioning,
|
|
1879
|
+
placement: effectivePlacement
|
|
1880
|
+
});
|
|
1881
|
+
floatingCleanup = result.cleanup;
|
|
1882
|
+
}
|
|
1883
|
+
onOpenChange?.(true);
|
|
1884
|
+
});
|
|
1885
|
+
trigger.addEventListener("blur", (event) => {
|
|
1886
|
+
const related = event.relatedTarget;
|
|
1887
|
+
if (related && (trigger.contains(related) || content.contains(related)))
|
|
1888
|
+
return;
|
|
1889
|
+
hide();
|
|
1890
|
+
});
|
|
1891
|
+
content.addEventListener("mouseenter", () => {
|
|
1892
|
+
if (closeTimeout) {
|
|
1893
|
+
clearTimeout(closeTimeout);
|
|
1894
|
+
closeTimeout = null;
|
|
1895
|
+
}
|
|
1896
|
+
});
|
|
1897
|
+
content.addEventListener("mouseleave", hide);
|
|
1898
|
+
content.addEventListener("focusin", () => {
|
|
1899
|
+
if (closeTimeout) {
|
|
1900
|
+
clearTimeout(closeTimeout);
|
|
1901
|
+
closeTimeout = null;
|
|
1902
|
+
}
|
|
1903
|
+
});
|
|
1904
|
+
content.addEventListener("focusout", (event) => {
|
|
1905
|
+
const related = event.relatedTarget;
|
|
1906
|
+
if (related && (trigger.contains(related) || content.contains(related)))
|
|
1907
|
+
return;
|
|
1908
|
+
hide();
|
|
1909
|
+
});
|
|
1910
|
+
function hideImmediate() {
|
|
1911
|
+
cancelTimers();
|
|
1912
|
+
state.open.value = false;
|
|
1913
|
+
setExpanded(trigger, false);
|
|
1914
|
+
setDataState(content, "closed");
|
|
1915
|
+
setHiddenAnimated(content, true);
|
|
1916
|
+
floatingCleanup?.();
|
|
1917
|
+
floatingCleanup = null;
|
|
1918
|
+
onOpenChange?.(false);
|
|
1919
|
+
}
|
|
1920
|
+
content.addEventListener("keydown", (event) => {
|
|
1921
|
+
if (isKey(event, Keys.Escape)) {
|
|
1922
|
+
hideImmediate();
|
|
1923
|
+
trigger.focus();
|
|
1924
|
+
}
|
|
1925
|
+
});
|
|
1926
|
+
trigger.addEventListener("keydown", (event) => {
|
|
1927
|
+
if (isKey(event, Keys.Escape) && state.open.peek()) {
|
|
1928
|
+
hideImmediate();
|
|
1929
|
+
}
|
|
1930
|
+
});
|
|
1931
|
+
return { trigger, content, state };
|
|
1932
|
+
}
|
|
1933
|
+
};
|
|
1934
|
+
// src/menubar/menubar.ts
|
|
1935
|
+
import { signal as signal15 } from "@vertz/ui";
|
|
1936
|
+
var Menubar = {
|
|
1937
|
+
Root(options = {}) {
|
|
1938
|
+
const { onSelect, positioning } = options;
|
|
1939
|
+
const state = { activeMenu: signal15(null) };
|
|
1940
|
+
const triggers = [];
|
|
1941
|
+
const menus = new Map;
|
|
1942
|
+
let floatingCleanup = null;
|
|
1943
|
+
let dismissCleanup = null;
|
|
1944
|
+
const root = document.createElement("div");
|
|
1945
|
+
root.setAttribute("role", "menubar");
|
|
1946
|
+
function closeAll() {
|
|
1947
|
+
for (const [, menu] of menus) {
|
|
1948
|
+
setExpanded(menu.trigger, false);
|
|
1949
|
+
setDataState(menu.trigger, "closed");
|
|
1950
|
+
setDataState(menu.content, "closed");
|
|
1951
|
+
setHiddenAnimated(menu.content, true);
|
|
1952
|
+
}
|
|
1953
|
+
state.activeMenu.value = null;
|
|
1954
|
+
if (positioning) {
|
|
1955
|
+
floatingCleanup?.();
|
|
1956
|
+
floatingCleanup = null;
|
|
1957
|
+
dismissCleanup?.();
|
|
1958
|
+
dismissCleanup = null;
|
|
1959
|
+
} else {
|
|
1960
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
function openMenu(value) {
|
|
1964
|
+
const current = state.activeMenu.peek();
|
|
1965
|
+
if (current && current !== value) {
|
|
1966
|
+
const prev = menus.get(current);
|
|
1967
|
+
if (prev) {
|
|
1968
|
+
setExpanded(prev.trigger, false);
|
|
1969
|
+
setDataState(prev.trigger, "closed");
|
|
1970
|
+
setDataState(prev.content, "closed");
|
|
1971
|
+
setHiddenAnimated(prev.content, true);
|
|
1972
|
+
}
|
|
1973
|
+
if (positioning) {
|
|
1974
|
+
floatingCleanup?.();
|
|
1975
|
+
floatingCleanup = null;
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
const menu = menus.get(value);
|
|
1979
|
+
if (!menu)
|
|
1980
|
+
return;
|
|
1981
|
+
state.activeMenu.value = value;
|
|
1982
|
+
setExpanded(menu.trigger, true);
|
|
1983
|
+
setHidden(menu.content, false);
|
|
1984
|
+
setDataState(menu.trigger, "open");
|
|
1985
|
+
setDataState(menu.content, "open");
|
|
1986
|
+
if (positioning) {
|
|
1987
|
+
const result = createFloatingPosition(menu.trigger, menu.content, positioning);
|
|
1988
|
+
floatingCleanup = result.cleanup;
|
|
1989
|
+
if (!dismissCleanup) {
|
|
1990
|
+
dismissCleanup = createDismiss({
|
|
1991
|
+
onDismiss: closeAll,
|
|
1992
|
+
insideElements: [root],
|
|
1993
|
+
escapeKey: false
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1996
|
+
} else {
|
|
1997
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1998
|
+
}
|
|
1999
|
+
const firstItem = menu.items[0];
|
|
2000
|
+
if (firstItem) {
|
|
2001
|
+
firstItem.setAttribute("tabindex", "0");
|
|
2002
|
+
firstItem.focus();
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
function handleClickOutside(event) {
|
|
2006
|
+
const target = event.target;
|
|
2007
|
+
if (!root.contains(target)) {
|
|
2008
|
+
closeAll();
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
root.addEventListener("keydown", (event) => {
|
|
2012
|
+
if (isKey(event, Keys.ArrowLeft, Keys.ArrowRight, Keys.Home, Keys.End)) {
|
|
2013
|
+
const focused = document.activeElement;
|
|
2014
|
+
const triggerIndex = triggers.indexOf(focused);
|
|
2015
|
+
if (triggerIndex >= 0) {
|
|
2016
|
+
const result = handleListNavigation(event, triggers, { orientation: "horizontal" });
|
|
2017
|
+
if (result && state.activeMenu.peek()) {
|
|
2018
|
+
const newTrigger = result;
|
|
2019
|
+
const menuValue = newTrigger.getAttribute("data-value");
|
|
2020
|
+
if (menuValue)
|
|
2021
|
+
openMenu(menuValue);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
});
|
|
2026
|
+
function Menu2(value, label) {
|
|
2027
|
+
const ids = linkedIds("menubar-menu");
|
|
2028
|
+
const items = [];
|
|
2029
|
+
const trigger = document.createElement("button");
|
|
2030
|
+
trigger.setAttribute("type", "button");
|
|
2031
|
+
trigger.setAttribute("role", "menuitem");
|
|
2032
|
+
trigger.id = ids.triggerId;
|
|
2033
|
+
trigger.setAttribute("aria-controls", ids.contentId);
|
|
2034
|
+
trigger.setAttribute("aria-haspopup", "menu");
|
|
2035
|
+
trigger.setAttribute("data-value", value);
|
|
2036
|
+
trigger.textContent = label ?? value;
|
|
2037
|
+
setExpanded(trigger, false);
|
|
2038
|
+
setDataState(trigger, "closed");
|
|
2039
|
+
setRovingTabindex(triggers.concat(trigger), triggers.length);
|
|
2040
|
+
const content = document.createElement("div");
|
|
2041
|
+
content.setAttribute("role", "menu");
|
|
2042
|
+
content.id = ids.contentId;
|
|
2043
|
+
setHidden(content, true);
|
|
2044
|
+
setDataState(content, "closed");
|
|
2045
|
+
trigger.addEventListener("click", () => {
|
|
2046
|
+
if (state.activeMenu.peek() === value) {
|
|
2047
|
+
closeAll();
|
|
2048
|
+
} else {
|
|
2049
|
+
openMenu(value);
|
|
2050
|
+
}
|
|
2051
|
+
});
|
|
2052
|
+
trigger.addEventListener("keydown", (event) => {
|
|
2053
|
+
if (isKey(event, Keys.ArrowDown, Keys.Enter, Keys.Space)) {
|
|
2054
|
+
event.preventDefault();
|
|
2055
|
+
openMenu(value);
|
|
2056
|
+
}
|
|
2057
|
+
});
|
|
2058
|
+
content.addEventListener("keydown", (event) => {
|
|
2059
|
+
if (isKey(event, Keys.Escape)) {
|
|
2060
|
+
event.preventDefault();
|
|
2061
|
+
event.stopPropagation();
|
|
2062
|
+
closeAll();
|
|
2063
|
+
trigger.focus();
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
if (isKey(event, Keys.Enter, Keys.Space)) {
|
|
2067
|
+
event.preventDefault();
|
|
2068
|
+
const active = document.activeElement;
|
|
2069
|
+
const activeItem = items.find((item) => item === active);
|
|
2070
|
+
if (activeItem) {
|
|
2071
|
+
const val = activeItem.getAttribute("data-value");
|
|
2072
|
+
if (val !== null) {
|
|
2073
|
+
onSelect?.(val);
|
|
2074
|
+
closeAll();
|
|
2075
|
+
trigger.focus();
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
if (isKey(event, Keys.ArrowLeft, Keys.ArrowRight)) {
|
|
2081
|
+
event.preventDefault();
|
|
2082
|
+
const triggerIdx = triggers.indexOf(trigger);
|
|
2083
|
+
let nextIdx;
|
|
2084
|
+
if (isKey(event, Keys.ArrowRight)) {
|
|
2085
|
+
nextIdx = (triggerIdx + 1) % triggers.length;
|
|
2086
|
+
} else {
|
|
2087
|
+
nextIdx = (triggerIdx - 1 + triggers.length) % triggers.length;
|
|
2088
|
+
}
|
|
2089
|
+
const nextTrigger = triggers[nextIdx];
|
|
2090
|
+
if (nextTrigger) {
|
|
2091
|
+
nextTrigger.focus();
|
|
2092
|
+
const nextValue = nextTrigger.getAttribute("data-value");
|
|
2093
|
+
if (nextValue)
|
|
2094
|
+
openMenu(nextValue);
|
|
2095
|
+
}
|
|
2096
|
+
return;
|
|
2097
|
+
}
|
|
2098
|
+
handleListNavigation(event, items, { orientation: "vertical" });
|
|
645
2099
|
});
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
2100
|
+
function createItem(val, itemLabel, parent) {
|
|
2101
|
+
const item = document.createElement("div");
|
|
2102
|
+
item.setAttribute("role", "menuitem");
|
|
2103
|
+
item.setAttribute("data-value", val);
|
|
2104
|
+
item.setAttribute("tabindex", "-1");
|
|
2105
|
+
item.textContent = itemLabel ?? val;
|
|
2106
|
+
item.addEventListener("click", () => {
|
|
2107
|
+
onSelect?.(val);
|
|
2108
|
+
closeAll();
|
|
2109
|
+
trigger.focus();
|
|
2110
|
+
});
|
|
2111
|
+
items.push(item);
|
|
2112
|
+
(parent ?? content).appendChild(item);
|
|
2113
|
+
return item;
|
|
2114
|
+
}
|
|
2115
|
+
function Item(val, itemLabel) {
|
|
2116
|
+
return createItem(val, itemLabel);
|
|
2117
|
+
}
|
|
2118
|
+
function Group(groupLabel) {
|
|
2119
|
+
const el = document.createElement("div");
|
|
2120
|
+
el.setAttribute("role", "group");
|
|
2121
|
+
el.setAttribute("aria-label", groupLabel);
|
|
2122
|
+
content.appendChild(el);
|
|
2123
|
+
return {
|
|
2124
|
+
el,
|
|
2125
|
+
Item: (val, l) => createItem(val, l, el)
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
function Separator() {
|
|
2129
|
+
const hr = document.createElement("hr");
|
|
2130
|
+
hr.setAttribute("role", "separator");
|
|
2131
|
+
content.appendChild(hr);
|
|
2132
|
+
return hr;
|
|
2133
|
+
}
|
|
2134
|
+
triggers.push(trigger);
|
|
2135
|
+
setRovingTabindex(triggers, 0);
|
|
2136
|
+
menus.set(value, { trigger, content, items });
|
|
2137
|
+
root.appendChild(trigger);
|
|
2138
|
+
return { trigger, content, Item, Group, Separator };
|
|
649
2139
|
}
|
|
650
|
-
return {
|
|
2140
|
+
return { root, state, Menu: Menu2 };
|
|
651
2141
|
}
|
|
652
2142
|
};
|
|
653
|
-
// src/
|
|
654
|
-
import { signal as
|
|
655
|
-
var
|
|
2143
|
+
// src/navigation-menu/navigation-menu.ts
|
|
2144
|
+
import { signal as signal16 } from "@vertz/ui";
|
|
2145
|
+
var NavigationMenu = {
|
|
656
2146
|
Root(options = {}) {
|
|
657
|
-
const {
|
|
658
|
-
const
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
setExpanded(trigger, true);
|
|
676
|
-
setHidden(content, false);
|
|
677
|
-
setDataState(trigger, "open");
|
|
678
|
-
setDataState(content, "open");
|
|
679
|
-
restoreFocus = saveFocus();
|
|
680
|
-
queueMicrotask(() => focusFirst(content));
|
|
681
|
-
onOpenChange?.(true);
|
|
2147
|
+
const { orientation = "horizontal", delayOpen = 200, delayClose = 300 } = options;
|
|
2148
|
+
const state = { activeItem: signal16(null) };
|
|
2149
|
+
const triggers = [];
|
|
2150
|
+
const items = new Map;
|
|
2151
|
+
let openTimeout = null;
|
|
2152
|
+
let closeTimeout = null;
|
|
2153
|
+
const root = document.createElement("nav");
|
|
2154
|
+
const list = document.createElement("div");
|
|
2155
|
+
const viewport = document.createElement("div");
|
|
2156
|
+
function cancelTimers() {
|
|
2157
|
+
if (openTimeout) {
|
|
2158
|
+
clearTimeout(openTimeout);
|
|
2159
|
+
openTimeout = null;
|
|
2160
|
+
}
|
|
2161
|
+
if (closeTimeout) {
|
|
2162
|
+
clearTimeout(closeTimeout);
|
|
2163
|
+
closeTimeout = null;
|
|
2164
|
+
}
|
|
682
2165
|
}
|
|
683
|
-
function
|
|
684
|
-
|
|
2166
|
+
function openItem(value) {
|
|
2167
|
+
cancelTimers();
|
|
2168
|
+
const current = state.activeItem.peek();
|
|
2169
|
+
if (current && current !== value) {
|
|
2170
|
+
const prev = items.get(current);
|
|
2171
|
+
if (prev) {
|
|
2172
|
+
setExpanded(prev.trigger, false);
|
|
2173
|
+
setDataState(prev.trigger, "closed");
|
|
2174
|
+
setDataState(prev.content, "closed");
|
|
2175
|
+
setHiddenAnimated(prev.content, true);
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
const item = items.get(value);
|
|
2179
|
+
if (!item)
|
|
2180
|
+
return;
|
|
2181
|
+
state.activeItem.value = value;
|
|
2182
|
+
setExpanded(item.trigger, true);
|
|
2183
|
+
setHidden(item.content, false);
|
|
2184
|
+
setDataState(item.trigger, "open");
|
|
2185
|
+
setDataState(item.content, "open");
|
|
2186
|
+
}
|
|
2187
|
+
function closeAll() {
|
|
2188
|
+
cancelTimers();
|
|
2189
|
+
const current = state.activeItem.peek();
|
|
2190
|
+
if (current) {
|
|
2191
|
+
const item = items.get(current);
|
|
2192
|
+
if (item) {
|
|
2193
|
+
setExpanded(item.trigger, false);
|
|
2194
|
+
setDataState(item.trigger, "closed");
|
|
2195
|
+
setDataState(item.content, "closed");
|
|
2196
|
+
setHiddenAnimated(item.content, true);
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
state.activeItem.value = null;
|
|
2200
|
+
}
|
|
2201
|
+
function Item(value, label) {
|
|
2202
|
+
const ids = linkedIds("nav-menu");
|
|
2203
|
+
const trigger = document.createElement("button");
|
|
2204
|
+
trigger.setAttribute("type", "button");
|
|
2205
|
+
trigger.id = ids.triggerId;
|
|
2206
|
+
trigger.setAttribute("aria-controls", ids.contentId);
|
|
2207
|
+
trigger.setAttribute("data-value", value);
|
|
2208
|
+
trigger.textContent = label ?? value;
|
|
685
2209
|
setExpanded(trigger, false);
|
|
686
|
-
setHidden(content, true);
|
|
687
2210
|
setDataState(trigger, "closed");
|
|
2211
|
+
const content = document.createElement("div");
|
|
2212
|
+
content.id = ids.contentId;
|
|
2213
|
+
setHidden(content, true);
|
|
688
2214
|
setDataState(content, "closed");
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
2215
|
+
trigger.addEventListener("click", () => {
|
|
2216
|
+
if (state.activeItem.peek() === value) {
|
|
2217
|
+
closeAll();
|
|
2218
|
+
} else {
|
|
2219
|
+
openItem(value);
|
|
2220
|
+
}
|
|
2221
|
+
});
|
|
2222
|
+
trigger.addEventListener("mouseenter", () => {
|
|
2223
|
+
cancelTimers();
|
|
2224
|
+
openTimeout = setTimeout(() => {
|
|
2225
|
+
openItem(value);
|
|
2226
|
+
openTimeout = null;
|
|
2227
|
+
}, delayOpen);
|
|
2228
|
+
});
|
|
2229
|
+
trigger.addEventListener("mouseleave", () => {
|
|
2230
|
+
cancelTimers();
|
|
2231
|
+
closeTimeout = setTimeout(() => {
|
|
2232
|
+
closeAll();
|
|
2233
|
+
closeTimeout = null;
|
|
2234
|
+
}, delayClose);
|
|
2235
|
+
});
|
|
2236
|
+
content.addEventListener("mouseenter", () => {
|
|
2237
|
+
cancelTimers();
|
|
2238
|
+
});
|
|
2239
|
+
content.addEventListener("mouseleave", () => {
|
|
2240
|
+
cancelTimers();
|
|
2241
|
+
closeTimeout = setTimeout(() => {
|
|
2242
|
+
closeAll();
|
|
2243
|
+
closeTimeout = null;
|
|
2244
|
+
}, delayClose);
|
|
2245
|
+
});
|
|
2246
|
+
trigger.addEventListener("keydown", (event) => {
|
|
2247
|
+
if (isKey(event, Keys.Enter, Keys.Space)) {
|
|
2248
|
+
event.preventDefault();
|
|
2249
|
+
openItem(value);
|
|
2250
|
+
queueMicrotask(() => focusFirst(content));
|
|
2251
|
+
}
|
|
2252
|
+
if (isKey(event, Keys.Escape)) {
|
|
2253
|
+
event.preventDefault();
|
|
2254
|
+
closeAll();
|
|
2255
|
+
}
|
|
2256
|
+
});
|
|
2257
|
+
content.addEventListener("keydown", (event) => {
|
|
2258
|
+
if (isKey(event, Keys.Escape)) {
|
|
2259
|
+
event.preventDefault();
|
|
2260
|
+
event.stopPropagation();
|
|
2261
|
+
closeAll();
|
|
2262
|
+
trigger.focus();
|
|
2263
|
+
}
|
|
2264
|
+
});
|
|
2265
|
+
triggers.push(trigger);
|
|
2266
|
+
setRovingTabindex(triggers, 0);
|
|
2267
|
+
items.set(value, { trigger, content });
|
|
2268
|
+
list.appendChild(trigger);
|
|
2269
|
+
viewport.appendChild(content);
|
|
2270
|
+
return { trigger, content };
|
|
692
2271
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
if (isKey(event, Keys.
|
|
702
|
-
event
|
|
703
|
-
|
|
2272
|
+
function Link(href, label) {
|
|
2273
|
+
const a = document.createElement("a");
|
|
2274
|
+
a.href = href;
|
|
2275
|
+
a.textContent = label;
|
|
2276
|
+
list.appendChild(a);
|
|
2277
|
+
return a;
|
|
2278
|
+
}
|
|
2279
|
+
list.addEventListener("keydown", (event) => {
|
|
2280
|
+
if (isKey(event, Keys.ArrowLeft, Keys.ArrowRight, Keys.Home, Keys.End)) {
|
|
2281
|
+
handleListNavigation(event, triggers, {
|
|
2282
|
+
orientation: orientation === "horizontal" ? "horizontal" : "vertical"
|
|
2283
|
+
});
|
|
704
2284
|
}
|
|
705
2285
|
});
|
|
706
|
-
|
|
2286
|
+
root.appendChild(list);
|
|
2287
|
+
root.appendChild(viewport);
|
|
2288
|
+
return { root, list, viewport, state, Item, Link };
|
|
707
2289
|
}
|
|
708
2290
|
};
|
|
709
2291
|
// src/progress/progress.ts
|
|
710
|
-
import { signal as
|
|
2292
|
+
import { signal as signal17 } from "@vertz/ui";
|
|
711
2293
|
var Progress = {
|
|
712
2294
|
Root(options = {}) {
|
|
713
2295
|
const { defaultValue = 0, min = 0, max = 100 } = options;
|
|
714
|
-
const state = { value:
|
|
2296
|
+
const state = { value: signal17(defaultValue) };
|
|
715
2297
|
const root = document.createElement("div");
|
|
716
2298
|
root.setAttribute("role", "progressbar");
|
|
717
2299
|
root.id = uniqueId("progress");
|
|
@@ -746,11 +2328,11 @@ var Progress = {
|
|
|
746
2328
|
}
|
|
747
2329
|
};
|
|
748
2330
|
// src/radio/radio.ts
|
|
749
|
-
import { signal as
|
|
2331
|
+
import { signal as signal18 } from "@vertz/ui";
|
|
750
2332
|
var Radio = {
|
|
751
2333
|
Root(options = {}) {
|
|
752
2334
|
const { defaultValue = "", onValueChange } = options;
|
|
753
|
-
const state = { value:
|
|
2335
|
+
const state = { value: signal18(defaultValue) };
|
|
754
2336
|
const items = [];
|
|
755
2337
|
const itemValues = [];
|
|
756
2338
|
const root = document.createElement("div");
|
|
@@ -802,18 +2384,275 @@ var Radio = {
|
|
|
802
2384
|
return { root, state, Item };
|
|
803
2385
|
}
|
|
804
2386
|
};
|
|
2387
|
+
// src/resizable-panel/resizable-panel.ts
|
|
2388
|
+
import { signal as signal19 } from "@vertz/ui";
|
|
2389
|
+
var ResizablePanel = {
|
|
2390
|
+
Root(options = {}) {
|
|
2391
|
+
const { orientation = "horizontal", onResize } = options;
|
|
2392
|
+
const state = { sizes: signal19([]) };
|
|
2393
|
+
const panels = [];
|
|
2394
|
+
const handles = [];
|
|
2395
|
+
const root = document.createElement("div");
|
|
2396
|
+
root.style.display = "flex";
|
|
2397
|
+
root.style.flexDirection = orientation === "horizontal" ? "row" : "column";
|
|
2398
|
+
root.setAttribute("data-orientation", orientation);
|
|
2399
|
+
function updateSizes(newSizes) {
|
|
2400
|
+
state.sizes.value = [...newSizes];
|
|
2401
|
+
for (let i = 0;i < panels.length; i++) {
|
|
2402
|
+
const panel = panels[i];
|
|
2403
|
+
if (!panel)
|
|
2404
|
+
continue;
|
|
2405
|
+
const size = newSizes[i] ?? 0;
|
|
2406
|
+
panel.el.style.flex = `0 0 ${size}%`;
|
|
2407
|
+
}
|
|
2408
|
+
for (let i = 0;i < handles.length; i++) {
|
|
2409
|
+
const handle = handles[i];
|
|
2410
|
+
const leftPanel = panels[i];
|
|
2411
|
+
if (handle && leftPanel) {
|
|
2412
|
+
const size = newSizes[i] ?? 0;
|
|
2413
|
+
setValueRange(handle, Math.round(size), Math.round(leftPanel.minSize), Math.round(leftPanel.maxSize));
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
onResize?.(newSizes);
|
|
2417
|
+
}
|
|
2418
|
+
function Panel(panelOptions = {}) {
|
|
2419
|
+
const { defaultSize, minSize = 0, maxSize = 100 } = panelOptions;
|
|
2420
|
+
const el = document.createElement("div");
|
|
2421
|
+
el.setAttribute("data-panel", "");
|
|
2422
|
+
panels.push({ el, minSize, maxSize });
|
|
2423
|
+
const sizes = state.sizes.peek();
|
|
2424
|
+
if (defaultSize != null) {
|
|
2425
|
+
sizes.push(defaultSize);
|
|
2426
|
+
} else {
|
|
2427
|
+
const equalSize = 100 / panels.length;
|
|
2428
|
+
sizes.length = 0;
|
|
2429
|
+
for (let i = 0;i < panels.length; i++) {
|
|
2430
|
+
sizes.push(equalSize);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
updateSizes(sizes);
|
|
2434
|
+
root.appendChild(el);
|
|
2435
|
+
return el;
|
|
2436
|
+
}
|
|
2437
|
+
function Handle() {
|
|
2438
|
+
const handleIndex = handles.length;
|
|
2439
|
+
const handle = document.createElement("div");
|
|
2440
|
+
handle.setAttribute("role", "separator");
|
|
2441
|
+
handle.setAttribute("tabindex", "0");
|
|
2442
|
+
handle.setAttribute("data-orientation", orientation);
|
|
2443
|
+
setDataState(handle, "idle");
|
|
2444
|
+
handles.push(handle);
|
|
2445
|
+
const STEP = 5;
|
|
2446
|
+
handle.addEventListener("keydown", (event) => {
|
|
2447
|
+
const sizes = [...state.sizes.peek()];
|
|
2448
|
+
const leftIdx = handleIndex;
|
|
2449
|
+
const rightIdx = handleIndex + 1;
|
|
2450
|
+
const leftPanel = panels[leftIdx];
|
|
2451
|
+
const rightPanel = panels[rightIdx];
|
|
2452
|
+
if (!leftPanel || !rightPanel)
|
|
2453
|
+
return;
|
|
2454
|
+
let leftSize = sizes[leftIdx] ?? 0;
|
|
2455
|
+
let rightSize = sizes[rightIdx] ?? 0;
|
|
2456
|
+
const growKey = orientation === "horizontal" ? Keys.ArrowRight : Keys.ArrowDown;
|
|
2457
|
+
const shrinkKey = orientation === "horizontal" ? Keys.ArrowLeft : Keys.ArrowUp;
|
|
2458
|
+
if (isKey(event, growKey)) {
|
|
2459
|
+
event.preventDefault();
|
|
2460
|
+
const delta = Math.min(STEP, rightSize - rightPanel.minSize, leftPanel.maxSize - leftSize);
|
|
2461
|
+
leftSize += delta;
|
|
2462
|
+
rightSize -= delta;
|
|
2463
|
+
} else if (isKey(event, shrinkKey)) {
|
|
2464
|
+
event.preventDefault();
|
|
2465
|
+
const delta = Math.min(STEP, leftSize - leftPanel.minSize, rightPanel.maxSize - rightSize);
|
|
2466
|
+
leftSize -= delta;
|
|
2467
|
+
rightSize += delta;
|
|
2468
|
+
} else if (isKey(event, Keys.Home)) {
|
|
2469
|
+
event.preventDefault();
|
|
2470
|
+
const delta = leftSize - leftPanel.minSize;
|
|
2471
|
+
leftSize -= delta;
|
|
2472
|
+
rightSize += delta;
|
|
2473
|
+
} else if (isKey(event, Keys.End)) {
|
|
2474
|
+
event.preventDefault();
|
|
2475
|
+
const delta = rightSize - rightPanel.minSize;
|
|
2476
|
+
leftSize += delta;
|
|
2477
|
+
rightSize -= delta;
|
|
2478
|
+
} else {
|
|
2479
|
+
return;
|
|
2480
|
+
}
|
|
2481
|
+
sizes[leftIdx] = leftSize;
|
|
2482
|
+
sizes[rightIdx] = rightSize;
|
|
2483
|
+
updateSizes(sizes);
|
|
2484
|
+
});
|
|
2485
|
+
handle.addEventListener("pointerdown", (event) => {
|
|
2486
|
+
event.preventDefault();
|
|
2487
|
+
handle.setPointerCapture(event.pointerId);
|
|
2488
|
+
setDataState(handle, "dragging");
|
|
2489
|
+
const startPos = orientation === "horizontal" ? event.clientX : event.clientY;
|
|
2490
|
+
const rootSize = orientation === "horizontal" ? root.offsetWidth : root.offsetHeight;
|
|
2491
|
+
const startSizes = [...state.sizes.peek()];
|
|
2492
|
+
function onMove(e) {
|
|
2493
|
+
const currentPos = orientation === "horizontal" ? e.clientX : e.clientY;
|
|
2494
|
+
const delta = (currentPos - startPos) / rootSize * 100;
|
|
2495
|
+
const sizes = [...startSizes];
|
|
2496
|
+
const leftIdx = handleIndex;
|
|
2497
|
+
const rightIdx = handleIndex + 1;
|
|
2498
|
+
const leftPanel = panels[leftIdx];
|
|
2499
|
+
const rightPanel = panels[rightIdx];
|
|
2500
|
+
if (!leftPanel || !rightPanel)
|
|
2501
|
+
return;
|
|
2502
|
+
let newLeft = (startSizes[leftIdx] ?? 0) + delta;
|
|
2503
|
+
let newRight = (startSizes[rightIdx] ?? 0) - delta;
|
|
2504
|
+
newLeft = Math.max(leftPanel.minSize, Math.min(leftPanel.maxSize, newLeft));
|
|
2505
|
+
newRight = Math.max(rightPanel.minSize, Math.min(rightPanel.maxSize, newRight));
|
|
2506
|
+
sizes[leftIdx] = newLeft;
|
|
2507
|
+
sizes[rightIdx] = newRight;
|
|
2508
|
+
updateSizes(sizes);
|
|
2509
|
+
}
|
|
2510
|
+
function onUp(e) {
|
|
2511
|
+
handle.releasePointerCapture(e.pointerId);
|
|
2512
|
+
setDataState(handle, "idle");
|
|
2513
|
+
handle.removeEventListener("pointermove", onMove);
|
|
2514
|
+
handle.removeEventListener("pointerup", onUp);
|
|
2515
|
+
}
|
|
2516
|
+
handle.addEventListener("pointermove", onMove);
|
|
2517
|
+
handle.addEventListener("pointerup", onUp);
|
|
2518
|
+
});
|
|
2519
|
+
root.appendChild(handle);
|
|
2520
|
+
return handle;
|
|
2521
|
+
}
|
|
2522
|
+
return { root, state, Panel, Handle };
|
|
2523
|
+
}
|
|
2524
|
+
};
|
|
2525
|
+
// src/scroll-area/scroll-area.ts
|
|
2526
|
+
import { signal as signal20 } from "@vertz/ui";
|
|
2527
|
+
var ScrollArea = {
|
|
2528
|
+
Root(options = {}) {
|
|
2529
|
+
const { orientation = "vertical" } = options;
|
|
2530
|
+
const state = {
|
|
2531
|
+
scrollTop: signal20(0),
|
|
2532
|
+
scrollLeft: signal20(0)
|
|
2533
|
+
};
|
|
2534
|
+
const root = document.createElement("div");
|
|
2535
|
+
root.style.position = "relative";
|
|
2536
|
+
root.style.overflow = "hidden";
|
|
2537
|
+
const viewport = document.createElement("div");
|
|
2538
|
+
viewport.style.overflow = "scroll";
|
|
2539
|
+
viewport.style.scrollbarWidth = "none";
|
|
2540
|
+
const content = document.createElement("div");
|
|
2541
|
+
const scrollbarY = document.createElement("div");
|
|
2542
|
+
scrollbarY.setAttribute("aria-hidden", "true");
|
|
2543
|
+
scrollbarY.setAttribute("data-orientation", "vertical");
|
|
2544
|
+
const thumbY = document.createElement("div");
|
|
2545
|
+
scrollbarY.appendChild(thumbY);
|
|
2546
|
+
const scrollbarX = document.createElement("div");
|
|
2547
|
+
scrollbarX.setAttribute("aria-hidden", "true");
|
|
2548
|
+
scrollbarX.setAttribute("data-orientation", "horizontal");
|
|
2549
|
+
const thumbX = document.createElement("div");
|
|
2550
|
+
scrollbarX.appendChild(thumbX);
|
|
2551
|
+
function syncThumbY() {
|
|
2552
|
+
const { scrollTop, scrollHeight, clientHeight } = viewport;
|
|
2553
|
+
if (scrollHeight <= clientHeight) {
|
|
2554
|
+
thumbY.style.height = "0";
|
|
2555
|
+
return;
|
|
2556
|
+
}
|
|
2557
|
+
const ratio = clientHeight / scrollHeight;
|
|
2558
|
+
thumbY.style.height = `${ratio * 100}%`;
|
|
2559
|
+
const scrollRatio = scrollTop / (scrollHeight - clientHeight);
|
|
2560
|
+
thumbY.style.transform = `translateY(${scrollRatio * (1 / ratio - 1) * 100}%)`;
|
|
2561
|
+
state.scrollTop.value = scrollTop;
|
|
2562
|
+
}
|
|
2563
|
+
function syncThumbX() {
|
|
2564
|
+
const { scrollLeft, scrollWidth, clientWidth } = viewport;
|
|
2565
|
+
if (scrollWidth <= clientWidth) {
|
|
2566
|
+
thumbX.style.width = "0";
|
|
2567
|
+
return;
|
|
2568
|
+
}
|
|
2569
|
+
const ratio = clientWidth / scrollWidth;
|
|
2570
|
+
thumbX.style.width = `${ratio * 100}%`;
|
|
2571
|
+
const scrollRatio = scrollLeft / (scrollWidth - clientWidth);
|
|
2572
|
+
thumbX.style.transform = `translateX(${scrollRatio * (1 / ratio - 1) * 100}%)`;
|
|
2573
|
+
state.scrollLeft.value = scrollLeft;
|
|
2574
|
+
}
|
|
2575
|
+
viewport.addEventListener("scroll", () => {
|
|
2576
|
+
if (orientation === "vertical" || orientation === "both")
|
|
2577
|
+
syncThumbY();
|
|
2578
|
+
if (orientation === "horizontal" || orientation === "both")
|
|
2579
|
+
syncThumbX();
|
|
2580
|
+
});
|
|
2581
|
+
let isDraggingY = false;
|
|
2582
|
+
let startY = 0;
|
|
2583
|
+
let startScrollTop = 0;
|
|
2584
|
+
thumbY.addEventListener("pointerdown", (e) => {
|
|
2585
|
+
isDraggingY = true;
|
|
2586
|
+
startY = e.clientY;
|
|
2587
|
+
startScrollTop = viewport.scrollTop;
|
|
2588
|
+
thumbY.setPointerCapture(e.pointerId);
|
|
2589
|
+
e.preventDefault();
|
|
2590
|
+
});
|
|
2591
|
+
thumbY.addEventListener("pointermove", (e) => {
|
|
2592
|
+
if (!isDraggingY)
|
|
2593
|
+
return;
|
|
2594
|
+
const delta = e.clientY - startY;
|
|
2595
|
+
const scrollbarHeight = scrollbarY.clientHeight;
|
|
2596
|
+
const scrollRange = viewport.scrollHeight - viewport.clientHeight;
|
|
2597
|
+
if (scrollbarHeight > 0) {
|
|
2598
|
+
viewport.scrollTop = startScrollTop + delta / scrollbarHeight * scrollRange;
|
|
2599
|
+
}
|
|
2600
|
+
});
|
|
2601
|
+
thumbY.addEventListener("pointerup", (e) => {
|
|
2602
|
+
isDraggingY = false;
|
|
2603
|
+
thumbY.releasePointerCapture(e.pointerId);
|
|
2604
|
+
});
|
|
2605
|
+
let isDraggingX = false;
|
|
2606
|
+
let startX = 0;
|
|
2607
|
+
let startScrollLeft = 0;
|
|
2608
|
+
thumbX.addEventListener("pointerdown", (e) => {
|
|
2609
|
+
isDraggingX = true;
|
|
2610
|
+
startX = e.clientX;
|
|
2611
|
+
startScrollLeft = viewport.scrollLeft;
|
|
2612
|
+
thumbX.setPointerCapture(e.pointerId);
|
|
2613
|
+
e.preventDefault();
|
|
2614
|
+
});
|
|
2615
|
+
thumbX.addEventListener("pointermove", (e) => {
|
|
2616
|
+
if (!isDraggingX)
|
|
2617
|
+
return;
|
|
2618
|
+
const delta = e.clientX - startX;
|
|
2619
|
+
const scrollbarWidth = scrollbarX.clientWidth;
|
|
2620
|
+
const scrollRange = viewport.scrollWidth - viewport.clientWidth;
|
|
2621
|
+
if (scrollbarWidth > 0) {
|
|
2622
|
+
viewport.scrollLeft = startScrollLeft + delta / scrollbarWidth * scrollRange;
|
|
2623
|
+
}
|
|
2624
|
+
});
|
|
2625
|
+
thumbX.addEventListener("pointerup", (e) => {
|
|
2626
|
+
isDraggingX = false;
|
|
2627
|
+
thumbX.releasePointerCapture(e.pointerId);
|
|
2628
|
+
});
|
|
2629
|
+
function update() {
|
|
2630
|
+
syncThumbY();
|
|
2631
|
+
syncThumbX();
|
|
2632
|
+
}
|
|
2633
|
+
viewport.appendChild(content);
|
|
2634
|
+
root.appendChild(viewport);
|
|
2635
|
+
if (orientation === "vertical" || orientation === "both")
|
|
2636
|
+
root.appendChild(scrollbarY);
|
|
2637
|
+
if (orientation === "horizontal" || orientation === "both")
|
|
2638
|
+
root.appendChild(scrollbarX);
|
|
2639
|
+
return { root, viewport, content, scrollbarY, thumbY, scrollbarX, thumbX, state, update };
|
|
2640
|
+
}
|
|
2641
|
+
};
|
|
805
2642
|
// src/select/select.ts
|
|
806
|
-
import { signal as
|
|
2643
|
+
import { signal as signal21 } from "@vertz/ui";
|
|
807
2644
|
var Select = {
|
|
808
2645
|
Root(options = {}) {
|
|
809
|
-
const { defaultValue = "", onValueChange } = options;
|
|
2646
|
+
const { defaultValue = "", placeholder = "", onValueChange, positioning } = options;
|
|
810
2647
|
const ids = linkedIds("select");
|
|
811
2648
|
const state = {
|
|
812
|
-
open:
|
|
813
|
-
value:
|
|
814
|
-
activeIndex:
|
|
2649
|
+
open: signal21(false),
|
|
2650
|
+
value: signal21(defaultValue),
|
|
2651
|
+
activeIndex: signal21(-1)
|
|
815
2652
|
};
|
|
816
2653
|
const items = [];
|
|
2654
|
+
let floatingCleanup = null;
|
|
2655
|
+
let dismissCleanup = null;
|
|
817
2656
|
const trigger = document.createElement("button");
|
|
818
2657
|
trigger.setAttribute("type", "button");
|
|
819
2658
|
trigger.setAttribute("role", "combobox");
|
|
@@ -822,8 +2661,13 @@ var Select = {
|
|
|
822
2661
|
trigger.setAttribute("aria-haspopup", "listbox");
|
|
823
2662
|
setExpanded(trigger, false);
|
|
824
2663
|
setDataState(trigger, "closed");
|
|
2664
|
+
const triggerText = document.createElement("span");
|
|
2665
|
+
triggerText.setAttribute("data-part", "value");
|
|
2666
|
+
triggerText.textContent = defaultValue || placeholder;
|
|
2667
|
+
trigger.appendChild(triggerText);
|
|
825
2668
|
const content = document.createElement("div");
|
|
826
2669
|
content.setAttribute("role", "listbox");
|
|
2670
|
+
content.setAttribute("tabindex", "-1");
|
|
827
2671
|
content.id = ids.contentId;
|
|
828
2672
|
setHidden(content, true);
|
|
829
2673
|
setDataState(content, "closed");
|
|
@@ -833,18 +2677,40 @@ var Select = {
|
|
|
833
2677
|
setHidden(content, false);
|
|
834
2678
|
setDataState(trigger, "open");
|
|
835
2679
|
setDataState(content, "open");
|
|
2680
|
+
if (positioning) {
|
|
2681
|
+
const result = createFloatingPosition(trigger, content, positioning);
|
|
2682
|
+
floatingCleanup = result.cleanup;
|
|
2683
|
+
dismissCleanup = createDismiss({
|
|
2684
|
+
onDismiss: close,
|
|
2685
|
+
insideElements: [trigger, content],
|
|
2686
|
+
escapeKey: false
|
|
2687
|
+
});
|
|
2688
|
+
} else {
|
|
2689
|
+
const rect = trigger.getBoundingClientRect();
|
|
2690
|
+
const side = window.innerHeight - rect.bottom >= rect.top ? "bottom" : "top";
|
|
2691
|
+
content.setAttribute("data-side", side);
|
|
2692
|
+
}
|
|
836
2693
|
const selectedIdx = items.findIndex((item) => item.getAttribute("data-value") === state.value.peek());
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
2694
|
+
if (selectedIdx >= 0) {
|
|
2695
|
+
state.activeIndex.value = selectedIdx;
|
|
2696
|
+
updateActiveItem(selectedIdx);
|
|
2697
|
+
items[selectedIdx]?.focus();
|
|
2698
|
+
} else {
|
|
2699
|
+
state.activeIndex.value = -1;
|
|
2700
|
+
updateActiveItem(-1);
|
|
2701
|
+
content.focus();
|
|
2702
|
+
}
|
|
841
2703
|
}
|
|
842
2704
|
function close() {
|
|
843
2705
|
state.open.value = false;
|
|
844
2706
|
setExpanded(trigger, false);
|
|
845
|
-
setHidden(content, true);
|
|
846
2707
|
setDataState(trigger, "closed");
|
|
847
2708
|
setDataState(content, "closed");
|
|
2709
|
+
setHiddenAnimated(content, true);
|
|
2710
|
+
floatingCleanup?.();
|
|
2711
|
+
floatingCleanup = null;
|
|
2712
|
+
dismissCleanup?.();
|
|
2713
|
+
dismissCleanup = null;
|
|
848
2714
|
trigger.focus();
|
|
849
2715
|
}
|
|
850
2716
|
function selectItem(value) {
|
|
@@ -853,6 +2719,9 @@ var Select = {
|
|
|
853
2719
|
const isActive = item.getAttribute("data-value") === value;
|
|
854
2720
|
setSelected(item, isActive);
|
|
855
2721
|
setDataState(item, isActive ? "active" : "inactive");
|
|
2722
|
+
if (isActive) {
|
|
2723
|
+
triggerText.textContent = item.textContent ?? value;
|
|
2724
|
+
}
|
|
856
2725
|
}
|
|
857
2726
|
onValueChange?.(value);
|
|
858
2727
|
close();
|
|
@@ -893,6 +2762,23 @@ var Select = {
|
|
|
893
2762
|
}
|
|
894
2763
|
return;
|
|
895
2764
|
}
|
|
2765
|
+
if (state.activeIndex.peek() === -1) {
|
|
2766
|
+
if (isKey(event, Keys.ArrowDown)) {
|
|
2767
|
+
event.preventDefault();
|
|
2768
|
+
state.activeIndex.value = 0;
|
|
2769
|
+
updateActiveItem(0);
|
|
2770
|
+
items[0]?.focus();
|
|
2771
|
+
return;
|
|
2772
|
+
}
|
|
2773
|
+
if (isKey(event, Keys.ArrowUp)) {
|
|
2774
|
+
event.preventDefault();
|
|
2775
|
+
const last = items.length - 1;
|
|
2776
|
+
state.activeIndex.value = last;
|
|
2777
|
+
updateActiveItem(last);
|
|
2778
|
+
items[last]?.focus();
|
|
2779
|
+
return;
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
896
2782
|
const result = handleListNavigation(event, items, { orientation: "vertical" });
|
|
897
2783
|
if (result) {
|
|
898
2784
|
const idx = items.indexOf(result);
|
|
@@ -900,9 +2786,20 @@ var Select = {
|
|
|
900
2786
|
state.activeIndex.value = idx;
|
|
901
2787
|
updateActiveItem(idx);
|
|
902
2788
|
}
|
|
2789
|
+
return;
|
|
2790
|
+
}
|
|
2791
|
+
if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
2792
|
+
const char = event.key.toLowerCase();
|
|
2793
|
+
const match = items.find((item) => item.textContent?.toLowerCase().startsWith(char));
|
|
2794
|
+
if (match) {
|
|
2795
|
+
const idx = items.indexOf(match);
|
|
2796
|
+
state.activeIndex.value = idx;
|
|
2797
|
+
updateActiveItem(idx);
|
|
2798
|
+
match.focus();
|
|
2799
|
+
}
|
|
903
2800
|
}
|
|
904
2801
|
});
|
|
905
|
-
function
|
|
2802
|
+
function createItem(value, label, parent) {
|
|
906
2803
|
const item = document.createElement("div");
|
|
907
2804
|
item.setAttribute("role", "option");
|
|
908
2805
|
item.setAttribute("data-value", value);
|
|
@@ -911,18 +2808,40 @@ var Select = {
|
|
|
911
2808
|
const isSelected = value === defaultValue;
|
|
912
2809
|
setSelected(item, isSelected);
|
|
913
2810
|
setDataState(item, isSelected ? "active" : "inactive");
|
|
2811
|
+
if (isSelected) {
|
|
2812
|
+
triggerText.textContent = item.textContent ?? value;
|
|
2813
|
+
}
|
|
914
2814
|
item.addEventListener("click", () => {
|
|
915
2815
|
selectItem(value);
|
|
916
2816
|
});
|
|
917
2817
|
items.push(item);
|
|
918
|
-
content.appendChild(item);
|
|
2818
|
+
(parent ?? content).appendChild(item);
|
|
919
2819
|
return item;
|
|
920
2820
|
}
|
|
921
|
-
|
|
2821
|
+
function Item(value, label) {
|
|
2822
|
+
return createItem(value, label);
|
|
2823
|
+
}
|
|
2824
|
+
function Group(label) {
|
|
2825
|
+
const el = document.createElement("div");
|
|
2826
|
+
el.setAttribute("role", "group");
|
|
2827
|
+
el.setAttribute("aria-label", label);
|
|
2828
|
+
content.appendChild(el);
|
|
2829
|
+
return {
|
|
2830
|
+
el,
|
|
2831
|
+
Item: (value, itemLabel) => createItem(value, itemLabel, el)
|
|
2832
|
+
};
|
|
2833
|
+
}
|
|
2834
|
+
function Separator() {
|
|
2835
|
+
const hr = document.createElement("hr");
|
|
2836
|
+
hr.setAttribute("role", "separator");
|
|
2837
|
+
content.appendChild(hr);
|
|
2838
|
+
return hr;
|
|
2839
|
+
}
|
|
2840
|
+
return { trigger, content, state, Item, Group, Separator };
|
|
922
2841
|
}
|
|
923
2842
|
};
|
|
924
2843
|
// src/slider/slider.ts
|
|
925
|
-
import { signal as
|
|
2844
|
+
import { signal as signal22 } from "@vertz/ui";
|
|
926
2845
|
var Slider = {
|
|
927
2846
|
Root(options = {}) {
|
|
928
2847
|
const {
|
|
@@ -934,8 +2853,8 @@ var Slider = {
|
|
|
934
2853
|
onValueChange
|
|
935
2854
|
} = options;
|
|
936
2855
|
const state = {
|
|
937
|
-
value:
|
|
938
|
-
disabled:
|
|
2856
|
+
value: signal22(defaultValue),
|
|
2857
|
+
disabled: signal22(disabled)
|
|
939
2858
|
};
|
|
940
2859
|
const root = document.createElement("div");
|
|
941
2860
|
root.id = uniqueId("slider");
|
|
@@ -953,6 +2872,13 @@ var Slider = {
|
|
|
953
2872
|
function clamp(val) {
|
|
954
2873
|
return Math.min(max, Math.max(min, val));
|
|
955
2874
|
}
|
|
2875
|
+
const fill = document.createElement("div");
|
|
2876
|
+
fill.setAttribute("data-part", "fill");
|
|
2877
|
+
fill.style.cssText = "position: absolute; height: 100%; border-radius: inherit;";
|
|
2878
|
+
function updatePosition(pct2) {
|
|
2879
|
+
thumb.style.left = `${pct2}%`;
|
|
2880
|
+
fill.style.width = `${pct2}%`;
|
|
2881
|
+
}
|
|
956
2882
|
function setValue(val) {
|
|
957
2883
|
if (state.disabled.peek())
|
|
958
2884
|
return;
|
|
@@ -960,10 +2886,16 @@ var Slider = {
|
|
|
960
2886
|
state.value.value = clamped;
|
|
961
2887
|
setValueRange(thumb, clamped, min, max);
|
|
962
2888
|
const pct2 = (clamped - min) / (max - min) * 100;
|
|
963
|
-
|
|
2889
|
+
updatePosition(pct2);
|
|
964
2890
|
setDataState(root, "active");
|
|
965
2891
|
onValueChange?.(clamped);
|
|
966
2892
|
}
|
|
2893
|
+
function valueFromPointer(event) {
|
|
2894
|
+
const rect = track.getBoundingClientRect();
|
|
2895
|
+
const pct2 = Math.max(0, Math.min(1, (event.clientX - rect.left) / rect.width));
|
|
2896
|
+
const raw = min + pct2 * (max - min);
|
|
2897
|
+
return Math.round(raw / step) * step;
|
|
2898
|
+
}
|
|
967
2899
|
thumb.addEventListener("keydown", (event) => {
|
|
968
2900
|
if (state.disabled.peek())
|
|
969
2901
|
return;
|
|
@@ -982,21 +2914,41 @@ var Slider = {
|
|
|
982
2914
|
setValue(max);
|
|
983
2915
|
}
|
|
984
2916
|
});
|
|
2917
|
+
root.addEventListener("pointerdown", (event) => {
|
|
2918
|
+
if (state.disabled.peek())
|
|
2919
|
+
return;
|
|
2920
|
+
event.preventDefault();
|
|
2921
|
+
setValue(valueFromPointer(event));
|
|
2922
|
+
thumb.focus();
|
|
2923
|
+
function onMove(e) {
|
|
2924
|
+
setValue(valueFromPointer(e));
|
|
2925
|
+
}
|
|
2926
|
+
function onUp() {
|
|
2927
|
+
document.removeEventListener("pointermove", onMove);
|
|
2928
|
+
document.removeEventListener("pointerup", onUp);
|
|
2929
|
+
}
|
|
2930
|
+
document.addEventListener("pointermove", onMove);
|
|
2931
|
+
document.addEventListener("pointerup", onUp);
|
|
2932
|
+
});
|
|
2933
|
+
thumb.style.position = "absolute";
|
|
2934
|
+
thumb.style.transform = "translate(-50%, -50%)";
|
|
2935
|
+
track.style.position = "relative";
|
|
2936
|
+
track.appendChild(fill);
|
|
985
2937
|
track.appendChild(thumb);
|
|
986
2938
|
root.appendChild(track);
|
|
987
2939
|
const pct = (defaultValue - min) / (max - min) * 100;
|
|
988
|
-
|
|
2940
|
+
updatePosition(pct);
|
|
989
2941
|
return { root, thumb, track, state };
|
|
990
2942
|
}
|
|
991
2943
|
};
|
|
992
2944
|
// src/switch/switch.ts
|
|
993
|
-
import { signal as
|
|
2945
|
+
import { signal as signal23 } from "@vertz/ui";
|
|
994
2946
|
var Switch = {
|
|
995
2947
|
Root(options = {}) {
|
|
996
2948
|
const { defaultChecked = false, disabled = false, onCheckedChange } = options;
|
|
997
2949
|
const state = {
|
|
998
|
-
checked:
|
|
999
|
-
disabled:
|
|
2950
|
+
checked: signal23(defaultChecked),
|
|
2951
|
+
disabled: signal23(disabled)
|
|
1000
2952
|
};
|
|
1001
2953
|
const root = document.createElement("button");
|
|
1002
2954
|
root.setAttribute("type", "button");
|
|
@@ -1028,17 +2980,20 @@ var Switch = {
|
|
|
1028
2980
|
}
|
|
1029
2981
|
};
|
|
1030
2982
|
// src/tabs/tabs.ts
|
|
1031
|
-
import { signal as
|
|
2983
|
+
import { signal as signal24 } from "@vertz/ui";
|
|
1032
2984
|
var Tabs = {
|
|
1033
2985
|
Root(options = {}) {
|
|
1034
|
-
const { defaultValue = "", onValueChange } = options;
|
|
1035
|
-
const state = { value:
|
|
2986
|
+
const { defaultValue = "", orientation = "horizontal", onValueChange } = options;
|
|
2987
|
+
const state = { value: signal24(defaultValue) };
|
|
1036
2988
|
const triggers = [];
|
|
1037
2989
|
const panels = [];
|
|
1038
2990
|
const tabValues = [];
|
|
1039
2991
|
const root = document.createElement("div");
|
|
1040
2992
|
const list = document.createElement("div");
|
|
1041
2993
|
list.setAttribute("role", "tablist");
|
|
2994
|
+
if (orientation === "vertical") {
|
|
2995
|
+
list.setAttribute("aria-orientation", "vertical");
|
|
2996
|
+
}
|
|
1042
2997
|
root.appendChild(list);
|
|
1043
2998
|
function selectTab(value) {
|
|
1044
2999
|
state.value.value = value;
|
|
@@ -1058,7 +3013,7 @@ var Tabs = {
|
|
|
1058
3013
|
}
|
|
1059
3014
|
list.addEventListener("keydown", (event) => {
|
|
1060
3015
|
const result = handleListNavigation(event, triggers, {
|
|
1061
|
-
orientation
|
|
3016
|
+
orientation
|
|
1062
3017
|
});
|
|
1063
3018
|
if (result) {
|
|
1064
3019
|
const idx = triggers.indexOf(result);
|
|
@@ -1106,11 +3061,11 @@ var Tabs = {
|
|
|
1106
3061
|
}
|
|
1107
3062
|
};
|
|
1108
3063
|
// src/toast/toast.ts
|
|
1109
|
-
import { signal as
|
|
3064
|
+
import { signal as signal25 } from "@vertz/ui";
|
|
1110
3065
|
var Toast = {
|
|
1111
3066
|
Root(options = {}) {
|
|
1112
3067
|
const { duration = 5000, politeness = "polite" } = options;
|
|
1113
|
-
const state = { messages:
|
|
3068
|
+
const state = { messages: signal25([]) };
|
|
1114
3069
|
const region = document.createElement("div");
|
|
1115
3070
|
region.setAttribute("role", "status");
|
|
1116
3071
|
region.setAttribute("aria-live", politeness);
|
|
@@ -1148,14 +3103,130 @@ var Toast = {
|
|
|
1148
3103
|
return { region, state, announce, dismiss };
|
|
1149
3104
|
}
|
|
1150
3105
|
};
|
|
3106
|
+
// src/toggle/toggle.ts
|
|
3107
|
+
import { signal as signal26 } from "@vertz/ui";
|
|
3108
|
+
var Toggle = {
|
|
3109
|
+
Root(options = {}) {
|
|
3110
|
+
const { defaultPressed = false, disabled = false, onPressedChange } = options;
|
|
3111
|
+
const state = {
|
|
3112
|
+
pressed: signal26(defaultPressed),
|
|
3113
|
+
disabled: signal26(disabled)
|
|
3114
|
+
};
|
|
3115
|
+
const root = document.createElement("button");
|
|
3116
|
+
root.setAttribute("type", "button");
|
|
3117
|
+
root.id = uniqueId("toggle");
|
|
3118
|
+
setPressed(root, defaultPressed);
|
|
3119
|
+
setDataState(root, defaultPressed ? "on" : "off");
|
|
3120
|
+
if (disabled) {
|
|
3121
|
+
root.disabled = true;
|
|
3122
|
+
root.setAttribute("aria-disabled", "true");
|
|
3123
|
+
}
|
|
3124
|
+
function toggle() {
|
|
3125
|
+
if (state.disabled.peek())
|
|
3126
|
+
return;
|
|
3127
|
+
const next = !state.pressed.peek();
|
|
3128
|
+
state.pressed.value = next;
|
|
3129
|
+
setPressed(root, next);
|
|
3130
|
+
setDataState(root, next ? "on" : "off");
|
|
3131
|
+
onPressedChange?.(next);
|
|
3132
|
+
}
|
|
3133
|
+
root.addEventListener("click", toggle);
|
|
3134
|
+
root.addEventListener("keydown", (event) => {
|
|
3135
|
+
if (isKey(event, Keys.Space)) {
|
|
3136
|
+
event.preventDefault();
|
|
3137
|
+
toggle();
|
|
3138
|
+
}
|
|
3139
|
+
});
|
|
3140
|
+
return { root, state };
|
|
3141
|
+
}
|
|
3142
|
+
};
|
|
3143
|
+
// src/toggle-group/toggle-group.ts
|
|
3144
|
+
import { signal as signal27 } from "@vertz/ui";
|
|
3145
|
+
var ToggleGroup = {
|
|
3146
|
+
Root(options = {}) {
|
|
3147
|
+
const {
|
|
3148
|
+
type = "single",
|
|
3149
|
+
defaultValue = [],
|
|
3150
|
+
orientation = "horizontal",
|
|
3151
|
+
disabled = false,
|
|
3152
|
+
onValueChange
|
|
3153
|
+
} = options;
|
|
3154
|
+
const state = {
|
|
3155
|
+
value: signal27([...defaultValue]),
|
|
3156
|
+
disabled: signal27(disabled)
|
|
3157
|
+
};
|
|
3158
|
+
const items = [];
|
|
3159
|
+
const root = document.createElement("div");
|
|
3160
|
+
root.setAttribute("role", "group");
|
|
3161
|
+
root.setAttribute("data-orientation", orientation);
|
|
3162
|
+
function toggleValue(itemValue) {
|
|
3163
|
+
if (state.disabled.peek())
|
|
3164
|
+
return;
|
|
3165
|
+
const current = [...state.value.peek()];
|
|
3166
|
+
const idx = current.indexOf(itemValue);
|
|
3167
|
+
if (type === "single") {
|
|
3168
|
+
if (idx >= 0) {
|
|
3169
|
+
current.length = 0;
|
|
3170
|
+
} else {
|
|
3171
|
+
current.length = 0;
|
|
3172
|
+
current.push(itemValue);
|
|
3173
|
+
}
|
|
3174
|
+
} else {
|
|
3175
|
+
if (idx >= 0) {
|
|
3176
|
+
current.splice(idx, 1);
|
|
3177
|
+
} else {
|
|
3178
|
+
current.push(itemValue);
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
state.value.value = current;
|
|
3182
|
+
onValueChange?.(current);
|
|
3183
|
+
for (const item of items) {
|
|
3184
|
+
const val = item.getAttribute("data-value") ?? "";
|
|
3185
|
+
const isOn = current.includes(val);
|
|
3186
|
+
setPressed(item, isOn);
|
|
3187
|
+
setDataState(item, isOn ? "on" : "off");
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
root.addEventListener("keydown", (event) => {
|
|
3191
|
+
if (isKey(event, Keys.ArrowLeft, Keys.ArrowRight, Keys.ArrowUp, Keys.ArrowDown, Keys.Home, Keys.End)) {
|
|
3192
|
+
const result = handleListNavigation(event, items, { orientation });
|
|
3193
|
+
if (result) {
|
|
3194
|
+
const idx = items.indexOf(result);
|
|
3195
|
+
if (idx >= 0) {
|
|
3196
|
+
setRovingTabindex(items, idx);
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
});
|
|
3201
|
+
function Item(value) {
|
|
3202
|
+
const item = document.createElement("button");
|
|
3203
|
+
item.setAttribute("type", "button");
|
|
3204
|
+
item.setAttribute("data-value", value);
|
|
3205
|
+
const isOn = state.value.peek().includes(value);
|
|
3206
|
+
setPressed(item, isOn);
|
|
3207
|
+
setDataState(item, isOn ? "on" : "off");
|
|
3208
|
+
if (state.disabled.peek()) {
|
|
3209
|
+
item.disabled = true;
|
|
3210
|
+
item.setAttribute("aria-disabled", "true");
|
|
3211
|
+
}
|
|
3212
|
+
item.addEventListener("click", () => toggleValue(value));
|
|
3213
|
+
items.push(item);
|
|
3214
|
+
setRovingTabindex(items, 0);
|
|
3215
|
+
root.appendChild(item);
|
|
3216
|
+
return item;
|
|
3217
|
+
}
|
|
3218
|
+
return { root, state, Item };
|
|
3219
|
+
}
|
|
3220
|
+
};
|
|
1151
3221
|
// src/tooltip/tooltip.ts
|
|
1152
|
-
import { signal as
|
|
3222
|
+
import { signal as signal28 } from "@vertz/ui";
|
|
1153
3223
|
var Tooltip = {
|
|
1154
3224
|
Root(options = {}) {
|
|
1155
|
-
const { delay = 300, onOpenChange } = options;
|
|
3225
|
+
const { delay = 300, onOpenChange, positioning } = options;
|
|
1156
3226
|
const contentId = uniqueId("tooltip");
|
|
1157
|
-
const state = { open:
|
|
3227
|
+
const state = { open: signal28(false) };
|
|
1158
3228
|
let showTimeout = null;
|
|
3229
|
+
let floatingCleanup = null;
|
|
1159
3230
|
const trigger = document.createElement("span");
|
|
1160
3231
|
setDescribedBy(trigger, contentId);
|
|
1161
3232
|
const content = document.createElement("div");
|
|
@@ -1170,6 +3241,14 @@ var Tooltip = {
|
|
|
1170
3241
|
state.open.value = true;
|
|
1171
3242
|
setHidden(content, false);
|
|
1172
3243
|
setDataState(content, "open");
|
|
3244
|
+
if (positioning) {
|
|
3245
|
+
const effectivePlacement = positioning.placement ?? "top";
|
|
3246
|
+
const result = createFloatingPosition(trigger, content, {
|
|
3247
|
+
...positioning,
|
|
3248
|
+
placement: effectivePlacement
|
|
3249
|
+
});
|
|
3250
|
+
floatingCleanup = result.cleanup;
|
|
3251
|
+
}
|
|
1173
3252
|
onOpenChange?.(true);
|
|
1174
3253
|
showTimeout = null;
|
|
1175
3254
|
}, delay);
|
|
@@ -1180,8 +3259,10 @@ var Tooltip = {
|
|
|
1180
3259
|
showTimeout = null;
|
|
1181
3260
|
}
|
|
1182
3261
|
state.open.value = false;
|
|
1183
|
-
setHidden(content, true);
|
|
1184
3262
|
setDataState(content, "closed");
|
|
3263
|
+
setHiddenAnimated(content, true);
|
|
3264
|
+
floatingCleanup?.();
|
|
3265
|
+
floatingCleanup = null;
|
|
1185
3266
|
onOpenChange?.(false);
|
|
1186
3267
|
}
|
|
1187
3268
|
trigger.addEventListener("mouseenter", show);
|
|
@@ -1198,18 +3279,32 @@ var Tooltip = {
|
|
|
1198
3279
|
};
|
|
1199
3280
|
export {
|
|
1200
3281
|
Tooltip,
|
|
3282
|
+
ToggleGroup,
|
|
3283
|
+
Toggle,
|
|
1201
3284
|
Toast,
|
|
1202
3285
|
Tabs,
|
|
1203
3286
|
Switch,
|
|
1204
3287
|
Slider,
|
|
1205
3288
|
Select,
|
|
3289
|
+
ScrollArea,
|
|
3290
|
+
ResizablePanel,
|
|
1206
3291
|
Radio,
|
|
1207
3292
|
Progress,
|
|
1208
3293
|
Popover,
|
|
3294
|
+
NavigationMenu,
|
|
3295
|
+
Menubar,
|
|
1209
3296
|
Menu,
|
|
3297
|
+
HoverCard,
|
|
3298
|
+
DropdownMenu,
|
|
1210
3299
|
Dialog,
|
|
3300
|
+
DatePicker,
|
|
3301
|
+
ContextMenu,
|
|
3302
|
+
Command,
|
|
1211
3303
|
Combobox,
|
|
3304
|
+
Collapsible,
|
|
1212
3305
|
Checkbox,
|
|
3306
|
+
Carousel,
|
|
3307
|
+
Calendar,
|
|
1213
3308
|
Button,
|
|
1214
3309
|
Accordion
|
|
1215
3310
|
};
|