@devxcrew/react-ui 0.0.0-stage → 0.1.7

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 (326) hide show
  1. package/README.md +85 -2
  2. package/package.json +105 -4
  3. package/src/blocks/agent-chat-workspace/agent-chat-workspace.tsx +162 -0
  4. package/src/blocks/agent-chat-workspace/index.ts +2 -0
  5. package/src/blocks/agent-provider-settings/agent-provider-settings.tsx +183 -0
  6. package/src/blocks/agent-provider-settings/index.ts +2 -0
  7. package/src/blocks/agent-task-workspace/agent-task-workspace.tsx +48 -0
  8. package/src/blocks/agent-task-workspace/index.ts +2 -0
  9. package/src/blocks/analysis-context/analysis-context.tsx +6 -0
  10. package/src/blocks/analysis-context/index.ts +1 -0
  11. package/src/blocks/app-header/app-header.tsx +159 -0
  12. package/src/blocks/app-header/index.ts +2 -0
  13. package/src/blocks/archived-chat-workspace/archived-chat-workspace.tsx +29 -0
  14. package/src/blocks/archived-chat-workspace/index.ts +2 -0
  15. package/src/blocks/auth/README.md +32 -0
  16. package/src/blocks/auth/admin-login-page.tsx +15 -0
  17. package/src/blocks/auth/admin-password-forgot-page.tsx +5 -0
  18. package/src/blocks/auth/auth-page-layout.tsx +90 -0
  19. package/src/blocks/auth/client-login-page.tsx +20 -0
  20. package/src/blocks/auth/client-password-forgot-page.tsx +5 -0
  21. package/src/blocks/auth/identity-management-desk.tsx +530 -0
  22. package/src/blocks/auth/index.ts +12 -0
  23. package/src/blocks/auth/login-page.tsx +206 -0
  24. package/src/blocks/auth/password-forgot-page.tsx +60 -0
  25. package/src/blocks/auth/portal-pages.tsx +32 -0
  26. package/src/blocks/auth/privileged-desk.tsx +55 -0
  27. package/src/blocks/auth/register-page.tsx +117 -0
  28. package/src/blocks/auth/session-boundary.tsx +296 -0
  29. package/src/blocks/auth/super-admin-login-page.tsx +15 -0
  30. package/src/blocks/auth/super-admin-password-forgot-page.tsx +5 -0
  31. package/src/blocks/blog/blog-card.tsx +156 -0
  32. package/src/blocks/blog/blog-reader.tsx +175 -0
  33. package/src/blocks/blog/blog-types.ts +47 -0
  34. package/src/blocks/blog/index.ts +3 -0
  35. package/src/blocks/chat-composer/chat-composer.tsx +140 -0
  36. package/src/blocks/chat-composer/index.ts +1 -0
  37. package/src/blocks/chat-history/chat-history.tsx +59 -0
  38. package/src/blocks/chat-history/index.ts +1 -0
  39. package/src/blocks/chat-response-progress/chat-response-progress.tsx +34 -0
  40. package/src/blocks/chat-response-progress/index.ts +2 -0
  41. package/src/blocks/chat-runtime-controls/chat-runtime-controls.tsx +56 -0
  42. package/src/blocks/chat-runtime-controls/index.ts +2 -0
  43. package/src/blocks/chat-runtime-trace/chat-runtime-trace.tsx +71 -0
  44. package/src/blocks/chat-runtime-trace/index.ts +2 -0
  45. package/src/blocks/codex-connection-settings/codex-connection-settings.tsx +42 -0
  46. package/src/blocks/codex-connection-settings/index.ts +1 -0
  47. package/src/blocks/dropzone/dropzone-file-item.tsx +113 -0
  48. package/src/blocks/dropzone/dropzone-types.ts +32 -0
  49. package/src/blocks/dropzone/dropzone.tsx +216 -0
  50. package/src/blocks/dropzone/index.ts +3 -0
  51. package/src/blocks/ecommerce/cart/cart-drawer.tsx +228 -0
  52. package/src/blocks/ecommerce/cart/cart-types.ts +27 -0
  53. package/src/blocks/ecommerce/cart/index.ts +2 -0
  54. package/src/blocks/ecommerce/categories/categories-types.ts +23 -0
  55. package/src/blocks/ecommerce/categories/category-showcase.tsx +125 -0
  56. package/src/blocks/ecommerce/categories/index.ts +2 -0
  57. package/src/blocks/ecommerce/checkout/checkout-types.ts +48 -0
  58. package/src/blocks/ecommerce/checkout/checkout-wizard.tsx +468 -0
  59. package/src/blocks/ecommerce/checkout/index.ts +2 -0
  60. package/src/blocks/ecommerce/comparison/comparison-types.ts +28 -0
  61. package/src/blocks/ecommerce/comparison/index.ts +2 -0
  62. package/src/blocks/ecommerce/comparison/product-comparison.tsx +174 -0
  63. package/src/blocks/ecommerce/coupon-wallet/coupon-wallet-types.ts +21 -0
  64. package/src/blocks/ecommerce/coupon-wallet/coupon-wallet.tsx +154 -0
  65. package/src/blocks/ecommerce/coupon-wallet/index.ts +2 -0
  66. package/src/blocks/ecommerce/delivery-tracker/delivery-tracker-types.ts +20 -0
  67. package/src/blocks/ecommerce/delivery-tracker/delivery-tracker.tsx +169 -0
  68. package/src/blocks/ecommerce/delivery-tracker/index.ts +2 -0
  69. package/src/blocks/ecommerce/index.ts +12 -0
  70. package/src/blocks/ecommerce/payment-methods/index.ts +2 -0
  71. package/src/blocks/ecommerce/payment-methods/payment-method-selector.tsx +224 -0
  72. package/src/blocks/ecommerce/payment-methods/payment-methods-types.ts +21 -0
  73. package/src/blocks/ecommerce/price-history/index.ts +2 -0
  74. package/src/blocks/ecommerce/price-history/price-history-chart.tsx +217 -0
  75. package/src/blocks/ecommerce/price-history/price-history-types.ts +16 -0
  76. package/src/blocks/ecommerce/reviews/index.ts +2 -0
  77. package/src/blocks/ecommerce/reviews/reviews-section.tsx +309 -0
  78. package/src/blocks/ecommerce/reviews/reviews-types.ts +26 -0
  79. package/src/blocks/ecommerce/wishlist/index.ts +2 -0
  80. package/src/blocks/ecommerce/wishlist/wishlist-grid.tsx +149 -0
  81. package/src/blocks/ecommerce/wishlist/wishlist-types.ts +19 -0
  82. package/src/blocks/execution-status/README.md +25 -0
  83. package/src/blocks/execution-status/execution-status.tsx +135 -0
  84. package/src/blocks/execution-status/index.ts +2 -0
  85. package/src/blocks/file-tree/file-icons.tsx +84 -0
  86. package/src/blocks/file-tree/file-tree-node.tsx +158 -0
  87. package/src/blocks/file-tree/file-tree-types.ts +33 -0
  88. package/src/blocks/file-tree/file-tree.tsx +163 -0
  89. package/src/blocks/file-tree/index.ts +5 -0
  90. package/src/blocks/filter-builder/filter-builder.tsx +169 -0
  91. package/src/blocks/filter-builder/filter-row.tsx +164 -0
  92. package/src/blocks/filter-builder/filter-types.ts +61 -0
  93. package/src/blocks/filter-builder/index.ts +12 -0
  94. package/src/blocks/footer/footer-types.ts +35 -0
  95. package/src/blocks/footer/index.ts +2 -0
  96. package/src/blocks/footer/site-footer.tsx +212 -0
  97. package/src/blocks/form/form-block.tsx +113 -0
  98. package/src/blocks/form/form-lookup-field.tsx +60 -0
  99. package/src/blocks/form/index.ts +4 -0
  100. package/src/blocks/handover-stack/handover-stack.tsx +41 -0
  101. package/src/blocks/handover-stack/index.ts +1 -0
  102. package/src/blocks/idea-handover/idea-handover-readiness.ts +41 -0
  103. package/src/blocks/idea-handover/idea-handover-workspace.tsx +150 -0
  104. package/src/blocks/idea-handover/index.ts +4 -0
  105. package/src/blocks/kanban/index.ts +11 -0
  106. package/src/blocks/kanban/kanban-board.tsx +146 -0
  107. package/src/blocks/kanban/kanban-card.tsx +149 -0
  108. package/src/blocks/kanban/kanban-column.tsx +102 -0
  109. package/src/blocks/kanban/kanban-types.ts +38 -0
  110. package/src/blocks/loader/global-loader.tsx +90 -0
  111. package/src/blocks/loader/index.ts +1 -0
  112. package/src/blocks/mascot/README.md +21 -0
  113. package/src/blocks/mascot/index.ts +2 -0
  114. package/src/blocks/mascot/mascot.tsx +202 -0
  115. package/src/blocks/master-list/README.md +21 -0
  116. package/src/blocks/master-list/index.ts +12 -0
  117. package/src/blocks/master-list/master-form.tsx +157 -0
  118. package/src/blocks/master-list/master-list-desk.tsx +370 -0
  119. package/src/blocks/master-list/master-list.tsx +189 -0
  120. package/src/blocks/master-list/types.ts +18 -0
  121. package/src/blocks/mermaid-preview/index.ts +1 -0
  122. package/src/blocks/mermaid-preview/mermaid-preview.tsx +164 -0
  123. package/src/blocks/notifications/README.md +7 -0
  124. package/src/blocks/notifications/index.ts +1 -0
  125. package/src/blocks/notifications/notification-center-page.tsx +73 -0
  126. package/src/blocks/pricing/index.ts +7 -0
  127. package/src/blocks/pricing/pricing-table.tsx +173 -0
  128. package/src/blocks/pricing/pricing-types.ts +34 -0
  129. package/src/blocks/product-card/index.ts +2 -0
  130. package/src/blocks/product-card/product-card-types.ts +35 -0
  131. package/src/blocks/product-card/product-card.tsx +183 -0
  132. package/src/blocks/table/data-table-block.tsx +313 -0
  133. package/src/blocks/table/data-table-filter-menu.tsx +48 -0
  134. package/src/blocks/table/data-table-icon-menu-trigger.tsx +44 -0
  135. package/src/blocks/table/data-table-menu-content.tsx +32 -0
  136. package/src/blocks/table/data-table-pagination.tsx +149 -0
  137. package/src/blocks/table/data-table-row-actions.tsx +53 -0
  138. package/src/blocks/table/data-table-status.tsx +42 -0
  139. package/src/blocks/table/data-table-toolbar.tsx +124 -0
  140. package/src/blocks/table/data-table-totals.tsx +31 -0
  141. package/src/blocks/table/index.ts +13 -0
  142. package/src/blocks/workspace/index.ts +19 -0
  143. package/src/blocks/workspace/workspace-action-card.tsx +73 -0
  144. package/src/blocks/workspace/workspace-application-header.tsx +67 -0
  145. package/src/blocks/workspace/workspace-entity-card.tsx +48 -0
  146. package/src/blocks/workspace/workspace-health-summary.tsx +15 -0
  147. package/src/blocks/workspace/workspace-metric-card.tsx +97 -0
  148. package/src/blocks/workspace/workspace-page-header.tsx +43 -0
  149. package/src/blocks/workspace/workspace-publish-status.tsx +15 -0
  150. package/src/blocks/workspace/workspace-route-checklist.tsx +28 -0
  151. package/src/blocks/workspace/workspace-runtime-status.tsx +48 -0
  152. package/src/blocks/workspace/workspace-section-card.tsx +42 -0
  153. package/src/components/accordion.tsx +65 -0
  154. package/src/components/alert-dialog.tsx +160 -0
  155. package/src/components/alert.tsx +68 -0
  156. package/src/components/aspect-ratio.tsx +22 -0
  157. package/src/components/attachment.tsx +195 -0
  158. package/src/components/avatar.tsx +90 -0
  159. package/src/components/badge.tsx +55 -0
  160. package/src/components/breadcrumb.tsx +100 -0
  161. package/src/components/bubble.tsx +123 -0
  162. package/src/components/button-group.tsx +79 -0
  163. package/src/components/button.tsx +71 -0
  164. package/src/components/calendar.tsx +185 -0
  165. package/src/components/card.tsx +100 -0
  166. package/src/components/carousel.tsx +229 -0
  167. package/src/components/chart.tsx +338 -0
  168. package/src/components/checkbox.tsx +27 -0
  169. package/src/components/collapsible.tsx +15 -0
  170. package/src/components/combobox.tsx +269 -0
  171. package/src/components/command.tsx +181 -0
  172. package/src/components/compact-model-switcher.tsx +128 -0
  173. package/src/components/context-menu.tsx +244 -0
  174. package/src/components/dialog.tsx +138 -0
  175. package/src/components/direction.tsx +1 -0
  176. package/src/components/drawer.tsx +208 -0
  177. package/src/components/dropdown-menu.tsx +257 -0
  178. package/src/components/empty.tsx +93 -0
  179. package/src/components/field.tsx +232 -0
  180. package/src/components/hover-card.tsx +45 -0
  181. package/src/components/input-group.tsx +146 -0
  182. package/src/components/input-otp.tsx +83 -0
  183. package/src/components/input.tsx +19 -0
  184. package/src/components/item.tsx +188 -0
  185. package/src/components/kbd.tsx +26 -0
  186. package/src/components/label.tsx +17 -0
  187. package/src/components/markdown-content.tsx +113 -0
  188. package/src/components/marker.tsx +67 -0
  189. package/src/components/menubar.tsx +267 -0
  190. package/src/components/message-scroller.tsx +91 -0
  191. package/src/components/message.tsx +84 -0
  192. package/src/components/native-select.tsx +54 -0
  193. package/src/components/navigation-menu.tsx +160 -0
  194. package/src/components/pagination.tsx +118 -0
  195. package/src/components/popover.tsx +74 -0
  196. package/src/components/progress.tsx +82 -0
  197. package/src/components/questionnaire.tsx +150 -0
  198. package/src/components/radio-group.tsx +35 -0
  199. package/src/components/resizable.tsx +41 -0
  200. package/src/components/rich-text-editor.tsx +604 -0
  201. package/src/components/scroll-area.tsx +47 -0
  202. package/src/components/select.tsx +189 -0
  203. package/src/components/separator.tsx +18 -0
  204. package/src/components/sheet.tsx +125 -0
  205. package/src/components/sidebar.tsx +693 -0
  206. package/src/components/skeleton.tsx +13 -0
  207. package/src/components/slider.tsx +51 -0
  208. package/src/components/sonner.tsx +45 -0
  209. package/src/components/sparkline.tsx +231 -0
  210. package/src/components/spinner.tsx +20 -0
  211. package/src/components/status-badge.tsx +114 -0
  212. package/src/components/switch.tsx +31 -0
  213. package/src/components/table.tsx +91 -0
  214. package/src/components/tabs.tsx +72 -0
  215. package/src/components/textarea.tsx +17 -0
  216. package/src/components/toast.tsx +222 -0
  217. package/src/components/toggle-group.tsx +85 -0
  218. package/src/components/toggle.tsx +42 -0
  219. package/src/components/tooltip.tsx +53 -0
  220. package/src/design-system/README.md +55 -0
  221. package/src/design-system/action-intents.ts +21 -0
  222. package/src/design-system/agent-manifest.ts +83 -0
  223. package/src/design-system/block-registry.ts +272 -0
  224. package/src/design-system/component-registry.ts +142 -0
  225. package/src/design-system/contracts.ts +69 -0
  226. package/src/design-system/defaults.ts +3 -0
  227. package/src/design-system/index.ts +29 -0
  228. package/src/design-system/mcp-server.ts +156 -0
  229. package/src/design-system/page-registry.ts +74 -0
  230. package/src/design-system/selection.ts +67 -0
  231. package/src/design-system/template-registry.ts +33 -0
  232. package/src/features/interface-topology/index.ts +14 -0
  233. package/src/features/interface-topology/interface-topology.types.ts +40 -0
  234. package/src/features/interface-topology/topology-control.tsx +18 -0
  235. package/src/features/interface-topology/topology-inspector.tsx +228 -0
  236. package/src/features/interface-topology/topology-marker.tsx +47 -0
  237. package/src/features/interface-topology/topology-region.tsx +44 -0
  238. package/src/features/interface-topology/use-interface-topology.ts +178 -0
  239. package/src/hooks/use-mobile.ts +19 -0
  240. package/src/hooks/use-scroll-follow.ts +22 -0
  241. package/src/index.ts +11 -0
  242. package/src/layouts/README.md +65 -0
  243. package/src/layouts/agent-workspace/agent-activity-rail.tsx +101 -0
  244. package/src/layouts/agent-workspace/agent-workspace.tsx +27 -0
  245. package/src/layouts/agent-workspace/agent-workspace.types.ts +28 -0
  246. package/src/layouts/agent-workspace/index.ts +7 -0
  247. package/src/layouts/blog-header/blog-header-types.ts +25 -0
  248. package/src/layouts/blog-header/blog-header.tsx +172 -0
  249. package/src/layouts/blog-header/index.ts +2 -0
  250. package/src/layouts/documentation-workspace/README.md +8 -0
  251. package/src/layouts/documentation-workspace/documentation-workspace.tsx +34 -0
  252. package/src/layouts/documentation-workspace/index.ts +1 -0
  253. package/src/layouts/ecommerce-header/ecommerce-actions.tsx +89 -0
  254. package/src/layouts/ecommerce-header/ecommerce-announcement.tsx +43 -0
  255. package/src/layouts/ecommerce-header/ecommerce-drawer.tsx +97 -0
  256. package/src/layouts/ecommerce-header/ecommerce-header-types.ts +59 -0
  257. package/src/layouts/ecommerce-header/ecommerce-header.tsx +47 -0
  258. package/src/layouts/ecommerce-header/ecommerce-navigation.tsx +98 -0
  259. package/src/layouts/ecommerce-header/ecommerce-search.tsx +86 -0
  260. package/src/layouts/ecommerce-header/index.ts +2 -0
  261. package/src/layouts/main-workspace/index.ts +28 -0
  262. package/src/layouts/mdi/index.ts +1 -0
  263. package/src/layouts/mdi/mdi.tsx +20 -0
  264. package/src/layouts/mdi-main/index.ts +27 -0
  265. package/src/layouts/mdi-main/mdi-app-catalog.generated.ts +159 -0
  266. package/src/layouts/mdi-main/mdi-app-catalog.ts +76 -0
  267. package/src/layouts/mdi-main/mdi-app-switcher.tsx +107 -0
  268. package/src/layouts/mdi-main/mdi-feature-settings.tsx +135 -0
  269. package/src/layouts/mdi-main/mdi-global-search.tsx +192 -0
  270. package/src/layouts/mdi-main/mdi-main.tsx +241 -0
  271. package/src/layouts/mdi-main/mdi-notifications-menu.tsx +197 -0
  272. package/src/layouts/mdi-main/mdi-profile-menu.tsx +79 -0
  273. package/src/layouts/mdi-main/mdi-sidebar-navigation.tsx +226 -0
  274. package/src/layouts/mdi-main/mdi-sidebar.tsx +110 -0
  275. package/src/layouts/mdi-main/mdi-status-bar.tsx +26 -0
  276. package/src/layouts/mdi-main/mdi-top-menu-control.ts +4 -0
  277. package/src/layouts/mdi-main/mdi-top-menu.tsx +117 -0
  278. package/src/layouts/mdi-main/mdi-topology.tsx +297 -0
  279. package/src/layouts/mdi-main/mdi-tweak-panel.tsx +104 -0
  280. package/src/layouts/mdi-main/mdi-types.ts +131 -0
  281. package/src/layouts/mdi-main/use-mdi-features.ts +36 -0
  282. package/src/layouts/mdi-main/use-mdi-sidebar-state.ts +59 -0
  283. package/src/layouts/site-header/announcement-bar.tsx +55 -0
  284. package/src/layouts/site-header/category-strip.tsx +41 -0
  285. package/src/layouts/site-header/index.ts +16 -0
  286. package/src/layouts/site-header/main-navbar.tsx +179 -0
  287. package/src/layouts/site-header/mobile-drawer.tsx +140 -0
  288. package/src/layouts/site-header/site-header-types.ts +63 -0
  289. package/src/layouts/site-header/site-header.tsx +58 -0
  290. package/src/lib/random-id.ts +19 -0
  291. package/src/lib/utils.ts +6 -0
  292. package/src/providers/pdf-provider.ts +137 -0
  293. package/src/styles/globals.css +374 -0
  294. package/src/templates/README.md +12 -0
  295. package/src/templates/dashboard-01/components/app-sidebar.tsx +177 -0
  296. package/src/templates/dashboard-01/components/chart-area-interactive.tsx +276 -0
  297. package/src/templates/dashboard-01/components/data-table-cell-viewer.tsx +213 -0
  298. package/src/templates/dashboard-01/components/data-table.tsx +577 -0
  299. package/src/templates/dashboard-01/components/nav-documents.tsx +79 -0
  300. package/src/templates/dashboard-01/components/nav-main.tsx +55 -0
  301. package/src/templates/dashboard-01/components/nav-secondary.tsx +39 -0
  302. package/src/templates/dashboard-01/components/nav-user.tsx +97 -0
  303. package/src/templates/dashboard-01/components/section-cards.tsx +99 -0
  304. package/src/templates/dashboard-01/components/site-header.tsx +14 -0
  305. package/src/templates/dashboard-01/data.json +614 -0
  306. package/src/templates/dashboard-01/index.ts +7 -0
  307. package/src/templates/documentation-sidebar/app-sidebar.tsx +95 -0
  308. package/src/templates/documentation-sidebar/index.ts +2 -0
  309. package/src/templates/sidebar-07/components/app-sidebar.tsx +175 -0
  310. package/src/templates/sidebar-07/components/nav-main.tsx +64 -0
  311. package/src/templates/sidebar-07/components/nav-projects.tsx +79 -0
  312. package/src/templates/sidebar-07/components/nav-user.tsx +105 -0
  313. package/src/templates/sidebar-07/components/team-switcher.tsx +94 -0
  314. package/src/templates/sidebar-07/index.ts +1 -0
  315. package/src/templates/ui-page/index.ts +7 -0
  316. package/src/templates/ui-page/ui-browser-frame.tsx +27 -0
  317. package/src/templates/ui-page/ui-template-code.tsx +48 -0
  318. package/src/templates/ui-page/ui-template-header.tsx +87 -0
  319. package/src/templates/ui-page/ui-template-navigation.tsx +60 -0
  320. package/src/templates/ui-page/ui-template-page.tsx +97 -0
  321. package/src/theme/index.ts +12 -0
  322. package/src/theme/theme-config.ts +30 -0
  323. package/src/theme/theme-provider.tsx +142 -0
  324. package/src/theme/theme-selector.tsx +75 -0
  325. package/src/tokens/index.ts +15 -0
  326. package/src/vite-env.d.ts +3 -0
@@ -0,0 +1,51 @@
1
+ import { Slider as SliderPrimitive } from '@base-ui/react/slider'
2
+ import { cn } from '../lib/utils'
3
+
4
+ function Slider({
5
+ className,
6
+ defaultValue,
7
+ value,
8
+ min = 0,
9
+ max = 100,
10
+ ...props
11
+ }: SliderPrimitive.Root.Props) {
12
+ const _values = Array.isArray(value)
13
+ ? value
14
+ : Array.isArray(defaultValue)
15
+ ? defaultValue
16
+ : [min, max]
17
+
18
+ return (
19
+ <SliderPrimitive.Root
20
+ className={cn('data-horizontal:w-full data-vertical:h-full', className)}
21
+ data-slot="slider"
22
+ defaultValue={defaultValue}
23
+ value={value}
24
+ min={min}
25
+ max={max}
26
+ thumbAlignment="edge"
27
+ {...props}
28
+ >
29
+ <SliderPrimitive.Control className="relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col">
30
+ <SliderPrimitive.Track
31
+ data-slot="slider-track"
32
+ className="relative grow overflow-hidden rounded-full bg-muted select-none data-horizontal:h-1 data-horizontal:w-full data-vertical:h-full data-vertical:w-1"
33
+ >
34
+ <SliderPrimitive.Indicator
35
+ data-slot="slider-range"
36
+ className="bg-primary select-none data-horizontal:h-full data-vertical:w-full"
37
+ />
38
+ </SliderPrimitive.Track>
39
+ {Array.from({ length: _values.length }, (_, index) => (
40
+ <SliderPrimitive.Thumb
41
+ data-slot="slider-thumb"
42
+ key={index}
43
+ className="relative block size-3 shrink-0 rounded-full border border-ring bg-white ring-ring/50 transition-[color,box-shadow] select-none after:absolute after:-inset-2 hover:ring-3 focus-visible:ring-3 focus-visible:outline-hidden active:ring-3 disabled:pointer-events-none disabled:opacity-50"
44
+ />
45
+ ))}
46
+ </SliderPrimitive.Control>
47
+ </SliderPrimitive.Root>
48
+ )
49
+ }
50
+
51
+ export { Slider }
@@ -0,0 +1,45 @@
1
+ 'use client'
2
+
3
+ import { Toaster as Sonner, type ToasterProps } from 'sonner'
4
+ import {
5
+ CircleCheckIcon,
6
+ InfoIcon,
7
+ TriangleAlertIcon,
8
+ OctagonXIcon,
9
+ Loader2Icon,
10
+ } from 'lucide-react'
11
+ import { useTheme } from '../theme/theme-provider'
12
+
13
+ const Toaster = ({ ...props }: ToasterProps) => {
14
+ const { theme = 'system' } = useTheme()
15
+
16
+ return (
17
+ <Sonner
18
+ theme={theme as ToasterProps['theme']}
19
+ className="toaster group"
20
+ icons={{
21
+ success: <CircleCheckIcon className="size-4" />,
22
+ info: <InfoIcon className="size-4" />,
23
+ warning: <TriangleAlertIcon className="size-4" />,
24
+ error: <OctagonXIcon className="size-4" />,
25
+ loading: <Loader2Icon className="size-4 animate-spin" />,
26
+ }}
27
+ style={
28
+ {
29
+ '--normal-bg': 'var(--popover)',
30
+ '--normal-text': 'var(--popover-foreground)',
31
+ '--normal-border': 'var(--border)',
32
+ '--border-radius': 'var(--radius)',
33
+ } as React.CSSProperties
34
+ }
35
+ toastOptions={{
36
+ classNames: {
37
+ toast: 'cn-toast',
38
+ },
39
+ }}
40
+ {...props}
41
+ />
42
+ )
43
+ }
44
+
45
+ export { Toaster }
@@ -0,0 +1,231 @@
1
+ import { useId } from 'react'
2
+ import { cn } from '../lib/utils'
3
+
4
+ export type SparklineTone = 'primary' | 'positive' | 'negative' | 'neutral' | 'warning'
5
+ export type SparklineType = 'line' | 'area' | 'bar' | 'trend'
6
+
7
+ export interface SparklineDatum {
8
+ value: number
9
+ }
10
+
11
+ export interface SparklineProps {
12
+ ariaLabel?: string
13
+ className?: string
14
+ data: readonly number[] | readonly SparklineDatum[]
15
+ height?: number
16
+ showDot?: boolean
17
+ showMinMax?: boolean
18
+ showTrendBadge?: boolean
19
+ strokeWidth?: number
20
+ tone?: SparklineTone
21
+ trendLabel?: string
22
+ type?: SparklineType
23
+ width?: number
24
+ }
25
+
26
+ const toneStyles: Record<
27
+ SparklineTone,
28
+ {
29
+ badge: string
30
+ bar: string
31
+ dot: string
32
+ stopColor: string
33
+ stroke: string
34
+ }
35
+ > = {
36
+ primary: {
37
+ badge: 'bg-primary/10 text-primary',
38
+ bar: 'fill-primary',
39
+ dot: 'fill-primary stroke-background',
40
+ stopColor: 'currentColor',
41
+ stroke: 'stroke-primary text-primary',
42
+ },
43
+ positive: {
44
+ badge: 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400',
45
+ bar: 'fill-emerald-500',
46
+ dot: 'fill-emerald-500 stroke-background',
47
+ stopColor: '#10b981',
48
+ stroke: 'stroke-emerald-600 dark:stroke-emerald-400',
49
+ },
50
+ negative: {
51
+ badge: 'bg-destructive/10 text-destructive',
52
+ bar: 'fill-destructive',
53
+ dot: 'fill-destructive stroke-background',
54
+ stopColor: '#ef4444',
55
+ stroke: 'stroke-destructive',
56
+ },
57
+ neutral: {
58
+ badge: 'bg-muted text-muted-foreground',
59
+ bar: 'fill-muted-foreground/60',
60
+ dot: 'fill-muted-foreground stroke-background',
61
+ stopColor: '#9ca3af',
62
+ stroke: 'stroke-muted-foreground',
63
+ },
64
+ warning: {
65
+ badge: 'bg-amber-500/10 text-amber-600 dark:text-amber-400',
66
+ bar: 'fill-amber-500',
67
+ dot: 'fill-amber-500 stroke-background',
68
+ stopColor: '#f59e0b',
69
+ stroke: 'stroke-amber-600 dark:stroke-amber-400',
70
+ },
71
+ }
72
+
73
+ export function Sparkline({
74
+ ariaLabel = 'Data trend sparkline',
75
+ className,
76
+ data,
77
+ height = 32,
78
+ showDot = true,
79
+ showMinMax = false,
80
+ showTrendBadge = false,
81
+ strokeWidth = 2,
82
+ tone = 'primary',
83
+ trendLabel,
84
+ type = 'line',
85
+ width = 120,
86
+ }: SparklineProps) {
87
+ const gradientId = useId()
88
+ const rawValues = data.map((d) => (typeof d === 'number' ? d : d.value))
89
+
90
+ if (rawValues.length === 0) {
91
+ return (
92
+ <div
93
+ aria-label="Empty sparkline"
94
+ className={cn('inline-flex items-center text-xs text-muted-foreground', className)}
95
+ style={{ height, width }}
96
+ >
97
+ --
98
+ </div>
99
+ )
100
+ }
101
+
102
+ const min = Math.min(...rawValues)
103
+ const max = Math.max(...rawValues)
104
+ const range = max - min === 0 ? 1 : max - min
105
+ const padding = strokeWidth + 2
106
+ const innerWidth = Math.max(10, width - padding * 2)
107
+ const innerHeight = Math.max(10, height - padding * 2)
108
+
109
+ const points = rawValues.map((val, idx) => {
110
+ const x = padding + (idx / Math.max(1, rawValues.length - 1)) * innerWidth
111
+ const y = padding + innerHeight - ((val - min) / range) * innerHeight
112
+ return { val, x, y }
113
+ })
114
+
115
+ const firstVal = rawValues[0]
116
+ const lastVal = rawValues[rawValues.length - 1]
117
+ const calculatedDelta =
118
+ firstVal !== 0 ? (((lastVal - firstVal) / Math.abs(firstVal)) * 100).toFixed(1) : '0'
119
+ const isPositive = lastVal >= firstVal
120
+ const deltaBadgeText = trendLabel ?? (isPositive ? `+${calculatedDelta}%` : `${calculatedDelta}%`)
121
+
122
+ const pathD = points.reduce((acc, pt, idx) => {
123
+ if (idx === 0) return `M ${pt.x.toFixed(1)} ${pt.y.toFixed(1)}`
124
+ return `${acc} L ${pt.x.toFixed(1)} ${pt.y.toFixed(1)}`
125
+ }, '')
126
+
127
+ const lastPoint = points[points.length - 1]
128
+ const firstPoint = points[0]
129
+ const areaD = `${pathD} L ${lastPoint.x.toFixed(1)} ${(height - padding / 2).toFixed(1)} L ${firstPoint.x.toFixed(1)} ${(height - padding / 2).toFixed(1)} Z`
130
+
131
+ const selectedTone = toneStyles[tone]
132
+
133
+ return (
134
+ <div
135
+ aria-label={ariaLabel}
136
+ className={cn('inline-flex items-center gap-2', className)}
137
+ role="img"
138
+ >
139
+ <svg
140
+ className="overflow-visible"
141
+ height={height}
142
+ viewBox={`0 0 ${width} ${height}`}
143
+ width={width}
144
+ >
145
+ <defs>
146
+ <linearGradient id={gradientId} x1="0" x2="0" y1="0" y2="1">
147
+ <stop offset="0%" stopColor={selectedTone.stopColor} stopOpacity={0.35} />
148
+ <stop offset="100%" stopColor={selectedTone.stopColor} stopOpacity={0.0} />
149
+ </linearGradient>
150
+ </defs>
151
+
152
+ {type === 'bar' ? (
153
+ <g>
154
+ {points.map((pt, idx) => {
155
+ const barWidth = Math.max(2, (innerWidth / rawValues.length) * 0.7)
156
+ const barHeight = Math.max(2, innerHeight - (pt.y - padding))
157
+ const barX = pt.x - barWidth / 2
158
+ const barY = pt.y
159
+ return (
160
+ <rect
161
+ className={selectedTone.bar}
162
+ height={barHeight}
163
+ key={idx}
164
+ rx={1.5}
165
+ width={barWidth}
166
+ x={barX}
167
+ y={barY}
168
+ />
169
+ )
170
+ })}
171
+ </g>
172
+ ) : (
173
+ <g>
174
+ {(type === 'area' || type === 'trend') && (
175
+ <path d={areaD} fill={`url(#${gradientId})`} />
176
+ )}
177
+ <path
178
+ className={cn(selectedTone.stroke, 'fill-none')}
179
+ d={pathD}
180
+ strokeLinecap="round"
181
+ strokeLinejoin="round"
182
+ strokeWidth={strokeWidth}
183
+ />
184
+ {showDot && (
185
+ <circle
186
+ className={selectedTone.dot}
187
+ cx={lastPoint.x}
188
+ cy={lastPoint.y}
189
+ r={strokeWidth + 1.5}
190
+ strokeWidth={1.5}
191
+ />
192
+ )}
193
+ {showMinMax && (
194
+ <>
195
+ {points.find((p) => p.val === max) && (
196
+ <circle
197
+ className="fill-emerald-500 stroke-background"
198
+ cx={points.find((p) => p.val === max)!.x}
199
+ cy={points.find((p) => p.val === max)!.y}
200
+ r={2.5}
201
+ strokeWidth={1}
202
+ />
203
+ )}
204
+ {points.find((p) => p.val === min) && (
205
+ <circle
206
+ className="fill-destructive stroke-background"
207
+ cx={points.find((p) => p.val === min)!.x}
208
+ cy={points.find((p) => p.val === min)!.y}
209
+ r={2.5}
210
+ strokeWidth={1}
211
+ />
212
+ )}
213
+ </>
214
+ )}
215
+ </g>
216
+ )}
217
+ </svg>
218
+
219
+ {showTrendBadge && (
220
+ <span
221
+ className={cn(
222
+ 'inline-flex items-center rounded-md px-1.5 py-0.5 text-[11px] font-semibold tracking-tight',
223
+ selectedTone.badge,
224
+ )}
225
+ >
226
+ {deltaBadgeText}
227
+ </span>
228
+ )}
229
+ </div>
230
+ )
231
+ }
@@ -0,0 +1,20 @@
1
+ import { cn } from '../lib/utils'
2
+ import { Loader2Icon } from 'lucide-react'
3
+
4
+ function Spinner({
5
+ className,
6
+ animated = true,
7
+ ...props
8
+ }: React.ComponentProps<'svg'> & { animated?: boolean }) {
9
+ return (
10
+ <Loader2Icon
11
+ data-slot="spinner"
12
+ role="status"
13
+ aria-label="Loading"
14
+ className={cn('size-4', animated && 'motion-safe:animate-spin', className)}
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+
20
+ export { Spinner }
@@ -0,0 +1,114 @@
1
+ import { CircleCheck } from 'lucide-react';
2
+ import { cn } from '../lib/utils';
3
+
4
+ export type StatusBadgeValue =
5
+ | 'active'
6
+ | 'in-review'
7
+ | 'complete'
8
+ | 'planned'
9
+ | 'draft'
10
+ | 'open'
11
+ | 'won'
12
+ | 'lost'
13
+ | 'pending'
14
+ | 'processing'
15
+ | 'failed'
16
+ | 'cancelled'
17
+ | 'archived'
18
+ | 'suspended'
19
+ | 'queued'
20
+ | 'attention'
21
+ | 'idle';
22
+
23
+ const statusStyles: Record<StatusBadgeValue, string> = {
24
+ active: 'border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-900 dark:bg-blue-950/40 dark:text-blue-300',
25
+ 'in-review':
26
+ 'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-300',
27
+ complete:
28
+ 'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900 dark:bg-emerald-950/40 dark:text-emerald-300',
29
+ planned:
30
+ 'border-orange-200 bg-orange-50 text-orange-700 dark:border-orange-900 dark:bg-orange-950/40 dark:text-orange-300',
31
+ draft: 'border-slate-200 bg-slate-50 text-slate-700 dark:border-slate-800 dark:bg-slate-900/40 dark:text-slate-300',
32
+ open: 'border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-900 dark:bg-sky-950/40 dark:text-sky-300',
33
+ won: 'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900 dark:bg-emerald-950/40 dark:text-emerald-300',
34
+ lost: 'border-red-200 bg-red-50 text-red-700 dark:border-red-900 dark:bg-red-950/40 dark:text-red-300',
35
+ pending:
36
+ 'border-yellow-200 bg-yellow-50 text-yellow-700 dark:border-yellow-900 dark:bg-yellow-950/40 dark:text-yellow-300',
37
+ processing:
38
+ 'border-violet-200 bg-violet-50 text-violet-700 dark:border-violet-900 dark:bg-violet-950/40 dark:text-violet-300',
39
+ failed: 'border-red-200 bg-red-50 text-red-700 dark:border-red-900 dark:bg-red-950/40 dark:text-red-300',
40
+ cancelled: 'border-zinc-200 bg-zinc-50 text-zinc-700 dark:border-zinc-800 dark:bg-zinc-900/40 dark:text-zinc-300',
41
+ archived: 'border-gray-200 bg-gray-50 text-gray-700 dark:border-gray-800 dark:bg-gray-900/40 dark:text-gray-300',
42
+ suspended:
43
+ 'border-purple-200 bg-purple-50 text-purple-700 dark:border-purple-900 dark:bg-purple-950/40 dark:text-purple-300',
44
+ queued:
45
+ 'border-indigo-200 bg-indigo-50 text-indigo-700 dark:border-indigo-900 dark:bg-indigo-950/40 dark:text-indigo-300',
46
+ attention: 'border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-900 dark:bg-rose-950/40 dark:text-rose-300',
47
+ idle: 'border-neutral-200 bg-neutral-50 text-neutral-700 dark:border-neutral-800 dark:bg-neutral-900/40 dark:text-neutral-300',
48
+ };
49
+
50
+ const statusLabels: Record<StatusBadgeValue, string> = {
51
+ active: 'Active',
52
+ 'in-review': 'In review',
53
+ complete: 'Complete',
54
+ planned: 'Planned',
55
+ draft: 'Draft',
56
+ open: 'Open',
57
+ won: 'Won',
58
+ lost: 'Lost',
59
+ pending: 'Pending',
60
+ processing: 'Processing',
61
+ failed: 'Failed',
62
+ cancelled: 'Cancelled',
63
+ archived: 'Archived',
64
+ suspended: 'Suspended',
65
+ queued: 'Queued',
66
+ attention: 'Attention',
67
+ idle: 'Idle',
68
+ };
69
+
70
+ export function getStatusBadgeValue(value: string): StatusBadgeValue {
71
+ const normalized = value.trim().toLowerCase();
72
+ if (['complete', 'completed', 'done'].includes(normalized)) return 'complete';
73
+ if (['in review', 'in-review', 'long hold', 'hold for approval'].includes(normalized)) return 'in-review';
74
+ if (['active'].includes(normalized)) return 'active';
75
+ if (['draft'].includes(normalized)) return 'draft';
76
+ if (['open'].includes(normalized)) return 'open';
77
+ if (['won'].includes(normalized)) return 'won';
78
+ if (['lost'].includes(normalized)) return 'lost';
79
+ if (['pending'].includes(normalized)) return 'pending';
80
+ if (['processing', 'in progress'].includes(normalized)) return 'processing';
81
+ if (['failed', 'error'].includes(normalized)) return 'failed';
82
+ if (['cancelled', 'canceled'].includes(normalized)) return 'cancelled';
83
+ if (['archived'].includes(normalized)) return 'archived';
84
+ if (['suspended', 'disabled'].includes(normalized)) return 'suspended';
85
+ if (['queued'].includes(normalized)) return 'queued';
86
+ if (['attention'].includes(normalized)) return 'attention';
87
+ if (['idle'].includes(normalized)) return 'idle';
88
+ return 'active';
89
+ }
90
+
91
+ export function StatusBadge({
92
+ className,
93
+ label,
94
+ status,
95
+ }: {
96
+ status: StatusBadgeValue;
97
+ label?: string;
98
+ className?: string;
99
+ }) {
100
+ const displayLabel = label ?? statusLabels[status];
101
+ return (
102
+ <span
103
+ aria-label={`Status: ${displayLabel}`}
104
+ className={cn(
105
+ 'inline-flex h-6 w-fit items-center gap-1 rounded-md border px-2 text-xs font-medium whitespace-nowrap',
106
+ statusStyles[status],
107
+ className,
108
+ )}
109
+ >
110
+ <CircleCheck aria-hidden="true" className="size-3.5" />
111
+ {displayLabel}
112
+ </span>
113
+ );
114
+ }
@@ -0,0 +1,31 @@
1
+ 'use client'
2
+
3
+ import { Switch as SwitchPrimitive } from '@base-ui/react/switch'
4
+ import { cn } from '../lib/utils'
5
+
6
+ function Switch({
7
+ className,
8
+ size = 'default',
9
+ ...props
10
+ }: SwitchPrimitive.Root.Props & {
11
+ size?: 'sm' | 'default'
12
+ }) {
13
+ return (
14
+ <SwitchPrimitive.Root
15
+ data-slot="switch"
16
+ data-size={size}
17
+ className={cn(
18
+ 'peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none group-has-[:focus-visible]/field-label:border-transparent group-has-[:focus-visible]/field-label:ring-0 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50',
19
+ className,
20
+ )}
21
+ {...props}
22
+ >
23
+ <SwitchPrimitive.Thumb
24
+ data-slot="switch-thumb"
25
+ className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
26
+ />
27
+ </SwitchPrimitive.Root>
28
+ )
29
+ }
30
+
31
+ export { Switch }
@@ -0,0 +1,91 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { cn } from '../lib/utils'
5
+
6
+ function Table({ className, ...props }: React.ComponentProps<'table'>) {
7
+ return (
8
+ <div
9
+ data-slot="table-container"
10
+ className="relative w-full overflow-x-auto [scrollbar-color:var(--border)_transparent] [scrollbar-width:thin] [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-border [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar]:h-1"
11
+ >
12
+ <table
13
+ data-slot="table"
14
+ className={cn('w-full caption-bottom text-sm', className)}
15
+ {...props}
16
+ />
17
+ </div>
18
+ )
19
+ }
20
+
21
+ function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
22
+ return <thead data-slot="table-header" className={cn('[&_tr]:border-b', className)} {...props} />
23
+ }
24
+
25
+ function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
26
+ return (
27
+ <tbody
28
+ data-slot="table-body"
29
+ className={cn('[&_tr:last-child]:border-0', className)}
30
+ {...props}
31
+ />
32
+ )
33
+ }
34
+
35
+ function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
36
+ return (
37
+ <tfoot
38
+ data-slot="table-footer"
39
+ className={cn('border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', className)}
40
+ {...props}
41
+ />
42
+ )
43
+ }
44
+
45
+ function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
46
+ return (
47
+ <tr
48
+ data-slot="table-row"
49
+ className={cn(
50
+ 'border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted',
51
+ className,
52
+ )}
53
+ {...props}
54
+ />
55
+ )
56
+ }
57
+
58
+ function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
59
+ return (
60
+ <th
61
+ data-slot="table-head"
62
+ className={cn(
63
+ 'h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0',
64
+ className,
65
+ )}
66
+ {...props}
67
+ />
68
+ )
69
+ }
70
+
71
+ function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
72
+ return (
73
+ <td
74
+ data-slot="table-cell"
75
+ className={cn('p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0', className)}
76
+ {...props}
77
+ />
78
+ )
79
+ }
80
+
81
+ function TableCaption({ className, ...props }: React.ComponentProps<'caption'>) {
82
+ return (
83
+ <caption
84
+ data-slot="table-caption"
85
+ className={cn('mt-4 text-sm text-muted-foreground', className)}
86
+ {...props}
87
+ />
88
+ )
89
+ }
90
+
91
+ export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }
@@ -0,0 +1,72 @@
1
+ import { Tabs as TabsPrimitive } from '@base-ui/react/tabs'
2
+ import { cva, type VariantProps } from 'class-variance-authority'
3
+ import { cn } from '../lib/utils'
4
+
5
+ function Tabs({ className, orientation = 'horizontal', ...props }: TabsPrimitive.Root.Props) {
6
+ return (
7
+ <TabsPrimitive.Root
8
+ data-slot="tabs"
9
+ data-orientation={orientation}
10
+ className={cn('group/tabs flex gap-2 data-[orientation=horizontal]:flex-col', className)}
11
+ {...props}
12
+ />
13
+ )
14
+ }
15
+
16
+ const tabsListVariants = cva(
17
+ 'group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-8 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none',
18
+ {
19
+ variants: {
20
+ variant: {
21
+ default: 'bg-muted',
22
+ line: 'gap-1 bg-transparent',
23
+ },
24
+ },
25
+ defaultVariants: {
26
+ variant: 'default',
27
+ },
28
+ },
29
+ )
30
+
31
+ function TabsList({
32
+ className,
33
+ variant = 'default',
34
+ ...props
35
+ }: TabsPrimitive.List.Props & VariantProps<typeof tabsListVariants>) {
36
+ return (
37
+ <TabsPrimitive.List
38
+ data-slot="tabs-list"
39
+ data-variant={variant}
40
+ className={cn(tabsListVariants({ variant }), className)}
41
+ {...props}
42
+ />
43
+ )
44
+ }
45
+
46
+ function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
47
+ return (
48
+ <TabsPrimitive.Tab
49
+ data-slot="tabs-trigger"
50
+ className={cn(
51
+ "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-[color,background-color,box-shadow,transform] duration-200 ease-out hover:-translate-y-px hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
52
+ 'group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent',
53
+ 'data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground',
54
+ 'after:absolute after:bg-foreground after:opacity-0 after:transition-[opacity,transform] after:duration-200 after:ease-out group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100',
55
+ className,
56
+ )}
57
+ {...props}
58
+ />
59
+ )
60
+ }
61
+
62
+ function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
63
+ return (
64
+ <TabsPrimitive.Panel
65
+ data-slot="tabs-content"
66
+ className={cn('flex-1 text-sm outline-none', className)}
67
+ {...props}
68
+ />
69
+ )
70
+ }
71
+
72
+ export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
@@ -0,0 +1,17 @@
1
+ import * as React from 'react'
2
+ import { cn } from '../lib/utils'
3
+
4
+ function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
5
+ return (
6
+ <textarea
7
+ data-slot="textarea"
8
+ className={cn(
9
+ 'flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',
10
+ className,
11
+ )}
12
+ {...props}
13
+ />
14
+ )
15
+ }
16
+
17
+ export { Textarea }