@tomny-dev/uzi 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,692 @@
1
+ // src/utils/cx.ts
2
+ function cx(...values) {
3
+ return values.filter(Boolean).join(" ");
4
+ }
5
+
6
+ // src/components/button/button.module.css
7
+ var button_default = {};
8
+
9
+ // src/components/button/Button.tsx
10
+ import { jsx } from "react/jsx-runtime";
11
+ function Button({
12
+ as,
13
+ variant = "primary",
14
+ size = "md",
15
+ className,
16
+ children,
17
+ ...rest
18
+ }) {
19
+ const classes = cx(
20
+ button_default.button,
21
+ button_default[`variant-${variant}`],
22
+ button_default[`size-${size}`],
23
+ className
24
+ );
25
+ if (as === "a") {
26
+ return /* @__PURE__ */ jsx("a", { className: classes, ...rest, children });
27
+ }
28
+ return /* @__PURE__ */ jsx(
29
+ "button",
30
+ {
31
+ type: "button",
32
+ className: classes,
33
+ ...rest,
34
+ children
35
+ }
36
+ );
37
+ }
38
+
39
+ // src/components/card/card.module.css
40
+ var card_default = {};
41
+
42
+ // src/components/card/Card.tsx
43
+ import { jsx as jsx2 } from "react/jsx-runtime";
44
+ function Card({
45
+ as,
46
+ tone = "default",
47
+ padding = "md",
48
+ interactive = false,
49
+ className,
50
+ children,
51
+ ...rest
52
+ }) {
53
+ const Component = as ?? "div";
54
+ const toneClass = tone !== "default" ? card_default[`tone-${tone}`] : null;
55
+ const classes = cx(
56
+ card_default.card,
57
+ toneClass,
58
+ card_default[`padding-${padding}`],
59
+ interactive && card_default.interactive,
60
+ className
61
+ );
62
+ return /* @__PURE__ */ jsx2(Component, { className: classes, ...rest, children });
63
+ }
64
+
65
+ // src/components/pill/pill.module.css
66
+ var pill_default = {};
67
+
68
+ // src/components/pill/Pill.tsx
69
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
70
+ function Pill({
71
+ as,
72
+ tone = "neutral",
73
+ size = "md",
74
+ icon,
75
+ className,
76
+ children,
77
+ ...rest
78
+ }) {
79
+ const Component = as ?? "span";
80
+ const classes = cx(pill_default.pill, pill_default[`tone-${tone}`], pill_default[`size-${size}`], className);
81
+ return /* @__PURE__ */ jsxs(Component, { className: classes, ...rest, children: [
82
+ icon ? /* @__PURE__ */ jsx3("span", { className: pill_default.icon, "aria-hidden": "true", children: icon }) : null,
83
+ /* @__PURE__ */ jsx3("span", { className: pill_default.content, children })
84
+ ] });
85
+ }
86
+
87
+ // src/components/modal/Modal.tsx
88
+ import { useRef } from "react";
89
+
90
+ // src/components/modal/modal.module.css
91
+ var modal_default = {};
92
+
93
+ // src/components/modal/Modal.tsx
94
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
95
+ function Modal({ open, onClose, title, subtitle, size = "md", children, footer }) {
96
+ const mouseDownOnBackdrop = useRef(false);
97
+ if (!open) return null;
98
+ return /* @__PURE__ */ jsx4(
99
+ "div",
100
+ {
101
+ className: modal_default.backdrop,
102
+ onMouseDown: (e) => {
103
+ mouseDownOnBackdrop.current = e.target === e.currentTarget;
104
+ },
105
+ onMouseUp: (e) => {
106
+ if (mouseDownOnBackdrop.current && e.target === e.currentTarget) onClose();
107
+ mouseDownOnBackdrop.current = false;
108
+ },
109
+ children: /* @__PURE__ */ jsxs2("div", { className: cx(modal_default.modal, modal_default[`size-${size}`]), children: [
110
+ /* @__PURE__ */ jsxs2("div", { className: modal_default.header, children: [
111
+ /* @__PURE__ */ jsxs2("div", { className: modal_default.titles, children: [
112
+ /* @__PURE__ */ jsx4("div", { className: modal_default.title, children: title }),
113
+ subtitle && /* @__PURE__ */ jsx4("div", { className: modal_default.subtitle, children: subtitle })
114
+ ] }),
115
+ /* @__PURE__ */ jsx4("button", { className: modal_default.closeButton, onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxs2("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
116
+ /* @__PURE__ */ jsx4("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
117
+ /* @__PURE__ */ jsx4("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
118
+ ] }) })
119
+ ] }),
120
+ /* @__PURE__ */ jsx4("div", { className: modal_default.body, children }),
121
+ footer && /* @__PURE__ */ jsx4("div", { className: modal_default.footer, children: footer })
122
+ ] })
123
+ }
124
+ );
125
+ }
126
+
127
+ // src/components/toast/ToastContext.tsx
128
+ import {
129
+ createContext,
130
+ useCallback,
131
+ useContext,
132
+ useEffect,
133
+ useMemo,
134
+ useRef as useRef2,
135
+ useState
136
+ } from "react";
137
+
138
+ // src/components/toast/toast.module.css
139
+ var toast_default = {};
140
+
141
+ // src/components/toast/ToastContext.tsx
142
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
143
+ var DEFAULT_CONFIG = {
144
+ position: "top-right",
145
+ maxToasts: 5,
146
+ defaultDuration: 4e3,
147
+ pauseOnHover: true,
148
+ pauseOnFocusLoss: true
149
+ };
150
+ var ToastContext = createContext(void 0);
151
+ var toastIdCounter = 0;
152
+ var generateToastId = () => `toast-${++toastIdCounter}-${Date.now()}`;
153
+ function ToastProvider({
154
+ children,
155
+ config
156
+ }) {
157
+ const [toasts, setToasts] = useState([]);
158
+ const [isPaused, setIsPaused] = useState(false);
159
+ const merged = useMemo(() => ({ ...DEFAULT_CONFIG, ...config }), [config]);
160
+ const push = useCallback(
161
+ (message, options = {}) => {
162
+ const id = generateToastId();
163
+ setToasts((prev) => {
164
+ const next = [
165
+ ...prev,
166
+ {
167
+ id,
168
+ message,
169
+ type: options.type ?? "info",
170
+ duration: options.duration ?? (options.type === "error" ? 6e3 : merged.defaultDuration),
171
+ dismissible: options.dismissible ?? true,
172
+ action: options.action
173
+ }
174
+ ];
175
+ if (next.length > merged.maxToasts) next.shift();
176
+ return next;
177
+ });
178
+ return id;
179
+ },
180
+ [merged.defaultDuration, merged.maxToasts]
181
+ );
182
+ const success = useCallback(
183
+ (message, options) => push(message, { ...options, type: "success" }),
184
+ [push]
185
+ );
186
+ const error = useCallback(
187
+ (message, options) => push(message, { ...options, type: "error", duration: options?.duration ?? 6e3 }),
188
+ [push]
189
+ );
190
+ const warning = useCallback(
191
+ (message, options) => push(message, { ...options, type: "warning" }),
192
+ [push]
193
+ );
194
+ const info = useCallback(
195
+ (message, options) => push(message, { ...options, type: "info" }),
196
+ [push]
197
+ );
198
+ const dismiss = useCallback((id) => {
199
+ setToasts((prev) => prev.filter((t) => t.id !== id));
200
+ }, []);
201
+ const dismissAll = useCallback(() => setToasts([]), []);
202
+ useEffect(() => {
203
+ if (!merged.pauseOnFocusLoss) return;
204
+ const handleVisibility = () => setIsPaused(document.visibilityState !== "visible");
205
+ document.addEventListener("visibilitychange", handleVisibility);
206
+ return () => document.removeEventListener("visibilitychange", handleVisibility);
207
+ }, [merged.pauseOnFocusLoss]);
208
+ const value = useMemo(
209
+ () => ({ toasts, push, success, error, warning, info, dismiss, dismissAll }),
210
+ [toasts, push, success, error, warning, info, dismiss, dismissAll]
211
+ );
212
+ return /* @__PURE__ */ jsxs3(ToastContext.Provider, { value, children: [
213
+ children,
214
+ /* @__PURE__ */ jsx5(
215
+ ToastContainer,
216
+ {
217
+ toasts,
218
+ position: merged.position,
219
+ pauseOnHover: merged.pauseOnHover,
220
+ isPaused,
221
+ onDismiss: dismiss,
222
+ onPauseChange: setIsPaused
223
+ }
224
+ )
225
+ ] });
226
+ }
227
+ function useToast() {
228
+ const ctx = useContext(ToastContext);
229
+ if (!ctx) throw new Error("useToast must be used within a ToastProvider");
230
+ return ctx;
231
+ }
232
+ function ToastContainer({
233
+ toasts,
234
+ position,
235
+ pauseOnHover,
236
+ isPaused,
237
+ onDismiss,
238
+ onPauseChange
239
+ }) {
240
+ const posClass = (() => {
241
+ switch (position) {
242
+ case "top-left":
243
+ return toast_default.topLeft;
244
+ case "top-center":
245
+ return toast_default.topCenter;
246
+ case "bottom-right":
247
+ return toast_default.bottomRight;
248
+ case "bottom-left":
249
+ return toast_default.bottomLeft;
250
+ case "bottom-center":
251
+ return toast_default.bottomCenter;
252
+ case "top-right":
253
+ default:
254
+ return toast_default.topRight;
255
+ }
256
+ })();
257
+ return /* @__PURE__ */ jsx5(
258
+ "div",
259
+ {
260
+ className: cx(toast_default.stack, posClass),
261
+ role: "presentation",
262
+ onMouseEnter: () => pauseOnHover && onPauseChange(true),
263
+ onMouseLeave: () => pauseOnHover && onPauseChange(false),
264
+ children: toasts.map((toast) => /* @__PURE__ */ jsx5(ToastItem, { toast, isPaused, onDismiss }, toast.id))
265
+ }
266
+ );
267
+ }
268
+ function ToastItem({
269
+ toast,
270
+ isPaused,
271
+ onDismiss
272
+ }) {
273
+ const [exiting, setExiting] = useState(false);
274
+ const timerRef = useRef2(null);
275
+ const startRef = useRef2(0);
276
+ const remainingRef = useRef2(toast.duration ?? 0);
277
+ const palette = getPalette(toast.type);
278
+ const styleVars = {
279
+ ["--toast-bg"]: palette.background,
280
+ ["--toast-border"]: palette.border,
281
+ ["--toast-text"]: palette.text,
282
+ ["--toast-button-bg"]: palette.buttonBg,
283
+ ["--toast-button-border"]: palette.buttonBorder,
284
+ ["--toast-action-bg"]: palette.actionBg,
285
+ ["--toast-action-border"]: palette.actionBorder
286
+ };
287
+ const stopTimer = () => {
288
+ if (timerRef.current) {
289
+ window.clearTimeout(timerRef.current);
290
+ timerRef.current = null;
291
+ }
292
+ };
293
+ const triggerDismiss = useCallback(() => {
294
+ setExiting(true);
295
+ stopTimer();
296
+ window.setTimeout(() => onDismiss(toast.id), 160);
297
+ }, [onDismiss, toast.id]);
298
+ const schedule = useCallback(
299
+ (delay) => {
300
+ if (!delay || delay <= 0) {
301
+ triggerDismiss();
302
+ return;
303
+ }
304
+ startRef.current = performance.now();
305
+ stopTimer();
306
+ timerRef.current = window.setTimeout(() => triggerDismiss(), delay);
307
+ },
308
+ [triggerDismiss]
309
+ );
310
+ useEffect(() => {
311
+ if (!toast.duration || toast.duration <= 0) return void 0;
312
+ schedule(toast.duration);
313
+ return stopTimer;
314
+ }, [schedule, toast.duration]);
315
+ useEffect(() => {
316
+ if (!toast.duration || toast.duration <= 0) return;
317
+ if (isPaused) {
318
+ const elapsed = performance.now() - startRef.current;
319
+ remainingRef.current = Math.max(0, remainingRef.current - elapsed);
320
+ stopTimer();
321
+ } else {
322
+ schedule(remainingRef.current);
323
+ }
324
+ }, [isPaused, schedule, toast.duration]);
325
+ const icon = getIcon(toast.type);
326
+ return /* @__PURE__ */ jsxs3("div", { className: cx(toast_default.toast, exiting && toast_default.exit), style: styleVars, role: "status", "aria-live": "polite", children: [
327
+ /* @__PURE__ */ jsx5("span", { className: toast_default.icon, "aria-hidden": true, children: icon }),
328
+ /* @__PURE__ */ jsxs3("div", { className: toast_default.body, children: [
329
+ /* @__PURE__ */ jsx5("div", { className: toast_default.message, children: toast.message }),
330
+ toast.action && /* @__PURE__ */ jsx5("div", { className: toast_default.actions, children: /* @__PURE__ */ jsx5(
331
+ "button",
332
+ {
333
+ type: "button",
334
+ className: toast_default.actionButton,
335
+ onClick: () => {
336
+ toast.action?.onClick();
337
+ triggerDismiss();
338
+ },
339
+ children: toast.action.label
340
+ }
341
+ ) })
342
+ ] }),
343
+ toast.dismissible !== false && /* @__PURE__ */ jsx5(
344
+ "button",
345
+ {
346
+ type: "button",
347
+ className: toast_default.closeButton,
348
+ onClick: triggerDismiss,
349
+ "aria-label": "Dismiss notification",
350
+ children: /* @__PURE__ */ jsx5("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx5(
351
+ "path",
352
+ {
353
+ d: "M11 3L3 11M3 3l8 8",
354
+ stroke: "currentColor",
355
+ strokeWidth: "1.5",
356
+ strokeLinecap: "round"
357
+ }
358
+ ) })
359
+ }
360
+ )
361
+ ] });
362
+ }
363
+ function getPalette(type) {
364
+ switch (type) {
365
+ case "success":
366
+ return {
367
+ background: "#22c55e",
368
+ border: "#16a34a",
369
+ text: "#0b1224",
370
+ buttonBg: "rgba(0, 0, 0, 0.08)",
371
+ buttonBorder: "rgba(0, 0, 0, 0.2)",
372
+ actionBg: "rgba(0, 0, 0, 0.1)",
373
+ actionBorder: "rgba(0, 0, 0, 0.2)"
374
+ };
375
+ case "error":
376
+ return {
377
+ background: "#f87171",
378
+ border: "#ef4444",
379
+ text: "#0b1224",
380
+ buttonBg: "rgba(0, 0, 0, 0.08)",
381
+ buttonBorder: "rgba(0, 0, 0, 0.2)",
382
+ actionBg: "rgba(0, 0, 0, 0.1)",
383
+ actionBorder: "rgba(0, 0, 0, 0.2)"
384
+ };
385
+ case "warning":
386
+ return {
387
+ background: "#fbbf24",
388
+ border: "#f59e0b",
389
+ text: "#0b1224",
390
+ buttonBg: "rgba(0, 0, 0, 0.08)",
391
+ buttonBorder: "rgba(0, 0, 0, 0.2)",
392
+ actionBg: "rgba(0, 0, 0, 0.1)",
393
+ actionBorder: "rgba(0, 0, 0, 0.2)"
394
+ };
395
+ case "info":
396
+ default:
397
+ return {
398
+ background: "#38bdf8",
399
+ border: "#0ea5e9",
400
+ text: "#0b1224",
401
+ buttonBg: "rgba(0, 0, 0, 0.08)",
402
+ buttonBorder: "rgba(0, 0, 0, 0.2)",
403
+ actionBg: "rgba(0, 0, 0, 0.1)",
404
+ actionBorder: "rgba(0, 0, 0, 0.2)"
405
+ };
406
+ }
407
+ }
408
+ function getIcon(type) {
409
+ switch (type) {
410
+ case "success":
411
+ return "OK";
412
+ case "error":
413
+ return "X";
414
+ case "warning":
415
+ return "!";
416
+ case "info":
417
+ default:
418
+ return "i";
419
+ }
420
+ }
421
+
422
+ // src/components/dropdown/Dropdown.tsx
423
+ import { useState as useState2, useRef as useRef3, useEffect as useEffect2 } from "react";
424
+
425
+ // src/components/dropdown/dropdown.module.css
426
+ var dropdown_default = {};
427
+
428
+ // src/components/dropdown/Dropdown.tsx
429
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
430
+ function Dropdown({
431
+ options,
432
+ value,
433
+ onChange,
434
+ placeholder = "All",
435
+ className,
436
+ allowClear = true,
437
+ "aria-labelledby": ariaLabelledBy,
438
+ "aria-label": ariaLabel
439
+ }) {
440
+ const [open, setOpen] = useState2(false);
441
+ const ref = useRef3(null);
442
+ const selected = options.find((o) => o.value === value);
443
+ const isActive = allowClear && value !== "";
444
+ useEffect2(() => {
445
+ function handleClickOutside(e) {
446
+ if (ref.current && !ref.current.contains(e.target)) {
447
+ setOpen(false);
448
+ }
449
+ }
450
+ document.addEventListener("mousedown", handleClickOutside);
451
+ return () => document.removeEventListener("mousedown", handleClickOutside);
452
+ }, []);
453
+ return /* @__PURE__ */ jsxs4("div", { className: cx(dropdown_default.wrapper, className), ref, children: [
454
+ /* @__PURE__ */ jsxs4(
455
+ "button",
456
+ {
457
+ type: "button",
458
+ className: cx(dropdown_default.trigger, isActive && dropdown_default["trigger-active"]),
459
+ onClick: () => setOpen((o) => !o),
460
+ "aria-labelledby": ariaLabelledBy,
461
+ "aria-label": ariaLabel,
462
+ children: [
463
+ selected ? selected.label : placeholder,
464
+ /* @__PURE__ */ jsx6("span", { className: cx(dropdown_default.chevron, open && dropdown_default["chevron-open"]), children: /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 10 10", fill: "none", xmlns: "http://www.w3.org/2000/svg", width: "10", height: "10", children: /* @__PURE__ */ jsx6("path", { d: "M2 3.5L5 6.5L8 3.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
465
+ ]
466
+ }
467
+ ),
468
+ open && /* @__PURE__ */ jsxs4("div", { className: dropdown_default.menu, children: [
469
+ allowClear && /* @__PURE__ */ jsx6(
470
+ "button",
471
+ {
472
+ type: "button",
473
+ className: cx(dropdown_default.option, value === "" && dropdown_default["option-selected"]),
474
+ onClick: () => {
475
+ onChange("");
476
+ setOpen(false);
477
+ },
478
+ children: placeholder
479
+ }
480
+ ),
481
+ options.map((opt) => /* @__PURE__ */ jsx6(
482
+ "button",
483
+ {
484
+ type: "button",
485
+ className: cx(dropdown_default.option, value === opt.value && dropdown_default["option-selected"]),
486
+ onClick: () => {
487
+ onChange(opt.value);
488
+ setOpen(false);
489
+ },
490
+ children: opt.label
491
+ },
492
+ opt.value
493
+ ))
494
+ ] })
495
+ ] });
496
+ }
497
+
498
+ // src/components/app-shell/AppShell.tsx
499
+ import {
500
+ useEffect as useEffect3,
501
+ useId,
502
+ useRef as useRef4,
503
+ useState as useState3
504
+ } from "react";
505
+
506
+ // src/components/app-shell/app-shell.module.css
507
+ var app_shell_default = {};
508
+
509
+ // src/components/app-shell/AppShell.tsx
510
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
511
+ var DESKTOP_BREAKPOINT = 960;
512
+ function getIsDesktop() {
513
+ if (typeof window === "undefined") return false;
514
+ return window.innerWidth >= DESKTOP_BREAKPOINT;
515
+ }
516
+ function AppShell({
517
+ children,
518
+ sidebar,
519
+ brand,
520
+ brandHref,
521
+ topbarStart,
522
+ topbarEnd,
523
+ className,
524
+ sidebarClassName,
525
+ topbarClassName,
526
+ mainClassName,
527
+ sidebarWidth,
528
+ closeSidebarOnChangeKey,
529
+ hamburgerLabel = "Toggle navigation",
530
+ onSidebarToggle
531
+ }) {
532
+ const [isDesktop, setIsDesktop] = useState3(false);
533
+ const [sidebarOpen, setSidebarOpen] = useState3(false);
534
+ const prevIsDesktopRef = useRef4(false);
535
+ const closeKeyRef = useRef4(closeSidebarOnChangeKey);
536
+ const sidebarRef = useRef4(null);
537
+ const hamburgerRef = useRef4(null);
538
+ const mainRef = useRef4(null);
539
+ const sidebarId = useId();
540
+ useEffect3(() => {
541
+ const desktop = getIsDesktop();
542
+ setIsDesktop(desktop);
543
+ setSidebarOpen(desktop);
544
+ prevIsDesktopRef.current = desktop;
545
+ const handleResize = () => {
546
+ const nowDesktop = getIsDesktop();
547
+ setIsDesktop(nowDesktop);
548
+ if (nowDesktop !== prevIsDesktopRef.current) {
549
+ setSidebarOpen(nowDesktop);
550
+ prevIsDesktopRef.current = nowDesktop;
551
+ }
552
+ };
553
+ window.addEventListener("resize", handleResize);
554
+ return () => window.removeEventListener("resize", handleResize);
555
+ }, []);
556
+ useEffect3(() => {
557
+ if (isDesktop || !sidebarOpen) return;
558
+ const mainElement = mainRef.current;
559
+ const closeSidebar = () => setSidebarOpen(false);
560
+ const onPointerDown = (e) => {
561
+ const target = e.target;
562
+ if (!target) return;
563
+ if (sidebarRef.current?.contains(target)) return;
564
+ if (hamburgerRef.current?.contains(target)) return;
565
+ closeSidebar();
566
+ };
567
+ const timeoutId = window.setTimeout(() => {
568
+ document.addEventListener("pointerdown", onPointerDown);
569
+ window.addEventListener("scroll", closeSidebar, { passive: true });
570
+ mainElement?.addEventListener("scroll", closeSidebar, { passive: true });
571
+ document.addEventListener("touchmove", closeSidebar, { passive: true });
572
+ }, 10);
573
+ return () => {
574
+ window.clearTimeout(timeoutId);
575
+ document.removeEventListener("pointerdown", onPointerDown);
576
+ window.removeEventListener("scroll", closeSidebar);
577
+ mainElement?.removeEventListener("scroll", closeSidebar);
578
+ document.removeEventListener("touchmove", closeSidebar);
579
+ };
580
+ }, [sidebarOpen, isDesktop]);
581
+ useEffect3(() => {
582
+ if (!isDesktop && closeKeyRef.current !== closeSidebarOnChangeKey) {
583
+ setSidebarOpen(false);
584
+ }
585
+ closeKeyRef.current = closeSidebarOnChangeKey;
586
+ }, [closeSidebarOnChangeKey, isDesktop]);
587
+ useEffect3(() => {
588
+ onSidebarToggle?.(sidebarOpen);
589
+ }, [sidebarOpen, onSidebarToggle]);
590
+ const toggleSidebar = () => setSidebarOpen((open) => !open);
591
+ const sidebarWidthValue = sidebarWidth === void 0 ? void 0 : typeof sidebarWidth === "number" ? `${sidebarWidth}px` : sidebarWidth;
592
+ const shellStyle = sidebarWidthValue ? { ["--app-shell-sidebar-width"]: sidebarWidthValue } : void 0;
593
+ const shellClasses = cx(
594
+ app_shell_default.shell,
595
+ sidebarOpen ? app_shell_default.sidebarOpen : app_shell_default.sidebarCollapsed,
596
+ className
597
+ );
598
+ const sidebarClasses = cx(app_shell_default.sidebar, sidebarOpen && app_shell_default.open, sidebarClassName);
599
+ return /* @__PURE__ */ jsxs5(
600
+ "div",
601
+ {
602
+ className: shellClasses,
603
+ style: shellStyle,
604
+ "data-app-shell": true,
605
+ "data-desktop": isDesktop ? "true" : "false",
606
+ "data-sidebar-open": sidebarOpen ? "true" : "false",
607
+ children: [
608
+ /* @__PURE__ */ jsxs5("header", { className: cx(app_shell_default.topbar, topbarClassName), children: [
609
+ /* @__PURE__ */ jsxs5("div", { className: app_shell_default.topbarLeft, children: [
610
+ /* @__PURE__ */ jsx7(
611
+ "button",
612
+ {
613
+ ref: hamburgerRef,
614
+ type: "button",
615
+ className: app_shell_default.hamburger,
616
+ onClick: toggleSidebar,
617
+ "aria-label": hamburgerLabel,
618
+ "aria-expanded": sidebarOpen,
619
+ "aria-controls": sidebarId,
620
+ children: /* @__PURE__ */ jsx7("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("path", { d: "M3 6h18M3 12h18M3 18h18", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round" }) })
621
+ }
622
+ ),
623
+ brand && (brandHref ? /* @__PURE__ */ jsx7("a", { className: app_shell_default.brand, href: brandHref, children: brand }) : /* @__PURE__ */ jsx7("div", { className: app_shell_default.brand, children: brand })),
624
+ topbarStart && /* @__PURE__ */ jsx7("div", { className: app_shell_default.topbarStart, children: topbarStart })
625
+ ] }),
626
+ /* @__PURE__ */ jsx7("div", { className: app_shell_default.topbarRight, children: topbarEnd })
627
+ ] }),
628
+ !isDesktop && sidebarOpen && /* @__PURE__ */ jsx7("div", { className: app_shell_default.backdrop, onClick: () => setSidebarOpen(false), onTouchStart: () => setSidebarOpen(false), "aria-hidden": "true" }),
629
+ /* @__PURE__ */ jsx7("aside", { ref: sidebarRef, id: sidebarId, className: sidebarClasses, "aria-label": "Sidebar navigation", children: sidebar }),
630
+ /* @__PURE__ */ jsx7("main", { ref: mainRef, className: cx(app_shell_default.main, mainClassName), children })
631
+ ]
632
+ }
633
+ );
634
+ }
635
+
636
+ // src/components/sidebar-nav/sidebar-nav.module.css
637
+ var sidebar_nav_default = {};
638
+
639
+ // src/components/sidebar-nav/SidebarNav.tsx
640
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
641
+ var defaultIsActive = (item, path) => {
642
+ if (item.active !== void 0) return item.active;
643
+ if (!path) return false;
644
+ if (item.href === "/") return path === "/";
645
+ return path.startsWith(item.href);
646
+ };
647
+ function SidebarNav({
648
+ items,
649
+ currentPath,
650
+ getIsActive = defaultIsActive,
651
+ onItemClick,
652
+ className,
653
+ itemClassName
654
+ }) {
655
+ return /* @__PURE__ */ jsx8("nav", { className: cx(sidebar_nav_default.nav, className), "aria-label": "Sidebar navigation", children: items.map((item) => {
656
+ const active = getIsActive(item, currentPath);
657
+ const rel = item.rel ?? (item.target === "_blank" ? "noreferrer" : void 0);
658
+ return /* @__PURE__ */ jsxs6(
659
+ "a",
660
+ {
661
+ className: cx(sidebar_nav_default.item, active && sidebar_nav_default.active, itemClassName),
662
+ href: item.href,
663
+ target: item.target,
664
+ rel,
665
+ "aria-current": active ? "page" : void 0,
666
+ onClick: () => {
667
+ item.onClick?.();
668
+ onItemClick?.(item);
669
+ },
670
+ children: [
671
+ item.icon && /* @__PURE__ */ jsx8("span", { className: sidebar_nav_default.icon, children: item.icon }),
672
+ /* @__PURE__ */ jsx8("span", { className: sidebar_nav_default.label, children: item.label }),
673
+ item.badge && /* @__PURE__ */ jsx8("span", { className: sidebar_nav_default.badge, children: item.badge })
674
+ ]
675
+ },
676
+ item.href
677
+ );
678
+ }) });
679
+ }
680
+ export {
681
+ AppShell,
682
+ Button,
683
+ Card,
684
+ Dropdown,
685
+ Modal,
686
+ Pill,
687
+ SidebarNav,
688
+ ToastProvider,
689
+ cx,
690
+ useToast
691
+ };
692
+ //# sourceMappingURL=index.js.map