@shellui/core 0.0.4

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 (247) hide show
  1. package/README.md +17 -0
  2. package/dist/ContentView-CZG-ro_B.js +146 -0
  3. package/dist/ContentView-CZG-ro_B.js.map +1 -0
  4. package/dist/CookiePreferencesView-MhO9FO-4.js +213 -0
  5. package/dist/CookiePreferencesView-MhO9FO-4.js.map +1 -0
  6. package/dist/DefaultLayout-Dbb3uJED.js +394 -0
  7. package/dist/DefaultLayout-Dbb3uJED.js.map +1 -0
  8. package/dist/FullscreenLayout-1SgPHWw-.js +30 -0
  9. package/dist/FullscreenLayout-1SgPHWw-.js.map +1 -0
  10. package/dist/HomeView-DYU-O_Il.js +21 -0
  11. package/dist/HomeView-DYU-O_Il.js.map +1 -0
  12. package/dist/NotFoundView-CeYjJNg0.js +52 -0
  13. package/dist/NotFoundView-CeYjJNg0.js.map +1 -0
  14. package/dist/OverlayShell-pzbqQW25.js +642 -0
  15. package/dist/OverlayShell-pzbqQW25.js.map +1 -0
  16. package/dist/SettingsView-Bndrta44.js +2207 -0
  17. package/dist/SettingsView-Bndrta44.js.map +1 -0
  18. package/dist/ViewRoute-ChSPabOy.js +32 -0
  19. package/dist/ViewRoute-ChSPabOy.js.map +1 -0
  20. package/dist/WindowsLayout-CXGNPKoY.js +633 -0
  21. package/dist/WindowsLayout-CXGNPKoY.js.map +1 -0
  22. package/dist/app.d.ts +3 -0
  23. package/dist/app.d.ts.map +1 -0
  24. package/dist/components/ContentView.d.ts +10 -0
  25. package/dist/components/ContentView.d.ts.map +1 -0
  26. package/dist/components/HomeView.d.ts +2 -0
  27. package/dist/components/HomeView.d.ts.map +1 -0
  28. package/dist/components/LoadingOverlay.d.ts +2 -0
  29. package/dist/components/LoadingOverlay.d.ts.map +1 -0
  30. package/dist/components/NotFoundView.d.ts +2 -0
  31. package/dist/components/NotFoundView.d.ts.map +1 -0
  32. package/dist/components/RouteErrorBoundary.d.ts +2 -0
  33. package/dist/components/RouteErrorBoundary.d.ts.map +1 -0
  34. package/dist/components/ViewRoute.d.ts +7 -0
  35. package/dist/components/ViewRoute.d.ts.map +1 -0
  36. package/dist/components/ui/alert-dialog.d.ts +32 -0
  37. package/dist/components/ui/alert-dialog.d.ts.map +1 -0
  38. package/dist/components/ui/breadcrumb.d.ts +20 -0
  39. package/dist/components/ui/breadcrumb.d.ts.map +1 -0
  40. package/dist/components/ui/button-group.d.ts +7 -0
  41. package/dist/components/ui/button-group.d.ts.map +1 -0
  42. package/dist/components/ui/button.d.ts +12 -0
  43. package/dist/components/ui/button.d.ts.map +1 -0
  44. package/dist/components/ui/dialog.d.ts +24 -0
  45. package/dist/components/ui/dialog.d.ts.map +1 -0
  46. package/dist/components/ui/drawer.d.ts +38 -0
  47. package/dist/components/ui/drawer.d.ts.map +1 -0
  48. package/dist/components/ui/select.d.ts +5 -0
  49. package/dist/components/ui/select.d.ts.map +1 -0
  50. package/dist/components/ui/sidebar.d.ts +46 -0
  51. package/dist/components/ui/sidebar.d.ts.map +1 -0
  52. package/dist/components/ui/sonner.d.ts +6 -0
  53. package/dist/components/ui/sonner.d.ts.map +1 -0
  54. package/dist/components/ui/switch.d.ts +8 -0
  55. package/dist/components/ui/switch.d.ts.map +1 -0
  56. package/dist/constants/urls.d.ts +6 -0
  57. package/dist/constants/urls.d.ts.map +1 -0
  58. package/dist/constants/urls.js +8 -0
  59. package/dist/constants/urls.js.map +1 -0
  60. package/dist/features/alertDialog/DialogContext.d.ts +12 -0
  61. package/dist/features/alertDialog/DialogContext.d.ts.map +1 -0
  62. package/dist/features/config/ConfigProvider.d.ts +15 -0
  63. package/dist/features/config/ConfigProvider.d.ts.map +1 -0
  64. package/dist/features/config/types.d.ts +177 -0
  65. package/dist/features/config/types.d.ts.map +1 -0
  66. package/dist/features/config/useConfig.d.ts +8 -0
  67. package/dist/features/config/useConfig.d.ts.map +1 -0
  68. package/dist/features/cookieConsent/CookieConsentModal.d.ts +6 -0
  69. package/dist/features/cookieConsent/CookieConsentModal.d.ts.map +1 -0
  70. package/dist/features/cookieConsent/CookiePreferencesView.d.ts +2 -0
  71. package/dist/features/cookieConsent/CookiePreferencesView.d.ts.map +1 -0
  72. package/dist/features/cookieConsent/cookieConsent.d.ts +22 -0
  73. package/dist/features/cookieConsent/cookieConsent.d.ts.map +1 -0
  74. package/dist/features/cookieConsent/useCookieConsent.d.ts +15 -0
  75. package/dist/features/cookieConsent/useCookieConsent.d.ts.map +1 -0
  76. package/dist/features/drawer/DrawerContext.d.ts +24 -0
  77. package/dist/features/drawer/DrawerContext.d.ts.map +1 -0
  78. package/dist/features/layouts/AppLayout.d.ts +12 -0
  79. package/dist/features/layouts/AppLayout.d.ts.map +1 -0
  80. package/dist/features/layouts/DefaultLayout.d.ts +10 -0
  81. package/dist/features/layouts/DefaultLayout.d.ts.map +1 -0
  82. package/dist/features/layouts/FullscreenLayout.d.ts +9 -0
  83. package/dist/features/layouts/FullscreenLayout.d.ts.map +1 -0
  84. package/dist/features/layouts/LayoutProviders.d.ts +9 -0
  85. package/dist/features/layouts/LayoutProviders.d.ts.map +1 -0
  86. package/dist/features/layouts/OverlayShell.d.ts +10 -0
  87. package/dist/features/layouts/OverlayShell.d.ts.map +1 -0
  88. package/dist/features/layouts/WindowsLayout.d.ts +24 -0
  89. package/dist/features/layouts/WindowsLayout.d.ts.map +1 -0
  90. package/dist/features/layouts/utils.d.ts +16 -0
  91. package/dist/features/layouts/utils.d.ts.map +1 -0
  92. package/dist/features/modal/ModalContext.d.ts +20 -0
  93. package/dist/features/modal/ModalContext.d.ts.map +1 -0
  94. package/dist/features/sentry/initSentry.d.ts +14 -0
  95. package/dist/features/sentry/initSentry.d.ts.map +1 -0
  96. package/dist/features/settings/SettingsContext.d.ts +10 -0
  97. package/dist/features/settings/SettingsContext.d.ts.map +1 -0
  98. package/dist/features/settings/SettingsIcons.d.ts +22 -0
  99. package/dist/features/settings/SettingsIcons.d.ts.map +1 -0
  100. package/dist/features/settings/SettingsProvider.d.ts +5 -0
  101. package/dist/features/settings/SettingsProvider.d.ts.map +1 -0
  102. package/dist/features/settings/SettingsRoutes.d.ts +7 -0
  103. package/dist/features/settings/SettingsRoutes.d.ts.map +1 -0
  104. package/dist/features/settings/SettingsView.d.ts +2 -0
  105. package/dist/features/settings/SettingsView.d.ts.map +1 -0
  106. package/dist/features/settings/components/Advanced.d.ts +2 -0
  107. package/dist/features/settings/components/Advanced.d.ts.map +1 -0
  108. package/dist/features/settings/components/Appearance.d.ts +2 -0
  109. package/dist/features/settings/components/Appearance.d.ts.map +1 -0
  110. package/dist/features/settings/components/DataPrivacy.d.ts +2 -0
  111. package/dist/features/settings/components/DataPrivacy.d.ts.map +1 -0
  112. package/dist/features/settings/components/Develop.d.ts +2 -0
  113. package/dist/features/settings/components/Develop.d.ts.map +1 -0
  114. package/dist/features/settings/components/LanguageAndRegion.d.ts +2 -0
  115. package/dist/features/settings/components/LanguageAndRegion.d.ts.map +1 -0
  116. package/dist/features/settings/components/ServiceWorker.d.ts +2 -0
  117. package/dist/features/settings/components/ServiceWorker.d.ts.map +1 -0
  118. package/dist/features/settings/components/UpdateApp.d.ts +2 -0
  119. package/dist/features/settings/components/UpdateApp.d.ts.map +1 -0
  120. package/dist/features/settings/components/develop/DialogTestButtons.d.ts +2 -0
  121. package/dist/features/settings/components/develop/DialogTestButtons.d.ts.map +1 -0
  122. package/dist/features/settings/components/develop/DrawerTestButtons.d.ts +2 -0
  123. package/dist/features/settings/components/develop/DrawerTestButtons.d.ts.map +1 -0
  124. package/dist/features/settings/components/develop/ModalTestButtons.d.ts +2 -0
  125. package/dist/features/settings/components/develop/ModalTestButtons.d.ts.map +1 -0
  126. package/dist/features/settings/components/develop/ToastTestButtons.d.ts +2 -0
  127. package/dist/features/settings/components/develop/ToastTestButtons.d.ts.map +1 -0
  128. package/dist/features/settings/hooks/useSettings.d.ts +2 -0
  129. package/dist/features/settings/hooks/useSettings.d.ts.map +1 -0
  130. package/dist/features/sonner/SonnerContext.d.ts +29 -0
  131. package/dist/features/sonner/SonnerContext.d.ts.map +1 -0
  132. package/dist/features/theme/ThemeProvider.d.ts +11 -0
  133. package/dist/features/theme/ThemeProvider.d.ts.map +1 -0
  134. package/dist/features/theme/themes.d.ts +114 -0
  135. package/dist/features/theme/themes.d.ts.map +1 -0
  136. package/dist/features/theme/useTheme.d.ts +10 -0
  137. package/dist/features/theme/useTheme.d.ts.map +1 -0
  138. package/dist/i18n/I18nProvider.d.ts +9 -0
  139. package/dist/i18n/I18nProvider.d.ts.map +1 -0
  140. package/dist/i18n/config.d.ts +23 -0
  141. package/dist/i18n/config.d.ts.map +1 -0
  142. package/dist/i18n/translations/en/common.json.d.ts +19 -0
  143. package/dist/i18n/translations/en/cookieConsent.json.d.ts +53 -0
  144. package/dist/i18n/translations/en/settings.json.d.ts +358 -0
  145. package/dist/i18n/translations/fr/common.json.d.ts +19 -0
  146. package/dist/i18n/translations/fr/cookieConsent.json.d.ts +53 -0
  147. package/dist/i18n/translations/fr/settings.json.d.ts +358 -0
  148. package/dist/index-lmRk5L6z.js +2160 -0
  149. package/dist/index-lmRk5L6z.js.map +1 -0
  150. package/dist/index.d.ts +7 -0
  151. package/dist/index.d.ts.map +1 -0
  152. package/dist/index.js +12 -0
  153. package/dist/index.js.map +1 -0
  154. package/dist/lib/utils.d.ts +3 -0
  155. package/dist/lib/utils.d.ts.map +1 -0
  156. package/dist/lib/z-index.d.ts +29 -0
  157. package/dist/lib/z-index.d.ts.map +1 -0
  158. package/dist/router/router.d.ts +3 -0
  159. package/dist/router/router.d.ts.map +1 -0
  160. package/dist/router/routes.d.ts +4 -0
  161. package/dist/router/routes.d.ts.map +1 -0
  162. package/dist/sidebar-ClIeZ2zb.js +303 -0
  163. package/dist/sidebar-ClIeZ2zb.js.map +1 -0
  164. package/dist/style.css +1 -0
  165. package/dist/switch-8SzUJz7Q.js +44 -0
  166. package/dist/switch-8SzUJz7Q.js.map +1 -0
  167. package/dist/types.js +2 -0
  168. package/dist/types.js.map +1 -0
  169. package/package.json +93 -0
  170. package/postcss.config.js +6 -0
  171. package/src/app.tsx +119 -0
  172. package/src/components/ContentView.tsx +258 -0
  173. package/src/components/HomeView.tsx +19 -0
  174. package/src/components/LoadingOverlay.tsx +12 -0
  175. package/src/components/NotFoundView.tsx +84 -0
  176. package/src/components/RouteErrorBoundary.tsx +95 -0
  177. package/src/components/ViewRoute.tsx +47 -0
  178. package/src/components/ui/alert-dialog.tsx +181 -0
  179. package/src/components/ui/breadcrumb.tsx +155 -0
  180. package/src/components/ui/button-group.tsx +52 -0
  181. package/src/components/ui/button.tsx +51 -0
  182. package/src/components/ui/dialog.tsx +160 -0
  183. package/src/components/ui/drawer.tsx +200 -0
  184. package/src/components/ui/select.tsx +24 -0
  185. package/src/components/ui/sidebar.tsx +406 -0
  186. package/src/components/ui/sonner.tsx +36 -0
  187. package/src/components/ui/switch.tsx +45 -0
  188. package/src/constants/urls.ts +4 -0
  189. package/src/features/alertDialog/DialogContext.tsx +468 -0
  190. package/src/features/config/ConfigProvider.ts +96 -0
  191. package/src/features/config/types.ts +195 -0
  192. package/src/features/config/useConfig.ts +15 -0
  193. package/src/features/cookieConsent/CookieConsentModal.tsx +122 -0
  194. package/src/features/cookieConsent/CookiePreferencesView.tsx +328 -0
  195. package/src/features/cookieConsent/cookieConsent.ts +84 -0
  196. package/src/features/cookieConsent/useCookieConsent.ts +39 -0
  197. package/src/features/drawer/DrawerContext.tsx +116 -0
  198. package/src/features/layouts/AppLayout.tsx +63 -0
  199. package/src/features/layouts/DefaultLayout.tsx +625 -0
  200. package/src/features/layouts/FullscreenLayout.tsx +55 -0
  201. package/src/features/layouts/LayoutProviders.tsx +20 -0
  202. package/src/features/layouts/OverlayShell.tsx +171 -0
  203. package/src/features/layouts/WindowsLayout.tsx +860 -0
  204. package/src/features/layouts/utils.ts +99 -0
  205. package/src/features/modal/ModalContext.tsx +112 -0
  206. package/src/features/sentry/initSentry.ts +72 -0
  207. package/src/features/settings/SettingsContext.tsx +19 -0
  208. package/src/features/settings/SettingsIcons.tsx +452 -0
  209. package/src/features/settings/SettingsProvider.tsx +341 -0
  210. package/src/features/settings/SettingsRoutes.tsx +66 -0
  211. package/src/features/settings/SettingsView.tsx +327 -0
  212. package/src/features/settings/components/Advanced.tsx +128 -0
  213. package/src/features/settings/components/Appearance.tsx +306 -0
  214. package/src/features/settings/components/DataPrivacy.tsx +142 -0
  215. package/src/features/settings/components/Develop.tsx +174 -0
  216. package/src/features/settings/components/LanguageAndRegion.tsx +329 -0
  217. package/src/features/settings/components/ServiceWorker.tsx +363 -0
  218. package/src/features/settings/components/UpdateApp.tsx +206 -0
  219. package/src/features/settings/components/develop/DialogTestButtons.tsx +137 -0
  220. package/src/features/settings/components/develop/DrawerTestButtons.tsx +67 -0
  221. package/src/features/settings/components/develop/ModalTestButtons.tsx +30 -0
  222. package/src/features/settings/components/develop/ToastTestButtons.tsx +179 -0
  223. package/src/features/settings/hooks/useSettings.tsx +10 -0
  224. package/src/features/sonner/SonnerContext.tsx +286 -0
  225. package/src/features/theme/ThemeProvider.tsx +16 -0
  226. package/src/features/theme/themes.ts +561 -0
  227. package/src/features/theme/useTheme.tsx +71 -0
  228. package/src/i18n/I18nProvider.tsx +32 -0
  229. package/src/i18n/config.ts +107 -0
  230. package/src/i18n/translations/en/common.json +16 -0
  231. package/src/i18n/translations/en/cookieConsent.json +50 -0
  232. package/src/i18n/translations/en/settings.json +355 -0
  233. package/src/i18n/translations/fr/common.json +16 -0
  234. package/src/i18n/translations/fr/cookieConsent.json +50 -0
  235. package/src/i18n/translations/fr/settings.json +355 -0
  236. package/src/index.css +412 -0
  237. package/src/index.html +100 -0
  238. package/src/index.ts +31 -0
  239. package/src/lib/utils.ts +6 -0
  240. package/src/lib/z-index.ts +29 -0
  241. package/src/main.tsx +26 -0
  242. package/src/router/router.tsx +8 -0
  243. package/src/router/routes.tsx +115 -0
  244. package/src/service-worker/register.ts +1199 -0
  245. package/src/service-worker/sw-dev.ts +87 -0
  246. package/src/service-worker/sw.ts +105 -0
  247. package/tailwind.config.js +60 -0
@@ -0,0 +1,200 @@
1
+ 'use client';
2
+
3
+ import {
4
+ forwardRef,
5
+ type ComponentProps,
6
+ type ComponentPropsWithoutRef,
7
+ type ComponentRef,
8
+ type ReactNode,
9
+ type CSSProperties,
10
+ type HTMLAttributes,
11
+ } from 'react';
12
+ import { Drawer as VaulDrawer } from 'vaul';
13
+ import { cn } from '@/lib/utils';
14
+ import { Z_INDEX } from '@/lib/z-index';
15
+
16
+ export type DrawerDirection = 'top' | 'bottom' | 'left' | 'right';
17
+
18
+ const Drawer = ({
19
+ open,
20
+ onOpenChange,
21
+ direction = 'right',
22
+ ...props
23
+ }: ComponentProps<typeof VaulDrawer.Root> & {
24
+ direction?: DrawerDirection;
25
+ }) => (
26
+ <VaulDrawer.Root
27
+ open={open}
28
+ onOpenChange={onOpenChange}
29
+ direction={direction}
30
+ {...props}
31
+ />
32
+ );
33
+ Drawer.displayName = 'Drawer';
34
+
35
+ const DrawerTrigger = VaulDrawer.Trigger;
36
+ DrawerTrigger.displayName = 'DrawerTrigger';
37
+
38
+ const DrawerPortal = VaulDrawer.Portal;
39
+ (DrawerPortal as unknown as { displayName: string }).displayName = 'DrawerPortal';
40
+
41
+ const DrawerOverlay = forwardRef<
42
+ ComponentRef<typeof VaulDrawer.Overlay>,
43
+ ComponentPropsWithoutRef<typeof VaulDrawer.Overlay>
44
+ >(({ className, ...props }, ref) => (
45
+ <VaulDrawer.Overlay
46
+ ref={ref}
47
+ data-drawer-overlay
48
+ className={cn('fixed inset-0 bg-[hsl(var(--background)/0.8)] backdrop-blur-[1px]', className)}
49
+ style={{ zIndex: Z_INDEX.DRAWER_OVERLAY }}
50
+ {...props}
51
+ />
52
+ ));
53
+ DrawerOverlay.displayName = VaulDrawer.Overlay.displayName;
54
+
55
+ /** Base layout classes per direction — max dimension is applied via style so size prop can override. */
56
+ const drawerContentByDirection: Record<DrawerDirection, string> = {
57
+ bottom: 'fixed inset-x-0 bottom-0 mt-24 flex h-auto flex-col border border-border bg-background',
58
+ top: 'fixed inset-x-0 top-0 mb-24 flex h-auto flex-col border border-border bg-background',
59
+ left: 'fixed inset-y-0 left-0 mr-24 flex h-full w-auto flex-col border border-border bg-background',
60
+ right:
61
+ 'fixed inset-y-0 right-0 ml-24 flex h-full w-auto flex-col border border-border bg-background',
62
+ };
63
+
64
+ interface DrawerContentProps extends Omit<
65
+ ComponentPropsWithoutRef<typeof VaulDrawer.Content>,
66
+ 'direction'
67
+ > {
68
+ direction?: DrawerDirection;
69
+ /** CSS length: height for top/bottom (e.g. "80vh", "400px"), width for left/right (e.g. "50vw", "320px") */
70
+ size?: string | null;
71
+ className?: string;
72
+ children?: ReactNode;
73
+ style?: CSSProperties;
74
+ }
75
+
76
+ const DrawerContent = forwardRef<ComponentRef<typeof VaulDrawer.Content>, DrawerContentProps>(
77
+ ({ className, direction = 'right', size, children, style, ...props }, ref) => {
78
+ const pos: DrawerDirection = direction;
79
+ const isVertical = pos === 'top' || pos === 'bottom';
80
+ // Set dimension via inline style; use both width/height and max so Vaul/defaults don't override.
81
+ const effectiveSize = size?.trim() || (isVertical ? '80vh' : '80vw');
82
+ const sizeStyle = isVertical
83
+ ? { height: effectiveSize, maxHeight: effectiveSize }
84
+ : { width: effectiveSize, maxWidth: effectiveSize };
85
+ return (
86
+ <DrawerPortal>
87
+ <DrawerOverlay />
88
+ <VaulDrawer.Content
89
+ ref={ref}
90
+ data-drawer-content
91
+ className={cn('outline-none', drawerContentByDirection[pos], className)}
92
+ style={{
93
+ backgroundColor: 'hsl(var(--background))',
94
+ zIndex: Z_INDEX.DRAWER_CONTENT,
95
+ ...sizeStyle,
96
+ ...style,
97
+ }}
98
+ {...props}
99
+ >
100
+ {children}
101
+ <VaulDrawer.Close
102
+ className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground cursor-pointer"
103
+ aria-label="Close"
104
+ >
105
+ <svg
106
+ xmlns="http://www.w3.org/2000/svg"
107
+ width="24"
108
+ height="24"
109
+ viewBox="0 0 24 24"
110
+ fill="none"
111
+ stroke="currentColor"
112
+ strokeWidth="2"
113
+ strokeLinecap="round"
114
+ strokeLinejoin="round"
115
+ className="h-4 w-4"
116
+ >
117
+ <path d="M18 6 6 18" />
118
+ <path d="m6 6 12 12" />
119
+ </svg>
120
+ <span className="sr-only">Close</span>
121
+ </VaulDrawer.Close>
122
+ </VaulDrawer.Content>
123
+ </DrawerPortal>
124
+ );
125
+ },
126
+ );
127
+ DrawerContent.displayName = 'DrawerContent';
128
+
129
+ const DrawerClose = VaulDrawer.Close;
130
+ DrawerClose.displayName = 'DrawerClose';
131
+
132
+ const DrawerHeader = ({ className, ...props }: HTMLAttributes<HTMLDivElement>) => (
133
+ <div
134
+ data-drawer-header
135
+ className={cn(
136
+ 'flex flex-col space-y-2 border-b border-border/60 px-6 pt-5 pb-4 text-left',
137
+ className,
138
+ )}
139
+ {...props}
140
+ />
141
+ );
142
+ DrawerHeader.displayName = 'DrawerHeader';
143
+
144
+ const DrawerFooter = ({ className, ...props }: HTMLAttributes<HTMLDivElement>) => (
145
+ <div
146
+ className={cn(
147
+ 'mt-auto flex flex-col-reverse gap-2 border-t border-border px-6 py-4 sm:flex-row sm:justify-end',
148
+ className,
149
+ )}
150
+ {...props}
151
+ />
152
+ );
153
+ DrawerFooter.displayName = 'DrawerFooter';
154
+
155
+ const DrawerTitle = forwardRef<
156
+ ComponentRef<typeof VaulDrawer.Title>,
157
+ ComponentPropsWithoutRef<typeof VaulDrawer.Title>
158
+ >(({ className, ...props }, ref) => (
159
+ <VaulDrawer.Title
160
+ ref={ref}
161
+ className={cn('text-lg font-semibold leading-none tracking-tight', className)}
162
+ {...props}
163
+ />
164
+ ));
165
+ DrawerTitle.displayName = VaulDrawer.Title.displayName;
166
+
167
+ const DrawerDescription = forwardRef<
168
+ ComponentRef<typeof VaulDrawer.Description>,
169
+ ComponentPropsWithoutRef<typeof VaulDrawer.Description>
170
+ >(({ className, ...props }, ref) => (
171
+ <VaulDrawer.Description
172
+ ref={ref}
173
+ className={cn('text-sm text-muted-foreground', className)}
174
+ {...props}
175
+ />
176
+ ));
177
+ DrawerDescription.displayName = VaulDrawer.Description.displayName;
178
+
179
+ const DrawerHandle = ({ className, ...props }: HTMLAttributes<HTMLDivElement>) => (
180
+ <div
181
+ data-drawer-handle
182
+ className={cn('mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted', className)}
183
+ {...props}
184
+ />
185
+ );
186
+ DrawerHandle.displayName = 'DrawerHandle';
187
+
188
+ export {
189
+ Drawer,
190
+ DrawerTrigger,
191
+ DrawerPortal,
192
+ DrawerOverlay,
193
+ DrawerContent,
194
+ DrawerClose,
195
+ DrawerHeader,
196
+ DrawerFooter,
197
+ DrawerTitle,
198
+ DrawerDescription,
199
+ DrawerHandle,
200
+ };
@@ -0,0 +1,24 @@
1
+ import { forwardRef, type SelectHTMLAttributes } from 'react';
2
+ import { cn } from '@/lib/utils';
3
+
4
+ export type SelectProps = SelectHTMLAttributes<HTMLSelectElement>;
5
+
6
+ const Select = forwardRef<HTMLSelectElement, SelectProps>(
7
+ ({ className, children, ...props }, ref) => {
8
+ return (
9
+ <select
10
+ className={cn(
11
+ 'select-field flex h-10 w-full rounded-md border border-input bg-background pl-3 pr-10 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50',
12
+ className,
13
+ )}
14
+ ref={ref}
15
+ {...props}
16
+ >
17
+ {children}
18
+ </select>
19
+ );
20
+ },
21
+ );
22
+ Select.displayName = 'Select';
23
+
24
+ export { Select };
@@ -0,0 +1,406 @@
1
+ import {
2
+ createContext,
3
+ useContext,
4
+ useState,
5
+ useCallback,
6
+ forwardRef,
7
+ type ReactNode,
8
+ type HTMLAttributes,
9
+ type ButtonHTMLAttributes,
10
+ type AnchorHTMLAttributes,
11
+ } from 'react';
12
+ import { Slot } from '@radix-ui/react-slot';
13
+ import { cva, type VariantProps } from 'class-variance-authority';
14
+ import { cn } from '@/lib/utils';
15
+ import { Z_INDEX } from '@/lib/z-index';
16
+
17
+ type SidebarContextValue = {
18
+ isCollapsed: boolean;
19
+ toggle: () => void;
20
+ };
21
+
22
+ const SidebarContext = createContext<SidebarContextValue | undefined>(undefined);
23
+
24
+ const useSidebar = () => {
25
+ const context = useContext(SidebarContext);
26
+ if (!context) {
27
+ throw new Error('useSidebar must be used within a SidebarProvider');
28
+ }
29
+ return context;
30
+ };
31
+
32
+ const SidebarProvider = ({ children }: { children: ReactNode }) => {
33
+ const [isCollapsed, setIsCollapsed] = useState(false);
34
+
35
+ const toggle = useCallback(() => {
36
+ setIsCollapsed((prev) => !prev);
37
+ }, []);
38
+
39
+ return (
40
+ <SidebarContext.Provider value={{ isCollapsed, toggle }}>{children}</SidebarContext.Provider>
41
+ );
42
+ };
43
+
44
+ const Sidebar = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
45
+ ({ className, ...props }, ref) => {
46
+ const { isCollapsed } = useSidebar();
47
+
48
+ return (
49
+ <div
50
+ ref={ref}
51
+ data-sidebar="sidebar"
52
+ data-collapsed={isCollapsed}
53
+ className={cn(
54
+ 'flex h-full flex-col gap-2 border-r bg-sidebar-background p-2 text-sidebar-foreground transition-all duration-300 ease-in-out overflow-hidden',
55
+ isCollapsed ? 'w-0 border-r-0 p-0' : 'w-64',
56
+ className,
57
+ )}
58
+ {...props}
59
+ />
60
+ );
61
+ },
62
+ );
63
+ Sidebar.displayName = 'Sidebar';
64
+
65
+ const SidebarTrigger = forwardRef<HTMLButtonElement, ButtonHTMLAttributes<HTMLButtonElement>>(
66
+ ({ className, ...props }, ref) => {
67
+ const { toggle, isCollapsed } = useSidebar();
68
+
69
+ return (
70
+ <button
71
+ ref={ref}
72
+ onClick={toggle}
73
+ className={cn(
74
+ 'relative flex items-center justify-center rounded-md p-2 text-foreground transition-colors hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring shadow-lg backdrop-blur-md cursor-pointer bg-background/95 border border-border',
75
+ className,
76
+ )}
77
+ aria-label={isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}
78
+ style={{ zIndex: Z_INDEX.SIDEBAR_TRIGGER }}
79
+ {...props}
80
+ >
81
+ <img
82
+ src={isCollapsed ? '/icons/panel-left.svg' : '/icons/panel-left-closed.svg'}
83
+ alt=""
84
+ className="h-5 w-5 transition-transform duration-300"
85
+ />
86
+ </button>
87
+ );
88
+ },
89
+ );
90
+ SidebarTrigger.displayName = 'SidebarTrigger';
91
+
92
+ const SidebarHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
93
+ ({ className, ...props }, ref) => {
94
+ return (
95
+ <div
96
+ ref={ref}
97
+ className={cn('flex flex-col gap-2 p-2', className)}
98
+ {...props}
99
+ />
100
+ );
101
+ },
102
+ );
103
+ SidebarHeader.displayName = 'SidebarHeader';
104
+
105
+ const SidebarContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
106
+ ({ className, ...props }, ref) => {
107
+ return (
108
+ <div
109
+ ref={ref}
110
+ className={cn('flex flex-1 flex-col gap-2 overflow-auto', className)}
111
+ {...props}
112
+ />
113
+ );
114
+ },
115
+ );
116
+ SidebarContent.displayName = 'SidebarContent';
117
+
118
+ const SidebarFooter = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
119
+ ({ className, ...props }, ref) => {
120
+ return (
121
+ <div
122
+ ref={ref}
123
+ className={cn('flex flex-col gap-2 p-2', className)}
124
+ {...props}
125
+ />
126
+ );
127
+ },
128
+ );
129
+ SidebarFooter.displayName = 'SidebarFooter';
130
+
131
+ const SidebarGroup = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
132
+ ({ className, ...props }, ref) => {
133
+ return (
134
+ <div
135
+ ref={ref}
136
+ className={cn('group/sidebar-group', className)}
137
+ {...props}
138
+ />
139
+ );
140
+ },
141
+ );
142
+ SidebarGroup.displayName = 'SidebarGroup';
143
+
144
+ const SidebarGroupLabel = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
145
+ ({ className, ...props }, ref) => {
146
+ return (
147
+ <div
148
+ ref={ref}
149
+ className={cn(
150
+ '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,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
151
+ className,
152
+ )}
153
+ style={{ fontFamily: 'var(--heading-font-family, inherit)' }}
154
+ {...props}
155
+ />
156
+ );
157
+ },
158
+ );
159
+ SidebarGroupLabel.displayName = 'SidebarGroupLabel';
160
+
161
+ const SidebarGroupAction = forwardRef<
162
+ HTMLButtonElement,
163
+ ButtonHTMLAttributes<HTMLButtonElement> & {
164
+ asChild?: boolean;
165
+ }
166
+ >(({ className, asChild = false, ...props }, ref) => {
167
+ const Comp = asChild ? Slot : 'button';
168
+ return (
169
+ <Comp
170
+ ref={ref}
171
+ className={cn(
172
+ 'absolute right-2 top-1/2 -translate-y-1/2 rounded-md p-1.5 text-sidebar-foreground outline-none ring-sidebar-ring transition-opacity hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:opacity-100 focus-visible:ring-2 group-hover/sidebar-group:opacity-100 peer-data-[size=sm]/sidebar-group-action:opacity-0 [&>svg]:size-4 [&>svg]:shrink-0',
173
+ className,
174
+ )}
175
+ {...props}
176
+ />
177
+ );
178
+ });
179
+ SidebarGroupAction.displayName = 'SidebarGroupAction';
180
+
181
+ const SidebarGroupContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
182
+ ({ className, ...props }, ref) => {
183
+ return (
184
+ <div
185
+ ref={ref}
186
+ className={cn('w-full text-sm', className)}
187
+ {...props}
188
+ />
189
+ );
190
+ },
191
+ );
192
+ SidebarGroupContent.displayName = 'SidebarGroupContent';
193
+
194
+ const sidebarMenuButtonVariants = cva(
195
+ 'flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:bg-sidebar-accent focus-visible:text-sidebar-accent-foreground focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground',
196
+ {
197
+ variants: {
198
+ variant: {
199
+ default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
200
+ outline:
201
+ '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-ring))]',
202
+ },
203
+ size: {
204
+ default: 'h-8 text-sm',
205
+ sm: 'h-7 text-xs',
206
+ lg: 'h-12 text-base',
207
+ },
208
+ },
209
+ defaultVariants: {
210
+ variant: 'default',
211
+ size: 'default',
212
+ },
213
+ },
214
+ );
215
+
216
+ const SidebarMenuButton = forwardRef<
217
+ HTMLButtonElement,
218
+ ButtonHTMLAttributes<HTMLButtonElement> &
219
+ VariantProps<typeof sidebarMenuButtonVariants> & {
220
+ asChild?: boolean;
221
+ isActive?: boolean;
222
+ }
223
+ >(({ className, variant, size, asChild = false, isActive, children, ...props }, ref) => {
224
+ const { isCollapsed } = useSidebar();
225
+ const Comp = asChild ? Slot : 'button';
226
+
227
+ return (
228
+ <Comp
229
+ ref={ref}
230
+ data-active={isActive}
231
+ data-collapsed={isCollapsed}
232
+ className={cn(
233
+ sidebarMenuButtonVariants({ variant, size }),
234
+ isCollapsed && 'justify-center',
235
+ className,
236
+ )}
237
+ {...props}
238
+ >
239
+ {children}
240
+ </Comp>
241
+ );
242
+ });
243
+ SidebarMenuButton.displayName = 'SidebarMenuButton';
244
+
245
+ const SidebarMenu = forwardRef<HTMLUListElement, HTMLAttributes<HTMLUListElement>>(
246
+ ({ className, ...props }, ref) => {
247
+ return (
248
+ <ul
249
+ ref={ref}
250
+ className={cn('flex w-full min-w-0 flex-col gap-1', className)}
251
+ {...props}
252
+ />
253
+ );
254
+ },
255
+ );
256
+ SidebarMenu.displayName = 'SidebarMenu';
257
+
258
+ const SidebarMenuItem = forwardRef<HTMLLIElement, HTMLAttributes<HTMLLIElement>>(
259
+ ({ className, ...props }, ref) => {
260
+ return (
261
+ <li
262
+ ref={ref}
263
+ className={cn('group/menu-item relative', className)}
264
+ {...props}
265
+ />
266
+ );
267
+ },
268
+ );
269
+ SidebarMenuItem.displayName = 'SidebarMenuItem';
270
+
271
+ const SidebarMenuAction = forwardRef<
272
+ HTMLButtonElement,
273
+ ButtonHTMLAttributes<HTMLButtonElement> & {
274
+ asChild?: boolean;
275
+ }
276
+ >(({ className, asChild = false, ...props }, ref) => {
277
+ const Comp = asChild ? Slot : 'button';
278
+ return (
279
+ <Comp
280
+ ref={ref}
281
+ className={cn(
282
+ 'absolute right-2 top-1/2 flex -translate-y-1/2 items-center justify-center rounded-md p-1 text-sidebar-foreground opacity-0 transition-opacity group-hover/menu-item:opacity-100 group-focus/menu-item:opacity-100 [&:has(~:hover)]:opacity-100 [&:has(~:focus)]:opacity-100',
283
+ className,
284
+ )}
285
+ {...props}
286
+ />
287
+ );
288
+ });
289
+ SidebarMenuAction.displayName = 'SidebarMenuAction';
290
+
291
+ const SidebarMenuBadge = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
292
+ ({ className, ...props }, ref) => {
293
+ return (
294
+ <div
295
+ ref={ref}
296
+ className={cn(
297
+ 'absolute right-2 top-1/2 flex -translate-y-1/2 items-center justify-center rounded-md px-1.5 py-0.5 text-xs font-medium tabular-nums text-sidebar-foreground',
298
+ className,
299
+ )}
300
+ {...props}
301
+ />
302
+ );
303
+ },
304
+ );
305
+ SidebarMenuBadge.displayName = 'SidebarMenuBadge';
306
+
307
+ const SidebarMenuSkeleton = forwardRef<
308
+ HTMLDivElement,
309
+ HTMLAttributes<HTMLDivElement> & {
310
+ showIcon?: boolean;
311
+ }
312
+ >(({ className, showIcon = false, ...props }, ref) => {
313
+ return (
314
+ <div
315
+ ref={ref}
316
+ className={cn('flex items-center gap-2 px-2 py-1.5', className)}
317
+ {...props}
318
+ >
319
+ {showIcon && <div className="flex h-4 w-4 rounded-md bg-sidebar-primary/10" />}
320
+ <div className="flex flex-1 flex-col gap-1.5">
321
+ <div className="h-2.5 w-16 rounded-md bg-sidebar-primary/10" />
322
+ <div className="h-2.5 w-24 rounded-md bg-sidebar-primary/10" />
323
+ </div>
324
+ </div>
325
+ );
326
+ });
327
+ SidebarMenuSkeleton.displayName = 'SidebarMenuSkeleton';
328
+
329
+ const SidebarMenuSub = forwardRef<HTMLUListElement, HTMLAttributes<HTMLUListElement>>(
330
+ ({ className, ...props }, ref) => {
331
+ return (
332
+ <ul
333
+ ref={ref}
334
+ className={cn(
335
+ 'ml-4 mt-1 flex min-w-0 flex-col gap-0.5 border-l border-sidebar-border pl-2.5',
336
+ className,
337
+ )}
338
+ {...props}
339
+ />
340
+ );
341
+ },
342
+ );
343
+ SidebarMenuSub.displayName = 'SidebarMenuSub';
344
+
345
+ const SidebarMenuSubButton = forwardRef<
346
+ HTMLAnchorElement,
347
+ AnchorHTMLAttributes<HTMLAnchorElement> & {
348
+ asChild?: boolean;
349
+ size?: 'sm' | 'md' | 'lg';
350
+ isActive?: boolean;
351
+ }
352
+ >(({ className, asChild = false, size = 'md', isActive, ...props }, ref) => {
353
+ const Comp = asChild ? Slot : 'a';
354
+ return (
355
+ <Comp
356
+ ref={ref}
357
+ data-size={size}
358
+ data-active={isActive}
359
+ className={cn(
360
+ 'flex min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md p-2 text-sidebar-foreground outline-none ring-sidebar-ring transition-[width,height,padding] 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 data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
361
+ size === 'sm' && 'text-xs',
362
+ size === 'md' && 'text-sm',
363
+ size === 'lg' && 'text-base',
364
+ className,
365
+ )}
366
+ {...props}
367
+ />
368
+ );
369
+ });
370
+ SidebarMenuSubButton.displayName = 'SidebarMenuSubButton';
371
+
372
+ const SidebarMenuSubItem = forwardRef<HTMLLIElement, HTMLAttributes<HTMLLIElement>>(
373
+ ({ className, ...props }, ref) => {
374
+ return (
375
+ <li
376
+ ref={ref}
377
+ className={cn('group/menu-sub-item relative', className)}
378
+ {...props}
379
+ />
380
+ );
381
+ },
382
+ );
383
+ SidebarMenuSubItem.displayName = 'SidebarMenuSubItem';
384
+
385
+ export {
386
+ Sidebar,
387
+ SidebarProvider,
388
+ SidebarTrigger,
389
+ SidebarHeader,
390
+ SidebarContent,
391
+ SidebarFooter,
392
+ SidebarGroup,
393
+ SidebarGroupLabel,
394
+ SidebarGroupAction,
395
+ SidebarGroupContent,
396
+ SidebarMenu,
397
+ SidebarMenuButton,
398
+ SidebarMenuItem,
399
+ SidebarMenuAction,
400
+ SidebarMenuBadge,
401
+ SidebarMenuSkeleton,
402
+ SidebarMenuSub,
403
+ SidebarMenuSubButton,
404
+ SidebarMenuSubItem,
405
+ useSidebar,
406
+ };
@@ -0,0 +1,36 @@
1
+ import type { ComponentProps } from 'react';
2
+ import { useSettings } from '@/features/settings/hooks/useSettings';
3
+ import { Toaster as Sonner } from 'sonner';
4
+ import { Z_INDEX } from '@/lib/z-index';
5
+
6
+ type ToasterProps = ComponentProps<typeof Sonner>;
7
+
8
+ const Toaster = ({ ...props }: ToasterProps) => {
9
+ const { settings } = useSettings();
10
+
11
+ return (
12
+ <Sonner
13
+ position="top-center"
14
+ theme={settings.appearance.theme as 'light' | 'dark' | 'system'}
15
+ className="toaster group"
16
+ style={{
17
+ zIndex: Z_INDEX.TOAST,
18
+ // Re-enable pointer events so toasts stay clickable when a Radix modal is open
19
+ // (Radix sets body.style.pointerEvents = 'none' and only the dialog content gets 'auto')
20
+ pointerEvents: 'auto',
21
+ }}
22
+ toastOptions={{
23
+ classNames: {
24
+ toast:
25
+ 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
26
+ description: 'group-[.toast]:text-muted-foreground',
27
+ actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
28
+ cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',
29
+ },
30
+ }}
31
+ {...props}
32
+ />
33
+ );
34
+ };
35
+
36
+ export { Toaster };