@otfdashkit/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (658) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.js +14886 -0
  3. package/dist/index.mjs +14389 -0
  4. package/package.json +124 -0
  5. package/src/advanced/BulkActions/BulkActions.stories.tsx +14 -0
  6. package/src/advanced/BulkActions/BulkActions.test.tsx +9 -0
  7. package/src/advanced/BulkActions/BulkActions.tsx +46 -0
  8. package/src/advanced/BulkActions/index.ts +2 -0
  9. package/src/advanced/CommandBar/CommandBar.stories.tsx +14 -0
  10. package/src/advanced/CommandBar/CommandBar.test.tsx +9 -0
  11. package/src/advanced/CommandBar/CommandBar.tsx +115 -0
  12. package/src/advanced/CommandBar/index.ts +2 -0
  13. package/src/advanced/Filters/Filters.stories.tsx +14 -0
  14. package/src/advanced/Filters/Filters.test.tsx +9 -0
  15. package/src/advanced/Filters/Filters.tsx +103 -0
  16. package/src/advanced/Filters/index.ts +2 -0
  17. package/src/advanced/Kanban/Kanban.stories.tsx +14 -0
  18. package/src/advanced/Kanban/Kanban.test.tsx +9 -0
  19. package/src/advanced/Kanban/Kanban.tsx +95 -0
  20. package/src/advanced/Kanban/index.ts +2 -0
  21. package/src/advanced/ToggleButton/ToggleButton.stories.tsx +14 -0
  22. package/src/advanced/ToggleButton/ToggleButton.test.tsx +9 -0
  23. package/src/advanced/ToggleButton/ToggleButton.tsx +78 -0
  24. package/src/advanced/ToggleButton/index.ts +2 -0
  25. package/src/advanced/bulk-actions.tsx +46 -0
  26. package/src/advanced/command-bar.tsx +115 -0
  27. package/src/advanced/filters.tsx +103 -0
  28. package/src/advanced/gantt.tsx +824 -0
  29. package/src/advanced/kanban.tsx +95 -0
  30. package/src/advanced/rich-editor.tsx +700 -0
  31. package/src/advanced/toggle-button.tsx +78 -0
  32. package/src/advanced/utils/cn.ts +1 -0
  33. package/src/app-shell/AppShell.tsx +24 -0
  34. package/src/app-shell/BillingStatus.tsx +63 -0
  35. package/src/app-shell/Sidebar.tsx +172 -0
  36. package/src/app-shell/SidebarResizer.tsx +109 -0
  37. package/src/app-shell/SortableNav.tsx +122 -0
  38. package/src/app-shell/UserMenu.tsx +135 -0
  39. package/src/app-shell/WorkspacesMenu.tsx +114 -0
  40. package/src/app-shell/index.ts +18 -0
  41. package/src/backgrounds/AnimatedGradient.tsx +151 -0
  42. package/src/backgrounds/DottedBackground.tsx +64 -0
  43. package/src/backgrounds/GradientBackground.tsx +113 -0
  44. package/src/backgrounds/HeroShaderA.tsx +255 -0
  45. package/src/backgrounds/NoiseBackground.tsx +51 -0
  46. package/src/backgrounds/index.ts +5 -0
  47. package/src/blocks/ChatDetail/ChatDetail.stories.tsx +14 -0
  48. package/src/blocks/ChatDetail/ChatDetail.test.tsx +9 -0
  49. package/src/blocks/ChatDetail/ChatDetail.tsx +73 -0
  50. package/src/blocks/ChatDetail/index.ts +2 -0
  51. package/src/blocks/FeedbackModal/FeedbackModal.stories.tsx +14 -0
  52. package/src/blocks/FeedbackModal/FeedbackModal.test.tsx +9 -0
  53. package/src/blocks/FeedbackModal/FeedbackModal.tsx +68 -0
  54. package/src/blocks/FeedbackModal/index.ts +2 -0
  55. package/src/blocks/FileCards/FileCards.stories.tsx +14 -0
  56. package/src/blocks/FileCards/FileCards.test.tsx +9 -0
  57. package/src/blocks/FileCards/FileCards.tsx +56 -0
  58. package/src/blocks/FileCards/index.ts +2 -0
  59. package/src/blocks/FilesList/FilesList.stories.tsx +14 -0
  60. package/src/blocks/FilesList/FilesList.test.tsx +9 -0
  61. package/src/blocks/FilesList/FilesList.tsx +72 -0
  62. package/src/blocks/FilesList/index.ts +2 -0
  63. package/src/blocks/FloatingActionButton/FloatingActionButton.stories.tsx +223 -0
  64. package/src/blocks/FloatingActionButton/FloatingActionButton.test.tsx +47 -0
  65. package/src/blocks/FloatingActionButton/FloatingActionButton.tsx +253 -0
  66. package/src/blocks/FloatingActionButton/index.ts +1 -0
  67. package/src/blocks/IntegrationCard/IntegrationCard.stories.tsx +14 -0
  68. package/src/blocks/IntegrationCard/IntegrationCard.test.tsx +9 -0
  69. package/src/blocks/IntegrationCard/IntegrationCard.tsx +50 -0
  70. package/src/blocks/IntegrationCard/index.ts +2 -0
  71. package/src/blocks/InviteModal/InviteModal.stories.tsx +14 -0
  72. package/src/blocks/InviteModal/InviteModal.test.tsx +9 -0
  73. package/src/blocks/InviteModal/InviteModal.tsx +69 -0
  74. package/src/blocks/InviteModal/index.ts +2 -0
  75. package/src/blocks/ManageTagsModal/ManageTagsModal.stories.tsx +14 -0
  76. package/src/blocks/ManageTagsModal/ManageTagsModal.test.tsx +9 -0
  77. package/src/blocks/ManageTagsModal/ManageTagsModal.tsx +63 -0
  78. package/src/blocks/ManageTagsModal/index.ts +2 -0
  79. package/src/blocks/MessagesCard/MessagesCard.stories.tsx +14 -0
  80. package/src/blocks/MessagesCard/MessagesCard.test.tsx +9 -0
  81. package/src/blocks/MessagesCard/MessagesCard.tsx +59 -0
  82. package/src/blocks/MessagesCard/index.ts +2 -0
  83. package/src/blocks/MetricCard/MetricCard.stories.tsx +14 -0
  84. package/src/blocks/MetricCard/MetricCard.test.tsx +9 -0
  85. package/src/blocks/MetricCard/MetricCard.tsx +79 -0
  86. package/src/blocks/MetricCard/index.ts +2 -0
  87. package/src/blocks/NotificationSettings/NotificationSettings.stories.tsx +14 -0
  88. package/src/blocks/NotificationSettings/NotificationSettings.test.tsx +9 -0
  89. package/src/blocks/NotificationSettings/NotificationSettings.tsx +61 -0
  90. package/src/blocks/NotificationSettings/index.ts +2 -0
  91. package/src/blocks/OrganizationMenu/OrganizationMenu.stories.tsx +14 -0
  92. package/src/blocks/OrganizationMenu/OrganizationMenu.test.tsx +9 -0
  93. package/src/blocks/OrganizationMenu/OrganizationMenu.tsx +66 -0
  94. package/src/blocks/OrganizationMenu/index.ts +2 -0
  95. package/src/blocks/RolesMenu/RolesMenu.stories.tsx +14 -0
  96. package/src/blocks/RolesMenu/RolesMenu.test.tsx +9 -0
  97. package/src/blocks/RolesMenu/RolesMenu.tsx +61 -0
  98. package/src/blocks/RolesMenu/index.ts +2 -0
  99. package/src/blocks/SelectUsersModal/SelectUsersModal.stories.tsx +14 -0
  100. package/src/blocks/SelectUsersModal/SelectUsersModal.test.tsx +9 -0
  101. package/src/blocks/SelectUsersModal/SelectUsersModal.tsx +64 -0
  102. package/src/blocks/SelectUsersModal/index.ts +2 -0
  103. package/src/blocks/SidebarLayoutDashboard/SidebarLayoutDashboard.stories.tsx +14 -0
  104. package/src/blocks/SidebarLayoutDashboard/SidebarLayoutDashboard.test.tsx +9 -0
  105. package/src/blocks/SidebarLayoutDashboard/SidebarLayoutDashboard.tsx +97 -0
  106. package/src/blocks/SidebarLayoutDashboard/index.ts +2 -0
  107. package/src/blocks/SidebarLayoutGroups/SidebarLayoutGroups.stories.tsx +14 -0
  108. package/src/blocks/SidebarLayoutGroups/SidebarLayoutGroups.test.tsx +9 -0
  109. package/src/blocks/SidebarLayoutGroups/SidebarLayoutGroups.tsx +79 -0
  110. package/src/blocks/SidebarLayoutGroups/index.ts +2 -0
  111. package/src/blocks/SidebarLayoutMinimal/SidebarLayoutMinimal.stories.tsx +14 -0
  112. package/src/blocks/SidebarLayoutMinimal/SidebarLayoutMinimal.test.tsx +9 -0
  113. package/src/blocks/SidebarLayoutMinimal/SidebarLayoutMinimal.tsx +73 -0
  114. package/src/blocks/SidebarLayoutMinimal/index.ts +2 -0
  115. package/src/blocks/SidebarLayoutSearch/SidebarLayoutSearch.stories.tsx +14 -0
  116. package/src/blocks/SidebarLayoutSearch/SidebarLayoutSearch.test.tsx +9 -0
  117. package/src/blocks/SidebarLayoutSearch/SidebarLayoutSearch.tsx +79 -0
  118. package/src/blocks/SidebarLayoutSearch/index.ts +2 -0
  119. package/src/blocks/SidebarLayoutUser/SidebarLayoutUser.stories.tsx +14 -0
  120. package/src/blocks/SidebarLayoutUser/SidebarLayoutUser.test.tsx +9 -0
  121. package/src/blocks/SidebarLayoutUser/SidebarLayoutUser.tsx +82 -0
  122. package/src/blocks/SidebarLayoutUser/index.ts +2 -0
  123. package/src/blocks/SortableTaskList/SortableTaskList.stories.tsx +14 -0
  124. package/src/blocks/SortableTaskList/SortableTaskList.test.tsx +9 -0
  125. package/src/blocks/SortableTaskList/SortableTaskList.tsx +70 -0
  126. package/src/blocks/SortableTaskList/index.ts +2 -0
  127. package/src/blocks/StackedLayoutBranded/StackedLayoutBranded.stories.tsx +14 -0
  128. package/src/blocks/StackedLayoutBranded/StackedLayoutBranded.test.tsx +9 -0
  129. package/src/blocks/StackedLayoutBranded/StackedLayoutBranded.tsx +64 -0
  130. package/src/blocks/StackedLayoutBranded/index.ts +2 -0
  131. package/src/blocks/StackedLayoutTabs/StackedLayoutTabs.stories.tsx +14 -0
  132. package/src/blocks/StackedLayoutTabs/StackedLayoutTabs.test.tsx +9 -0
  133. package/src/blocks/StackedLayoutTabs/StackedLayoutTabs.tsx +80 -0
  134. package/src/blocks/StackedLayoutTabs/index.ts +2 -0
  135. package/src/blocks/TaskCard/TaskCard.stories.tsx +14 -0
  136. package/src/blocks/TaskCard/TaskCard.test.tsx +9 -0
  137. package/src/blocks/TaskCard/TaskCard.tsx +88 -0
  138. package/src/blocks/TaskCard/index.ts +2 -0
  139. package/src/blocks/UserMenu/UserMenu.stories.tsx +14 -0
  140. package/src/blocks/UserMenu/UserMenu.test.tsx +9 -0
  141. package/src/blocks/UserMenu/UserMenu.tsx +62 -0
  142. package/src/blocks/UserMenu/index.ts +2 -0
  143. package/src/blocks/WorkspaceMembers/WorkspaceMembers.stories.tsx +14 -0
  144. package/src/blocks/WorkspaceMembers/WorkspaceMembers.test.tsx +9 -0
  145. package/src/blocks/WorkspaceMembers/WorkspaceMembers.tsx +61 -0
  146. package/src/blocks/WorkspaceMembers/index.ts +2 -0
  147. package/src/blocks/activity-heatmap.tsx +122 -0
  148. package/src/blocks/bar-list.tsx +75 -0
  149. package/src/blocks/chat-detail.tsx +73 -0
  150. package/src/blocks/feedback-modal.tsx +68 -0
  151. package/src/blocks/file-cards.tsx +56 -0
  152. package/src/blocks/files-list.tsx +72 -0
  153. package/src/blocks/integration-card.tsx +50 -0
  154. package/src/blocks/invite-modal.tsx +69 -0
  155. package/src/blocks/manage-tags-modal.tsx +63 -0
  156. package/src/blocks/messages-card.tsx +59 -0
  157. package/src/blocks/metric-card.tsx +79 -0
  158. package/src/blocks/notification-settings.tsx +61 -0
  159. package/src/blocks/organization-menu.tsx +85 -0
  160. package/src/blocks/roles-menu.tsx +61 -0
  161. package/src/blocks/select-users-modal.tsx +64 -0
  162. package/src/blocks/sidebar-layout-dashboard.tsx +97 -0
  163. package/src/blocks/sidebar-layout-groups.tsx +79 -0
  164. package/src/blocks/sidebar-layout-minimal.tsx +73 -0
  165. package/src/blocks/sidebar-layout-search.tsx +79 -0
  166. package/src/blocks/sidebar-layout-user.tsx +82 -0
  167. package/src/blocks/sortable-task-list.tsx +70 -0
  168. package/src/blocks/stacked-layout-branded.tsx +64 -0
  169. package/src/blocks/stacked-layout-tabs.tsx +80 -0
  170. package/src/blocks/task-card.tsx +88 -0
  171. package/src/blocks/user-menu.tsx +62 -0
  172. package/src/blocks/utils/cn.ts +1 -0
  173. package/src/blocks/workspace-members.tsx +61 -0
  174. package/src/charts/area-chart.tsx +83 -0
  175. package/src/charts/bar-chart.tsx +146 -0
  176. package/src/charts/heatmap.tsx +185 -0
  177. package/src/charts/line-chart.tsx +89 -0
  178. package/src/charts/sparkline.tsx +62 -0
  179. package/src/charts/utils/cn.ts +1 -0
  180. package/src/components/Banner/Banner.stories.tsx +14 -0
  181. package/src/components/Banner/Banner.test.tsx +9 -0
  182. package/src/components/Banner/Banner.tsx +203 -0
  183. package/src/components/Banner/index.ts +2 -0
  184. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +14 -0
  185. package/src/components/Breadcrumb/Breadcrumb.test.tsx +9 -0
  186. package/src/components/Breadcrumb/Breadcrumb.tsx +58 -0
  187. package/src/components/Breadcrumb/index.ts +2 -0
  188. package/src/components/CommandItem/CommandItem.stories.tsx +14 -0
  189. package/src/components/CommandItem/CommandItem.test.tsx +9 -0
  190. package/src/components/CommandItem/CommandItem.tsx +36 -0
  191. package/src/components/CommandItem/index.ts +2 -0
  192. package/src/components/DataGrid/DataGrid.stories.tsx +14 -0
  193. package/src/components/DataGrid/DataGrid.test.tsx +9 -0
  194. package/src/components/DataGrid/DataGrid.tsx +125 -0
  195. package/src/components/DataGrid/index.ts +2 -0
  196. package/src/components/DataTable/DataTable.stories.tsx +14 -0
  197. package/src/components/DataTable/DataTable.test.tsx +9 -0
  198. package/src/components/DataTable/DataTable.tsx +139 -0
  199. package/src/components/DataTable/index.ts +2 -0
  200. package/src/components/EmptyState/EmptyState.stories.tsx +14 -0
  201. package/src/components/EmptyState/EmptyState.test.tsx +9 -0
  202. package/src/components/EmptyState/EmptyState.tsx +106 -0
  203. package/src/components/EmptyState/index.ts +2 -0
  204. package/src/components/Hotkeys/Hotkeys.stories.tsx +14 -0
  205. package/src/components/Hotkeys/Hotkeys.test.tsx +9 -0
  206. package/src/components/Hotkeys/Hotkeys.tsx +31 -0
  207. package/src/components/Hotkeys/index.ts +2 -0
  208. package/src/components/IconBadge/IconBadge.stories.tsx +14 -0
  209. package/src/components/IconBadge/IconBadge.test.tsx +9 -0
  210. package/src/components/IconBadge/IconBadge.tsx +35 -0
  211. package/src/components/IconBadge/index.ts +2 -0
  212. package/src/components/LoadingOverlay/LoadingOverlay.stories.tsx +14 -0
  213. package/src/components/LoadingOverlay/LoadingOverlay.test.tsx +9 -0
  214. package/src/components/LoadingOverlay/LoadingOverlay.tsx +38 -0
  215. package/src/components/LoadingOverlay/index.ts +2 -0
  216. package/src/components/Persona/Persona.stories.tsx +14 -0
  217. package/src/components/Persona/Persona.test.tsx +9 -0
  218. package/src/components/Persona/Persona.tsx +38 -0
  219. package/src/components/Persona/index.ts +2 -0
  220. package/src/components/PropertyList/PropertyList.stories.tsx +14 -0
  221. package/src/components/PropertyList/PropertyList.test.tsx +9 -0
  222. package/src/components/PropertyList/PropertyList.tsx +29 -0
  223. package/src/components/PropertyList/index.ts +2 -0
  224. package/src/components/Stat/Stat.stories.tsx +14 -0
  225. package/src/components/Stat/Stat.test.tsx +9 -0
  226. package/src/components/Stat/Stat.tsx +51 -0
  227. package/src/components/Stat/index.ts +2 -0
  228. package/src/components/Stepper/Stepper.stories.tsx +14 -0
  229. package/src/components/Stepper/Stepper.test.tsx +9 -0
  230. package/src/components/Stepper/Stepper.tsx +58 -0
  231. package/src/components/Stepper/index.ts +2 -0
  232. package/src/components/StructuredList/StructuredList.stories.tsx +14 -0
  233. package/src/components/StructuredList/StructuredList.test.tsx +9 -0
  234. package/src/components/StructuredList/StructuredList.tsx +51 -0
  235. package/src/components/StructuredList/index.ts +2 -0
  236. package/src/components/Timeline/Timeline.stories.tsx +14 -0
  237. package/src/components/Timeline/Timeline.test.tsx +9 -0
  238. package/src/components/Timeline/Timeline.tsx +69 -0
  239. package/src/components/Timeline/index.ts +2 -0
  240. package/src/components/Toaster/Toaster.stories.tsx +14 -0
  241. package/src/components/Toaster/Toaster.test.tsx +9 -0
  242. package/src/components/Toaster/Toaster.tsx +42 -0
  243. package/src/components/Toaster/index.ts +2 -0
  244. package/src/components/banner.tsx +203 -0
  245. package/src/components/breadcrumb.tsx +58 -0
  246. package/src/components/command-item.tsx +36 -0
  247. package/src/components/data-grid.tsx +125 -0
  248. package/src/components/data-table.tsx +139 -0
  249. package/src/components/empty-state.tsx +31 -0
  250. package/src/components/hotkeys.tsx +31 -0
  251. package/src/components/icon-badge.tsx +35 -0
  252. package/src/components/loading-overlay.tsx +38 -0
  253. package/src/components/persona.tsx +38 -0
  254. package/src/components/property-list.tsx +29 -0
  255. package/src/components/stat.tsx +51 -0
  256. package/src/components/stepper.tsx +69 -0
  257. package/src/components/structured-list.tsx +51 -0
  258. package/src/components/timeline.tsx +69 -0
  259. package/src/components/toaster.tsx +42 -0
  260. package/src/components/utils/cn.ts +1 -0
  261. package/src/data/ActionBar.tsx +115 -0
  262. package/src/data/DataGrid.tsx +453 -0
  263. package/src/data/DataList.tsx +79 -0
  264. package/src/data/EventCalendar.tsx +794 -0
  265. package/src/data/Filters.tsx +271 -0
  266. package/src/data/GridList.tsx +67 -0
  267. package/src/data/ImageCrop.tsx +198 -0
  268. package/src/data/SortableList.tsx +258 -0
  269. package/src/data/Tree.tsx +510 -0
  270. package/src/data/VideoPlayer.tsx +564 -0
  271. package/src/data/index.ts +6 -0
  272. package/src/engagement/Beacon/Beacon.stories.tsx +14 -0
  273. package/src/engagement/Beacon/Beacon.test.tsx +9 -0
  274. package/src/engagement/Beacon/Beacon.tsx +50 -0
  275. package/src/engagement/Beacon/index.ts +2 -0
  276. package/src/engagement/Tour/Tour.stories.tsx +14 -0
  277. package/src/engagement/Tour/Tour.test.tsx +9 -0
  278. package/src/engagement/Tour/Tour.tsx +106 -0
  279. package/src/engagement/Tour/index.ts +2 -0
  280. package/src/engagement/beacon.tsx +50 -0
  281. package/src/engagement/checklist.tsx +259 -0
  282. package/src/engagement/intro-disclosure.tsx +210 -0
  283. package/src/engagement/tour.tsx +106 -0
  284. package/src/engagement/utils/cn.ts +1 -0
  285. package/src/feedback/Toaster.tsx +141 -0
  286. package/src/feedback/index.ts +2 -0
  287. package/src/feedback/toast-emitter.ts +74 -0
  288. package/src/forms/ArrayField/ArrayField.stories.tsx +14 -0
  289. package/src/forms/ArrayField/ArrayField.test.tsx +9 -0
  290. package/src/forms/ArrayField/ArrayField.tsx +61 -0
  291. package/src/forms/ArrayField/index.ts +2 -0
  292. package/src/forms/AutoForm/AutoForm.stories.tsx +14 -0
  293. package/src/forms/AutoForm/AutoForm.test.tsx +9 -0
  294. package/src/forms/AutoForm/AutoForm.tsx +133 -0
  295. package/src/forms/AutoForm/index.ts +2 -0
  296. package/src/forms/DatePicker/DatePicker.stories.tsx +14 -0
  297. package/src/forms/DatePicker/DatePicker.test.tsx +9 -0
  298. package/src/forms/DatePicker/DatePicker.tsx +109 -0
  299. package/src/forms/DatePicker/index.ts +2 -0
  300. package/src/forms/Field/Field.stories.tsx +14 -0
  301. package/src/forms/Field/Field.test.tsx +9 -0
  302. package/src/forms/Field/Field.tsx +62 -0
  303. package/src/forms/Field/index.ts +2 -0
  304. package/src/forms/FileUpload/FileUpload.stories.tsx +14 -0
  305. package/src/forms/FileUpload/FileUpload.test.tsx +9 -0
  306. package/src/forms/FileUpload/FileUpload.tsx +101 -0
  307. package/src/forms/FileUpload/index.ts +2 -0
  308. package/src/forms/Form/Form.stories.tsx +14 -0
  309. package/src/forms/Form/Form.test.tsx +9 -0
  310. package/src/forms/Form/Form.tsx +57 -0
  311. package/src/forms/Form/index.ts +2 -0
  312. package/src/forms/ObjectField/ObjectField.stories.tsx +14 -0
  313. package/src/forms/ObjectField/ObjectField.test.tsx +9 -0
  314. package/src/forms/ObjectField/ObjectField.tsx +53 -0
  315. package/src/forms/ObjectField/index.ts +2 -0
  316. package/src/forms/PasswordInput/PasswordInput.stories.tsx +14 -0
  317. package/src/forms/PasswordInput/PasswordInput.test.tsx +9 -0
  318. package/src/forms/PasswordInput/PasswordInput.tsx +54 -0
  319. package/src/forms/PasswordInput/index.ts +2 -0
  320. package/src/forms/SearchInput/SearchInput.stories.tsx +14 -0
  321. package/src/forms/SearchInput/SearchInput.test.tsx +9 -0
  322. package/src/forms/SearchInput/SearchInput.tsx +64 -0
  323. package/src/forms/SearchInput/index.ts +2 -0
  324. package/src/forms/StepForm/StepForm.stories.tsx +14 -0
  325. package/src/forms/StepForm/StepForm.test.tsx +9 -0
  326. package/src/forms/StepForm/StepForm.tsx +113 -0
  327. package/src/forms/StepForm/index.ts +2 -0
  328. package/src/forms/array-field.tsx +61 -0
  329. package/src/forms/auto-form.tsx +133 -0
  330. package/src/forms/color-picker.tsx +258 -0
  331. package/src/forms/combobox.tsx +106 -0
  332. package/src/forms/date-picker.tsx +109 -0
  333. package/src/forms/field.tsx +62 -0
  334. package/src/forms/file-upload.tsx +101 -0
  335. package/src/forms/form.tsx +57 -0
  336. package/src/forms/object-field.tsx +53 -0
  337. package/src/forms/password-input.tsx +54 -0
  338. package/src/forms/search-input.tsx +64 -0
  339. package/src/forms/step-form.tsx +113 -0
  340. package/src/forms/tags.tsx +270 -0
  341. package/src/forms/utils/cn.ts +1 -0
  342. package/src/headers/ScrollHeader.tsx +110 -0
  343. package/src/headers/index.ts +1 -0
  344. package/src/index.ts +171 -0
  345. package/src/layout/PageContainer.tsx +40 -0
  346. package/src/layout/PageLayout.tsx +55 -0
  347. package/src/layout/index.ts +2 -0
  348. package/src/layouts/AppShell/AppShell.stories.tsx +14 -0
  349. package/src/layouts/AppShell/AppShell.test.tsx +9 -0
  350. package/src/layouts/AppShell/AppShell.tsx +129 -0
  351. package/src/layouts/AppShell/index.ts +2 -0
  352. package/src/layouts/Container/Container.stories.tsx +14 -0
  353. package/src/layouts/Container/Container.test.tsx +9 -0
  354. package/src/layouts/Container/Container.tsx +33 -0
  355. package/src/layouts/Container/index.ts +2 -0
  356. package/src/layouts/Navbar/Navbar.stories.tsx +14 -0
  357. package/src/layouts/Navbar/Navbar.test.tsx +9 -0
  358. package/src/layouts/Navbar/Navbar.tsx +60 -0
  359. package/src/layouts/Navbar/index.ts +2 -0
  360. package/src/layouts/Page/Page.stories.tsx +14 -0
  361. package/src/layouts/Page/Page.test.tsx +9 -0
  362. package/src/layouts/Page/Page.tsx +61 -0
  363. package/src/layouts/Page/index.ts +2 -0
  364. package/src/layouts/ResizeBox/ResizeBox.stories.tsx +14 -0
  365. package/src/layouts/ResizeBox/ResizeBox.test.tsx +9 -0
  366. package/src/layouts/ResizeBox/ResizeBox.tsx +45 -0
  367. package/src/layouts/ResizeBox/index.ts +2 -0
  368. package/src/layouts/Sidebar/Sidebar.stories.tsx +14 -0
  369. package/src/layouts/Sidebar/Sidebar.test.tsx +9 -0
  370. package/src/layouts/Sidebar/Sidebar.tsx +99 -0
  371. package/src/layouts/Sidebar/index.ts +2 -0
  372. package/src/layouts/SplitPage/SplitPage.stories.tsx +14 -0
  373. package/src/layouts/SplitPage/SplitPage.test.tsx +9 -0
  374. package/src/layouts/SplitPage/SplitPage.tsx +32 -0
  375. package/src/layouts/SplitPage/index.ts +2 -0
  376. package/src/layouts/Stack/Stack.stories.tsx +14 -0
  377. package/src/layouts/Stack/Stack.test.tsx +9 -0
  378. package/src/layouts/Stack/Stack.tsx +56 -0
  379. package/src/layouts/Stack/index.ts +2 -0
  380. package/src/layouts/app-shell.tsx +129 -0
  381. package/src/layouts/container.tsx +33 -0
  382. package/src/layouts/navbar.tsx +60 -0
  383. package/src/layouts/page.tsx +61 -0
  384. package/src/layouts/resize-box.tsx +45 -0
  385. package/src/layouts/sidebar.tsx +99 -0
  386. package/src/layouts/split-page.tsx +32 -0
  387. package/src/layouts/stack.tsx +56 -0
  388. package/src/layouts/utils/cn.ts +1 -0
  389. package/src/motion/FadeIn.tsx +33 -0
  390. package/src/motion/Morph.tsx +17 -0
  391. package/src/motion/Parallax.tsx +37 -0
  392. package/src/motion/Reveal.tsx +32 -0
  393. package/src/motion/Ripple.tsx +78 -0
  394. package/src/motion/ScaleIn.tsx +33 -0
  395. package/src/motion/SlideIn.tsx +42 -0
  396. package/src/motion/Stagger.tsx +48 -0
  397. package/src/motion/Transition.tsx +13 -0
  398. package/src/motion/index.ts +13 -0
  399. package/src/motion/presets.ts +33 -0
  400. package/src/motion/useLayoutId.ts +6 -0
  401. package/src/nav/CommandPalette.tsx +197 -0
  402. package/src/nav/HotkeysProvider.tsx +223 -0
  403. package/src/nav/index.ts +2 -0
  404. package/src/primitives/Accordion/Accordion.stories.tsx +14 -0
  405. package/src/primitives/Accordion/Accordion.test.tsx +9 -0
  406. package/src/primitives/Accordion/Accordion.tsx +56 -0
  407. package/src/primitives/Accordion/index.ts +2 -0
  408. package/src/primitives/Alert/Alert.stories.tsx +14 -0
  409. package/src/primitives/Alert/Alert.test.tsx +9 -0
  410. package/src/primitives/Alert/Alert.tsx +60 -0
  411. package/src/primitives/Alert/index.ts +2 -0
  412. package/src/primitives/AlertDialog/AlertDialog.stories.tsx +14 -0
  413. package/src/primitives/AlertDialog/AlertDialog.test.tsx +9 -0
  414. package/src/primitives/AlertDialog/AlertDialog.tsx +140 -0
  415. package/src/primitives/AlertDialog/index.ts +2 -0
  416. package/src/primitives/AspectRatio/AspectRatio.stories.tsx +14 -0
  417. package/src/primitives/AspectRatio/AspectRatio.test.tsx +9 -0
  418. package/src/primitives/AspectRatio/AspectRatio.tsx +6 -0
  419. package/src/primitives/AspectRatio/index.ts +2 -0
  420. package/src/primitives/Avatar/Avatar.stories.tsx +14 -0
  421. package/src/primitives/Avatar/Avatar.test.tsx +9 -0
  422. package/src/primitives/Avatar/Avatar.tsx +35 -0
  423. package/src/primitives/Avatar/index.ts +2 -0
  424. package/src/primitives/Badge/Badge.stories.tsx +14 -0
  425. package/src/primitives/Badge/Badge.test.tsx +9 -0
  426. package/src/primitives/Badge/Badge.tsx +28 -0
  427. package/src/primitives/Badge/index.ts +2 -0
  428. package/src/primitives/Button/Button.stories.tsx +14 -0
  429. package/src/primitives/Button/Button.test.tsx +9 -0
  430. package/src/primitives/Button/Button.tsx +113 -0
  431. package/src/primitives/Button/index.ts +2 -0
  432. package/src/primitives/Calendar/Calendar.stories.tsx +14 -0
  433. package/src/primitives/Calendar/Calendar.test.tsx +9 -0
  434. package/src/primitives/Calendar/Calendar.tsx +214 -0
  435. package/src/primitives/Calendar/index.ts +2 -0
  436. package/src/primitives/Card/Card.stories.tsx +14 -0
  437. package/src/primitives/Card/Card.test.tsx +9 -0
  438. package/src/primitives/Card/Card.tsx +48 -0
  439. package/src/primitives/Card/TextureCard.tsx +93 -0
  440. package/src/primitives/Card/index.ts +3 -0
  441. package/src/primitives/Carousel/Carousel.stories.tsx +14 -0
  442. package/src/primitives/Carousel/Carousel.test.tsx +9 -0
  443. package/src/primitives/Carousel/Carousel.tsx +261 -0
  444. package/src/primitives/Carousel/index.ts +2 -0
  445. package/src/primitives/Chart/Chart.stories.tsx +14 -0
  446. package/src/primitives/Chart/Chart.test.tsx +9 -0
  447. package/src/primitives/Chart/Chart.tsx +368 -0
  448. package/src/primitives/Chart/index.ts +2 -0
  449. package/src/primitives/Checkbox/Checkbox.stories.tsx +14 -0
  450. package/src/primitives/Checkbox/Checkbox.test.tsx +9 -0
  451. package/src/primitives/Checkbox/Checkbox.tsx +27 -0
  452. package/src/primitives/Checkbox/index.ts +2 -0
  453. package/src/primitives/Collapsible/Collapsible.stories.tsx +14 -0
  454. package/src/primitives/Collapsible/Collapsible.test.tsx +9 -0
  455. package/src/primitives/Collapsible/Collapsible.tsx +12 -0
  456. package/src/primitives/Collapsible/index.ts +2 -0
  457. package/src/primitives/Command/Command.stories.tsx +14 -0
  458. package/src/primitives/Command/Command.test.tsx +9 -0
  459. package/src/primitives/Command/Command.tsx +154 -0
  460. package/src/primitives/Command/index.ts +2 -0
  461. package/src/primitives/ContextMenu/ContextMenu.stories.tsx +14 -0
  462. package/src/primitives/ContextMenu/ContextMenu.test.tsx +9 -0
  463. package/src/primitives/ContextMenu/ContextMenu.tsx +101 -0
  464. package/src/primitives/ContextMenu/index.ts +2 -0
  465. package/src/primitives/Dialog/Dialog.stories.tsx +14 -0
  466. package/src/primitives/Dialog/Dialog.test.tsx +9 -0
  467. package/src/primitives/Dialog/Dialog.tsx +88 -0
  468. package/src/primitives/Dialog/DialogProvider.tsx +130 -0
  469. package/src/primitives/Dialog/index.ts +3 -0
  470. package/src/primitives/Drawer/Drawer.stories.tsx +14 -0
  471. package/src/primitives/Drawer/Drawer.test.tsx +9 -0
  472. package/src/primitives/Drawer/Drawer.tsx +123 -0
  473. package/src/primitives/Drawer/index.ts +2 -0
  474. package/src/primitives/DropdownMenu/DropdownMenu.stories.tsx +14 -0
  475. package/src/primitives/DropdownMenu/DropdownMenu.test.tsx +9 -0
  476. package/src/primitives/DropdownMenu/DropdownMenu.tsx +111 -0
  477. package/src/primitives/DropdownMenu/index.ts +2 -0
  478. package/src/primitives/Form/Form.stories.tsx +14 -0
  479. package/src/primitives/Form/Form.test.tsx +9 -0
  480. package/src/primitives/Form/Form.tsx +179 -0
  481. package/src/primitives/Form/index.ts +2 -0
  482. package/src/primitives/HoverCard/HoverCard.stories.tsx +14 -0
  483. package/src/primitives/HoverCard/HoverCard.test.tsx +9 -0
  484. package/src/primitives/HoverCard/HoverCard.tsx +31 -0
  485. package/src/primitives/HoverCard/index.ts +2 -0
  486. package/src/primitives/Input/Input.stories.tsx +14 -0
  487. package/src/primitives/Input/Input.test.tsx +9 -0
  488. package/src/primitives/Input/Input.tsx +110 -0
  489. package/src/primitives/Input/index.ts +2 -0
  490. package/src/primitives/InputOtp/InputOtp.stories.tsx +14 -0
  491. package/src/primitives/InputOtp/InputOtp.test.tsx +9 -0
  492. package/src/primitives/InputOtp/InputOtp.tsx +70 -0
  493. package/src/primitives/InputOtp/index.ts +2 -0
  494. package/src/primitives/Label/Label.stories.tsx +14 -0
  495. package/src/primitives/Label/Label.test.tsx +9 -0
  496. package/src/primitives/Label/Label.tsx +27 -0
  497. package/src/primitives/Label/index.ts +2 -0
  498. package/src/primitives/Menubar/Menubar.stories.tsx +14 -0
  499. package/src/primitives/Menubar/Menubar.test.tsx +9 -0
  500. package/src/primitives/Menubar/Menubar.tsx +255 -0
  501. package/src/primitives/Menubar/index.ts +2 -0
  502. package/src/primitives/NavigationMenu/NavigationMenu.stories.tsx +14 -0
  503. package/src/primitives/NavigationMenu/NavigationMenu.test.tsx +9 -0
  504. package/src/primitives/NavigationMenu/NavigationMenu.tsx +129 -0
  505. package/src/primitives/NavigationMenu/index.ts +2 -0
  506. package/src/primitives/Pagination/Pagination.stories.tsx +14 -0
  507. package/src/primitives/Pagination/Pagination.test.tsx +9 -0
  508. package/src/primitives/Pagination/Pagination.tsx +118 -0
  509. package/src/primitives/Pagination/index.ts +2 -0
  510. package/src/primitives/Popover/Popover.stories.tsx +14 -0
  511. package/src/primitives/Popover/Popover.test.tsx +9 -0
  512. package/src/primitives/Popover/Popover.tsx +30 -0
  513. package/src/primitives/Popover/index.ts +2 -0
  514. package/src/primitives/Progress/Progress.stories.tsx +14 -0
  515. package/src/primitives/Progress/Progress.test.tsx +9 -0
  516. package/src/primitives/Progress/Progress.tsx +20 -0
  517. package/src/primitives/Progress/index.ts +2 -0
  518. package/src/primitives/Radio/Radio.stories.tsx +14 -0
  519. package/src/primitives/Radio/Radio.test.tsx +9 -0
  520. package/src/primitives/Radio/Radio.tsx +33 -0
  521. package/src/primitives/Radio/index.ts +2 -0
  522. package/src/primitives/Resizable/Resizable.stories.tsx +14 -0
  523. package/src/primitives/Resizable/Resizable.test.tsx +9 -0
  524. package/src/primitives/Resizable/Resizable.tsx +44 -0
  525. package/src/primitives/Resizable/index.ts +2 -0
  526. package/src/primitives/ScrollArea/ScrollArea.stories.tsx +14 -0
  527. package/src/primitives/ScrollArea/ScrollArea.test.tsx +9 -0
  528. package/src/primitives/ScrollArea/ScrollArea.tsx +49 -0
  529. package/src/primitives/ScrollArea/index.ts +2 -0
  530. package/src/primitives/Select/Select.stories.tsx +14 -0
  531. package/src/primitives/Select/Select.test.tsx +9 -0
  532. package/src/primitives/Select/Select.tsx +140 -0
  533. package/src/primitives/Select/index.ts +2 -0
  534. package/src/primitives/Separator/Separator.stories.tsx +14 -0
  535. package/src/primitives/Separator/Separator.test.tsx +9 -0
  536. package/src/primitives/Separator/Separator.tsx +21 -0
  537. package/src/primitives/Separator/index.ts +2 -0
  538. package/src/primitives/Sheet/Sheet.stories.tsx +14 -0
  539. package/src/primitives/Sheet/Sheet.test.tsx +9 -0
  540. package/src/primitives/Sheet/Sheet.tsx +88 -0
  541. package/src/primitives/Sheet/index.ts +2 -0
  542. package/src/primitives/Skeleton/Skeleton.stories.tsx +14 -0
  543. package/src/primitives/Skeleton/Skeleton.test.tsx +9 -0
  544. package/src/primitives/Skeleton/Skeleton.tsx +71 -0
  545. package/src/primitives/Skeleton/index.ts +2 -0
  546. package/src/primitives/Slider/Slider.stories.tsx +14 -0
  547. package/src/primitives/Slider/Slider.test.tsx +9 -0
  548. package/src/primitives/Slider/Slider.tsx +20 -0
  549. package/src/primitives/Slider/index.ts +2 -0
  550. package/src/primitives/Sonner/Sonner.stories.tsx +14 -0
  551. package/src/primitives/Sonner/Sonner.test.tsx +9 -0
  552. package/src/primitives/Sonner/Sonner.tsx +30 -0
  553. package/src/primitives/Sonner/index.ts +2 -0
  554. package/src/primitives/Spinner/Spinner.stories.tsx +14 -0
  555. package/src/primitives/Spinner/Spinner.test.tsx +9 -0
  556. package/src/primitives/Spinner/Spinner.tsx +17 -0
  557. package/src/primitives/Spinner/index.ts +2 -0
  558. package/src/primitives/Switch/Switch.stories.tsx +14 -0
  559. package/src/primitives/Switch/Switch.test.tsx +9 -0
  560. package/src/primitives/Switch/Switch.tsx +31 -0
  561. package/src/primitives/Switch/index.ts +2 -0
  562. package/src/primitives/Table/Table.stories.tsx +14 -0
  563. package/src/primitives/Table/Table.test.tsx +9 -0
  564. package/src/primitives/Table/Table.tsx +121 -0
  565. package/src/primitives/Table/index.ts +2 -0
  566. package/src/primitives/Tabs/Tabs.stories.tsx +14 -0
  567. package/src/primitives/Tabs/Tabs.test.tsx +9 -0
  568. package/src/primitives/Tabs/Tabs.tsx +53 -0
  569. package/src/primitives/Tabs/index.ts +2 -0
  570. package/src/primitives/Textarea/Textarea.stories.tsx +14 -0
  571. package/src/primitives/Textarea/Textarea.test.tsx +9 -0
  572. package/src/primitives/Textarea/Textarea.tsx +22 -0
  573. package/src/primitives/Textarea/index.ts +2 -0
  574. package/src/primitives/Toggle/Toggle.stories.tsx +14 -0
  575. package/src/primitives/Toggle/Toggle.test.tsx +9 -0
  576. package/src/primitives/Toggle/Toggle.tsx +44 -0
  577. package/src/primitives/Toggle/index.ts +2 -0
  578. package/src/primitives/ToggleGroup/ToggleGroup.stories.tsx +14 -0
  579. package/src/primitives/ToggleGroup/ToggleGroup.test.tsx +9 -0
  580. package/src/primitives/ToggleGroup/ToggleGroup.tsx +62 -0
  581. package/src/primitives/ToggleGroup/index.ts +2 -0
  582. package/src/primitives/Tooltip/Tooltip.stories.tsx +14 -0
  583. package/src/primitives/Tooltip/Tooltip.test.tsx +9 -0
  584. package/src/primitives/Tooltip/Tooltip.tsx +29 -0
  585. package/src/primitives/Tooltip/index.ts +2 -0
  586. package/src/primitives/accordion.tsx +56 -0
  587. package/src/primitives/alert-dialog.tsx +140 -0
  588. package/src/primitives/alert.tsx +60 -0
  589. package/src/primitives/aspect-ratio.tsx +6 -0
  590. package/src/primitives/avatar.tsx +100 -0
  591. package/src/primitives/badge.tsx +28 -0
  592. package/src/primitives/button.tsx +43 -0
  593. package/src/primitives/calendar.tsx +214 -0
  594. package/src/primitives/card.tsx +48 -0
  595. package/src/primitives/carousel.tsx +261 -0
  596. package/src/primitives/chart.tsx +368 -0
  597. package/src/primitives/checkbox.tsx +26 -0
  598. package/src/primitives/code-block.tsx +173 -0
  599. package/src/primitives/collapsible.tsx +12 -0
  600. package/src/primitives/command.tsx +154 -0
  601. package/src/primitives/comparison.tsx +184 -0
  602. package/src/primitives/context-menu.tsx +101 -0
  603. package/src/primitives/dialog.tsx +88 -0
  604. package/src/primitives/drawer.tsx +117 -0
  605. package/src/primitives/dropdown-menu.tsx +111 -0
  606. package/src/primitives/form.tsx +179 -0
  607. package/src/primitives/hover-card.tsx +30 -0
  608. package/src/primitives/input-otp.tsx +70 -0
  609. package/src/primitives/input.tsx +23 -0
  610. package/src/primitives/label.tsx +27 -0
  611. package/src/primitives/logo-carousel.tsx +110 -0
  612. package/src/primitives/marquee.tsx +84 -0
  613. package/src/primitives/menubar.tsx +255 -0
  614. package/src/primitives/navigation-menu.tsx +129 -0
  615. package/src/primitives/pagination.tsx +118 -0
  616. package/src/primitives/popover.tsx +29 -0
  617. package/src/primitives/progress.tsx +20 -0
  618. package/src/primitives/qr-code.tsx +89 -0
  619. package/src/primitives/radio.tsx +33 -0
  620. package/src/primitives/rating.tsx +157 -0
  621. package/src/primitives/resizable.tsx +43 -0
  622. package/src/primitives/scroll-area.tsx +49 -0
  623. package/src/primitives/select.tsx +137 -0
  624. package/src/primitives/separator.tsx +21 -0
  625. package/src/primitives/sheet.tsx +87 -0
  626. package/src/primitives/skeleton.tsx +11 -0
  627. package/src/primitives/slider.tsx +20 -0
  628. package/src/primitives/sonner.tsx +27 -0
  629. package/src/primitives/spinner.tsx +17 -0
  630. package/src/primitives/switch.tsx +30 -0
  631. package/src/primitives/table.tsx +121 -0
  632. package/src/primitives/tabs.tsx +53 -0
  633. package/src/primitives/textarea.tsx +22 -0
  634. package/src/primitives/texture-card.tsx +52 -0
  635. package/src/primitives/toggle-group.tsx +62 -0
  636. package/src/primitives/toggle.tsx +44 -0
  637. package/src/primitives/tooltip.tsx +28 -0
  638. package/src/primitives/utils/cn.ts +1 -0
  639. package/src/provider/otf-ui-provider.tsx +66 -0
  640. package/src/styles.css +6 -0
  641. package/src/text/AnimatedNumber.tsx +157 -0
  642. package/src/text/Body.tsx +47 -0
  643. package/src/text/Caption.tsx +32 -0
  644. package/src/text/Code.tsx +43 -0
  645. package/src/text/GradientHeading.tsx +90 -0
  646. package/src/text/Heading.tsx +44 -0
  647. package/src/text/TextReveal.tsx +96 -0
  648. package/src/text/Typewriter.tsx +107 -0
  649. package/src/text/index.ts +8 -0
  650. package/src/theme/ColorSchemeSelect.tsx +74 -0
  651. package/src/theme/ThemeSwitch.tsx +116 -0
  652. package/src/theme/index.ts +2 -0
  653. package/src/themes/base.css +25 -0
  654. package/src/themes/glass.css +142 -0
  655. package/src/themes/linear.css +123 -0
  656. package/src/themes/midnight.css +131 -0
  657. package/src/themes/minimal.css +121 -0
  658. package/src/utils/cn.ts +6 -0
@@ -0,0 +1,72 @@
1
+ // @ts-nocheck
2
+ import React from 'react'
3
+ import { FileText, Image, FileCode, Film, Archive, MoreHorizontal, Download, Trash2 } from 'lucide-react'
4
+ import { cn } from '../utils/cn'
5
+ import { Button } from '../primitives/button'
6
+ import {
7
+ DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger,
8
+ } from '../primitives/dropdown-menu'
9
+
10
+ interface FileItem { id: string; name: string; type: string; size: string; modified: string }
11
+
12
+ const FILES: FileItem[] = [
13
+ { id: '1', name: 'design-system.fig', type: 'Figma', size: '14.2 MB', modified: 'Mar 12, 2024' },
14
+ { id: '2', name: 'hero-banner.png', type: 'PNG Image', size: '3.8 MB', modified: 'Mar 11, 2024' },
15
+ { id: '3', name: 'api-spec.yaml', type: 'YAML', size: '42 KB', modified: 'Mar 10, 2024' },
16
+ { id: '4', name: 'demo-video.mp4', type: 'MP4 Video', size: '128 MB', modified: 'Mar 9, 2024' },
17
+ { id: '5', name: 'assets.zip', type: 'ZIP Archive', size: '56 MB', modified: 'Mar 8, 2024' },
18
+ ]
19
+
20
+ const TYPE_ICON: Record<string, React.ElementType> = {
21
+ Figma: FileText, 'PNG Image': Image, YAML: FileCode, 'MP4 Video': Film, 'ZIP Archive': Archive,
22
+ }
23
+
24
+ function FileRow({ file, onDelete }: { file: FileItem; onDelete: () => void }) {
25
+ const Icon = TYPE_ICON[file.type] ?? FileText
26
+ return (
27
+ <tr className="border-b border-[hsl(var(--border))] last:border-0 hover:bg-[hsl(var(--accent)/0.5)] transition-colors">
28
+ <td className="px-4 py-3">
29
+ <div className="flex items-center gap-2.5">
30
+ <Icon className="h-4 w-4 shrink-0 text-[hsl(var(--muted-foreground))]" />
31
+ <span className="text-sm font-medium">{file.name}</span>
32
+ </div>
33
+ </td>
34
+ <td className="px-4 py-3 text-sm text-[hsl(var(--muted-foreground))]">{file.type}</td>
35
+ <td className="px-4 py-3 text-sm text-[hsl(var(--muted-foreground))]">{file.size}</td>
36
+ <td className="px-4 py-3 text-sm text-[hsl(var(--muted-foreground))]">{file.modified}</td>
37
+ <td className="px-4 py-3 text-right">
38
+ <DropdownMenu>
39
+ <DropdownMenuTrigger asChild>
40
+ <Button size="icon" variant="ghost" className="h-7 w-7"><MoreHorizontal className="h-4 w-4" /></Button>
41
+ </DropdownMenuTrigger>
42
+ <DropdownMenuContent align="end">
43
+ <DropdownMenuItem><Download className="h-4 w-4" />Download</DropdownMenuItem>
44
+ <DropdownMenuItem onClick={onDelete} className="text-[hsl(var(--destructive))]">
45
+ <Trash2 className="h-4 w-4" />Delete
46
+ </DropdownMenuItem>
47
+ </DropdownMenuContent>
48
+ </DropdownMenu>
49
+ </td>
50
+ </tr>
51
+ )
52
+ }
53
+
54
+ export interface FilesListProps { className?: string }
55
+
56
+ export function FilesList({ className }: FilesListProps) {
57
+ const [files, setFiles] = React.useState(FILES)
58
+ return (
59
+ <div className={cn('rounded-md border border-[hsl(var(--border))] overflow-hidden', className)}>
60
+ <table className="w-full">
61
+ <thead className="bg-[hsl(var(--muted)/0.5)]">
62
+ <tr>{['Name', 'Type', 'Size', 'Modified', ''].map(h => (
63
+ <th key={h} className="px-4 py-2.5 text-left text-xs font-medium text-[hsl(var(--muted-foreground))]">{h}</th>
64
+ ))}</tr>
65
+ </thead>
66
+ <tbody>
67
+ {files.map(f => <FileRow key={f.id} file={f} onDelete={() => setFiles(fs => fs.filter(x => x.id !== f.id))} />)}
68
+ </tbody>
69
+ </table>
70
+ </div>
71
+ )
72
+ }
@@ -0,0 +1,2 @@
1
+ // @tier: stub — full checklist in Phase 3
2
+ export * from './FilesList'
@@ -0,0 +1,223 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { Bell, Settings, Sliders } from 'lucide-react'
3
+ import { Switch } from '../../primitives/switch'
4
+ import { Slider } from '../../primitives/slider'
5
+ import { FloatingActionButton, FloatingThemePicker, DEFAULT_THEME_OPTIONS } from './FloatingActionButton'
6
+
7
+ // ── Generic FloatingActionButton stories ─────────────────────────────────────
8
+
9
+ const fabMeta: Meta<typeof FloatingActionButton> = {
10
+ title: 'Blocks/FloatingActionButton',
11
+ component: FloatingActionButton,
12
+ tags: ['autodocs'],
13
+ parameters: {
14
+ layout: 'fullscreen',
15
+ docs: {
16
+ description: {
17
+ component:
18
+ 'A generic fixed floating action button that opens a popover panel. ' +
19
+ 'Accepts any icon, title, description, and an array of `FabSection` objects — ' +
20
+ 'each section has a `label` and arbitrary `content: ReactNode`. ' +
21
+ '`FloatingThemePicker` is a pre-wired convenience wrapper on top of this component.',
22
+ },
23
+ },
24
+ },
25
+ argTypes: {
26
+ position: {
27
+ control: 'select',
28
+ options: ['bottom-right', 'bottom-left', 'top-right', 'top-left'],
29
+ description: 'Which corner the FAB is anchored to.',
30
+ },
31
+ },
32
+ }
33
+
34
+ export default fabMeta
35
+ type FabStory = StoryObj<typeof FloatingActionButton>
36
+
37
+ /** Minimal FAB — custom icon and a single section with arbitrary React content */
38
+ export const GenericFAB: FabStory = {
39
+ name: 'Generic — custom icon + sections',
40
+ args: {
41
+ icon: <Settings className="h-[18px] w-[18px]" />,
42
+ label: 'Open settings',
43
+ title: 'Quick Settings',
44
+ description: 'Adjust your preferences.',
45
+ position: 'bottom-right',
46
+ sections: [
47
+ {
48
+ label: 'Notifications',
49
+ content: (
50
+ <div className="space-y-2.5">
51
+ {['Email alerts', 'Push notifications', 'Digest'].map(item => (
52
+ <div key={item} className="flex items-center justify-between">
53
+ <span className="text-sm text-foreground">{item}</span>
54
+ <Switch />
55
+ </div>
56
+ ))}
57
+ </div>
58
+ ),
59
+ },
60
+ {
61
+ label: 'Volume',
62
+ content: (
63
+ <div className="py-1">
64
+ <Slider defaultValue={[60]} max={100} step={1} className="w-full" />
65
+ </div>
66
+ ),
67
+ },
68
+ ],
69
+ },
70
+ render: args => (
71
+ <div className="relative w-full h-[400px] bg-background flex items-center justify-center">
72
+ <div className="text-center space-y-1">
73
+ <p className="text-base font-semibold text-foreground">Generic FAB</p>
74
+ <p className="text-sm text-muted-foreground">Custom icon, sections, and content</p>
75
+ </div>
76
+ <FloatingActionButton {...args} />
77
+ </div>
78
+ ),
79
+ }
80
+
81
+ /** FAB with a single section and no title/description */
82
+ export const MinimalFAB: FabStory = {
83
+ name: 'Minimal — no header',
84
+ args: {
85
+ icon: <Bell className="h-[18px] w-[18px]" />,
86
+ label: 'Notification filter',
87
+ position: 'bottom-right',
88
+ sections: [
89
+ {
90
+ label: 'Filter',
91
+ content: (
92
+ <div className="space-y-2">
93
+ {['All', 'Unread', 'Mentions', 'Assigned'].map(f => (
94
+ <button
95
+ key={f}
96
+ type="button"
97
+ className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
98
+ >
99
+ {f}
100
+ </button>
101
+ ))}
102
+ </div>
103
+ ),
104
+ },
105
+ ],
106
+ },
107
+ render: args => (
108
+ <div className="relative w-full h-[400px] bg-background flex items-center justify-center">
109
+ <div className="text-center space-y-1">
110
+ <p className="text-base font-semibold text-foreground">Minimal FAB</p>
111
+ <p className="text-sm text-muted-foreground">No header, single section</p>
112
+ </div>
113
+ <FloatingActionButton {...args} />
114
+ </div>
115
+ ),
116
+ }
117
+
118
+ /** All four corner positions */
119
+ export const CornerPositions: FabStory = {
120
+ name: 'All corner positions',
121
+ render: () => {
122
+ const positions = ['bottom-right', 'bottom-left', 'top-right', 'top-left'] as const
123
+ return (
124
+ <div className="relative w-full h-[400px] bg-background flex items-center justify-center">
125
+ <div className="text-center space-y-1">
126
+ <p className="text-base font-semibold text-foreground">Four corners</p>
127
+ <p className="text-sm text-muted-foreground">Each FAB anchors to a different corner</p>
128
+ </div>
129
+ {positions.map(pos => (
130
+ <FloatingActionButton
131
+ key={pos}
132
+ icon={<Sliders className="h-[18px] w-[18px]" />}
133
+ label={`FAB ${pos}`}
134
+ title={pos}
135
+ position={pos}
136
+ sections={[{ label: 'Position', content: <p className="text-sm text-muted-foreground">{pos}</p> }]}
137
+ />
138
+ ))}
139
+ </div>
140
+ )
141
+ },
142
+ parameters: { layout: 'fullscreen' },
143
+ }
144
+
145
+ // ── FloatingThemePicker (pre-wired convenience) ───────────────────────────────
146
+
147
+ export const ThemePicker: FabStory = {
148
+ name: 'FloatingThemePicker — default',
149
+ render: () => (
150
+ <div className="relative w-full h-[400px] bg-background flex items-center justify-center">
151
+ <div className="text-center space-y-1">
152
+ <p className="text-base font-semibold text-foreground">Click the palette button</p>
153
+ <p className="text-sm text-muted-foreground">Pre-wired mode + palette sections</p>
154
+ </div>
155
+ <FloatingThemePicker />
156
+ </div>
157
+ ),
158
+ }
159
+
160
+ export const ThemePickerCustomPalette: FabStory = {
161
+ name: 'FloatingThemePicker — custom palette',
162
+ render: () => (
163
+ <div className="relative w-full h-[400px] bg-background flex items-center justify-center">
164
+ <div className="text-center space-y-1">
165
+ <p className="text-base font-semibold text-foreground">Three palettes only</p>
166
+ <p className="text-sm text-muted-foreground">Pass your own options array</p>
167
+ </div>
168
+ <FloatingThemePicker
169
+ position="bottom-right"
170
+ options={[
171
+ { value: '__brand__', label: 'Default', color: '#F97316' },
172
+ { value: 'theme-slate', label: 'Slate', color: '#6366F1' },
173
+ { value: 'theme-cosmic', label: 'Cosmic', color: '#7C3AED' },
174
+ ]}
175
+ />
176
+ </div>
177
+ ),
178
+ }
179
+
180
+ /** Static panel preview for documentation */
181
+ export const PanelPreview: FabStory = {
182
+ name: 'Panel preview (static layout)',
183
+ render: () => (
184
+ <div className="w-64 rounded-lg border border-border bg-card shadow-lg overflow-hidden">
185
+ <div className="px-4 pt-3.5 pb-3">
186
+ <p className="text-sm font-semibold leading-none">Appearance</p>
187
+ <p className="text-[11px] text-muted-foreground mt-1">Customize your experience.</p>
188
+ </div>
189
+ <div className="h-px bg-border" />
190
+ <div className="px-4 py-3 space-y-4">
191
+ <div>
192
+ <p className="text-[10px] font-semibold uppercase tracking-widest text-muted-foreground mb-2">Mode</p>
193
+ <div className="inline-flex rounded-md border border-border bg-background p-0.5">
194
+ {['Light', 'Dark', 'System'].map((m, i) => (
195
+ <span
196
+ key={m}
197
+ className={`inline-flex items-center px-2.5 h-7 text-xs rounded ${
198
+ i === 2 ? 'bg-foreground text-background' : 'text-muted-foreground'
199
+ }`}
200
+ >
201
+ {m}
202
+ </span>
203
+ ))}
204
+ </div>
205
+ </div>
206
+ <div>
207
+ <p className="text-[10px] font-semibold uppercase tracking-widest text-muted-foreground mb-2.5">Palette</p>
208
+ <div className="flex items-center gap-2.5">
209
+ {DEFAULT_THEME_OPTIONS.map((opt, i) => (
210
+ <span
211
+ key={opt.value}
212
+ className={`h-7 w-7 rounded-full block ${i === 0 ? 'ring-2 ring-offset-2 ring-offset-card scale-110' : 'opacity-60'}`}
213
+ style={{ background: opt.color, ['--tw-ring-color' as string]: opt.color }}
214
+ />
215
+ ))}
216
+ </div>
217
+ <p className="text-[11px] text-muted-foreground mt-2 font-medium">Brand</p>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ ),
222
+ parameters: { layout: 'centered' },
223
+ }
@@ -0,0 +1,47 @@
1
+ import { render, screen, fireEvent } from '@testing-library/react'
2
+ import { FloatingActionButton, FloatingThemePicker } from './FloatingActionButton'
3
+
4
+ describe('FloatingActionButton', () => {
5
+ it('renders the FAB trigger without throwing', () => {
6
+ expect(() =>
7
+ render(<FloatingActionButton label="Open panel" sections={[]} />)
8
+ ).not.toThrow()
9
+ })
10
+
11
+ it('shows the FAB button with aria-label', () => {
12
+ render(<FloatingActionButton label="Open panel" sections={[]} />)
13
+ expect(screen.getByRole('button', { name: /open panel/i })).toBeInTheDocument()
14
+ })
15
+
16
+ it('opens the panel when FAB is clicked', () => {
17
+ render(
18
+ <FloatingActionButton
19
+ label="Open panel"
20
+ title="My Panel"
21
+ sections={[{ label: 'Section', content: <span>Hello</span> }]}
22
+ />
23
+ )
24
+ fireEvent.click(screen.getByRole('button', { name: /open panel/i }))
25
+ expect(screen.getByText('My Panel')).toBeInTheDocument()
26
+ expect(screen.getByText('Hello')).toBeInTheDocument()
27
+ })
28
+ })
29
+
30
+ describe('FloatingThemePicker', () => {
31
+ it('renders without throwing', () => {
32
+ expect(() => render(<FloatingThemePicker />)).not.toThrow()
33
+ })
34
+
35
+ it('shows the FAB button with aria-label', () => {
36
+ render(<FloatingThemePicker />)
37
+ expect(screen.getByRole('button', { name: /open theme picker/i })).toBeInTheDocument()
38
+ })
39
+
40
+ it('opens the appearance panel when FAB is clicked', () => {
41
+ render(<FloatingThemePicker />)
42
+ fireEvent.click(screen.getByRole('button', { name: /open theme picker/i }))
43
+ expect(screen.getByText('Appearance')).toBeInTheDocument()
44
+ })
45
+
46
+ // TODO: add tests for palette switching, mode switching, localStorage persistence
47
+ })
@@ -0,0 +1,253 @@
1
+ 'use client'
2
+ import React from 'react'
3
+ import { Palette } from 'lucide-react'
4
+ import { cn } from '../../utils/cn'
5
+ import { Popover, PopoverContent, PopoverTrigger } from '../../primitives/popover'
6
+ import { Separator } from '../../primitives/separator'
7
+ import { ThemeSwitch } from '../../theme/ThemeSwitch'
8
+
9
+ // ── Generic FAB types ─────────────────────────────────────────────────────────
10
+
11
+ export type FabPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
12
+
13
+ export interface FabSection {
14
+ /** Section heading shown in small-caps above the items. */
15
+ label: string
16
+ /** The content to render inside this section. */
17
+ content: React.ReactNode
18
+ }
19
+
20
+ export interface FloatingActionButtonProps {
21
+ /** Icon rendered inside the FAB. Defaults to a palette icon. */
22
+ icon?: React.ReactNode
23
+ /** Tooltip / aria-label for the FAB button. */
24
+ label?: string
25
+ /** Panel title shown at the top of the popover. */
26
+ title?: string
27
+ /** Panel subtitle shown below the title. */
28
+ description?: string
29
+ /** Sections of content to render inside the popover panel. */
30
+ sections?: FabSection[]
31
+ /** Which corner to anchor the FAB. @default 'bottom-right' */
32
+ position?: FabPosition
33
+ /** Extra className on the root wrapper. */
34
+ className?: string
35
+ }
36
+
37
+ const POSITION_CLASSES: Record<FabPosition, string> = {
38
+ 'bottom-right': 'bottom-5 right-5',
39
+ 'bottom-left': 'bottom-5 left-5',
40
+ 'top-right': 'top-5 right-5',
41
+ 'top-left': 'top-5 left-5',
42
+ }
43
+
44
+ const POPOVER_ALIGN: Record<FabPosition, 'start' | 'end'> = {
45
+ 'bottom-right': 'end',
46
+ 'bottom-left': 'start',
47
+ 'top-right': 'end',
48
+ 'top-left': 'start',
49
+ }
50
+
51
+ const POPOVER_SIDE: Record<FabPosition, 'top' | 'bottom'> = {
52
+ 'bottom-right': 'top',
53
+ 'bottom-left': 'top',
54
+ 'top-right': 'bottom',
55
+ 'top-left': 'bottom',
56
+ }
57
+
58
+ // ── Generic FloatingActionButton ─────────────────────────────────────────────
59
+
60
+ export function FloatingActionButton({
61
+ icon = <Palette className="h-[18px] w-[18px]" />,
62
+ label = 'Open panel',
63
+ title,
64
+ description,
65
+ sections = [],
66
+ position = 'bottom-right',
67
+ className,
68
+ }: FloatingActionButtonProps) {
69
+ const [open, setOpen] = React.useState(false)
70
+
71
+ return (
72
+ <div
73
+ data-slot="floating-action-button"
74
+ className={cn('fixed z-50', POSITION_CLASSES[position], className)}
75
+ >
76
+ <Popover open={open} onOpenChange={setOpen}>
77
+ <PopoverTrigger asChild>
78
+ <button
79
+ type="button"
80
+ aria-label={label}
81
+ aria-expanded={open}
82
+ className={cn(
83
+ 'h-11 w-11 rounded-full shadow-lg',
84
+ 'flex items-center justify-center',
85
+ 'bg-primary text-primary-foreground',
86
+ 'border border-primary/20',
87
+ 'hover:opacity-90 active:scale-95',
88
+ 'transition-all duration-150',
89
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
90
+ 'focus-visible:ring-offset-2 focus-visible:ring-offset-background',
91
+ )}
92
+ >
93
+ {icon}
94
+ </button>
95
+ </PopoverTrigger>
96
+
97
+ <PopoverContent
98
+ side={POPOVER_SIDE[position]}
99
+ align={POPOVER_ALIGN[position]}
100
+ sideOffset={10}
101
+ className="w-60 p-0 overflow-hidden"
102
+ >
103
+ {(title || description) && (
104
+ <>
105
+ <div className="px-3 pt-3 pb-2">
106
+ {title && <p className="text-xs font-semibold leading-none">{title}</p>}
107
+ {description && (
108
+ <p className="text-[10px] text-muted-foreground mt-0.5">{description}</p>
109
+ )}
110
+ </div>
111
+ {sections.length > 0 && <Separator />}
112
+ </>
113
+ )}
114
+
115
+ {sections.length > 0 && (
116
+ <div className="px-3 py-2.5 space-y-3">
117
+ {sections.map((section, i) => (
118
+ <React.Fragment key={section.label}>
119
+ <div>
120
+ <p className="text-[9px] font-semibold uppercase tracking-widest text-muted-foreground mb-1.5">
121
+ {section.label}
122
+ </p>
123
+ {section.content}
124
+ </div>
125
+ {i < sections.length - 1 && <Separator />}
126
+ </React.Fragment>
127
+ ))}
128
+ </div>
129
+ )}
130
+ </PopoverContent>
131
+ </Popover>
132
+ </div>
133
+ )
134
+ }
135
+
136
+ // ── Theme-picker helpers ──────────────────────────────────────────────────────
137
+
138
+ const STORAGE_KEY = 'otf-theme'
139
+ const BRAND_SENTINEL = '__brand__'
140
+
141
+ export interface ThemePickerOption {
142
+ /** CSS class applied to <html>. Use '__brand__' for the default brand palette (no class). */
143
+ value: string
144
+ /** Display label shown as tooltip and below the active swatch. */
145
+ label: string
146
+ /** Hex or CSS color used to render the swatch circle. */
147
+ color: string
148
+ }
149
+
150
+ export const DEFAULT_THEME_OPTIONS: ThemePickerOption[] = [
151
+ { value: 'theme-minimal', label: 'Minimal', color: '#F97316' },
152
+ { value: 'theme-linear', label: 'Linear', color: '#171717' },
153
+ { value: 'theme-glass', label: 'Glass', color: '#3B82F6' },
154
+ { value: 'theme-midnight', label: 'Midnight', color: '#7C3AED' },
155
+ ]
156
+
157
+ function readStoredTheme(): string {
158
+ if (typeof window === 'undefined') return BRAND_SENTINEL
159
+ try { return localStorage.getItem(STORAGE_KEY) || BRAND_SENTINEL } catch { return BRAND_SENTINEL }
160
+ }
161
+
162
+ function applyTheme(val: string) {
163
+ if (typeof document === 'undefined') return
164
+ const themeClass = val === BRAND_SENTINEL ? '' : val
165
+ const root = document.documentElement
166
+ const preserved = Array.from(root.classList).filter(c => !c.startsWith('theme-'))
167
+ root.className = [...preserved, ...(themeClass ? [themeClass] : [])].join(' ').trim()
168
+ try { localStorage.setItem(STORAGE_KEY, themeClass) } catch {}
169
+ }
170
+
171
+ // ── PaletteSwatches (section content for the theme FAB) ──────────────────────
172
+
173
+ export interface PaletteSwatchesProps {
174
+ options?: ThemePickerOption[]
175
+ }
176
+
177
+ export function PaletteSwatches({ options = DEFAULT_THEME_OPTIONS }: PaletteSwatchesProps) {
178
+ const [activeValue, setActiveValue] = React.useState<string>(readStoredTheme)
179
+ const activeOption = options.find(o => o.value === activeValue) ?? options[0]
180
+
181
+ const handleSelect = (value: string) => {
182
+ setActiveValue(value)
183
+ applyTheme(value)
184
+ }
185
+
186
+ return (
187
+ <div>
188
+ <div className="flex items-center gap-2">
189
+ {options.map(opt => {
190
+ const isActive = activeValue === opt.value
191
+ return (
192
+ <button
193
+ key={opt.value}
194
+ type="button"
195
+ title={opt.label}
196
+ aria-label={opt.label}
197
+ aria-pressed={isActive}
198
+ onClick={() => handleSelect(opt.value)}
199
+ className={cn(
200
+ 'h-5 w-5 rounded-full transition-all duration-150',
201
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',
202
+ isActive
203
+ ? 'ring-2 ring-offset-1 ring-offset-card scale-110'
204
+ : 'opacity-50 hover:opacity-80 hover:scale-105',
205
+ )}
206
+ style={{
207
+ background: opt.color,
208
+ ['--tw-ring-color' as string]: opt.color,
209
+ }}
210
+ />
211
+ )
212
+ })}
213
+ </div>
214
+ <p className="text-[10px] text-muted-foreground mt-1.5 font-medium">
215
+ {activeOption?.label ?? ''}
216
+ </p>
217
+ </div>
218
+ )
219
+ }
220
+
221
+ // ── FloatingThemePicker (pre-wired convenience export) ────────────────────────
222
+
223
+ export interface FloatingThemePickerProps {
224
+ options?: ThemePickerOption[]
225
+ position?: FabPosition
226
+ className?: string
227
+ }
228
+
229
+ export function FloatingThemePicker({
230
+ options = DEFAULT_THEME_OPTIONS,
231
+ position = 'bottom-right',
232
+ className,
233
+ }: FloatingThemePickerProps) {
234
+ return (
235
+ <FloatingActionButton
236
+ icon={<Palette className="h-[18px] w-[18px]" />}
237
+ label="Open theme picker"
238
+ title="Appearance"
239
+ position={position}
240
+ className={className}
241
+ sections={[
242
+ {
243
+ label: 'Mode',
244
+ content: <ThemeSwitch variant="segmented" className="w-full" />,
245
+ },
246
+ {
247
+ label: 'Palette',
248
+ content: <PaletteSwatches options={options} />,
249
+ },
250
+ ]}
251
+ />
252
+ )
253
+ }
@@ -0,0 +1 @@
1
+ export * from './FloatingActionButton'
@@ -0,0 +1,14 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { IntegrationCard } from './IntegrationCard'
3
+
4
+ // TODO: add all variants
5
+ const meta: Meta<typeof IntegrationCard> = {
6
+ title: 'Blocks/IntegrationCard',
7
+ component: IntegrationCard,
8
+ tags: ['autodocs'],
9
+ }
10
+
11
+ export default meta
12
+ type Story = StoryObj<typeof IntegrationCard>
13
+
14
+ export const Default: Story = {}
@@ -0,0 +1,9 @@
1
+ import { render } from '@testing-library/react'
2
+ import { IntegrationCard } from './IntegrationCard'
3
+
4
+ // TODO: add full a11y + variant tests
5
+ describe('IntegrationCard', () => {
6
+ it('renders without throwing', () => {
7
+ expect(() => render(<IntegrationCard />)).not.toThrow()
8
+ })
9
+ })
@@ -0,0 +1,50 @@
1
+ import React, { useState } from 'react'
2
+ import { cn } from '../utils/cn'
3
+ import { Badge } from '../primitives/badge'
4
+ import { Button } from '../primitives/button'
5
+
6
+ export interface IntegrationCardProps {
7
+ icon?: React.ReactNode
8
+ name?: string
9
+ description?: string
10
+ connected?: boolean
11
+ className?: string
12
+ }
13
+
14
+ export function IntegrationCard({
15
+ icon,
16
+ name = 'Slack',
17
+ description = 'Send notifications and updates to your Slack workspace.',
18
+ connected = false,
19
+ className,
20
+ }: IntegrationCardProps) {
21
+ const [isConnected, setIsConnected] = useState(connected)
22
+
23
+ return (
24
+ <div className={cn('flex items-center gap-4 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4', className)}>
25
+ <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-md bg-[hsl(var(--muted))] text-[hsl(var(--muted-foreground))]">
26
+ {icon ?? <span className="text-base font-bold">{name[0]}</span>}
27
+ </div>
28
+ <div className="flex-1 min-w-0">
29
+ <div className="flex items-center gap-2">
30
+ <p className="text-sm font-semibold">{name}</p>
31
+ <Badge
32
+ variant={isConnected ? 'default' : 'outline'}
33
+ className="text-[10px] px-1.5 py-0.5 shrink-0"
34
+ >
35
+ {isConnected ? 'Connected' : 'Not connected'}
36
+ </Badge>
37
+ </div>
38
+ <p className="text-xs text-[hsl(var(--muted-foreground))] mt-0.5 truncate">{description}</p>
39
+ </div>
40
+ <Button
41
+ size="sm"
42
+ variant={isConnected ? 'outline' : 'default'}
43
+ className="shrink-0"
44
+ onClick={() => setIsConnected(v => !v)}
45
+ >
46
+ {isConnected ? 'Disconnect' : 'Connect'}
47
+ </Button>
48
+ </div>
49
+ )
50
+ }
@@ -0,0 +1,2 @@
1
+ // @tier: stub — full checklist in Phase 3
2
+ export * from './IntegrationCard'