create-app-ui 1.0.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.
Files changed (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +117 -0
  3. package/boilerplate/README.md +18 -0
  4. package/boilerplate/react-base/.env.example +1 -0
  5. package/boilerplate/react-base/README.md +3 -0
  6. package/boilerplate/react-base/components.json +19 -0
  7. package/boilerplate/react-base/eslint.config.js +32 -0
  8. package/boilerplate/react-base/index.html +12 -0
  9. package/boilerplate/react-base/package.json +71 -0
  10. package/boilerplate/react-base/postcss.config.js +6 -0
  11. package/boilerplate/react-base/prettier.config.js +6 -0
  12. package/boilerplate/react-base/src/api/axios.ts +20 -0
  13. package/boilerplate/react-base/src/app/store.ts +13 -0
  14. package/boilerplate/react-base/src/components/data-table.tsx +919 -0
  15. package/boilerplate/react-base/src/components/ui/accordion.tsx +44 -0
  16. package/boilerplate/react-base/src/components/ui/alert-dialog.tsx +105 -0
  17. package/boilerplate/react-base/src/components/ui/alert.tsx +40 -0
  18. package/boilerplate/react-base/src/components/ui/avatar.tsx +30 -0
  19. package/boilerplate/react-base/src/components/ui/badge.tsx +27 -0
  20. package/boilerplate/react-base/src/components/ui/bar-chart.tsx +76 -0
  21. package/boilerplate/react-base/src/components/ui/breadcrumb.tsx +87 -0
  22. package/boilerplate/react-base/src/components/ui/button.tsx +34 -0
  23. package/boilerplate/react-base/src/components/ui/calendar.tsx +63 -0
  24. package/boilerplate/react-base/src/components/ui/card.tsx +36 -0
  25. package/boilerplate/react-base/src/components/ui/chart.tsx +280 -0
  26. package/boilerplate/react-base/src/components/ui/checkbox.tsx +51 -0
  27. package/boilerplate/react-base/src/components/ui/context-menu.tsx +173 -0
  28. package/boilerplate/react-base/src/components/ui/date-picker.tsx +42 -0
  29. package/boilerplate/react-base/src/components/ui/dialog.tsx +87 -0
  30. package/boilerplate/react-base/src/components/ui/drawer.tsx +81 -0
  31. package/boilerplate/react-base/src/components/ui/dropdown-menu.tsx +81 -0
  32. package/boilerplate/react-base/src/components/ui/dropdown-types.ts +28 -0
  33. package/boilerplate/react-base/src/components/ui/field.tsx +194 -0
  34. package/boilerplate/react-base/src/components/ui/hover-card.tsx +26 -0
  35. package/boilerplate/react-base/src/components/ui/input-group.tsx +98 -0
  36. package/boilerplate/react-base/src/components/ui/input-otp.tsx +63 -0
  37. package/boilerplate/react-base/src/components/ui/input.tsx +12 -0
  38. package/boilerplate/react-base/src/components/ui/item.tsx +152 -0
  39. package/boilerplate/react-base/src/components/ui/kbd.tsx +13 -0
  40. package/boilerplate/react-base/src/components/ui/label.tsx +14 -0
  41. package/boilerplate/react-base/src/components/ui/line-chart.tsx +65 -0
  42. package/boilerplate/react-base/src/components/ui/menubar.tsx +217 -0
  43. package/boilerplate/react-base/src/components/ui/multi-select-dropdown.tsx +200 -0
  44. package/boilerplate/react-base/src/components/ui/navigation-menu.tsx +120 -0
  45. package/boilerplate/react-base/src/components/ui/pie-chart.tsx +87 -0
  46. package/boilerplate/react-base/src/components/ui/popover.tsx +29 -0
  47. package/boilerplate/react-base/src/components/ui/progress.tsx +19 -0
  48. package/boilerplate/react-base/src/components/ui/radio-group.tsx +36 -0
  49. package/boilerplate/react-base/src/components/ui/scroll-area.tsx +38 -0
  50. package/boilerplate/react-base/src/components/ui/searchable-dropdown.tsx +118 -0
  51. package/boilerplate/react-base/src/components/ui/select.tsx +140 -0
  52. package/boilerplate/react-base/src/components/ui/separator.tsx +20 -0
  53. package/boilerplate/react-base/src/components/ui/sheet.tsx +70 -0
  54. package/boilerplate/react-base/src/components/ui/sidebar.tsx +470 -0
  55. package/boilerplate/react-base/src/components/ui/skeleton.tsx +11 -0
  56. package/boilerplate/react-base/src/components/ui/slider.tsx +23 -0
  57. package/boilerplate/react-base/src/components/ui/sonner.tsx +21 -0
  58. package/boilerplate/react-base/src/components/ui/sparkline.tsx +38 -0
  59. package/boilerplate/react-base/src/components/ui/spinner.tsx +10 -0
  60. package/boilerplate/react-base/src/components/ui/switch.tsx +16 -0
  61. package/boilerplate/react-base/src/components/ui/table.tsx +80 -0
  62. package/boilerplate/react-base/src/components/ui/tabs.tsx +32 -0
  63. package/boilerplate/react-base/src/components/ui/textarea.tsx +12 -0
  64. package/boilerplate/react-base/src/components/ui/toggle-group.tsx +49 -0
  65. package/boilerplate/react-base/src/components/ui/toggle.tsx +33 -0
  66. package/boilerplate/react-base/src/components/ui/tooltip.tsx +23 -0
  67. package/boilerplate/react-base/src/components/ui/typography.tsx +76 -0
  68. package/boilerplate/react-base/src/config/constants.ts +3 -0
  69. package/boilerplate/react-base/src/config/theme.ts +432 -0
  70. package/boilerplate/react-base/src/config/user.ts +52 -0
  71. package/boilerplate/react-base/src/context/theme-provider.tsx +12 -0
  72. package/boilerplate/react-base/src/features/auth/authSlice.ts +19 -0
  73. package/boilerplate/react-base/src/hooks/index.ts +1 -0
  74. package/boilerplate/react-base/src/hooks/use-mobile.ts +17 -0
  75. package/boilerplate/react-base/src/lib/utils.ts +6 -0
  76. package/boilerplate/react-base/src/routes/index.tsx +7 -0
  77. package/boilerplate/react-base/src/styles/globals.css +15 -0
  78. package/boilerplate/react-base/src/vite-env.d.ts +31 -0
  79. package/boilerplate/react-base/tailwind.config.ts +75 -0
  80. package/boilerplate/react-base/tsconfig.app.json +20 -0
  81. package/boilerplate/react-base/tsconfig.json +7 -0
  82. package/boilerplate/react-base/tsconfig.node.json +16 -0
  83. package/boilerplate/react-base/vite.config.ts +12 -0
  84. package/dist/bin/index.js +8 -0
  85. package/dist/src/cli-args.js +52 -0
  86. package/dist/src/generator.js +85 -0
  87. package/dist/src/installer.js +7 -0
  88. package/dist/src/paths.js +61 -0
  89. package/dist/src/prompts.js +79 -0
  90. package/dist/src/replace-placeholders.js +22 -0
  91. package/dist/src/utils.js +16 -0
  92. package/package.json +63 -0
  93. package/templates/admin-portal/README.md +26 -0
  94. package/templates/admin-portal/src/App.tsx +85 -0
  95. package/templates/admin-portal/src/assets/auth-hero.jpg +0 -0
  96. package/templates/admin-portal/src/assets/brand-logo.png +0 -0
  97. package/templates/admin-portal/src/components/app-breadcrumb.tsx +41 -0
  98. package/templates/admin-portal/src/components/app-header.tsx +20 -0
  99. package/templates/admin-portal/src/components/app-sidebar.tsx +78 -0
  100. package/templates/admin-portal/src/components/auth-layout.tsx +66 -0
  101. package/templates/admin-portal/src/components/dashboard-metric-card.tsx +105 -0
  102. package/templates/admin-portal/src/components/data-table.tsx +919 -0
  103. package/templates/admin-portal/src/components/layout-shell.tsx +23 -0
  104. package/templates/admin-portal/src/components/notifications-sheet.tsx +91 -0
  105. package/templates/admin-portal/src/components/sidebar-nav.tsx +164 -0
  106. package/templates/admin-portal/src/components/user-avatar.tsx +26 -0
  107. package/templates/admin-portal/src/components/user-menu.tsx +163 -0
  108. package/templates/admin-portal/src/config/branding.ts +17 -0
  109. package/templates/admin-portal/src/config/chart-data.ts +44 -0
  110. package/templates/admin-portal/src/config/navigation.ts +42 -0
  111. package/templates/admin-portal/src/context/auth-context.tsx +32 -0
  112. package/templates/admin-portal/src/lib/breadcrumbs.ts +58 -0
  113. package/templates/admin-portal/src/main.tsx +18 -0
  114. package/templates/admin-portal/src/pages/components/demo-columns.tsx +170 -0
  115. package/templates/admin-portal/src/pages/components.tsx +1368 -0
  116. package/templates/admin-portal/src/pages/dashboard.tsx +143 -0
  117. package/templates/admin-portal/src/pages/login.tsx +81 -0
  118. package/templates/admin-portal/src/pages/settings/notifications.tsx +31 -0
  119. package/templates/admin-portal/src/pages/settings/profile.tsx +26 -0
  120. package/templates/admin-portal/src/pages/signup.tsx +81 -0
  121. package/templates/admin-portal/src/pages/users.tsx +12 -0
  122. package/templates/admin-portal/tsconfig.json +10 -0
  123. package/templates/blank/README.md +15 -0
  124. package/templates/blank/src/App.tsx +5 -0
  125. package/templates/blank/src/main.tsx +15 -0
  126. package/templates/blank/src/pages/home.tsx +20 -0
  127. package/templates/blank/tsconfig.json +10 -0
  128. package/templates/tsconfig.overlay.base.json +7 -0
@@ -0,0 +1,470 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import { PanelLeft } from "lucide-react";
5
+ import { Button } from "@/components/ui/button";
6
+ import { Sheet, SheetContent } from "@/components/ui/sheet";
7
+ import { useIsMobile } from "@/hooks/use-mobile";
8
+ import { THEME } from "@/config/theme";
9
+ import { cn } from "@/lib/utils";
10
+
11
+ const SIDEBAR_WIDTH = "16rem";
12
+ const SIDEBAR_WIDTH_ICON = "3.5rem";
13
+ const SIDEBAR_KEYBOARD_SHORTCUT = "b";
14
+
15
+ type SidebarContextProps = {
16
+ state: "expanded" | "collapsed";
17
+ open: boolean;
18
+ setOpen: (open: boolean) => void;
19
+ openMobile: boolean;
20
+ setOpenMobile: (open: boolean) => void;
21
+ isMobile: boolean;
22
+ toggleSidebar: () => void;
23
+ };
24
+
25
+ const SidebarContext = React.createContext<SidebarContextProps | null>(null);
26
+
27
+ function useSidebar() {
28
+ const context = React.useContext(SidebarContext);
29
+ if (!context) {
30
+ throw new Error("useSidebar must be used within a SidebarProvider.");
31
+ }
32
+ return context;
33
+ }
34
+
35
+ function SidebarProvider({
36
+ defaultOpen = true,
37
+ open: openProp,
38
+ onOpenChange: setOpenProp,
39
+ className,
40
+ style,
41
+ children,
42
+ ...props
43
+ }: React.ComponentProps<"div"> & {
44
+ defaultOpen?: boolean;
45
+ open?: boolean;
46
+ onOpenChange?: (open: boolean) => void;
47
+ }) {
48
+ const isMobile = useIsMobile();
49
+ const [openMobile, setOpenMobile] = React.useState(false);
50
+ const [_open, _setOpen] = React.useState(defaultOpen);
51
+ const open = openProp ?? _open;
52
+ const setOpen = React.useCallback(
53
+ (value: boolean | ((value: boolean) => boolean)) => {
54
+ const next = typeof value === "function" ? value(open) : value;
55
+ if (setOpenProp) {
56
+ setOpenProp(next);
57
+ } else {
58
+ _setOpen(next);
59
+ }
60
+ },
61
+ [open, setOpenProp],
62
+ );
63
+
64
+ const toggleSidebar = React.useCallback(() => {
65
+ if (isMobile) {
66
+ setOpenMobile((current) => !current);
67
+ return;
68
+ }
69
+ setOpen((current) => !current);
70
+ }, [isMobile, setOpen]);
71
+
72
+ const state = open ? "expanded" : "collapsed";
73
+
74
+ React.useEffect(() => {
75
+ const onKeyDown = (event: KeyboardEvent) => {
76
+ if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
77
+ event.preventDefault();
78
+ toggleSidebar();
79
+ }
80
+ };
81
+ window.addEventListener("keydown", onKeyDown);
82
+ return () => window.removeEventListener("keydown", onKeyDown);
83
+ }, [toggleSidebar]);
84
+
85
+ const contextValue = React.useMemo<SidebarContextProps>(
86
+ () => ({
87
+ state,
88
+ open,
89
+ setOpen,
90
+ isMobile,
91
+ openMobile,
92
+ setOpenMobile,
93
+ toggleSidebar,
94
+ }),
95
+ [state, open, setOpen, isMobile, openMobile, toggleSidebar],
96
+ );
97
+
98
+ return (
99
+ <SidebarContext.Provider value={contextValue}>
100
+ <div
101
+ style={
102
+ {
103
+ "--sidebar-width": SIDEBAR_WIDTH,
104
+ "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
105
+ ...style,
106
+ } as React.CSSProperties
107
+ }
108
+ className={cn("group/sidebar-wrapper flex h-svh w-full overflow-hidden has-[[data-variant=inset]]:bg-sidebar", className)}
109
+ {...props}
110
+ >
111
+ {children}
112
+ </div>
113
+ </SidebarContext.Provider>
114
+ );
115
+ }
116
+
117
+ function Sidebar({
118
+ side = "left",
119
+ variant = "sidebar",
120
+ collapsible = "icon",
121
+ className,
122
+ children,
123
+ ...props
124
+ }: React.ComponentProps<"aside"> & {
125
+ side?: "left" | "right";
126
+ variant?: "sidebar" | "floating" | "inset";
127
+ collapsible?: "offcanvas" | "icon" | "none";
128
+ }) {
129
+ const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
130
+
131
+ if (collapsible === "none") {
132
+ return (
133
+ <aside
134
+ data-side={side}
135
+ className={cn(
136
+ "flex h-svh w-[--sidebar-width] shrink-0 flex-col bg-sidebar text-sidebar-foreground",
137
+ className,
138
+ )}
139
+ {...props}
140
+ >
141
+ {children}
142
+ </aside>
143
+ );
144
+ }
145
+
146
+ if (isMobile) {
147
+ return (
148
+ <Sheet open={openMobile} onOpenChange={setOpenMobile}>
149
+ <SheetContent
150
+ data-sidebar="sidebar"
151
+ data-mobile="true"
152
+ className="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
153
+ style={{ "--sidebar-width": SIDEBAR_WIDTH } as React.CSSProperties}
154
+ >
155
+ <div className="flex h-full w-full flex-col">{children}</div>
156
+ </SheetContent>
157
+ </Sheet>
158
+ );
159
+ }
160
+
161
+ return (
162
+ <div
163
+ data-state={state}
164
+ data-collapsible={state === "collapsed" ? collapsible : ""}
165
+ data-variant={variant}
166
+ data-side={side}
167
+ className={cn("group peer hidden text-sidebar-foreground md:block", className)}
168
+ >
169
+ <div
170
+ aria-hidden
171
+ className={cn(
172
+ "relative h-svh bg-transparent transition-[width] duration-200 ease-linear",
173
+ "w-[--sidebar-width] group-data-[collapsible=icon]:w-[--sidebar-width-icon]",
174
+ variant === "floating" || variant === "inset"
175
+ ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]"
176
+ : "",
177
+ )}
178
+ />
179
+ <aside
180
+ {...props}
181
+ className={cn(
182
+ "fixed inset-y-0 z-30 hidden h-svh transition-[width] duration-200 ease-linear md:flex",
183
+ side === "left" ? "left-0" : "right-0",
184
+ "w-[--sidebar-width] group-data-[collapsible=icon]:w-[--sidebar-width-icon]",
185
+ variant === "floating" || variant === "inset"
186
+ ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]"
187
+ : "",
188
+ )}
189
+ >
190
+ <div
191
+ data-sidebar="sidebar"
192
+ className={cn(
193
+ "flex h-full w-full flex-col bg-sidebar text-sidebar-foreground",
194
+ "border-r border-sidebar-border",
195
+ variant === "floating" && "rounded-lg border shadow-sm",
196
+ variant === "inset" && "rounded-lg border shadow-sm",
197
+ )}
198
+ >
199
+ {children}
200
+ </div>
201
+ </aside>
202
+ </div>
203
+ );
204
+ }
205
+
206
+ function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<typeof Button>) {
207
+ const { toggleSidebar } = useSidebar();
208
+
209
+ return (
210
+ <Button
211
+ variant="outline"
212
+ size="sm"
213
+ className={cn("h-8 w-8 p-0", className)}
214
+ onClick={(event) => {
215
+ onClick?.(event);
216
+ toggleSidebar();
217
+ }}
218
+ {...props}
219
+ >
220
+ <PanelLeft className="h-4 w-4" />
221
+ <span className="sr-only">Toggle sidebar</span>
222
+ </Button>
223
+ );
224
+ }
225
+
226
+ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
227
+ const { toggleSidebar } = useSidebar();
228
+
229
+ return (
230
+ <button
231
+ type="button"
232
+ aria-label="Toggle sidebar"
233
+ tabIndex={-1}
234
+ onClick={toggleSidebar}
235
+ title="Toggle sidebar"
236
+ className={cn(
237
+ "absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex",
238
+ "[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize",
239
+ "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
240
+ "group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
241
+ "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
242
+ "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
243
+ className,
244
+ )}
245
+ {...props}
246
+ />
247
+ );
248
+ }
249
+
250
+ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
251
+ return (
252
+ <main
253
+ className={cn(
254
+ "relative flex h-svh min-h-0 min-w-0 flex-1 flex-col overflow-hidden bg-background",
255
+ "peer-data-[variant=inset]:m-2 peer-data-[variant=inset]:ml-0 peer-data-[variant=inset]:h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
256
+ className,
257
+ )}
258
+ {...props}
259
+ />
260
+ );
261
+ }
262
+
263
+ function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
264
+ return <div className={cn("flex flex-col gap-2 p-2", className)} {...props} />;
265
+ }
266
+
267
+ function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
268
+ return <div className={cn("mt-auto flex flex-col gap-2 p-2", className)} {...props} />;
269
+ }
270
+
271
+ function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
272
+ return (
273
+ <div
274
+ className={cn(
275
+ "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
276
+ className,
277
+ )}
278
+ {...props}
279
+ />
280
+ );
281
+ }
282
+
283
+ function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
284
+ return <div className={cn("relative flex w-full min-w-0 flex-col p-2", className)} {...props} />;
285
+ }
286
+
287
+ function SidebarGroupLabel({
288
+ className,
289
+ asChild = false,
290
+ ...props
291
+ }: React.ComponentProps<"div"> & { asChild?: boolean }) {
292
+ const Comp = asChild ? Slot : "div";
293
+
294
+ return (
295
+ <Comp
296
+ className={cn(
297
+ "flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2",
298
+ "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
299
+ className,
300
+ )}
301
+ {...props}
302
+ />
303
+ );
304
+ }
305
+
306
+ function SidebarGroupContent({ className, ...props }: React.ComponentProps<"div">) {
307
+ return <div className={cn("w-full text-sm", className)} {...props} />;
308
+ }
309
+
310
+ function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
311
+ return <ul className={cn("flex w-full min-w-0 flex-col gap-1", className)} {...props} />;
312
+ }
313
+
314
+ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
315
+ return <li className={cn("group/menu-item relative", className)} {...props} />;
316
+ }
317
+
318
+ const sidebarMenuButtonVariants = cva(
319
+ `peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 ${THEME.classes.sidebarMenuActive} group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0`,
320
+ {
321
+ variants: {
322
+ variant: {
323
+ default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
324
+ outline:
325
+ "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]",
326
+ },
327
+ size: {
328
+ default: "h-8 text-sm",
329
+ sm: "h-7 text-xs",
330
+ lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0",
331
+ },
332
+ },
333
+ defaultVariants: {
334
+ variant: "default",
335
+ size: "default",
336
+ },
337
+ },
338
+ );
339
+
340
+ const SidebarMenuButton = React.forwardRef<
341
+ HTMLButtonElement,
342
+ React.ComponentProps<"button"> &
343
+ VariantProps<typeof sidebarMenuButtonVariants> & {
344
+ asChild?: boolean;
345
+ isActive?: boolean;
346
+ tooltip?: string;
347
+ }
348
+ >(({ asChild = false, isActive = false, variant = "default", size = "default", tooltip, className, ...props }, ref) => {
349
+ const Comp = asChild ? Slot : "button";
350
+ const { state } = useSidebar();
351
+ const title = state === "collapsed" ? tooltip : undefined;
352
+
353
+ return (
354
+ <Comp
355
+ ref={ref}
356
+ data-active={isActive}
357
+ data-size={size}
358
+ title={title}
359
+ type={asChild ? undefined : "button"}
360
+ className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
361
+ {...props}
362
+ />
363
+ );
364
+ });
365
+ SidebarMenuButton.displayName = "SidebarMenuButton";
366
+
367
+ function SidebarMenuBadge({ className, ...props }: React.ComponentProps<"div">) {
368
+ return (
369
+ <div
370
+ className={cn(
371
+ "pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground",
372
+ "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground",
373
+ "group-data-[collapsible=icon]:hidden",
374
+ className,
375
+ )}
376
+ {...props}
377
+ />
378
+ );
379
+ }
380
+
381
+ function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
382
+ return (
383
+ <ul
384
+ data-sidebar="menu-sub"
385
+ className={cn(
386
+ "mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5",
387
+ "group-data-[collapsible=icon]:hidden",
388
+ className,
389
+ )}
390
+ {...props}
391
+ />
392
+ );
393
+ }
394
+
395
+ function SidebarMenuSubItem({ className, ...props }: React.ComponentProps<"li">) {
396
+ return <li data-sidebar="menu-sub-item" className={cn("group/menu-sub-item relative", className)} {...props} />;
397
+ }
398
+
399
+ function SidebarMenuSubButton({
400
+ asChild = false,
401
+ size = "md",
402
+ isActive = false,
403
+ className,
404
+ ...props
405
+ }: React.ComponentProps<"a"> & {
406
+ asChild?: boolean;
407
+ size?: "sm" | "md";
408
+ isActive?: boolean;
409
+ }) {
410
+ const Comp = asChild ? Slot : "a";
411
+
412
+ return (
413
+ <Comp
414
+ data-active={isActive}
415
+ data-size={size}
416
+ className={cn(
417
+ "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
418
+ THEME.classes.sidebarMenuActive,
419
+ size === "sm" && "text-xs",
420
+ size === "md" && "text-sm",
421
+ className,
422
+ )}
423
+ {...props}
424
+ />
425
+ );
426
+ }
427
+
428
+ function SidebarMenuCollapsible({
429
+ open,
430
+ className,
431
+ children,
432
+ ...props
433
+ }: React.ComponentProps<"div"> & { open: boolean }) {
434
+ return (
435
+ <div
436
+ data-state={open ? "open" : "closed"}
437
+ className={cn(
438
+ "grid transition-[grid-template-rows] duration-200 ease-linear group-data-[collapsible=icon]:hidden",
439
+ open ? "grid-rows-[1fr]" : "grid-rows-[0fr]",
440
+ className,
441
+ )}
442
+ {...props}
443
+ >
444
+ <div className="overflow-hidden">{children}</div>
445
+ </div>
446
+ );
447
+ }
448
+
449
+ export {
450
+ Sidebar,
451
+ SidebarContent,
452
+ SidebarFooter,
453
+ SidebarGroup,
454
+ SidebarGroupContent,
455
+ SidebarGroupLabel,
456
+ SidebarHeader,
457
+ SidebarInset,
458
+ SidebarMenu,
459
+ SidebarMenuBadge,
460
+ SidebarMenuButton,
461
+ SidebarMenuCollapsible,
462
+ SidebarMenuItem,
463
+ SidebarMenuSub,
464
+ SidebarMenuSubButton,
465
+ SidebarMenuSubItem,
466
+ SidebarProvider,
467
+ SidebarRail,
468
+ SidebarTrigger,
469
+ useSidebar,
470
+ };
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+ import { THEME } from "@/config/theme";
3
+ import { cn } from "@/lib/utils";
4
+
5
+ function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
6
+ return (
7
+ <div className={cn("animate-pulse rounded-md", THEME.classes.skeleton, className)} {...props} />
8
+ );
9
+ }
10
+
11
+ export { Skeleton };
@@ -0,0 +1,23 @@
1
+ import * as React from "react";
2
+ import * as SliderPrimitive from "@radix-ui/react-slider";
3
+ import { ui } from "@/config/theme";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ const Slider = React.forwardRef<
7
+ React.ElementRef<typeof SliderPrimitive.Root>,
8
+ React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
9
+ >(({ className, ...props }, ref) => (
10
+ <SliderPrimitive.Root
11
+ ref={ref}
12
+ className={cn("relative flex w-full touch-none select-none items-center", className)}
13
+ {...props}
14
+ >
15
+ <SliderPrimitive.Track className={ui("sliderTrack")}>
16
+ <SliderPrimitive.Range className={ui("sliderRange")} />
17
+ </SliderPrimitive.Track>
18
+ <SliderPrimitive.Thumb className={ui("sliderThumb")} />
19
+ </SliderPrimitive.Root>
20
+ ));
21
+ Slider.displayName = SliderPrimitive.Root.displayName;
22
+
23
+ export { Slider };
@@ -0,0 +1,21 @@
1
+ import { ui } from "@/config/theme";
2
+ import { Toaster as Sonner, type ToasterProps } from "sonner";
3
+
4
+ function Toaster({ ...props }: ToasterProps) {
5
+ return (
6
+ <Sonner
7
+ className="toaster group"
8
+ toastOptions={{
9
+ classNames: {
10
+ toast: ui("sonnerToast"),
11
+ description: ui("sonnerDescription"),
12
+ actionButton: ui("sonnerAction"),
13
+ cancelButton: ui("sonnerCancel"),
14
+ },
15
+ }}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+
21
+ export { Toaster };
@@ -0,0 +1,38 @@
1
+ import * as React from "react";
2
+ import { Area, AreaChart, ResponsiveContainer } from "recharts";
3
+ import { chartColor } from "@/config/theme";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ type SparklineProps = {
7
+ data: { value: number }[];
8
+ className?: string;
9
+ color?: string;
10
+ };
11
+
12
+ export function Sparkline({ data, className, color = chartColor(1) }: SparklineProps) {
13
+ const gradientId = React.useId().replace(/:/g, "");
14
+
15
+ return (
16
+ <div className={cn("h-12 w-28", className)}>
17
+ <ResponsiveContainer width="100%" height="100%">
18
+ <AreaChart data={data} margin={{ top: 4, right: 0, left: 0, bottom: 0 }}>
19
+ <defs>
20
+ <linearGradient id={gradientId} x1="0" y1="0" x2="0" y2="1">
21
+ <stop offset="0%" stopColor={color} stopOpacity={0.35} />
22
+ <stop offset="100%" stopColor={color} stopOpacity={0} />
23
+ </linearGradient>
24
+ </defs>
25
+ <Area
26
+ type="monotone"
27
+ dataKey="value"
28
+ stroke={color}
29
+ strokeWidth={2}
30
+ fill={`url(#${gradientId})`}
31
+ dot={false}
32
+ isAnimationActive={false}
33
+ />
34
+ </AreaChart>
35
+ </ResponsiveContainer>
36
+ </div>
37
+ );
38
+ }
@@ -0,0 +1,10 @@
1
+ import { Loader2 } from "lucide-react";
2
+ import * as React from "react";
3
+ import { ui } from "@/config/theme";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ function Spinner({ className, ...props }: React.ComponentProps<typeof Loader2>) {
7
+ return <Loader2 role="status" aria-label="Loading" className={cn(ui("spinner"), className)} {...props} />;
8
+ }
9
+
10
+ export { Spinner };
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ import * as SwitchPrimitives from "@radix-ui/react-switch";
3
+ import { ui } from "@/config/theme";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ const Switch = React.forwardRef<
7
+ React.ElementRef<typeof SwitchPrimitives.Root>,
8
+ React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
9
+ >(({ className, ...props }, ref) => (
10
+ <SwitchPrimitives.Root className={cn(ui("switchRoot"), className)} {...props} ref={ref}>
11
+ <SwitchPrimitives.Thumb className={ui("switchThumb")} />
12
+ </SwitchPrimitives.Root>
13
+ ));
14
+ Switch.displayName = SwitchPrimitives.Root.displayName;
15
+
16
+ export { Switch };
@@ -0,0 +1,80 @@
1
+ import * as React from "react";
2
+ import { ui } from "@/config/theme";
3
+ import { cn } from "@/lib/utils";
4
+
5
+ const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
6
+ ({ className, ...props }, ref) => (
7
+ <div className="relative w-full overflow-auto">
8
+ <table ref={ref} className={cn("w-full caption-bottom text-sm", className)} {...props} />
9
+ </div>
10
+ ),
11
+ );
12
+ Table.displayName = "Table";
13
+
14
+ const TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
15
+ ({ className, ...props }, ref) => <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />,
16
+ );
17
+ TableHeader.displayName = "TableHeader";
18
+
19
+ const TableBody = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
20
+ ({ className, ...props }, ref) => (
21
+ <tbody ref={ref} className={cn("[&_tr:last-child]:border-0", className)} {...props} />
22
+ ),
23
+ );
24
+ TableBody.displayName = "TableBody";
25
+
26
+ const TableFooter = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
27
+ ({ className, ...props }, ref) => (
28
+ <tfoot
29
+ ref={ref}
30
+ className={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className)}
31
+ {...props}
32
+ />
33
+ ),
34
+ );
35
+ TableFooter.displayName = "TableFooter";
36
+
37
+ const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
38
+ ({ className, ...props }, ref) => (
39
+ <tr
40
+ ref={ref}
41
+ className={cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className)}
42
+ {...props}
43
+ />
44
+ ),
45
+ );
46
+ TableRow.displayName = "TableRow";
47
+
48
+ const TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<HTMLTableCellElement>>(
49
+ ({ className, ...props }, ref) => (
50
+ <th
51
+ ref={ref}
52
+ className={cn(
53
+ ui("tableHeader"),
54
+ className,
55
+ )}
56
+ {...props}
57
+ />
58
+ ),
59
+ );
60
+ TableHead.displayName = "TableHead";
61
+
62
+ const TableCell = React.forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(
63
+ ({ className, ...props }, ref) => (
64
+ <td
65
+ ref={ref}
66
+ className={cn(ui("tableCell"), className)}
67
+ {...props}
68
+ />
69
+ ),
70
+ );
71
+ TableCell.displayName = "TableCell";
72
+
73
+ const TableCaption = React.forwardRef<HTMLTableCaptionElement, React.HTMLAttributes<HTMLTableCaptionElement>>(
74
+ ({ className, ...props }, ref) => (
75
+ <caption ref={ref} className={cn("mt-4", ui("typographyMuted"), className)} {...props} />
76
+ ),
77
+ );
78
+ TableCaption.displayName = "TableCaption";
79
+
80
+ export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow };