@terminal-blueprint/react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2141 @@
1
+ // src/index.ts
2
+ import "@terminal-blueprint/core/css";
3
+
4
+ // src/utils/cn.ts
5
+ import { clsx } from "clsx";
6
+ function cn(...inputs) {
7
+ return clsx(inputs);
8
+ }
9
+
10
+ // src/components/Button.tsx
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ function Button({
13
+ as,
14
+ variant = "default",
15
+ size = "md",
16
+ isLoading = false,
17
+ loadingText,
18
+ icon,
19
+ iconPosition = "left",
20
+ className,
21
+ disabled,
22
+ children,
23
+ ref,
24
+ ...props
25
+ }) {
26
+ const Component = as || "button";
27
+ const isDisabled = disabled || isLoading;
28
+ return /* @__PURE__ */ jsxs(
29
+ Component,
30
+ {
31
+ ref,
32
+ className: cn(
33
+ "tb-btn",
34
+ variant !== "default" && `tb-btn--${variant}`,
35
+ size !== "md" && `tb-btn--${size}`,
36
+ isLoading && "tb-btn--loading",
37
+ isLoading && loadingText && "tb-btn--loading-text",
38
+ isDisabled && "tb-btn--disabled",
39
+ className
40
+ ),
41
+ disabled: Component === "button" ? isDisabled : void 0,
42
+ "aria-disabled": Component !== "button" && isDisabled ? true : void 0,
43
+ ...props,
44
+ children: [
45
+ isLoading && /* @__PURE__ */ jsxs("span", { className: "tb-btn__spinner", "aria-hidden": "true", children: [
46
+ /* @__PURE__ */ jsx("span", { className: "tb-btn__spinner-square" }),
47
+ /* @__PURE__ */ jsx("span", { className: "tb-btn__spinner-square" }),
48
+ /* @__PURE__ */ jsx("span", { className: "tb-btn__spinner-square" })
49
+ ] }),
50
+ icon && iconPosition === "left" && !isLoading && /* @__PURE__ */ jsx("span", { className: "tb-btn__icon", children: icon }),
51
+ /* @__PURE__ */ jsx("span", { style: isLoading && !loadingText ? { visibility: "hidden" } : void 0, children: isLoading && loadingText ? loadingText : children }),
52
+ icon && iconPosition === "right" && !isLoading && /* @__PURE__ */ jsx("span", { className: "tb-btn__icon", children: icon })
53
+ ]
54
+ }
55
+ );
56
+ }
57
+
58
+ // src/components/Input.tsx
59
+ import {
60
+ useId
61
+ } from "react";
62
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
63
+ function Input({
64
+ label,
65
+ error,
66
+ hint,
67
+ size = "md",
68
+ icon,
69
+ iconPosition = "left",
70
+ className,
71
+ id: idProp,
72
+ ref,
73
+ ...props
74
+ }) {
75
+ const autoId = useId();
76
+ const id = idProp ?? autoId;
77
+ const errorId = error ? `${id}-error` : void 0;
78
+ const hintId = hint && !error ? `${id}-hint` : void 0;
79
+ return /* @__PURE__ */ jsxs2("div", { className: "tb-form-group", children: [
80
+ label && /* @__PURE__ */ jsx2("label", { htmlFor: id, className: "tb-label", children: label }),
81
+ /* @__PURE__ */ jsxs2(
82
+ "div",
83
+ {
84
+ className: cn(
85
+ "tb-input-wrapper",
86
+ icon && `tb-input-wrapper--icon-${iconPosition}`
87
+ ),
88
+ children: [
89
+ icon && /* @__PURE__ */ jsx2("span", { className: "tb-input__icon", "aria-hidden": "true", children: icon }),
90
+ /* @__PURE__ */ jsx2(
91
+ "input",
92
+ {
93
+ ref,
94
+ id,
95
+ className: cn(
96
+ "tb-input",
97
+ size !== "md" && `tb-input--${size}`,
98
+ error && "tb-input--error",
99
+ className
100
+ ),
101
+ "aria-invalid": error ? true : void 0,
102
+ "aria-describedby": errorId ?? hintId,
103
+ ...props
104
+ }
105
+ )
106
+ ]
107
+ }
108
+ ),
109
+ error && /* @__PURE__ */ jsx2("span", { id: errorId, className: "tb-input__error", role: "alert", children: error }),
110
+ hint && !error && /* @__PURE__ */ jsx2("span", { id: hintId, className: "tb-input__hint", children: hint })
111
+ ] });
112
+ }
113
+
114
+ // src/components/Textarea.tsx
115
+ import { useId as useId2 } from "react";
116
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
117
+ function Textarea({
118
+ label,
119
+ error,
120
+ hint,
121
+ className,
122
+ id: idProp,
123
+ ref,
124
+ ...props
125
+ }) {
126
+ const autoId = useId2();
127
+ const id = idProp ?? autoId;
128
+ const errorId = error ? `${id}-error` : void 0;
129
+ const hintId = hint && !error ? `${id}-hint` : void 0;
130
+ return /* @__PURE__ */ jsxs3("div", { className: "tb-form-group", children: [
131
+ label && /* @__PURE__ */ jsx3("label", { htmlFor: id, className: "tb-label", children: label }),
132
+ /* @__PURE__ */ jsx3(
133
+ "textarea",
134
+ {
135
+ ref,
136
+ id,
137
+ className: cn(
138
+ "tb-textarea",
139
+ error && "tb-textarea--error",
140
+ className
141
+ ),
142
+ "aria-invalid": error ? true : void 0,
143
+ "aria-describedby": errorId ?? hintId,
144
+ ...props
145
+ }
146
+ ),
147
+ error && /* @__PURE__ */ jsx3("span", { id: errorId, className: "tb-textarea__error", role: "alert", children: error }),
148
+ hint && !error && /* @__PURE__ */ jsx3("span", { id: hintId, className: "tb-textarea__hint", children: hint })
149
+ ] });
150
+ }
151
+
152
+ // src/components/Card/Card.tsx
153
+ import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
154
+ function CardRoot({
155
+ as,
156
+ brackets = false,
157
+ pulse = false,
158
+ hoverable = false,
159
+ className,
160
+ children,
161
+ ref,
162
+ ...props
163
+ }) {
164
+ const Component = as || "div";
165
+ return /* @__PURE__ */ jsxs4(
166
+ Component,
167
+ {
168
+ ref,
169
+ className: cn(
170
+ "tb-card",
171
+ brackets && "tb-card--bracketed",
172
+ pulse && "tb-card--pulse",
173
+ hoverable && "tb-card--hoverable",
174
+ className
175
+ ),
176
+ ...props,
177
+ children: [
178
+ brackets && /* @__PURE__ */ jsxs4(Fragment, { children: [
179
+ /* @__PURE__ */ jsx4("span", { className: "tb-card__bracket tb-card__bracket--tl" }),
180
+ /* @__PURE__ */ jsx4("span", { className: "tb-card__bracket tb-card__bracket--tr" }),
181
+ /* @__PURE__ */ jsx4("span", { className: "tb-card__bracket tb-card__bracket--bl" }),
182
+ /* @__PURE__ */ jsx4("span", { className: "tb-card__bracket tb-card__bracket--br" })
183
+ ] }),
184
+ children
185
+ ]
186
+ }
187
+ );
188
+ }
189
+ function Header({ className, ref, ...props }) {
190
+ return /* @__PURE__ */ jsx4(
191
+ "div",
192
+ {
193
+ ref,
194
+ className: cn("tb-card__header", className),
195
+ ...props
196
+ }
197
+ );
198
+ }
199
+ function Title({ className, ref, ...props }) {
200
+ return /* @__PURE__ */ jsx4(
201
+ "div",
202
+ {
203
+ ref,
204
+ className: cn("tb-card__title", className),
205
+ ...props
206
+ }
207
+ );
208
+ }
209
+ function Body({ className, ref, ...props }) {
210
+ return /* @__PURE__ */ jsx4(
211
+ "div",
212
+ {
213
+ ref,
214
+ className: cn("tb-card__body", className),
215
+ ...props
216
+ }
217
+ );
218
+ }
219
+ function Tags({ className, ref, ...props }) {
220
+ return /* @__PURE__ */ jsx4(
221
+ "div",
222
+ {
223
+ ref,
224
+ className: cn("tb-card__tags", className),
225
+ ...props
226
+ }
227
+ );
228
+ }
229
+ function Footer({ className, ref, ...props }) {
230
+ return /* @__PURE__ */ jsx4(
231
+ "div",
232
+ {
233
+ ref,
234
+ className: cn("tb-card__footer", className),
235
+ ...props
236
+ }
237
+ );
238
+ }
239
+ var Card = Object.assign(CardRoot, {
240
+ Header,
241
+ Title,
242
+ Body,
243
+ Tags,
244
+ Footer
245
+ });
246
+
247
+ // src/components/Badge.tsx
248
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
249
+ function Badge({
250
+ variant = "default",
251
+ dot = false,
252
+ pulse = false,
253
+ className,
254
+ children,
255
+ ref,
256
+ ...props
257
+ }) {
258
+ return /* @__PURE__ */ jsxs5(
259
+ "span",
260
+ {
261
+ ref,
262
+ className: cn(
263
+ "tb-badge",
264
+ variant !== "default" && `tb-badge--${variant}`,
265
+ pulse && "tb-badge--pulse",
266
+ className
267
+ ),
268
+ ...props,
269
+ children: [
270
+ dot && /* @__PURE__ */ jsx5(
271
+ "span",
272
+ {
273
+ className: cn(
274
+ "tb-badge__dot"
275
+ ),
276
+ "aria-hidden": "true"
277
+ }
278
+ ),
279
+ children
280
+ ]
281
+ }
282
+ );
283
+ }
284
+
285
+ // src/components/Tag.tsx
286
+ import { jsx as jsx6 } from "react/jsx-runtime";
287
+ function Tag({ className, children, ref, ...props }) {
288
+ return /* @__PURE__ */ jsx6("span", { ref, className: cn("tb-tag", className), ...props, children });
289
+ }
290
+
291
+ // src/components/TitleLine.tsx
292
+ import { jsx as jsx7 } from "react/jsx-runtime";
293
+ function TitleLine({ label, className }) {
294
+ return /* @__PURE__ */ jsx7("div", { className: cn("tb-title-line", className), children: /* @__PURE__ */ jsx7("span", { className: "tb-title-line__label", children: label }) });
295
+ }
296
+
297
+ // src/components/CornerBrackets.tsx
298
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
299
+ function CornerBrackets({ pulse, children, className, ref, ...props }) {
300
+ return /* @__PURE__ */ jsxs6(
301
+ "div",
302
+ {
303
+ ref,
304
+ className: cn("tb-brackets", pulse && "tb-brackets--pulse", className),
305
+ ...props,
306
+ children: [
307
+ /* @__PURE__ */ jsx8("span", { className: "tb-brackets__corner tb-brackets__corner--tl" }),
308
+ /* @__PURE__ */ jsx8("span", { className: "tb-brackets__corner tb-brackets__corner--tr" }),
309
+ /* @__PURE__ */ jsx8("span", { className: "tb-brackets__corner tb-brackets__corner--bl" }),
310
+ /* @__PURE__ */ jsx8("span", { className: "tb-brackets__corner tb-brackets__corner--br" }),
311
+ children
312
+ ]
313
+ }
314
+ );
315
+ }
316
+
317
+ // src/components/Select/Select.tsx
318
+ import {
319
+ createContext,
320
+ useCallback,
321
+ useContext,
322
+ useEffect,
323
+ useRef,
324
+ useState
325
+ } from "react";
326
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
327
+ var SelectContext = createContext(null);
328
+ function useSelectContext() {
329
+ const ctx = useContext(SelectContext);
330
+ if (!ctx) throw new Error("Select compound components must be used within <Select>");
331
+ return ctx;
332
+ }
333
+ function Select({
334
+ value: controlledValue,
335
+ defaultValue = "",
336
+ onValueChange,
337
+ placeholder = "Select...",
338
+ disabled = false,
339
+ error,
340
+ label,
341
+ children,
342
+ className,
343
+ ref
344
+ }) {
345
+ const [internalValue, setInternalValue] = useState(defaultValue);
346
+ const [open, setOpen] = useState(false);
347
+ const [focusedIndex, setFocusedIndex] = useState(-1);
348
+ const [itemValues, setItemValues] = useState([]);
349
+ const isControlled = controlledValue !== void 0;
350
+ const value = isControlled ? controlledValue : internalValue;
351
+ const triggerRef = useRef(null);
352
+ const listRef = useRef(null);
353
+ const rootRef = useRef(null);
354
+ const onSelect = useCallback(
355
+ (val) => {
356
+ if (!isControlled) setInternalValue(val);
357
+ onValueChange?.(val);
358
+ setOpen(false);
359
+ triggerRef.current?.focus();
360
+ },
361
+ [isControlled, onValueChange]
362
+ );
363
+ const registerItem = useCallback((val) => {
364
+ setItemValues((prev) => prev.includes(val) ? prev : [...prev, val]);
365
+ }, []);
366
+ const unregisterItem = useCallback((val) => {
367
+ setItemValues((prev) => prev.filter((v) => v !== val));
368
+ }, []);
369
+ useEffect(() => {
370
+ if (!open) return;
371
+ function handleClick(e) {
372
+ if (rootRef.current && !rootRef.current.contains(e.target)) {
373
+ setOpen(false);
374
+ }
375
+ }
376
+ document.addEventListener("pointerdown", handleClick);
377
+ return () => document.removeEventListener("pointerdown", handleClick);
378
+ }, [open]);
379
+ useEffect(() => {
380
+ if (!open) return;
381
+ function handleKey(e) {
382
+ if (e.key === "Escape") {
383
+ setOpen(false);
384
+ triggerRef.current?.focus();
385
+ }
386
+ }
387
+ document.addEventListener("keydown", handleKey);
388
+ return () => document.removeEventListener("keydown", handleKey);
389
+ }, [open]);
390
+ useEffect(() => {
391
+ if (open) {
392
+ const idx = itemValues.indexOf(value);
393
+ setFocusedIndex(idx >= 0 ? idx : 0);
394
+ }
395
+ }, [open, itemValues, value]);
396
+ return /* @__PURE__ */ jsx9(
397
+ SelectContext.Provider,
398
+ {
399
+ value: {
400
+ open,
401
+ setOpen,
402
+ value,
403
+ onSelect,
404
+ placeholder,
405
+ disabled,
406
+ focusedIndex,
407
+ setFocusedIndex,
408
+ itemValues,
409
+ registerItem,
410
+ unregisterItem,
411
+ triggerRef,
412
+ listRef
413
+ },
414
+ children: /* @__PURE__ */ jsxs7(
415
+ "div",
416
+ {
417
+ ref: (node) => {
418
+ rootRef.current = node;
419
+ if (typeof ref === "function") ref(node);
420
+ else if (ref) ref.current = node;
421
+ },
422
+ className: cn(
423
+ "tb-select",
424
+ error && "tb-select--error",
425
+ disabled && "tb-select--disabled",
426
+ className
427
+ ),
428
+ children: [
429
+ label && /* @__PURE__ */ jsx9("label", { className: "tb-select__label", children: label }),
430
+ children,
431
+ error && /* @__PURE__ */ jsx9("span", { className: "tb-select__error", children: error })
432
+ ]
433
+ }
434
+ )
435
+ }
436
+ );
437
+ }
438
+
439
+ // src/components/Select/SelectTrigger.tsx
440
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
441
+ function SelectTrigger({ className, ref }) {
442
+ const { open, setOpen, value, placeholder, disabled, triggerRef, itemValues, focusedIndex, setFocusedIndex, onSelect } = useSelectContext();
443
+ function handleKeyDown(e) {
444
+ if (disabled) return;
445
+ switch (e.key) {
446
+ case "ArrowDown": {
447
+ e.preventDefault();
448
+ if (!open) {
449
+ setOpen(true);
450
+ } else {
451
+ setFocusedIndex(Math.min(focusedIndex + 1, itemValues.length - 1));
452
+ }
453
+ break;
454
+ }
455
+ case "ArrowUp": {
456
+ e.preventDefault();
457
+ if (open) {
458
+ setFocusedIndex(Math.max(focusedIndex - 1, 0));
459
+ }
460
+ break;
461
+ }
462
+ case "Enter":
463
+ case " ": {
464
+ e.preventDefault();
465
+ if (open && focusedIndex >= 0 && itemValues[focusedIndex]) {
466
+ onSelect(itemValues[focusedIndex]);
467
+ } else {
468
+ setOpen(!open);
469
+ }
470
+ break;
471
+ }
472
+ }
473
+ }
474
+ return /* @__PURE__ */ jsxs8(
475
+ "button",
476
+ {
477
+ ref: (node) => {
478
+ triggerRef.current = node;
479
+ if (typeof ref === "function") ref(node);
480
+ else if (ref) ref.current = node;
481
+ },
482
+ type: "button",
483
+ role: "combobox",
484
+ "aria-expanded": open,
485
+ "aria-haspopup": "listbox",
486
+ disabled,
487
+ className: cn("tb-select__trigger", open && "tb-select__trigger--open", className),
488
+ onPointerDown: (e) => {
489
+ e.preventDefault();
490
+ if (!disabled) setOpen(!open);
491
+ },
492
+ onKeyDown: handleKeyDown,
493
+ children: [
494
+ /* @__PURE__ */ jsx10("span", { className: cn("tb-select__value", !value && "tb-select__placeholder"), children: value || placeholder }),
495
+ /* @__PURE__ */ jsx10("span", { className: "tb-select__chevron", "aria-hidden": "true" })
496
+ ]
497
+ }
498
+ );
499
+ }
500
+
501
+ // src/components/Select/SelectContent.tsx
502
+ import { jsx as jsx11 } from "react/jsx-runtime";
503
+ function SelectContent({ children, className, ref }) {
504
+ const { open, listRef } = useSelectContext();
505
+ return /* @__PURE__ */ jsx11(
506
+ "div",
507
+ {
508
+ ref: (node) => {
509
+ listRef.current = node;
510
+ if (typeof ref === "function") ref(node);
511
+ else if (ref) ref.current = node;
512
+ },
513
+ role: "listbox",
514
+ className: cn("tb-select__dropdown", open && "tb-select__dropdown--open", className),
515
+ children
516
+ }
517
+ );
518
+ }
519
+
520
+ // src/components/Select/SelectItem.tsx
521
+ import { useEffect as useEffect2 } from "react";
522
+ import { jsx as jsx12 } from "react/jsx-runtime";
523
+ function SelectItem({
524
+ value: itemValue,
525
+ disabled = false,
526
+ children,
527
+ className,
528
+ ref
529
+ }) {
530
+ const { value, onSelect, focusedIndex, setFocusedIndex, itemValues, registerItem, unregisterItem } = useSelectContext();
531
+ const index = itemValues.indexOf(itemValue);
532
+ const isSelected = value === itemValue;
533
+ const isFocused = focusedIndex === index;
534
+ useEffect2(() => {
535
+ registerItem(itemValue);
536
+ return () => unregisterItem(itemValue);
537
+ }, [itemValue, registerItem, unregisterItem]);
538
+ return /* @__PURE__ */ jsx12(
539
+ "div",
540
+ {
541
+ ref,
542
+ role: "option",
543
+ "aria-selected": isSelected,
544
+ "aria-disabled": disabled,
545
+ "data-focused": isFocused || void 0,
546
+ className: cn(
547
+ "tb-select__option",
548
+ isSelected && "tb-select__option--selected",
549
+ isFocused && "tb-select__option--focused",
550
+ disabled && "tb-select__option--disabled",
551
+ className
552
+ ),
553
+ onClick: () => {
554
+ if (!disabled) onSelect(itemValue);
555
+ },
556
+ onMouseEnter: () => {
557
+ if (!disabled) setFocusedIndex(index);
558
+ },
559
+ children
560
+ }
561
+ );
562
+ }
563
+
564
+ // src/components/Modal/Modal.tsx
565
+ import {
566
+ createContext as createContext2,
567
+ useContext as useContext2,
568
+ useEffect as useEffect4,
569
+ useRef as useRef2,
570
+ useCallback as useCallback2,
571
+ useId as useId3
572
+ } from "react";
573
+
574
+ // src/utils/SafePortal.tsx
575
+ import { useState as useState2, useEffect as useEffect3 } from "react";
576
+ import { createPortal } from "react-dom";
577
+ function SafePortal({ children, target }) {
578
+ const [mounted, setMounted] = useState2(false);
579
+ useEffect3(() => {
580
+ setMounted(true);
581
+ }, []);
582
+ if (!mounted) return null;
583
+ return createPortal(children, target ?? document.body);
584
+ }
585
+
586
+ // src/components/Modal/Modal.tsx
587
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
588
+ var ModalContext = createContext2(null);
589
+ function useModalContext() {
590
+ const ctx = useContext2(ModalContext);
591
+ if (!ctx) throw new Error("Modal sub-components must be used within <Modal>");
592
+ return ctx;
593
+ }
594
+ function ModalRoot({
595
+ open,
596
+ onOpenChange,
597
+ closeOnEscape = true,
598
+ closeOnBackdrop = true,
599
+ hideCloseButton = false,
600
+ portalTarget,
601
+ children,
602
+ className,
603
+ ref
604
+ }) {
605
+ const modalRef = useRef2(null);
606
+ const previousFocusRef = useRef2(null);
607
+ const uid = useId3();
608
+ const titleId = `tb-modal-title-${uid}`;
609
+ const descId = `tb-modal-desc-${uid}`;
610
+ const onClose = useCallback2(() => onOpenChange(false), [onOpenChange]);
611
+ useEffect4(() => {
612
+ if (!open) return;
613
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
614
+ const originalOverflow = document.body.style.overflow;
615
+ const originalPaddingRight = document.body.style.paddingRight;
616
+ document.body.style.overflow = "hidden";
617
+ document.body.style.paddingRight = `${scrollbarWidth}px`;
618
+ return () => {
619
+ document.body.style.overflow = originalOverflow;
620
+ document.body.style.paddingRight = originalPaddingRight;
621
+ };
622
+ }, [open]);
623
+ useEffect4(() => {
624
+ if (open) {
625
+ previousFocusRef.current = document.activeElement;
626
+ requestAnimationFrame(() => {
627
+ const focusable = modalRef.current?.querySelector(
628
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
629
+ );
630
+ (focusable ?? modalRef.current)?.focus();
631
+ });
632
+ } else {
633
+ previousFocusRef.current?.focus();
634
+ }
635
+ }, [open]);
636
+ useEffect4(() => {
637
+ if (!open || !closeOnEscape) return;
638
+ function handleKey(e) {
639
+ if (e.key === "Escape") onOpenChange(false);
640
+ }
641
+ document.addEventListener("keydown", handleKey);
642
+ return () => document.removeEventListener("keydown", handleKey);
643
+ }, [open, closeOnEscape, onOpenChange]);
644
+ const handleKeyDown = useCallback2(
645
+ (e) => {
646
+ if (e.key !== "Tab" || !modalRef.current) return;
647
+ const focusable = modalRef.current.querySelectorAll(
648
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
649
+ );
650
+ if (focusable.length === 0) return;
651
+ const first = focusable[0];
652
+ const last = focusable[focusable.length - 1];
653
+ if (e.shiftKey) {
654
+ if (document.activeElement === first) {
655
+ e.preventDefault();
656
+ last.focus();
657
+ }
658
+ } else {
659
+ if (document.activeElement === last) {
660
+ e.preventDefault();
661
+ first.focus();
662
+ }
663
+ }
664
+ },
665
+ []
666
+ );
667
+ return /* @__PURE__ */ jsx13(SafePortal, { target: portalTarget, children: /* @__PURE__ */ jsx13(
668
+ "div",
669
+ {
670
+ className: cn("tb-modal-overlay", open && "tb-modal-overlay--open"),
671
+ onClick: (e) => {
672
+ if (closeOnBackdrop && e.target === e.currentTarget) onClose();
673
+ },
674
+ children: /* @__PURE__ */ jsxs9(
675
+ "div",
676
+ {
677
+ ref: (node) => {
678
+ modalRef.current = node;
679
+ if (typeof ref === "function") ref(node);
680
+ else if (ref) ref.current = node;
681
+ },
682
+ role: "dialog",
683
+ "aria-modal": "true",
684
+ "aria-labelledby": titleId,
685
+ "aria-describedby": descId,
686
+ className: cn("tb-modal", className),
687
+ tabIndex: -1,
688
+ onKeyDown: handleKeyDown,
689
+ children: [
690
+ !hideCloseButton && /* @__PURE__ */ jsx13(
691
+ "button",
692
+ {
693
+ type: "button",
694
+ className: "tb-modal__close",
695
+ "aria-label": "Close",
696
+ onClick: onClose,
697
+ children: "\xD7"
698
+ }
699
+ ),
700
+ /* @__PURE__ */ jsx13(ModalContext.Provider, { value: { onClose, titleId, descId }, children })
701
+ ]
702
+ }
703
+ )
704
+ }
705
+ ) });
706
+ }
707
+ function Title2({ className, ref, ...props }) {
708
+ const { titleId } = useModalContext();
709
+ return /* @__PURE__ */ jsx13(
710
+ "h2",
711
+ {
712
+ ref,
713
+ id: titleId,
714
+ className: cn("tb-modal__title", className),
715
+ ...props
716
+ }
717
+ );
718
+ }
719
+ function Body2({ className, ref, ...props }) {
720
+ return /* @__PURE__ */ jsx13(
721
+ "div",
722
+ {
723
+ ref,
724
+ className: cn("tb-modal__body", className),
725
+ ...props
726
+ }
727
+ );
728
+ }
729
+ function Footer2({ className, ref, ...props }) {
730
+ return /* @__PURE__ */ jsx13(
731
+ "div",
732
+ {
733
+ ref,
734
+ className: cn("tb-modal__footer", className),
735
+ ...props
736
+ }
737
+ );
738
+ }
739
+ function Close({
740
+ className,
741
+ children,
742
+ ref,
743
+ ...props
744
+ }) {
745
+ const { onClose } = useModalContext();
746
+ return /* @__PURE__ */ jsx13(
747
+ "button",
748
+ {
749
+ ref,
750
+ type: "button",
751
+ className: cn("tb-modal__close", className),
752
+ "aria-label": "Close",
753
+ onClick: onClose,
754
+ ...props,
755
+ children: children ?? "\xD7"
756
+ }
757
+ );
758
+ }
759
+ var Modal = Object.assign(ModalRoot, {
760
+ Title: Title2,
761
+ Body: Body2,
762
+ Footer: Footer2,
763
+ Close
764
+ });
765
+
766
+ // src/components/Tabs/Tabs.tsx
767
+ import {
768
+ createContext as createContext3,
769
+ useCallback as useCallback3,
770
+ useContext as useContext3,
771
+ useRef as useRef3,
772
+ useState as useState3
773
+ } from "react";
774
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
775
+ var TabsContext = createContext3(null);
776
+ function useTabsContext() {
777
+ const ctx = useContext3(TabsContext);
778
+ if (!ctx) throw new Error("Tabs compound components must be used within <Tabs>");
779
+ return ctx;
780
+ }
781
+ function Tabs({
782
+ value: controlledValue,
783
+ defaultValue = "",
784
+ onValueChange: onValueChangeProp,
785
+ children,
786
+ className,
787
+ ref
788
+ }) {
789
+ const [internalValue, setInternalValue] = useState3(defaultValue);
790
+ const isControlled = controlledValue !== void 0;
791
+ const value = isControlled ? controlledValue : internalValue;
792
+ const onValueChange = useCallback3(
793
+ (val) => {
794
+ if (!isControlled) setInternalValue(val);
795
+ onValueChangeProp?.(val);
796
+ },
797
+ [isControlled, onValueChangeProp]
798
+ );
799
+ return /* @__PURE__ */ jsx14(TabsContext.Provider, { value: { value, onValueChange }, children: /* @__PURE__ */ jsx14("div", { ref, className: cn("tb-tabs", className), children }) });
800
+ }
801
+ function TabsList({ children, className, ref }) {
802
+ const listRef = useRef3(null);
803
+ function handleKeyDown(e) {
804
+ if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
805
+ const triggers = Array.from(
806
+ (listRef.current ?? e.currentTarget).querySelectorAll(
807
+ ".tb-tabs__trigger:not(:disabled)"
808
+ )
809
+ );
810
+ if (triggers.length === 0) return;
811
+ const current = document.activeElement;
812
+ const idx = triggers.indexOf(current);
813
+ if (idx < 0) return;
814
+ e.preventDefault();
815
+ let next;
816
+ if (e.key === "ArrowRight") {
817
+ next = (idx + 1) % triggers.length;
818
+ } else {
819
+ next = (idx - 1 + triggers.length) % triggers.length;
820
+ }
821
+ triggers[next].focus();
822
+ }
823
+ return /* @__PURE__ */ jsx14(
824
+ "div",
825
+ {
826
+ ref: (node) => {
827
+ listRef.current = node;
828
+ if (typeof ref === "function") ref(node);
829
+ else if (ref) ref.current = node;
830
+ },
831
+ role: "tablist",
832
+ className: cn("tb-tabs__list", className),
833
+ onKeyDown: handleKeyDown,
834
+ children
835
+ }
836
+ );
837
+ }
838
+ function TabsTrigger({
839
+ value: triggerValue,
840
+ icon,
841
+ disabled = false,
842
+ children,
843
+ className,
844
+ ref
845
+ }) {
846
+ const { value, onValueChange } = useTabsContext();
847
+ const isActive = value === triggerValue;
848
+ return /* @__PURE__ */ jsxs10(
849
+ "button",
850
+ {
851
+ ref,
852
+ type: "button",
853
+ role: "tab",
854
+ "aria-selected": isActive,
855
+ tabIndex: isActive ? 0 : -1,
856
+ disabled,
857
+ className: cn(
858
+ "tb-tabs__trigger",
859
+ isActive && "tb-tabs__trigger--active",
860
+ disabled && "tb-tabs__trigger--disabled",
861
+ className
862
+ ),
863
+ onClick: () => {
864
+ if (!disabled) onValueChange(triggerValue);
865
+ },
866
+ children: [
867
+ icon && /* @__PURE__ */ jsx14("span", { className: "tb-tabs__trigger-icon", children: icon }),
868
+ children
869
+ ]
870
+ }
871
+ );
872
+ }
873
+ function TabsContent({ value: contentValue, children, className, ref }) {
874
+ const { value } = useTabsContext();
875
+ const isActive = value === contentValue;
876
+ if (!isActive) return null;
877
+ return /* @__PURE__ */ jsx14(
878
+ "div",
879
+ {
880
+ ref,
881
+ role: "tabpanel",
882
+ className: cn("tb-tabs__content", isActive && "tb-tabs__content--active", className),
883
+ children
884
+ }
885
+ );
886
+ }
887
+
888
+ // src/components/Toast/ToastProvider.tsx
889
+ import { useSyncExternalStore } from "react";
890
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
891
+ var toasts = [];
892
+ var nextId = 0;
893
+ var listeners = /* @__PURE__ */ new Set();
894
+ var timers = /* @__PURE__ */ new Map();
895
+ function emit() {
896
+ for (const fn of listeners) fn();
897
+ }
898
+ function getSnapshot() {
899
+ return toasts;
900
+ }
901
+ function subscribe(fn) {
902
+ listeners.add(fn);
903
+ return () => listeners.delete(fn);
904
+ }
905
+ function addToast(options, maxCount = 3) {
906
+ const id = `toast-${++nextId}`;
907
+ const entry = {
908
+ ...options,
909
+ id,
910
+ type: options.type ?? "info"
911
+ };
912
+ toasts = [...toasts, entry];
913
+ while (toasts.length > maxCount) {
914
+ const removed = toasts[0];
915
+ toasts = toasts.slice(1);
916
+ const timer = timers.get(removed.id);
917
+ if (timer) {
918
+ clearTimeout(timer);
919
+ timers.delete(removed.id);
920
+ }
921
+ }
922
+ const duration = options.duration === void 0 ? 3500 : options.duration;
923
+ if (duration !== null) {
924
+ const timer = setTimeout(() => {
925
+ timers.delete(id);
926
+ toasts = toasts.filter((t) => t.id !== id);
927
+ emit();
928
+ }, duration);
929
+ timers.set(id, timer);
930
+ }
931
+ emit();
932
+ return id;
933
+ }
934
+ function dismissToast(id) {
935
+ const timer = timers.get(id);
936
+ if (timer) {
937
+ clearTimeout(timer);
938
+ timers.delete(id);
939
+ }
940
+ toasts = toasts.filter((t) => t.id !== id);
941
+ emit();
942
+ }
943
+ function dismissAll() {
944
+ timers.forEach((timer) => clearTimeout(timer));
945
+ timers.clear();
946
+ toasts = [];
947
+ emit();
948
+ }
949
+ function createToast(options) {
950
+ return addToast(options);
951
+ }
952
+ createToast.success = (title, options) => addToast({ ...options, title, type: "success" });
953
+ createToast.error = (title, options) => addToast({ ...options, title, type: "error" });
954
+ createToast.warning = (title, options) => addToast({ ...options, title, type: "warning" });
955
+ createToast.info = (title, options) => addToast({ ...options, title, type: "info" });
956
+ createToast.dismiss = dismissToast;
957
+ createToast.dismissAll = dismissAll;
958
+ var ICONS = {
959
+ success: "\u2713",
960
+ error: "\u2717",
961
+ warning: "\u26A0",
962
+ info: "\u2139"
963
+ };
964
+ function Toaster({ position = "top-right" }) {
965
+ const currentToasts = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
966
+ if (currentToasts.length === 0) return null;
967
+ return /* @__PURE__ */ jsx15(SafePortal, { children: /* @__PURE__ */ jsx15(
968
+ "div",
969
+ {
970
+ className: cn("tb-toast-container", `tb-toast-container--${position}`),
971
+ "aria-live": "polite",
972
+ children: currentToasts.map((t) => /* @__PURE__ */ jsxs11(
973
+ "div",
974
+ {
975
+ className: cn("tb-toast", `tb-toast--${t.type}`, "tb-toast--show"),
976
+ role: "alert",
977
+ children: [
978
+ /* @__PURE__ */ jsx15("span", { className: "tb-toast__icon", "aria-hidden": "true", children: ICONS[t.type] }),
979
+ /* @__PURE__ */ jsxs11("div", { className: "tb-toast__content", children: [
980
+ /* @__PURE__ */ jsx15("span", { className: "tb-toast__title", children: t.title }),
981
+ t.description && /* @__PURE__ */ jsx15("span", { className: "tb-toast__description", children: t.description })
982
+ ] }),
983
+ t.action && /* @__PURE__ */ jsx15(
984
+ "button",
985
+ {
986
+ type: "button",
987
+ className: "tb-toast__action",
988
+ onClick: () => {
989
+ t.action.onClick();
990
+ dismissToast(t.id);
991
+ },
992
+ children: t.action.label
993
+ }
994
+ ),
995
+ /* @__PURE__ */ jsx15(
996
+ "button",
997
+ {
998
+ type: "button",
999
+ className: "tb-toast__dismiss",
1000
+ "aria-label": "Dismiss",
1001
+ onClick: () => dismissToast(t.id),
1002
+ children: "\xD7"
1003
+ }
1004
+ )
1005
+ ]
1006
+ },
1007
+ t.id
1008
+ ))
1009
+ }
1010
+ ) });
1011
+ }
1012
+
1013
+ // src/components/Tooltip.tsx
1014
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
1015
+ function Tooltip({
1016
+ content,
1017
+ side = "top",
1018
+ align = "center",
1019
+ children,
1020
+ className,
1021
+ ref
1022
+ }) {
1023
+ return /* @__PURE__ */ jsxs12(
1024
+ "div",
1025
+ {
1026
+ ref,
1027
+ className: cn("tb-tooltip-wrap", className),
1028
+ children: [
1029
+ children,
1030
+ /* @__PURE__ */ jsx16(
1031
+ "span",
1032
+ {
1033
+ role: "tooltip",
1034
+ className: cn(
1035
+ "tb-tooltip",
1036
+ `tb-tooltip--${side}`,
1037
+ align !== "center" && `tb-tooltip--align-${align}`
1038
+ ),
1039
+ children: content
1040
+ }
1041
+ )
1042
+ ]
1043
+ }
1044
+ );
1045
+ }
1046
+
1047
+ // src/components/NavDropdown.tsx
1048
+ import { useState as useState4, useEffect as useEffect5, useRef as useRef4 } from "react";
1049
+ import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
1050
+ function NavDropdown({ trigger, items, align = "left", className, ref }) {
1051
+ const [open, setOpen] = useState4(false);
1052
+ const rootRef = useRef4(null);
1053
+ useEffect5(() => {
1054
+ if (!open) return;
1055
+ function handlePointer(e) {
1056
+ if (rootRef.current && !rootRef.current.contains(e.target)) {
1057
+ setOpen(false);
1058
+ }
1059
+ }
1060
+ document.addEventListener("pointerdown", handlePointer);
1061
+ return () => document.removeEventListener("pointerdown", handlePointer);
1062
+ }, [open]);
1063
+ return /* @__PURE__ */ jsxs13(
1064
+ "div",
1065
+ {
1066
+ ref: (node) => {
1067
+ rootRef.current = node;
1068
+ if (typeof ref === "function") ref(node);
1069
+ else if (ref) ref.current = node;
1070
+ },
1071
+ className: cn("tb-nav-item", open && "tb-nav-item--open", align !== "left" && `tb-nav-item--align-${align}`, className),
1072
+ children: [
1073
+ /* @__PURE__ */ jsxs13(
1074
+ "button",
1075
+ {
1076
+ type: "button",
1077
+ className: "tb-nav-item__trigger",
1078
+ onClick: () => setOpen(!open),
1079
+ children: [
1080
+ typeof trigger === "string" ? /* @__PURE__ */ jsx17("span", { children: trigger }) : trigger,
1081
+ /* @__PURE__ */ jsx17("span", { className: "tb-nav-item__chevron", "aria-hidden": "true" })
1082
+ ]
1083
+ }
1084
+ ),
1085
+ /* @__PURE__ */ jsx17("div", { className: cn("tb-nav-dropdown", open && "tb-nav-dropdown--open"), children: items.map((item, i) => {
1086
+ const content = /* @__PURE__ */ jsxs13(Fragment2, { children: [
1087
+ item.icon && /* @__PURE__ */ jsx17("span", { className: "tb-nav-dropdown__icon", children: item.icon }),
1088
+ /* @__PURE__ */ jsx17("span", { children: item.label })
1089
+ ] });
1090
+ if (item.href) {
1091
+ return /* @__PURE__ */ jsx17(
1092
+ "a",
1093
+ {
1094
+ href: item.href,
1095
+ className: "tb-nav-dropdown__item",
1096
+ onClick: () => {
1097
+ setOpen(false);
1098
+ item.onClick?.();
1099
+ },
1100
+ children: content
1101
+ },
1102
+ i
1103
+ );
1104
+ }
1105
+ return /* @__PURE__ */ jsx17(
1106
+ "button",
1107
+ {
1108
+ type: "button",
1109
+ className: "tb-nav-dropdown__item",
1110
+ onClick: () => {
1111
+ setOpen(false);
1112
+ item.onClick?.();
1113
+ },
1114
+ children: content
1115
+ },
1116
+ i
1117
+ );
1118
+ }) })
1119
+ ]
1120
+ }
1121
+ );
1122
+ }
1123
+
1124
+ // src/components/Checkbox.tsx
1125
+ import { useState as useState5 } from "react";
1126
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
1127
+ function Checkbox({
1128
+ checked: controlledChecked,
1129
+ defaultChecked = false,
1130
+ onCheckedChange,
1131
+ disabled = false,
1132
+ label,
1133
+ error,
1134
+ ref,
1135
+ className
1136
+ }) {
1137
+ const [internalChecked, setInternalChecked] = useState5(defaultChecked);
1138
+ const isControlled = controlledChecked !== void 0;
1139
+ const isChecked = isControlled ? controlledChecked : internalChecked;
1140
+ function handleChange() {
1141
+ if (disabled) return;
1142
+ const next = !isChecked;
1143
+ if (!isControlled) setInternalChecked(next);
1144
+ onCheckedChange?.(next);
1145
+ }
1146
+ return /* @__PURE__ */ jsxs14("div", { children: [
1147
+ /* @__PURE__ */ jsxs14(
1148
+ "label",
1149
+ {
1150
+ className: cn(
1151
+ "tb-checkbox",
1152
+ error && "tb-checkbox--error",
1153
+ className
1154
+ ),
1155
+ children: [
1156
+ /* @__PURE__ */ jsx18(
1157
+ "input",
1158
+ {
1159
+ ref,
1160
+ type: "checkbox",
1161
+ checked: isChecked,
1162
+ disabled,
1163
+ onChange: handleChange
1164
+ }
1165
+ ),
1166
+ label && /* @__PURE__ */ jsx18("span", { children: label })
1167
+ ]
1168
+ }
1169
+ ),
1170
+ error && /* @__PURE__ */ jsx18("span", { style: { color: "var(--tb-error)", fontSize: "0.5rem", letterSpacing: "0.2em", marginTop: 4, display: "block" }, children: error })
1171
+ ] });
1172
+ }
1173
+
1174
+ // src/components/Toggle.tsx
1175
+ import { useState as useState6 } from "react";
1176
+ import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
1177
+ function Toggle({
1178
+ checked: controlledChecked,
1179
+ defaultChecked = false,
1180
+ onCheckedChange,
1181
+ disabled = false,
1182
+ label,
1183
+ ref,
1184
+ className
1185
+ }) {
1186
+ const [internalChecked, setInternalChecked] = useState6(defaultChecked);
1187
+ const isControlled = controlledChecked !== void 0;
1188
+ const isChecked = isControlled ? controlledChecked : internalChecked;
1189
+ function handleChange() {
1190
+ if (disabled) return;
1191
+ const next = !isChecked;
1192
+ if (!isControlled) setInternalChecked(next);
1193
+ onCheckedChange?.(next);
1194
+ }
1195
+ return /* @__PURE__ */ jsxs15(
1196
+ "label",
1197
+ {
1198
+ className: cn(
1199
+ "tb-toggle",
1200
+ isChecked && "tb-toggle--checked",
1201
+ disabled && "tb-toggle--disabled",
1202
+ className
1203
+ ),
1204
+ children: [
1205
+ /* @__PURE__ */ jsx19(
1206
+ "input",
1207
+ {
1208
+ ref,
1209
+ type: "checkbox",
1210
+ className: "tb-toggle__input",
1211
+ checked: isChecked,
1212
+ disabled,
1213
+ onChange: handleChange
1214
+ }
1215
+ ),
1216
+ /* @__PURE__ */ jsx19("span", { className: "tb-toggle__track", "aria-hidden": "true", children: /* @__PURE__ */ jsx19("span", { className: "tb-toggle__thumb" }) }),
1217
+ label && /* @__PURE__ */ jsx19("span", { className: "tb-toggle__label", children: label })
1218
+ ]
1219
+ }
1220
+ );
1221
+ }
1222
+
1223
+ // src/components/Radio.tsx
1224
+ import { createContext as createContext4, useContext as useContext4, useState as useState7 } from "react";
1225
+ import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
1226
+ var RadioContext = createContext4(null);
1227
+ var radioGroupId = 0;
1228
+ function RadioGroup({
1229
+ value: controlledValue,
1230
+ defaultValue = "",
1231
+ onValueChange,
1232
+ disabled = false,
1233
+ label,
1234
+ orientation = "vertical",
1235
+ children,
1236
+ className
1237
+ }) {
1238
+ const [internalValue, setInternalValue] = useState7(defaultValue);
1239
+ const isControlled = controlledValue !== void 0;
1240
+ const currentValue = isControlled ? controlledValue : internalValue;
1241
+ const [name] = useState7(() => `tb-radio-group-${++radioGroupId}`);
1242
+ function handleChange(val) {
1243
+ if (!isControlled) setInternalValue(val);
1244
+ onValueChange?.(val);
1245
+ }
1246
+ return /* @__PURE__ */ jsx20(RadioContext.Provider, { value: { value: currentValue, onChange: handleChange, disabled, name }, children: /* @__PURE__ */ jsxs16(
1247
+ "fieldset",
1248
+ {
1249
+ className: cn(
1250
+ "tb-radio-group",
1251
+ orientation === "horizontal" && "tb-radio-group--horizontal",
1252
+ disabled && "tb-radio-group--disabled",
1253
+ className
1254
+ ),
1255
+ disabled,
1256
+ children: [
1257
+ label && /* @__PURE__ */ jsx20("legend", { className: "tb-radio-group__label", children: label }),
1258
+ children
1259
+ ]
1260
+ }
1261
+ ) });
1262
+ }
1263
+ function RadioItem({
1264
+ value,
1265
+ label,
1266
+ disabled: itemDisabled = false,
1267
+ className
1268
+ }) {
1269
+ const ctx = useContext4(RadioContext);
1270
+ if (!ctx) throw new Error("RadioItem must be used within a RadioGroup");
1271
+ const isDisabled = ctx.disabled || itemDisabled;
1272
+ const isChecked = ctx.value === value;
1273
+ return /* @__PURE__ */ jsxs16(
1274
+ "label",
1275
+ {
1276
+ className: cn(
1277
+ "tb-radio",
1278
+ isChecked && "tb-radio--checked",
1279
+ isDisabled && "tb-radio--disabled",
1280
+ className
1281
+ ),
1282
+ children: [
1283
+ /* @__PURE__ */ jsx20(
1284
+ "input",
1285
+ {
1286
+ type: "radio",
1287
+ name: ctx.name,
1288
+ value,
1289
+ checked: isChecked,
1290
+ disabled: isDisabled,
1291
+ onChange: () => ctx.onChange(value)
1292
+ }
1293
+ ),
1294
+ label
1295
+ ]
1296
+ }
1297
+ );
1298
+ }
1299
+
1300
+ // src/components/Progress.tsx
1301
+ import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
1302
+ function Progress({
1303
+ value,
1304
+ variant = "default",
1305
+ animated = false,
1306
+ indeterminate = false,
1307
+ label,
1308
+ showValue = false,
1309
+ className
1310
+ }) {
1311
+ const clampedValue = Math.max(0, Math.min(100, value));
1312
+ return /* @__PURE__ */ jsxs17(
1313
+ "div",
1314
+ {
1315
+ className: cn(
1316
+ "tb-progress",
1317
+ variant !== "default" && `tb-progress--${variant}`,
1318
+ animated && "tb-progress--animated",
1319
+ indeterminate && "tb-progress--indeterminate",
1320
+ className
1321
+ ),
1322
+ role: "progressbar",
1323
+ "aria-valuenow": indeterminate ? void 0 : clampedValue,
1324
+ "aria-valuemin": 0,
1325
+ "aria-valuemax": 100,
1326
+ "aria-label": label,
1327
+ children: [
1328
+ label && /* @__PURE__ */ jsx21("span", { className: "tb-progress__label", children: label }),
1329
+ /* @__PURE__ */ jsx21("div", { className: "tb-progress__track", children: /* @__PURE__ */ jsx21(
1330
+ "div",
1331
+ {
1332
+ className: "tb-progress__fill",
1333
+ style: indeterminate ? void 0 : { width: `${clampedValue}%` }
1334
+ }
1335
+ ) }),
1336
+ showValue && !indeterminate && /* @__PURE__ */ jsxs17("span", { className: "tb-progress__value", children: [
1337
+ clampedValue,
1338
+ "%"
1339
+ ] })
1340
+ ]
1341
+ }
1342
+ );
1343
+ }
1344
+
1345
+ // src/components/Battery.tsx
1346
+ import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
1347
+ function Battery({
1348
+ value,
1349
+ total = 10,
1350
+ variant = "default",
1351
+ animated = false,
1352
+ label,
1353
+ className
1354
+ }) {
1355
+ const clampedValue = Math.max(0, Math.min(total, value));
1356
+ return /* @__PURE__ */ jsxs18(
1357
+ "div",
1358
+ {
1359
+ className: cn(
1360
+ "tb-battery",
1361
+ variant !== "default" && `tb-battery--${variant}`,
1362
+ animated && "tb-battery--animated",
1363
+ className
1364
+ ),
1365
+ role: "meter",
1366
+ "aria-valuenow": clampedValue,
1367
+ "aria-valuemin": 0,
1368
+ "aria-valuemax": total,
1369
+ "aria-label": label,
1370
+ children: [
1371
+ label && /* @__PURE__ */ jsx22("span", { className: "tb-battery__label", children: label }),
1372
+ /* @__PURE__ */ jsxs18("div", { className: "tb-battery__body", children: [
1373
+ /* @__PURE__ */ jsx22("div", { className: "tb-battery__cap" }),
1374
+ /* @__PURE__ */ jsx22("div", { className: "tb-battery__segments", children: Array.from({ length: total }, (_, i) => /* @__PURE__ */ jsx22(
1375
+ "div",
1376
+ {
1377
+ className: cn(
1378
+ "tb-battery__segment",
1379
+ i < clampedValue && "tb-battery__segment--filled"
1380
+ )
1381
+ },
1382
+ i
1383
+ )) })
1384
+ ] })
1385
+ ]
1386
+ }
1387
+ );
1388
+ }
1389
+
1390
+ // src/components/BatteryInline.tsx
1391
+ import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
1392
+ function BatteryInline({
1393
+ value,
1394
+ total = 10,
1395
+ variant = "default",
1396
+ animated = false,
1397
+ label,
1398
+ showValue = false,
1399
+ className
1400
+ }) {
1401
+ const clampedValue = Math.max(0, Math.min(total, value));
1402
+ const percent = Math.round(clampedValue / total * 100);
1403
+ return /* @__PURE__ */ jsxs19(
1404
+ "div",
1405
+ {
1406
+ className: cn(
1407
+ "tb-battery tb-battery--inline",
1408
+ animated && "tb-battery--animated",
1409
+ className
1410
+ ),
1411
+ role: "meter",
1412
+ "aria-valuenow": clampedValue,
1413
+ "aria-valuemin": 0,
1414
+ "aria-valuemax": total,
1415
+ "aria-label": label,
1416
+ children: [
1417
+ label && /* @__PURE__ */ jsx23("span", { className: "tb-battery__label", children: label }),
1418
+ /* @__PURE__ */ jsx23("div", { className: "tb-battery__track", children: Array.from({ length: total }, (_, i) => /* @__PURE__ */ jsx23(
1419
+ "div",
1420
+ {
1421
+ className: cn(
1422
+ "tb-battery__segment",
1423
+ i < clampedValue && "tb-battery__segment--filled",
1424
+ i < clampedValue && variant !== "default" && `tb-battery__segment--${variant}`
1425
+ )
1426
+ },
1427
+ i
1428
+ )) }),
1429
+ /* @__PURE__ */ jsx23("div", { className: "tb-battery__cap" }),
1430
+ showValue && /* @__PURE__ */ jsxs19("span", { className: "tb-battery__value", children: [
1431
+ percent,
1432
+ "%"
1433
+ ] })
1434
+ ]
1435
+ }
1436
+ );
1437
+ }
1438
+
1439
+ // src/components/Table.tsx
1440
+ import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
1441
+ function getCellValue(row, accessor) {
1442
+ if (typeof accessor === "function") return accessor(row);
1443
+ return row[accessor];
1444
+ }
1445
+ function Table({
1446
+ columns,
1447
+ data,
1448
+ onRowClick,
1449
+ className
1450
+ }) {
1451
+ return /* @__PURE__ */ jsx24("div", { className: cn("tb-table__wrapper", className), children: /* @__PURE__ */ jsxs20("table", { className: "tb-table", children: [
1452
+ /* @__PURE__ */ jsx24("thead", { className: "tb-table__head", children: /* @__PURE__ */ jsx24("tr", { className: "tb-table__row", children: columns.map((col) => /* @__PURE__ */ jsx24("th", { className: "tb-table__th", children: col.header }, col.id)) }) }),
1453
+ /* @__PURE__ */ jsx24("tbody", { className: "tb-table__body", children: data.map((row, rowIndex) => /* @__PURE__ */ jsx24(
1454
+ "tr",
1455
+ {
1456
+ className: cn(
1457
+ "tb-table__row",
1458
+ onRowClick && "tb-table__row--clickable"
1459
+ ),
1460
+ onClick: onRowClick ? () => onRowClick(row) : void 0,
1461
+ children: columns.map((col) => {
1462
+ const value = getCellValue(row, col.accessor);
1463
+ return /* @__PURE__ */ jsx24("td", { className: "tb-table__td", children: col.cell ? col.cell(value, row) : value }, col.id);
1464
+ })
1465
+ },
1466
+ rowIndex
1467
+ )) })
1468
+ ] }) });
1469
+ }
1470
+
1471
+ // src/components/Skeleton.tsx
1472
+ import { jsx as jsx25 } from "react/jsx-runtime";
1473
+ function Skeleton({
1474
+ variant = "text",
1475
+ width,
1476
+ height,
1477
+ lines = 1,
1478
+ className
1479
+ }) {
1480
+ const style = {
1481
+ width: typeof width === "number" ? `${width}px` : width,
1482
+ height: typeof height === "number" ? `${height}px` : height
1483
+ };
1484
+ if (variant === "text" && lines > 1) {
1485
+ return /* @__PURE__ */ jsx25("div", { className: cn("tb-skeleton__group", className), children: Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx25(
1486
+ "div",
1487
+ {
1488
+ className: cn(
1489
+ "tb-skeleton",
1490
+ "tb-skeleton--text"
1491
+ ),
1492
+ style: i === lines - 1 ? { ...style, width: style.width ?? "75%" } : style
1493
+ },
1494
+ i
1495
+ )) });
1496
+ }
1497
+ return /* @__PURE__ */ jsx25(
1498
+ "div",
1499
+ {
1500
+ className: cn(
1501
+ "tb-skeleton",
1502
+ `tb-skeleton--${variant}`,
1503
+ className
1504
+ ),
1505
+ style
1506
+ }
1507
+ );
1508
+ }
1509
+
1510
+ // src/components/Spinner.tsx
1511
+ import { Fragment as Fragment3, jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
1512
+ function Spinner({
1513
+ variant = "multi-square",
1514
+ size = "md",
1515
+ className
1516
+ }) {
1517
+ return /* @__PURE__ */ jsx26(
1518
+ "span",
1519
+ {
1520
+ className: cn(
1521
+ "tb-spinner",
1522
+ `tb-spinner--${variant}`,
1523
+ size !== "md" && `tb-spinner--${size}`,
1524
+ className
1525
+ ),
1526
+ role: "status",
1527
+ "aria-label": "Loading",
1528
+ children: variant === "multi-square" && /* @__PURE__ */ jsxs21(Fragment3, { children: [
1529
+ /* @__PURE__ */ jsx26("span", { className: "tb-spinner__square" }),
1530
+ /* @__PURE__ */ jsx26("span", { className: "tb-spinner__square" }),
1531
+ /* @__PURE__ */ jsx26("span", { className: "tb-spinner__square" }),
1532
+ /* @__PURE__ */ jsx26("span", { className: "tb-spinner__square" }),
1533
+ /* @__PURE__ */ jsx26("span", { className: "tb-spinner__square" })
1534
+ ] })
1535
+ }
1536
+ );
1537
+ }
1538
+
1539
+ // src/components/Pagination.tsx
1540
+ import { jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
1541
+ function getPageRange(page, totalPages, siblingCount) {
1542
+ const totalSlots = siblingCount * 2 + 5;
1543
+ if (totalPages <= totalSlots) {
1544
+ return Array.from({ length: totalPages }, (_, i) => i + 1);
1545
+ }
1546
+ const leftSibling = Math.max(page - siblingCount, 2);
1547
+ const rightSibling = Math.min(page + siblingCount, totalPages - 1);
1548
+ const showLeftEllipsis = leftSibling > 2;
1549
+ const showRightEllipsis = rightSibling < totalPages - 1;
1550
+ const pages = [1];
1551
+ if (showLeftEllipsis) {
1552
+ pages.push("ellipsis");
1553
+ } else {
1554
+ for (let i = 2; i < leftSibling; i++) pages.push(i);
1555
+ }
1556
+ for (let i = leftSibling; i <= rightSibling; i++) pages.push(i);
1557
+ if (showRightEllipsis) {
1558
+ pages.push("ellipsis");
1559
+ } else {
1560
+ for (let i = rightSibling + 1; i < totalPages; i++) pages.push(i);
1561
+ }
1562
+ pages.push(totalPages);
1563
+ return pages;
1564
+ }
1565
+ function Pagination({
1566
+ page,
1567
+ totalPages,
1568
+ onPageChange,
1569
+ siblingCount = 1,
1570
+ className
1571
+ }) {
1572
+ const pages = getPageRange(page, totalPages, siblingCount);
1573
+ return /* @__PURE__ */ jsxs22("nav", { className: cn("tb-pagination", className), "aria-label": "Pagination", children: [
1574
+ /* @__PURE__ */ jsx27(
1575
+ "button",
1576
+ {
1577
+ className: "tb-pagination__btn tb-pagination__btn--prev",
1578
+ disabled: page <= 1,
1579
+ onClick: () => onPageChange(page - 1),
1580
+ "aria-label": "Previous page",
1581
+ children: "\u2039"
1582
+ }
1583
+ ),
1584
+ pages.map(
1585
+ (p, i) => p === "ellipsis" ? /* @__PURE__ */ jsx27("span", { className: "tb-pagination__ellipsis", children: "..." }, `ellipsis-${i}`) : /* @__PURE__ */ jsx27(
1586
+ "button",
1587
+ {
1588
+ className: cn(
1589
+ "tb-pagination__btn",
1590
+ p === page && "tb-pagination__btn--active"
1591
+ ),
1592
+ onClick: () => onPageChange(p),
1593
+ "aria-current": p === page ? "page" : void 0,
1594
+ children: p
1595
+ },
1596
+ p
1597
+ )
1598
+ ),
1599
+ /* @__PURE__ */ jsx27(
1600
+ "button",
1601
+ {
1602
+ className: "tb-pagination__btn tb-pagination__btn--next",
1603
+ disabled: page >= totalPages,
1604
+ onClick: () => onPageChange(page + 1),
1605
+ "aria-label": "Next page",
1606
+ children: "\u203A"
1607
+ }
1608
+ )
1609
+ ] });
1610
+ }
1611
+
1612
+ // src/components/Breadcrumbs.tsx
1613
+ import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
1614
+ function Breadcrumbs({
1615
+ items,
1616
+ separator = "/",
1617
+ linkComponent,
1618
+ className
1619
+ }) {
1620
+ const LinkComponent = linkComponent || "a";
1621
+ return /* @__PURE__ */ jsx28("nav", { className: cn("tb-breadcrumbs", className), "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx28("ol", { className: "tb-breadcrumbs__list", children: items.map((item, i) => {
1622
+ const isLast = i === items.length - 1;
1623
+ return /* @__PURE__ */ jsxs23("li", { className: "tb-breadcrumbs__item", children: [
1624
+ item.href && !isLast ? /* @__PURE__ */ jsx28(LinkComponent, { href: item.href, className: "tb-breadcrumbs__link", children: item.label }) : /* @__PURE__ */ jsx28(
1625
+ "span",
1626
+ {
1627
+ className: "tb-breadcrumbs__current",
1628
+ "aria-current": isLast ? "page" : void 0,
1629
+ children: item.label
1630
+ }
1631
+ ),
1632
+ !isLast && /* @__PURE__ */ jsx28("span", { className: "tb-breadcrumbs__separator", "aria-hidden": "true", children: separator })
1633
+ ] }, i);
1634
+ }) }) });
1635
+ }
1636
+
1637
+ // src/components/Toolbar.tsx
1638
+ import { useState as useState8, useRef as useRef5, useEffect as useEffect6 } from "react";
1639
+ import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
1640
+ function ToolbarSelect({
1641
+ options,
1642
+ value: controlledValue,
1643
+ defaultValue,
1644
+ onChange,
1645
+ dropDown = false,
1646
+ minWidth,
1647
+ className
1648
+ }) {
1649
+ const [internalValue, setInternalValue] = useState8(defaultValue ?? options[0]?.value ?? "");
1650
+ const [open, setOpen] = useState8(false);
1651
+ const ref = useRef5(null);
1652
+ const value = controlledValue ?? internalValue;
1653
+ const activeOption = options.find((o) => o.value === value);
1654
+ useEffect6(() => {
1655
+ function handleClick(e) {
1656
+ if (ref.current && !ref.current.contains(e.target)) {
1657
+ setOpen(false);
1658
+ }
1659
+ }
1660
+ document.addEventListener("click", handleClick);
1661
+ return () => document.removeEventListener("click", handleClick);
1662
+ }, []);
1663
+ function select(opt) {
1664
+ if (controlledValue === void 0) setInternalValue(opt.value);
1665
+ onChange?.(opt.value);
1666
+ setOpen(false);
1667
+ }
1668
+ return /* @__PURE__ */ jsxs24(
1669
+ "div",
1670
+ {
1671
+ ref,
1672
+ className: cn("tb-toolbar__select", open && "tb-toolbar__select--open", className),
1673
+ children: [
1674
+ /* @__PURE__ */ jsxs24(
1675
+ "button",
1676
+ {
1677
+ type: "button",
1678
+ className: "tb-toolbar__trigger",
1679
+ style: minWidth ? { minWidth } : void 0,
1680
+ onClick: (e) => {
1681
+ e.stopPropagation();
1682
+ setOpen(!open);
1683
+ },
1684
+ children: [
1685
+ /* @__PURE__ */ jsx29("span", { children: activeOption?.label ?? "" }),
1686
+ /* @__PURE__ */ jsx29("span", { className: "tb-toolbar__chevron" })
1687
+ ]
1688
+ }
1689
+ ),
1690
+ /* @__PURE__ */ jsx29("div", { className: cn("tb-toolbar__dropdown", dropDown && "tb-toolbar__dropdown--down"), children: options.map((opt) => /* @__PURE__ */ jsxs24(
1691
+ "div",
1692
+ {
1693
+ className: cn("tb-toolbar__option", opt.value === value && "tb-toolbar__option--active"),
1694
+ style: { fontFamily: opt.value },
1695
+ onClick: () => select(opt),
1696
+ children: [
1697
+ opt.label,
1698
+ opt.meta && /* @__PURE__ */ jsx29("span", { className: "tb-toolbar__option-meta", children: opt.meta })
1699
+ ]
1700
+ },
1701
+ opt.value
1702
+ )) })
1703
+ ]
1704
+ }
1705
+ );
1706
+ }
1707
+ function ToolbarLabel({ children, className, ...props }) {
1708
+ return /* @__PURE__ */ jsx29("span", { className: cn("tb-toolbar__label", className), ...props, children });
1709
+ }
1710
+ function ToolbarDivider({ className, ...props }) {
1711
+ return /* @__PURE__ */ jsx29("div", { className: cn("tb-toolbar__divider", className), ...props });
1712
+ }
1713
+ function ToolbarRoot({ position, fixed = false, children, className, ref, ...props }) {
1714
+ return /* @__PURE__ */ jsx29(
1715
+ "div",
1716
+ {
1717
+ ref,
1718
+ className: cn(
1719
+ "tb-toolbar",
1720
+ fixed && "tb-toolbar--fixed",
1721
+ position && `tb-toolbar--${position}`,
1722
+ className
1723
+ ),
1724
+ ...props,
1725
+ children
1726
+ }
1727
+ );
1728
+ }
1729
+ var Toolbar = Object.assign(ToolbarRoot, {
1730
+ Label: ToolbarLabel,
1731
+ Select: ToolbarSelect,
1732
+ Divider: ToolbarDivider
1733
+ });
1734
+
1735
+ // src/components/Sidenav/Sidenav.tsx
1736
+ import {
1737
+ createContext as createContext5,
1738
+ useContext as useContext5,
1739
+ useState as useState9
1740
+ } from "react";
1741
+ import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
1742
+ var SidenavContext = createContext5({
1743
+ iconPosition: "left",
1744
+ borderSide: "left"
1745
+ });
1746
+ function SidenavRoot({
1747
+ iconPosition = "left",
1748
+ borderSide = "left",
1749
+ className,
1750
+ children,
1751
+ ref,
1752
+ ...props
1753
+ }) {
1754
+ return /* @__PURE__ */ jsx30(SidenavContext.Provider, { value: { iconPosition, borderSide }, children: /* @__PURE__ */ jsx30(
1755
+ "nav",
1756
+ {
1757
+ ref,
1758
+ className: cn(
1759
+ "tb-sidenav",
1760
+ iconPosition === "right" && "tb-sidenav--icon-right",
1761
+ borderSide === "right" && "tb-sidenav--border-right",
1762
+ className
1763
+ ),
1764
+ ...props,
1765
+ children
1766
+ }
1767
+ ) });
1768
+ }
1769
+ function Item({
1770
+ as,
1771
+ active = false,
1772
+ className,
1773
+ children,
1774
+ ref,
1775
+ ...props
1776
+ }) {
1777
+ const Component = as || "a";
1778
+ return /* @__PURE__ */ jsx30(
1779
+ Component,
1780
+ {
1781
+ ref,
1782
+ className: cn(
1783
+ "tb-sidenav__item",
1784
+ active && "tb-sidenav__item--active",
1785
+ className
1786
+ ),
1787
+ ...props,
1788
+ children
1789
+ }
1790
+ );
1791
+ }
1792
+ function Icon({ className, ref, ...props }) {
1793
+ return /* @__PURE__ */ jsx30(
1794
+ "span",
1795
+ {
1796
+ ref,
1797
+ className: cn("tb-sidenav__icon", className),
1798
+ ...props
1799
+ }
1800
+ );
1801
+ }
1802
+ function Group({
1803
+ label,
1804
+ icon,
1805
+ defaultOpen = false,
1806
+ open: controlledOpen,
1807
+ onOpenChange,
1808
+ className,
1809
+ children,
1810
+ ref,
1811
+ ...props
1812
+ }) {
1813
+ const [internalOpen, setInternalOpen] = useState9(defaultOpen);
1814
+ const isControlled = controlledOpen !== void 0;
1815
+ const isOpen = isControlled ? controlledOpen : internalOpen;
1816
+ function toggle() {
1817
+ const next = !isOpen;
1818
+ if (!isControlled) setInternalOpen(next);
1819
+ onOpenChange?.(next);
1820
+ }
1821
+ return /* @__PURE__ */ jsxs25(
1822
+ "div",
1823
+ {
1824
+ ref,
1825
+ className: cn(
1826
+ "tb-sidenav__group",
1827
+ isOpen && "tb-sidenav__group--open",
1828
+ className
1829
+ ),
1830
+ ...props,
1831
+ children: [
1832
+ /* @__PURE__ */ jsxs25(
1833
+ "button",
1834
+ {
1835
+ type: "button",
1836
+ className: "tb-sidenav__group-trigger",
1837
+ onClick: toggle,
1838
+ "aria-expanded": isOpen,
1839
+ children: [
1840
+ icon && /* @__PURE__ */ jsx30("span", { className: "tb-sidenav__icon", children: icon }),
1841
+ label,
1842
+ /* @__PURE__ */ jsx30("span", { className: "tb-sidenav__group-chevron material-symbols-outlined", "aria-hidden": "true", children: "expand_more" })
1843
+ ]
1844
+ }
1845
+ ),
1846
+ /* @__PURE__ */ jsx30("div", { className: "tb-sidenav__group-content", children })
1847
+ ]
1848
+ }
1849
+ );
1850
+ }
1851
+ var Sidenav = Object.assign(SidenavRoot, {
1852
+ Item,
1853
+ Icon,
1854
+ Group
1855
+ });
1856
+
1857
+ // src/components/CodeBlock.tsx
1858
+ import { useState as useState10, useRef as useRef6 } from "react";
1859
+ import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
1860
+ function CodeBlock({ children, copyText, className, ref, ...props }) {
1861
+ const [copied, setCopied] = useState10(false);
1862
+ const contentRef = useRef6(null);
1863
+ async function handleCopy() {
1864
+ const text = copyText ?? contentRef.current?.textContent ?? "";
1865
+ await navigator.clipboard.writeText(text);
1866
+ setCopied(true);
1867
+ setTimeout(() => setCopied(false), 2e3);
1868
+ }
1869
+ return /* @__PURE__ */ jsxs26("div", { ref, className: cn("tb-code-block", className), ...props, children: [
1870
+ /* @__PURE__ */ jsx31("div", { ref: contentRef, children }),
1871
+ /* @__PURE__ */ jsx31(
1872
+ "button",
1873
+ {
1874
+ type: "button",
1875
+ className: cn("tb-code-block__copy", copied && "tb-code-block__copy--copied"),
1876
+ onClick: handleCopy,
1877
+ "aria-label": copied ? "Copied" : "Copy to clipboard",
1878
+ children: /* @__PURE__ */ jsx31("span", { className: "material-symbols-outlined", style: { fontSize: 14 }, children: copied ? "check" : "content_copy" })
1879
+ }
1880
+ )
1881
+ ] });
1882
+ }
1883
+
1884
+ // src/components/ThemeProvider.tsx
1885
+ import {
1886
+ createContext as createContext6,
1887
+ useContext as useContext6,
1888
+ useState as useState11,
1889
+ useEffect as useEffect7,
1890
+ useCallback as useCallback4
1891
+ } from "react";
1892
+ import { jsx as jsx32 } from "react/jsx-runtime";
1893
+ var ThemeContext = createContext6(null);
1894
+ function useTheme() {
1895
+ const ctx = useContext6(ThemeContext);
1896
+ if (!ctx) throw new Error("useTheme must be used within <ThemeProvider>");
1897
+ return ctx;
1898
+ }
1899
+ function ThemeProvider({
1900
+ theme: controlledTheme,
1901
+ contrast: controlledContrast,
1902
+ defaultTheme = "dark",
1903
+ defaultContrast = "enhanced",
1904
+ onThemeChange,
1905
+ onContrastChange,
1906
+ children
1907
+ }) {
1908
+ const isThemeControlled = controlledTheme !== void 0;
1909
+ const isContrastControlled = controlledContrast !== void 0;
1910
+ const [internalTheme, setInternalTheme] = useState11(defaultTheme);
1911
+ const [internalContrast, setInternalContrast] = useState11(defaultContrast);
1912
+ const theme = isThemeControlled ? controlledTheme : internalTheme;
1913
+ const contrast = isContrastControlled ? controlledContrast : internalContrast;
1914
+ const setTheme = useCallback4(
1915
+ (next) => {
1916
+ if (!isThemeControlled) setInternalTheme(next);
1917
+ onThemeChange?.(next);
1918
+ },
1919
+ [isThemeControlled, onThemeChange]
1920
+ );
1921
+ const setContrast = useCallback4(
1922
+ (next) => {
1923
+ if (!isContrastControlled) setInternalContrast(next);
1924
+ onContrastChange?.(next);
1925
+ },
1926
+ [isContrastControlled, onContrastChange]
1927
+ );
1928
+ const toggleTheme = useCallback4(
1929
+ () => setTheme(theme === "dark" ? "light" : "dark"),
1930
+ [theme, setTheme]
1931
+ );
1932
+ useEffect7(() => {
1933
+ const root = document.documentElement;
1934
+ root.setAttribute("data-theme", theme);
1935
+ if (contrast === "high") {
1936
+ root.setAttribute("data-contrast", "high");
1937
+ } else {
1938
+ root.removeAttribute("data-contrast");
1939
+ }
1940
+ }, [theme, contrast]);
1941
+ return /* @__PURE__ */ jsx32(ThemeContext.Provider, { value: { theme, setTheme, contrast, setContrast, toggleTheme }, children });
1942
+ }
1943
+
1944
+ // src/hooks/useDisclosure.ts
1945
+ import { useState as useState12, useCallback as useCallback5 } from "react";
1946
+ function useDisclosure(options = {}) {
1947
+ const { defaultOpen = false, onOpen: onOpenCallback, onClose: onCloseCallback } = options;
1948
+ const [isOpen, setIsOpen] = useState12(defaultOpen);
1949
+ const onOpen = useCallback5(() => {
1950
+ setIsOpen(true);
1951
+ onOpenCallback?.();
1952
+ }, [onOpenCallback]);
1953
+ const onClose = useCallback5(() => {
1954
+ setIsOpen(false);
1955
+ onCloseCallback?.();
1956
+ }, [onCloseCallback]);
1957
+ const onToggle = useCallback5(() => {
1958
+ if (isOpen) {
1959
+ onClose();
1960
+ } else {
1961
+ onOpen();
1962
+ }
1963
+ }, [isOpen, onOpen, onClose]);
1964
+ return { isOpen, onOpen, onClose, onToggle };
1965
+ }
1966
+
1967
+ // src/hooks/useFocusTrap.ts
1968
+ import { useEffect as useEffect8, useRef as useRef7 } from "react";
1969
+ var FOCUSABLE_SELECTOR = [
1970
+ "a[href]",
1971
+ "button:not([disabled])",
1972
+ "input:not([disabled])",
1973
+ "textarea:not([disabled])",
1974
+ "select:not([disabled])",
1975
+ '[tabindex]:not([tabindex="-1"])'
1976
+ ].join(", ");
1977
+ function useFocusTrap(ref, active) {
1978
+ const previouslyFocusedRef = useRef7(null);
1979
+ useEffect8(() => {
1980
+ if (!active || !ref.current) return;
1981
+ previouslyFocusedRef.current = document.activeElement;
1982
+ const container = ref.current;
1983
+ const focusableElements = container.querySelectorAll(FOCUSABLE_SELECTOR);
1984
+ if (focusableElements.length > 0) {
1985
+ focusableElements[0].focus();
1986
+ }
1987
+ function handleKeyDown(e) {
1988
+ if (e.key !== "Tab") return;
1989
+ const focusable = container.querySelectorAll(FOCUSABLE_SELECTOR);
1990
+ if (focusable.length === 0) return;
1991
+ const first = focusable[0];
1992
+ const last = focusable[focusable.length - 1];
1993
+ if (e.shiftKey) {
1994
+ if (document.activeElement === first) {
1995
+ e.preventDefault();
1996
+ last.focus();
1997
+ }
1998
+ } else {
1999
+ if (document.activeElement === last) {
2000
+ e.preventDefault();
2001
+ first.focus();
2002
+ }
2003
+ }
2004
+ }
2005
+ document.addEventListener("keydown", handleKeyDown);
2006
+ return () => {
2007
+ document.removeEventListener("keydown", handleKeyDown);
2008
+ if (previouslyFocusedRef.current && typeof previouslyFocusedRef.current.focus === "function") {
2009
+ previouslyFocusedRef.current.focus();
2010
+ }
2011
+ };
2012
+ }, [active, ref]);
2013
+ }
2014
+
2015
+ // src/hooks/useKeyboardNav.ts
2016
+ import { useState as useState13, useCallback as useCallback6 } from "react";
2017
+ function useKeyboardNav(items, options = {}) {
2018
+ const { orientation = "vertical", loop = true, onSelect } = options;
2019
+ const [activeIndex, setActiveIndex] = useState13(0);
2020
+ const prevKey = orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
2021
+ const nextKey = orientation === "vertical" ? "ArrowDown" : "ArrowRight";
2022
+ const onKeyDown = useCallback6(
2023
+ (e) => {
2024
+ const list = items.current;
2025
+ if (!list || list.length === 0) return;
2026
+ const count = list.length;
2027
+ if (e.key === nextKey) {
2028
+ e.preventDefault();
2029
+ setActiveIndex((prev) => {
2030
+ const next = prev + 1;
2031
+ if (next >= count) {
2032
+ const wrapped = loop ? 0 : prev;
2033
+ list[wrapped]?.focus();
2034
+ return wrapped;
2035
+ }
2036
+ list[next]?.focus();
2037
+ return next;
2038
+ });
2039
+ } else if (e.key === prevKey) {
2040
+ e.preventDefault();
2041
+ setActiveIndex((prev) => {
2042
+ const next = prev - 1;
2043
+ if (next < 0) {
2044
+ const wrapped = loop ? count - 1 : prev;
2045
+ list[wrapped]?.focus();
2046
+ return wrapped;
2047
+ }
2048
+ list[next]?.focus();
2049
+ return next;
2050
+ });
2051
+ } else if (e.key === "Enter" || e.key === " ") {
2052
+ e.preventDefault();
2053
+ onSelect?.(activeIndex);
2054
+ } else if (e.key === "Home") {
2055
+ e.preventDefault();
2056
+ setActiveIndex(0);
2057
+ list[0]?.focus();
2058
+ } else if (e.key === "End") {
2059
+ e.preventDefault();
2060
+ const last = count - 1;
2061
+ setActiveIndex(last);
2062
+ list[last]?.focus();
2063
+ }
2064
+ },
2065
+ [items, activeIndex, prevKey, nextKey, loop, onSelect]
2066
+ );
2067
+ return { activeIndex, setActiveIndex, onKeyDown };
2068
+ }
2069
+
2070
+ // src/hooks/useReducedMotion.ts
2071
+ import { useState as useState14, useEffect as useEffect9 } from "react";
2072
+ function useReducedMotion() {
2073
+ const [reduced, setReduced] = useState14(false);
2074
+ useEffect9(() => {
2075
+ const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
2076
+ setReduced(mq.matches);
2077
+ const handler = (e) => setReduced(e.matches);
2078
+ mq.addEventListener("change", handler);
2079
+ return () => mq.removeEventListener("change", handler);
2080
+ }, []);
2081
+ return reduced;
2082
+ }
2083
+
2084
+ // src/hooks/useMergedRef.ts
2085
+ import { useCallback as useCallback7 } from "react";
2086
+ function useMergedRef(...refs) {
2087
+ return useCallback7((node) => {
2088
+ refs.forEach((ref) => {
2089
+ if (typeof ref === "function") ref(node);
2090
+ else if (ref) ref.current = node;
2091
+ });
2092
+ }, refs);
2093
+ }
2094
+ export {
2095
+ Badge,
2096
+ Battery,
2097
+ BatteryInline,
2098
+ Breadcrumbs,
2099
+ Button,
2100
+ Card,
2101
+ Checkbox,
2102
+ CodeBlock,
2103
+ CornerBrackets,
2104
+ Input,
2105
+ Modal,
2106
+ NavDropdown,
2107
+ Pagination,
2108
+ Progress,
2109
+ RadioGroup,
2110
+ RadioItem,
2111
+ SafePortal,
2112
+ Select,
2113
+ SelectContent,
2114
+ SelectItem,
2115
+ SelectTrigger,
2116
+ Sidenav,
2117
+ Skeleton,
2118
+ Spinner,
2119
+ Table,
2120
+ Tabs,
2121
+ TabsContent,
2122
+ TabsList,
2123
+ TabsTrigger,
2124
+ Tag,
2125
+ Textarea,
2126
+ ThemeProvider,
2127
+ TitleLine,
2128
+ Toaster,
2129
+ Toggle,
2130
+ Toolbar,
2131
+ Tooltip,
2132
+ cn,
2133
+ createToast as toast,
2134
+ useDisclosure,
2135
+ useFocusTrap,
2136
+ useKeyboardNav,
2137
+ useMergedRef,
2138
+ useReducedMotion,
2139
+ useTheme
2140
+ };
2141
+ //# sourceMappingURL=index.js.map