@trackany-device/components 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 (289) hide show
  1. package/package.json +185 -0
  2. package/src/assets/logo.png +0 -0
  3. package/src/assets/map/arrows/map-arrow-blue.png +0 -0
  4. package/src/assets/map/arrows/map-arrow-green.png +0 -0
  5. package/src/assets/map/arrows/map-arrow-purple.png +0 -0
  6. package/src/assets/map/arrows/map-arrow-red.png +0 -0
  7. package/src/assets/map/flags/flag-blue.png +0 -0
  8. package/src/assets/map/flags/flag-green.png +0 -0
  9. package/src/assets/map/flags/flag-red.png +0 -0
  10. package/src/assets/map/flags/flag-yellow.png +0 -0
  11. package/src/assets/map/pins/map-pin-blue.png +0 -0
  12. package/src/assets/map/pins/map-pin-green.png +0 -0
  13. package/src/assets/map/pins/map-pin-purple.png +0 -0
  14. package/src/assets/map/pins/map-pin-red.png +0 -0
  15. package/src/components/Card.tsx +9 -0
  16. package/src/components/alert-error.tsx +24 -0
  17. package/src/components/app-content.tsx +22 -0
  18. package/src/components/app-header.tsx +153 -0
  19. package/src/components/app-logo-icon.tsx +13 -0
  20. package/src/components/app-logo.tsx +21 -0
  21. package/src/components/app-shell.tsx +19 -0
  22. package/src/components/app-sidebar-header.tsx +68 -0
  23. package/src/components/app-sidebar.tsx +106 -0
  24. package/src/components/appearance-tabs.tsx +46 -0
  25. package/src/components/breadcrumbs.tsx +50 -0
  26. package/src/components/cms/blurred-image.tsx +111 -0
  27. package/src/components/cms/section-bg.tsx +473 -0
  28. package/src/components/cms/section-button.tsx +127 -0
  29. package/src/components/cms/sections/banner-5050-section.tsx +135 -0
  30. package/src/components/cms/sections/blogs-listing-section.tsx +270 -0
  31. package/src/components/cms/sections/cards-grid-section.tsx +185 -0
  32. package/src/components/cms/sections/contact-form-section.tsx +157 -0
  33. package/src/components/cms/sections/cta-section.tsx +101 -0
  34. package/src/components/cms/sections/featured-blog-slider-section.tsx +256 -0
  35. package/src/components/cms/sections/featured-products-grid-section.tsx +173 -0
  36. package/src/components/cms/sections/featured-solutions-grid-section.tsx +183 -0
  37. package/src/components/cms/sections/hero-section.tsx +180 -0
  38. package/src/components/cms/sections/solutions-with-filter-section.tsx +234 -0
  39. package/src/components/cms/sections/text-section.tsx +77 -0
  40. package/src/components/cutout-image.tsx +228 -0
  41. package/src/components/devices/devices-mini-map.tsx +275 -0
  42. package/src/components/docs/docs-shell.tsx +280 -0
  43. package/src/components/fleet-hero-animated.tsx +383 -0
  44. package/src/components/input-error.tsx +17 -0
  45. package/src/components/keenicons/assets/duotone/Read Me.txt +7 -0
  46. package/src/components/keenicons/assets/duotone/demo-files/demo.css +160 -0
  47. package/src/components/keenicons/assets/duotone/demo-files/demo.js +32 -0
  48. package/src/components/keenicons/assets/duotone/demo.html +12424 -0
  49. package/src/components/keenicons/assets/duotone/fonts/keenicons-duotone.svg +1109 -0
  50. package/src/components/keenicons/assets/duotone/fonts/keenicons-duotone.ttf +0 -0
  51. package/src/components/keenicons/assets/duotone/fonts/keenicons-duotone.woff +0 -0
  52. package/src/components/keenicons/assets/duotone/selection.json +17313 -0
  53. package/src/components/keenicons/assets/duotone/style.css +4931 -0
  54. package/src/components/keenicons/assets/filled/Read Me.txt +7 -0
  55. package/src/components/keenicons/assets/filled/demo-files/demo.css +160 -0
  56. package/src/components/keenicons/assets/filled/demo-files/demo.js +32 -0
  57. package/src/components/keenicons/assets/filled/demo.html +12370 -0
  58. package/src/components/keenicons/assets/filled/fonts/keenicons-filled.svg +1082 -0
  59. package/src/components/keenicons/assets/filled/fonts/keenicons-filled.ttf +0 -0
  60. package/src/components/keenicons/assets/filled/fonts/keenicons-filled.woff +0 -0
  61. package/src/components/keenicons/assets/filled/selection.json +17096 -0
  62. package/src/components/keenicons/assets/filled/style.css +4769 -0
  63. package/src/components/keenicons/assets/outline/Read Me.txt +7 -0
  64. package/src/components/keenicons/assets/outline/demo-files/demo.css +160 -0
  65. package/src/components/keenicons/assets/outline/demo-files/demo.js +32 -0
  66. package/src/components/keenicons/assets/outline/demo.html +11356 -0
  67. package/src/components/keenicons/assets/outline/fonts/keenicons-outline.svg +575 -0
  68. package/src/components/keenicons/assets/outline/fonts/keenicons-outline.ttf +0 -0
  69. package/src/components/keenicons/assets/outline/fonts/keenicons-outline.woff +0 -0
  70. package/src/components/keenicons/assets/outline/selection.json +13054 -0
  71. package/src/components/keenicons/assets/outline/style.css +1721 -0
  72. package/src/components/keenicons/assets/solid/Read Me.txt +7 -0
  73. package/src/components/keenicons/assets/solid/demo-files/demo.css +160 -0
  74. package/src/components/keenicons/assets/solid/demo-files/demo.js +32 -0
  75. package/src/components/keenicons/assets/solid/demo.html +11356 -0
  76. package/src/components/keenicons/assets/solid/fonts/keenicons-solid.svg +575 -0
  77. package/src/components/keenicons/assets/solid/fonts/keenicons-solid.ttf +0 -0
  78. package/src/components/keenicons/assets/solid/fonts/keenicons-solid.woff +0 -0
  79. package/src/components/keenicons/assets/solid/selection.json +13048 -0
  80. package/src/components/keenicons/assets/solid/style.css +1721 -0
  81. package/src/components/keenicons/assets/styles.css +4 -0
  82. package/src/components/keenicons/index.ts +2 -0
  83. package/src/components/keenicons/keenicons.tsx +16 -0
  84. package/src/components/keenicons/types.ts +7 -0
  85. package/src/components/nav-footer.tsx +49 -0
  86. package/src/components/nav-main.tsx +53 -0
  87. package/src/components/nav-user.tsx +59 -0
  88. package/src/components/notification-bell.tsx +190 -0
  89. package/src/components/products/product-card.tsx +159 -0
  90. package/src/components/text-link.tsx +23 -0
  91. package/src/components/ui/accordion-menu.tsx +322 -0
  92. package/src/components/ui/accordion.tsx +133 -0
  93. package/src/components/ui/alert-dialog.tsx +82 -0
  94. package/src/components/ui/alert.tsx +63 -0
  95. package/src/components/ui/avatar-group.tsx +129 -0
  96. package/src/components/ui/avatar.tsx +67 -0
  97. package/src/components/ui/badge.tsx +230 -0
  98. package/src/components/ui/breadcrumb.tsx +88 -0
  99. package/src/components/ui/button.tsx +412 -0
  100. package/src/components/ui/calendar.tsx +56 -0
  101. package/src/components/ui/card.tsx +147 -0
  102. package/src/components/ui/chart.tsx +290 -0
  103. package/src/components/ui/checkbox.tsx +47 -0
  104. package/src/components/ui/code.tsx +45 -0
  105. package/src/components/ui/collapsible.tsx +31 -0
  106. package/src/components/ui/command-palette.tsx +189 -0
  107. package/src/components/ui/command.tsx +138 -0
  108. package/src/components/ui/cookie-banner.tsx +220 -0
  109. package/src/components/ui/copy-button.tsx +60 -0
  110. package/src/components/ui/data-grid-column-filter.tsx +124 -0
  111. package/src/components/ui/data-grid-column-header.tsx +284 -0
  112. package/src/components/ui/data-grid-column-visibility.tsx +38 -0
  113. package/src/components/ui/data-grid-pagination.tsx +206 -0
  114. package/src/components/ui/data-grid-table-dnd-rows.tsx +147 -0
  115. package/src/components/ui/data-grid-table-dnd.tsx +175 -0
  116. package/src/components/ui/data-grid-table.tsx +500 -0
  117. package/src/components/ui/data-grid.tsx +193 -0
  118. package/src/components/ui/data-list.tsx +76 -0
  119. package/src/components/ui/datefield.tsx +91 -0
  120. package/src/components/ui/dialog.tsx +139 -0
  121. package/src/components/ui/divider.tsx +41 -0
  122. package/src/components/ui/drawer.tsx +59 -0
  123. package/src/components/ui/dropdown-menu.tsx +224 -0
  124. package/src/components/ui/empty-state.tsx +54 -0
  125. package/src/components/ui/file-upload.tsx +152 -0
  126. package/src/components/ui/form.tsx +88 -0
  127. package/src/components/ui/icon.tsx +14 -0
  128. package/src/components/ui/input-otp.tsx +71 -0
  129. package/src/components/ui/input.tsx +155 -0
  130. package/src/components/ui/kbd.tsx +26 -0
  131. package/src/components/ui/label.tsx +31 -0
  132. package/src/components/ui/navigation-menu.tsx +168 -0
  133. package/src/components/ui/pagination.tsx +37 -0
  134. package/src/components/ui/placeholder-pattern.tsx +21 -0
  135. package/src/components/ui/popover.tsx +50 -0
  136. package/src/components/ui/progress.tsx +65 -0
  137. package/src/components/ui/radio-group.tsx +73 -0
  138. package/src/components/ui/resizable.tsx +39 -0
  139. package/src/components/ui/scroll-area.tsx +50 -0
  140. package/src/components/ui/select.tsx +234 -0
  141. package/src/components/ui/separator.tsx +24 -0
  142. package/src/components/ui/sheet.tsx +147 -0
  143. package/src/components/ui/sidebar.tsx +721 -0
  144. package/src/components/ui/skeleton.tsx +15 -0
  145. package/src/components/ui/slider.tsx +35 -0
  146. package/src/components/ui/sonner.tsx +28 -0
  147. package/src/components/ui/sortable.tsx +724 -0
  148. package/src/components/ui/spinner.tsx +17 -0
  149. package/src/components/ui/stat-card.tsx +82 -0
  150. package/src/components/ui/stepper.tsx +410 -0
  151. package/src/components/ui/switch.tsx +68 -0
  152. package/src/components/ui/table.tsx +42 -0
  153. package/src/components/ui/tabs.tsx +196 -0
  154. package/src/components/ui/timeline.tsx +90 -0
  155. package/src/components/ui/toggle-group.tsx +73 -0
  156. package/src/components/ui/toggle.tsx +45 -0
  157. package/src/components/ui/tooltip.tsx +55 -0
  158. package/src/components/user-info.tsx +33 -0
  159. package/src/components/user-menu-content.tsx +53 -0
  160. package/src/components/web/SiteFooter.tsx +154 -0
  161. package/src/components/web/SiteHeader.tsx +159 -0
  162. package/src/components/workflows/workflow-canvas.tsx +321 -0
  163. package/src/controls/Blockquote.tsx +25 -0
  164. package/src/controls/Button.tsx +101 -0
  165. package/src/controls/Checkbox.tsx +29 -0
  166. package/src/controls/DateField.tsx +37 -0
  167. package/src/controls/FormField.tsx +20 -0
  168. package/src/controls/Heading.tsx +28 -0
  169. package/src/controls/Input.tsx +21 -0
  170. package/src/controls/Label.tsx +18 -0
  171. package/src/controls/Paragraph.tsx +39 -0
  172. package/src/controls/PasswordInput.tsx +40 -0
  173. package/src/controls/RadioGroup.tsx +70 -0
  174. package/src/controls/Select.tsx +24 -0
  175. package/src/controls/Slider.tsx +33 -0
  176. package/src/controls/Switch.tsx +31 -0
  177. package/src/controls/Textarea.tsx +22 -0
  178. package/src/elements/ConfirmPasswordForm.tsx +43 -0
  179. package/src/elements/DeviceStatusBadge.tsx +38 -0
  180. package/src/elements/DriverCard.tsx +67 -0
  181. package/src/elements/ForgotPasswordForm.tsx +64 -0
  182. package/src/elements/IncidentCard.tsx +67 -0
  183. package/src/elements/LoginForm.tsx +100 -0
  184. package/src/elements/OtpForm.tsx +71 -0
  185. package/src/elements/RegisterForm.tsx +150 -0
  186. package/src/elements/ResetPasswordForm.tsx +72 -0
  187. package/src/elements/SmsChallengeForm.tsx +104 -0
  188. package/src/elements/VehicleCard.tsx +73 -0
  189. package/src/elements/VerifyEmailForm.tsx +39 -0
  190. package/src/hooks/use-appearance.tsx +117 -0
  191. package/src/hooks/use-applied-theme.ts +98 -0
  192. package/src/hooks/use-clipboard.ts +34 -0
  193. package/src/hooks/use-current-url.ts +83 -0
  194. package/src/hooks/use-dark-mode.ts +48 -0
  195. package/src/hooks/use-flash-toast.ts +29 -0
  196. package/src/hooks/use-initials.tsx +24 -0
  197. package/src/hooks/use-mobile-navigation.ts +12 -0
  198. package/src/hooks/use-mobile.tsx +38 -0
  199. package/src/index.ts +408 -0
  200. package/src/layouts/AppLayout.tsx +60 -0
  201. package/src/layouts/AuthLayout.tsx +32 -0
  202. package/src/layouts/SettingsLayout.tsx +21 -0
  203. package/src/layouts/app/AIChatLayout.tsx +73 -0
  204. package/src/layouts/app/AsideSidebarLayout.tsx +3 -0
  205. package/src/layouts/app/CalendarSidebarLayout.tsx +69 -0
  206. package/src/layouts/app/CommunitiesNavbarLayout.tsx +3 -0
  207. package/src/layouts/app/DualNavbarSidebarLayout.tsx +3 -0
  208. package/src/layouts/app/FocusSidebarLayout.tsx +75 -0
  209. package/src/layouts/app/MailLayout.tsx +69 -0
  210. package/src/layouts/app/MegaMenuHeaderLayout.tsx +3 -0
  211. package/src/layouts/app/MegaMenuLayout.tsx +81 -0
  212. package/src/layouts/app/MegaMenuNavbarLayout.tsx +88 -0
  213. package/src/layouts/app/MegaMenuSearchNavbarLayout.tsx +3 -0
  214. package/src/layouts/app/NavbarCollapsibleLayout.tsx +88 -0
  215. package/src/layouts/app/NavbarCollapsibleLinksLayout.tsx +3 -0
  216. package/src/layouts/app/NavbarMinimalLayout.tsx +3 -0
  217. package/src/layouts/app/NavbarMinimalSidebarLayout.tsx +3 -0
  218. package/src/layouts/app/NavbarSidebarDashboardLayout.tsx +3 -0
  219. package/src/layouts/app/NavbarSidebarLayout.tsx +92 -0
  220. package/src/layouts/app/NavbarSimpleSidebarLayout.tsx +3 -0
  221. package/src/layouts/app/NavbarTitledSidebarLayout.tsx +3 -0
  222. package/src/layouts/app/PanelSidebarLayout.tsx +3 -0
  223. package/src/layouts/app/SearchNavbarSidebarLayout.tsx +3 -0
  224. package/src/layouts/app/SidebarBreadcrumbLayout.tsx +3 -0
  225. package/src/layouts/app/SidebarCleanLayout.tsx +3 -0
  226. package/src/layouts/app/SidebarCommunitiesLayout.tsx +3 -0
  227. package/src/layouts/app/SidebarContentLayout.tsx +3 -0
  228. package/src/layouts/app/SidebarDualMenuLayout.tsx +104 -0
  229. package/src/layouts/app/SidebarFixedLayout.tsx +166 -0
  230. package/src/layouts/app/SidebarFooterNavbarLayout.tsx +3 -0
  231. package/src/layouts/app/SidebarHeaderMenuLayout.tsx +3 -0
  232. package/src/layouts/app/SidebarMegaMenuLayout.tsx +4 -0
  233. package/src/layouts/app/SidebarMinimalLayout.tsx +70 -0
  234. package/src/layouts/app/SidebarMobileSearchLayout.tsx +3 -0
  235. package/src/layouts/app/SidebarMultiPanelLayout.tsx +3 -0
  236. package/src/layouts/app/SidebarPrimarySecondaryLayout.tsx +3 -0
  237. package/src/layouts/app/SidebarSearchHeaderLayout.tsx +103 -0
  238. package/src/layouts/app/SidebarSearchToolbarLayout.tsx +3 -0
  239. package/src/layouts/app/SidebarTabsDualLayout.tsx +3 -0
  240. package/src/layouts/app/SidebarTabsLayout.tsx +98 -0
  241. package/src/layouts/app/SidebarTreeLayout.tsx +3 -0
  242. package/src/layouts/app/SplitNavbarLayout.tsx +3 -0
  243. package/src/layouts/app/SplitSidebarDashboardLayout.tsx +3 -0
  244. package/src/layouts/app/SplitSidebarLayout.tsx +99 -0
  245. package/src/layouts/app/TopNavLayout.tsx +105 -0
  246. package/src/layouts/app/TopNavLinksLayout.tsx +3 -0
  247. package/src/layouts/app/WorkspaceBreadcrumbLayout.tsx +3 -0
  248. package/src/layouts/app/WorkspaceCommunitiesLayout.tsx +3 -0
  249. package/src/layouts/app/WorkspaceNavbarLayout.tsx +3 -0
  250. package/src/layouts/app/WorkspaceSidebarLayout.tsx +98 -0
  251. package/src/layouts/app/WorkspaceSidebarTitleLayout.tsx +3 -0
  252. package/src/layouts/app/app-header-layout.tsx +45 -0
  253. package/src/layouts/app/app-sidebar-layout.tsx +56 -0
  254. package/src/layouts/app/layout-context.tsx +44 -0
  255. package/src/layouts/app/layout-types.ts +47 -0
  256. package/src/layouts/app/partials/Footer.tsx +35 -0
  257. package/src/layouts/app/partials/HeaderTopbar.tsx +96 -0
  258. package/src/layouts/app/partials/Navbar.tsx +85 -0
  259. package/src/layouts/app/partials/Toolbar.tsx +47 -0
  260. package/src/layouts/app-layout.tsx +29 -0
  261. package/src/layouts/auth/AuthBrandedLayout.tsx +58 -0
  262. package/src/layouts/auth/AuthCardLayout.tsx +31 -0
  263. package/src/layouts/auth/AuthCenteredLayout.tsx +41 -0
  264. package/src/layouts/auth/AuthClassicLayout.tsx +41 -0
  265. package/src/layouts/auth/AuthSimpleLayout.tsx +33 -0
  266. package/src/layouts/auth/AuthSplitLayout.tsx +89 -0
  267. package/src/layouts/web-app-layout.tsx +162 -0
  268. package/src/layouts/web-layout.tsx +23 -0
  269. package/src/lib/datetime.ts +188 -0
  270. package/src/lib/google-maps-loader.ts +99 -0
  271. package/src/lib/location.ts +127 -0
  272. package/src/lib/lucide-icon-map.ts +132 -0
  273. package/src/lib/map-markers.ts +124 -0
  274. package/src/lib/map-styles.ts +351 -0
  275. package/src/lib/utils.ts +11 -0
  276. package/src/platform/adapters/default.tsx +156 -0
  277. package/src/platform/adapters/inertia.tsx +88 -0
  278. package/src/platform/adapters/nextjs.ts +86 -0
  279. package/src/platform/context.tsx +106 -0
  280. package/src/platform/index.ts +27 -0
  281. package/src/platform/types.ts +105 -0
  282. package/src/styles/layouts/sidebar-fixed.css +161 -0
  283. package/src/styles/themes.css +583 -0
  284. package/src/types/assets.d.ts +5 -0
  285. package/src/types/auth.ts +25 -0
  286. package/src/types/global.d.ts +13 -0
  287. package/src/types/index.ts +9 -0
  288. package/src/types/navigation.ts +15 -0
  289. package/src/types/ui.ts +32 -0
@@ -0,0 +1,322 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import * as AccordionPrimitive from '@radix-ui/react-accordion';
5
+ import { ChevronDown } from 'lucide-react';
6
+ import { cva, type VariantProps } from 'class-variance-authority';
7
+ import { cn } from '../../lib/utils';
8
+
9
+ interface AccordionMenuContextValue {
10
+ matchPath: (href: string) => boolean;
11
+ selectedValue: string | undefined;
12
+ setSelectedValue: React.Dispatch<React.SetStateAction<string | undefined>>;
13
+ classNames?: AccordionMenuClassNames;
14
+ nestedStates: Record<string, string | string[]>;
15
+ setNestedStates: React.Dispatch<React.SetStateAction<Record<string, string | string[]>>>;
16
+ onItemClick?: (value: string, event: React.MouseEvent) => void;
17
+ }
18
+
19
+ export interface AccordionMenuClassNames {
20
+ root?: string;
21
+ group?: string;
22
+ label?: string;
23
+ separator?: string;
24
+ item?: string;
25
+ sub?: string;
26
+ subTrigger?: string;
27
+ subContent?: string;
28
+ subWrapper?: string;
29
+ indicator?: string;
30
+ }
31
+
32
+ interface AccordionMenuProps {
33
+ selectedValue?: string;
34
+ matchPath?: (href: string) => boolean;
35
+ classNames?: AccordionMenuClassNames;
36
+ onItemClick?: (value: string, event: React.MouseEvent) => void;
37
+ }
38
+
39
+ const AccordionMenuContext = React.createContext<AccordionMenuContextValue>({
40
+ matchPath: () => false,
41
+ selectedValue: '',
42
+ setSelectedValue: () => {},
43
+ nestedStates: {},
44
+ setNestedStates: () => {},
45
+ });
46
+
47
+ function AccordionMenu({
48
+ className,
49
+ matchPath = () => false,
50
+ classNames,
51
+ children,
52
+ selectedValue,
53
+ onItemClick,
54
+ ...props
55
+ }: React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Root> & AccordionMenuProps) {
56
+ const [internalSelectedValue, setInternalSelectedValue] = React.useState<string | undefined>(selectedValue);
57
+
58
+ React.useEffect(() => {
59
+ setInternalSelectedValue(selectedValue);
60
+ }, [selectedValue]);
61
+
62
+ const initialNestedStates = React.useMemo(() => {
63
+ const getActiveChain = (nodes: React.ReactNode, chain: string[] = []): string[] => {
64
+ let result: string[] = [];
65
+ React.Children.forEach(nodes, (node) => {
66
+ if (React.isValidElement(node)) {
67
+ const { value, children: nodeChildren } = node.props as {
68
+ value?: string;
69
+ children?: React.ReactNode;
70
+ };
71
+ const newChain = value ? [...chain, value] : chain;
72
+ if (value && (value === selectedValue || matchPath(value))) {
73
+ result = newChain;
74
+ } else if (nodeChildren) {
75
+ const childChain = getActiveChain(nodeChildren, newChain);
76
+ if (childChain.length > 0) result = childChain;
77
+ }
78
+ }
79
+ });
80
+ return result;
81
+ };
82
+
83
+ const chain = getActiveChain(children);
84
+ const trimmedChain = chain.length > 1 ? chain.slice(0, chain.length - 1) : chain;
85
+ const mapping: Record<string, string | string[]> = {};
86
+ if (trimmedChain.length > 0) {
87
+ if (props.type === 'multiple') {
88
+ mapping['root'] = trimmedChain;
89
+ } else {
90
+ mapping['root'] = trimmedChain[0];
91
+ for (let i = 0; i < trimmedChain.length - 1; i++) {
92
+ mapping[trimmedChain[i]] = trimmedChain[i + 1];
93
+ }
94
+ }
95
+ }
96
+ return mapping;
97
+ }, [children, matchPath, selectedValue, props.type]);
98
+
99
+ const [nestedStates, setNestedStates] = React.useState<Record<string, string | string[]>>(initialNestedStates);
100
+ const multipleValue = (
101
+ Array.isArray(nestedStates['root'])
102
+ ? nestedStates['root']
103
+ : typeof nestedStates['root'] === 'string'
104
+ ? [nestedStates['root']]
105
+ : []
106
+ ) as string[];
107
+ const singleValue = (nestedStates['root'] ?? '') as string;
108
+
109
+ return (
110
+ <AccordionMenuContext.Provider
111
+ value={{ matchPath, selectedValue: internalSelectedValue, setSelectedValue: setInternalSelectedValue, classNames, onItemClick, nestedStates, setNestedStates }}
112
+ >
113
+ {props.type === 'single' ? (
114
+ <AccordionPrimitive.Root
115
+ data-slot="accordion-menu"
116
+ value={singleValue}
117
+ className={cn('w-full', classNames?.root, className)}
118
+ onValueChange={(value: string) => setNestedStates((prev) => ({ ...prev, root: value }))}
119
+ {...props}
120
+ role="menu"
121
+ >
122
+ {children}
123
+ </AccordionPrimitive.Root>
124
+ ) : (
125
+ <AccordionPrimitive.Root
126
+ data-slot="accordion-menu"
127
+ value={multipleValue}
128
+ className={cn('w-full', classNames?.root, className)}
129
+ onValueChange={(value: string[]) => setNestedStates((prev) => ({ ...prev, root: value }))}
130
+ {...(props as React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Root> & { type: 'multiple' })}
131
+ role="menu"
132
+ >
133
+ {children}
134
+ </AccordionPrimitive.Root>
135
+ )}
136
+ </AccordionMenuContext.Provider>
137
+ );
138
+ }
139
+
140
+ function AccordionMenuGroup({ children, className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
141
+ const { classNames } = React.useContext(AccordionMenuContext);
142
+ return (
143
+ <div data-slot="accordion-menu-group" role="group" className={cn('space-y-0.5', classNames?.group, className)} {...props}>
144
+ {children}
145
+ </div>
146
+ );
147
+ }
148
+
149
+ function AccordionMenuLabel({ children, className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
150
+ const { classNames } = React.useContext(AccordionMenuContext);
151
+ return (
152
+ <div data-slot="accordion-menu-label" role="presentation" className={cn('px-2 py-1.5 text-xs font-medium text-muted-foreground', classNames?.label, className)} {...props}>
153
+ {children}
154
+ </div>
155
+ );
156
+ }
157
+
158
+ function AccordionMenuSeparator({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {
159
+ const { classNames } = React.useContext(AccordionMenuContext);
160
+ return (
161
+ <div data-slot="accordion-menu-separator" role="separator" className={cn('my-1 h-px bg-border', classNames?.separator, className)} {...props} />
162
+ );
163
+ }
164
+
165
+ const itemVariants = cva(
166
+ 'relative cursor-pointer select-none flex w-full text-start items-center text-foreground rounded-lg gap-2 px-2 py-1.5 text-sm outline-hidden transition-colors hover:bg-accent hover:text-accent-foreground data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground disabled:opacity-50 disabled:bg-transparent focus-visible:bg-accent focus-visible:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:opacity-60 [&_svg:not([class*=size-])]:size-4 [&_svg]:shrink-0 [&_a]:flex [&>a]:w-full [&>a]:items-center [&>a]:gap-2',
167
+ {
168
+ variants: {
169
+ variant: {
170
+ default: '',
171
+ destructive: 'text-destructive hover:text-destructive focus:text-destructive hover:bg-destructive/5 focus:bg-destructive/5 data-[active=true]:bg-destructive/5',
172
+ },
173
+ },
174
+ defaultVariants: { variant: 'default' },
175
+ },
176
+ );
177
+
178
+ function AccordionMenuItem({
179
+ className,
180
+ children,
181
+ variant,
182
+ asChild,
183
+ onClick,
184
+ ...props
185
+ }: React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item> &
186
+ VariantProps<typeof itemVariants> & {
187
+ onClick?: React.MouseEventHandler<HTMLElement>;
188
+ }) {
189
+ const { classNames, selectedValue, matchPath, onItemClick } = React.useContext(AccordionMenuContext);
190
+ return (
191
+ <AccordionPrimitive.Item className="flex" {...props}>
192
+ <AccordionPrimitive.Header className="flex w-full">
193
+ <AccordionPrimitive.Trigger
194
+ asChild={asChild}
195
+ data-slot="accordion-menu-item"
196
+ className={cn(itemVariants({ variant }), classNames?.item, className)}
197
+ onClick={(e) => {
198
+ onItemClick?.(props.value, e);
199
+ onClick?.(e);
200
+ e.preventDefault();
201
+ }}
202
+ onKeyDown={(e) => {
203
+ if (e.key === 'Enter') {
204
+ e.preventDefault();
205
+ (e.currentTarget.firstElementChild as HTMLElement | null)?.click();
206
+ }
207
+ }}
208
+ data-selected={matchPath(props.value as string) || selectedValue === props.value ? 'true' : undefined}
209
+ >
210
+ {children}
211
+ </AccordionPrimitive.Trigger>
212
+ </AccordionPrimitive.Header>
213
+ </AccordionPrimitive.Item>
214
+ );
215
+ }
216
+
217
+ function AccordionMenuSub({ className, children, ...props }: React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>) {
218
+ const { classNames } = React.useContext(AccordionMenuContext);
219
+ return (
220
+ <AccordionPrimitive.Item data-slot="accordion-menu-sub" className={cn(classNames?.sub, className)} {...props}>
221
+ {children}
222
+ </AccordionPrimitive.Item>
223
+ );
224
+ }
225
+
226
+ function AccordionMenuSubTrigger({ className, children }: React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>) {
227
+ const { classNames } = React.useContext(AccordionMenuContext);
228
+ return (
229
+ <AccordionPrimitive.Header className="flex">
230
+ <AccordionPrimitive.Trigger
231
+ data-slot="accordion-menu-sub-trigger"
232
+ className={cn(
233
+ 'w-full relative flex items-center cursor-pointer select-none text-start rounded-lg gap-2 px-2 py-1.5 text-sm outline-hidden text-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([role=img]):not([class*=text-])]:opacity-60 [&_svg:not([class*=size-])]:size-4 [&_svg]:shrink-0',
234
+ classNames?.subTrigger,
235
+ className,
236
+ )}
237
+ >
238
+ {children}
239
+ <ChevronDown
240
+ data-slot="accordion-menu-sub-indicator"
241
+ className="ms-auto size-3.5! shrink-0 text-muted-foreground transition-transform duration-200 [[data-state=open]>&]:-rotate-180"
242
+ />
243
+ </AccordionPrimitive.Trigger>
244
+ </AccordionPrimitive.Header>
245
+ );
246
+ }
247
+
248
+ type AccordionMenuSubContentProps = (
249
+ | (React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content> & {
250
+ type: 'single';
251
+ collapsible: boolean;
252
+ defaultValue?: string;
253
+ })
254
+ | (React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content> & {
255
+ type: 'multiple';
256
+ collapsible?: boolean;
257
+ defaultValue?: string | string[];
258
+ })
259
+ ) & { parentValue: string };
260
+
261
+ function AccordionMenuSubContent({ className, children, type, collapsible, defaultValue, parentValue, ...props }: AccordionMenuSubContentProps) {
262
+ const { nestedStates, setNestedStates, classNames } = React.useContext(AccordionMenuContext);
263
+ let currentValue: string | string[];
264
+ if (type === 'multiple') {
265
+ const sv = nestedStates[parentValue];
266
+ currentValue = Array.isArray(sv) ? sv : typeof sv === 'string' ? [sv] : defaultValue ? (Array.isArray(defaultValue) ? defaultValue : [defaultValue]) : [];
267
+ } else {
268
+ currentValue = (nestedStates[parentValue] ?? defaultValue ?? '') as string;
269
+ }
270
+
271
+ return (
272
+ <AccordionPrimitive.Content
273
+ data-slot="accordion-menu-sub-content"
274
+ className={cn('ps-5 overflow-hidden transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down', classNames?.subContent, className)}
275
+ {...props}
276
+ >
277
+ {type === 'multiple' ? (
278
+ <AccordionPrimitive.Root
279
+ className={cn('w-full py-0.5', classNames?.subWrapper)}
280
+ type="multiple"
281
+ value={currentValue as string[]}
282
+ role="menu"
283
+ data-slot="accordion-menu-sub-wrapper"
284
+ onValueChange={(value: string[]) => setNestedStates((prev) => ({ ...prev, [parentValue]: value }))}
285
+ >
286
+ {children}
287
+ </AccordionPrimitive.Root>
288
+ ) : (
289
+ <AccordionPrimitive.Root
290
+ className={cn('w-full py-0.5', classNames?.subWrapper)}
291
+ type="single"
292
+ collapsible={collapsible}
293
+ value={currentValue as string}
294
+ role="menu"
295
+ data-slot="accordion-menu-sub-wrapper"
296
+ onValueChange={(value: string) => setNestedStates((prev) => ({ ...prev, [parentValue]: value }))}
297
+ >
298
+ {children}
299
+ </AccordionPrimitive.Root>
300
+ )}
301
+ </AccordionPrimitive.Content>
302
+ );
303
+ }
304
+
305
+ function AccordionMenuIndicator({ className, ...props }: React.ComponentPropsWithoutRef<'span'>) {
306
+ const { classNames } = React.useContext(AccordionMenuContext);
307
+ return (
308
+ <span aria-hidden="true" data-slot="accordion-menu-indicator" className={cn('ms-auto flex items-center font-medium', classNames?.indicator, className)} {...props} />
309
+ );
310
+ }
311
+
312
+ export {
313
+ AccordionMenu,
314
+ AccordionMenuGroup,
315
+ AccordionMenuIndicator,
316
+ AccordionMenuItem,
317
+ AccordionMenuLabel,
318
+ AccordionMenuSeparator,
319
+ AccordionMenuSub,
320
+ AccordionMenuSubContent,
321
+ AccordionMenuSubTrigger,
322
+ };
@@ -0,0 +1,133 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import * as AccordionPrimitive from '@radix-ui/react-accordion';
5
+ import { ChevronDown, Plus } from 'lucide-react';
6
+ import { cva, type VariantProps } from 'class-variance-authority';
7
+ import { cn } from '../../lib/utils';
8
+
9
+ const accordionRootVariants = cva('', {
10
+ variants: {
11
+ variant: {
12
+ default: '',
13
+ outline: 'space-y-2',
14
+ solid: 'space-y-2',
15
+ },
16
+ },
17
+ defaultVariants: { variant: 'default' },
18
+ });
19
+
20
+ const accordionItemVariants = cva('', {
21
+ variants: {
22
+ variant: {
23
+ default: 'border-b border-border',
24
+ outline: 'border border-border rounded-lg px-4',
25
+ solid: 'rounded-lg bg-accent/70 px-4',
26
+ },
27
+ },
28
+ defaultVariants: { variant: 'default' },
29
+ });
30
+
31
+ const accordionTriggerVariants = cva(
32
+ 'flex flex-1 items-center justify-between py-4 gap-2.5 text-foreground font-medium transition-all [&[data-state=open]>svg]:rotate-180 cursor-pointer',
33
+ {
34
+ variants: {
35
+ variant: { default: '', outline: '', solid: '' },
36
+ indicator: {
37
+ arrow: '',
38
+ plus: '[&>svg>path:last-child]:origin-center [&>svg>path:last-child]:transition-all [&>svg>path:last-child]:duration-200 [&[data-state=open]>svg>path:last-child]:rotate-90 [&[data-state=open]>svg>path:last-child]:opacity-0 [&[data-state=open]>svg]:rotate-180',
39
+ none: '',
40
+ },
41
+ },
42
+ defaultVariants: { variant: 'default', indicator: 'arrow' },
43
+ },
44
+ );
45
+
46
+ const accordionContentVariants = cva(
47
+ 'overflow-hidden text-sm text-accent-foreground transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',
48
+ {
49
+ variants: { variant: { default: '', outline: '', solid: '' } },
50
+ defaultVariants: { variant: 'default' },
51
+ },
52
+ );
53
+
54
+ type AccordionContextType = {
55
+ variant?: 'default' | 'outline' | 'solid';
56
+ indicator?: 'arrow' | 'plus' | 'none';
57
+ };
58
+ const AccordionContext = React.createContext<AccordionContextType>({
59
+ variant: 'default',
60
+ indicator: 'arrow',
61
+ });
62
+
63
+ function Accordion(
64
+ props: React.ComponentProps<typeof AccordionPrimitive.Root> &
65
+ VariantProps<typeof accordionRootVariants> & {
66
+ indicator?: 'arrow' | 'plus';
67
+ },
68
+ ) {
69
+ const { className, variant = 'default', indicator = 'arrow', children, ...rest } = props;
70
+ return (
71
+ <AccordionContext.Provider value={{ variant: variant || 'default', indicator }}>
72
+ <AccordionPrimitive.Root
73
+ data-slot="accordion"
74
+ className={cn(accordionRootVariants({ variant }), className)}
75
+ {...rest}
76
+ >
77
+ {children}
78
+ </AccordionPrimitive.Root>
79
+ </AccordionContext.Provider>
80
+ );
81
+ }
82
+
83
+ function AccordionItem(props: React.ComponentProps<typeof AccordionPrimitive.Item>) {
84
+ const { className, children, ...rest } = props;
85
+ const { variant } = React.useContext(AccordionContext);
86
+ return (
87
+ <AccordionPrimitive.Item
88
+ data-slot="accordion-item"
89
+ className={cn(accordionItemVariants({ variant }), className)}
90
+ {...rest}
91
+ >
92
+ {children}
93
+ </AccordionPrimitive.Item>
94
+ );
95
+ }
96
+
97
+ function AccordionTrigger(props: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
98
+ const { className, children, ...rest } = props;
99
+ const { variant, indicator } = React.useContext(AccordionContext);
100
+ return (
101
+ <AccordionPrimitive.Header className="flex">
102
+ <AccordionPrimitive.Trigger
103
+ data-slot="accordion-trigger"
104
+ className={cn(accordionTriggerVariants({ variant, indicator }), className)}
105
+ {...rest}
106
+ >
107
+ {children}
108
+ {indicator === 'plus' && (
109
+ <Plus className="size-4 shrink-0 transition-transform duration-200" strokeWidth={1} />
110
+ )}
111
+ {indicator === 'arrow' && (
112
+ <ChevronDown className="size-4 shrink-0 transition-transform duration-200" strokeWidth={1} />
113
+ )}
114
+ </AccordionPrimitive.Trigger>
115
+ </AccordionPrimitive.Header>
116
+ );
117
+ }
118
+
119
+ function AccordionContent(props: React.ComponentProps<typeof AccordionPrimitive.Content>) {
120
+ const { className, children, ...rest } = props;
121
+ const { variant } = React.useContext(AccordionContext);
122
+ return (
123
+ <AccordionPrimitive.Content
124
+ data-slot="accordion-content"
125
+ className={cn(accordionContentVariants({ variant }), className)}
126
+ {...rest}
127
+ >
128
+ <div className={cn('pb-5 pt-0', className)}>{children}</div>
129
+ </AccordionPrimitive.Content>
130
+ );
131
+ }
132
+
133
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
@@ -0,0 +1,82 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
5
+ import { type VariantProps } from 'class-variance-authority';
6
+ import { cn } from '../../lib/utils';
7
+ import { buttonVariants } from '../../controls/Button';
8
+
9
+ function AlertDialog({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
10
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
11
+ }
12
+
13
+ function AlertDialogTrigger({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
14
+ return <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />;
15
+ }
16
+
17
+ function AlertDialogPortal({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
18
+ return <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />;
19
+ }
20
+
21
+ function AlertDialogOverlay({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
22
+ return (
23
+ <AlertDialogPrimitive.Overlay
24
+ data-slot="alert-dialog-overlay"
25
+ className={cn(
26
+ 'fixed inset-0 z-50 bg-black/30 [backdrop-filter:blur(4px)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
27
+ className,
28
+ )}
29
+ {...props}
30
+ />
31
+ );
32
+ }
33
+
34
+ function AlertDialogContent({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
35
+ return (
36
+ <AlertDialogPortal>
37
+ <AlertDialogOverlay />
38
+ <AlertDialogPrimitive.Content
39
+ data-slot="alert-dialog-content"
40
+ className={cn(
41
+ 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg shadow-black/5 duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 sm:rounded-lg',
42
+ className,
43
+ )}
44
+ {...props}
45
+ />
46
+ </AlertDialogPortal>
47
+ );
48
+ }
49
+
50
+ const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
51
+ <div data-slot="alert-dialog-header" className={cn('flex flex-col space-y-2 text-center sm:text-left', className)} {...props} />
52
+ );
53
+
54
+ const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
55
+ <div data-slot="alert-dialog-footer" className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2.5', className)} {...props} />
56
+ );
57
+
58
+ function AlertDialogTitle({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
59
+ return <AlertDialogPrimitive.Title data-slot="alert-dialog-title" className={cn('text-lg font-semibold', className)} {...props} />;
60
+ }
61
+
62
+ function AlertDialogDescription({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
63
+ return <AlertDialogPrimitive.Description data-slot="alert-dialog-description" className={cn('text-sm text-muted-foreground', className)} {...props} />;
64
+ }
65
+
66
+ function AlertDialogAction({
67
+ className,
68
+ variant,
69
+ ...props
70
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Action> & VariantProps<typeof buttonVariants>) {
71
+ return <AlertDialogPrimitive.Action data-slot="alert-dialog-action" className={cn(buttonVariants({ variant }), className)} {...props} />;
72
+ }
73
+
74
+ function AlertDialogCancel({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
75
+ return <AlertDialogPrimitive.Cancel data-slot="alert-dialog-cancel" className={cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', className)} {...props} />;
76
+ }
77
+
78
+ export {
79
+ AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
80
+ AlertDialogDescription, AlertDialogFooter, AlertDialogHeader,
81
+ AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger,
82
+ };
@@ -0,0 +1,63 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority';
2
+ import * as React from 'react';
3
+
4
+ import { cn } from '../../lib/utils';
5
+
6
+ const alertVariants = cva(
7
+ 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: 'bg-background text-foreground',
12
+ destructive:
13
+ 'text-destructive-foreground [&>svg]:text-current *:data-[slot=alert-description]:text-destructive-foreground/80',
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: 'default',
18
+ },
19
+ },
20
+ );
21
+
22
+ function Alert({
23
+ className,
24
+ variant,
25
+ ...props
26
+ }: React.ComponentProps<'div'> & VariantProps<typeof alertVariants>) {
27
+ return (
28
+ <div
29
+ data-slot="alert"
30
+ role="alert"
31
+ className={cn(alertVariants({ variant }), className)}
32
+ {...props}
33
+ />
34
+ );
35
+ }
36
+
37
+ function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
38
+ return (
39
+ <div
40
+ data-slot="alert-title"
41
+ className={cn(
42
+ 'col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight',
43
+ className,
44
+ )}
45
+ {...props}
46
+ />
47
+ );
48
+ }
49
+
50
+ function AlertDescription({ className, ...props }: React.ComponentProps<'div'>) {
51
+ return (
52
+ <div
53
+ data-slot="alert-description"
54
+ className={cn(
55
+ 'text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed',
56
+ className,
57
+ )}
58
+ {...props}
59
+ />
60
+ );
61
+ }
62
+
63
+ export { Alert, AlertTitle, AlertDescription };