@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,146 @@
1
+ import {
2
+ closestCorners,
3
+ DndContext,
4
+ DragOverlay,
5
+ KeyboardSensor,
6
+ PointerSensor,
7
+ useSensor,
8
+ useSensors,
9
+ type DragEndEvent,
10
+ type DragOverEvent,
11
+ type DragStartEvent,
12
+ } from '@dnd-kit/core'
13
+ import { sortableKeyboardCoordinates } from '@dnd-kit/sortable'
14
+ import { useState } from 'react'
15
+ import { cn } from '../../lib/utils'
16
+ import { KanbanCard } from './kanban-card'
17
+ import { KanbanColumn } from './kanban-column'
18
+ import type { KanbanBoardProps, KanbanCardItem } from './kanban-types'
19
+
20
+ export function KanbanBoard({
21
+ cards,
22
+ className,
23
+ columns,
24
+ emptyColumnMessage,
25
+ onAddCard,
26
+ onCardClick,
27
+ onCardMove,
28
+ renderCardFooter,
29
+ showAddCard = true,
30
+ }: KanbanBoardProps) {
31
+ const [activeCard, setActiveCard] = useState<KanbanCardItem | null>(null)
32
+
33
+ const sensors = useSensors(
34
+ useSensor(PointerSensor, {
35
+ activationConstraint: {
36
+ distance: 5,
37
+ },
38
+ }),
39
+ useSensor(KeyboardSensor, {
40
+ coordinateGetter: sortableKeyboardCoordinates,
41
+ }),
42
+ )
43
+
44
+ function handleDragStart(event: DragStartEvent) {
45
+ const { active } = event
46
+ const foundCard = cards.find((c) => c.id === active.id)
47
+ if (foundCard) {
48
+ setActiveCard(foundCard)
49
+ }
50
+ }
51
+
52
+ function handleDragOver(event: DragOverEvent) {
53
+ const { active, over } = event
54
+ if (!over) return
55
+
56
+ const activeId = String(active.id)
57
+ const overId = String(over.id)
58
+
59
+ if (activeId === overId) return
60
+
61
+ const isActiveCard = active.data.current?.type === 'Card'
62
+ const isOverCard = over.data.current?.type === 'Card'
63
+ const isOverColumn = over.data.current?.type === 'Column'
64
+
65
+ if (!isActiveCard) return
66
+
67
+ const draggedCard = cards.find((c) => c.id === activeId)
68
+ if (!draggedCard) return
69
+
70
+ if (isOverCard) {
71
+ const overCard = cards.find((c) => c.id === overId)
72
+ if (overCard && draggedCard.columnId !== overCard.columnId) {
73
+ const columnCards = cards.filter((c) => c.columnId === overCard.columnId)
74
+ const targetIndex = columnCards.findIndex((c) => c.id === overId)
75
+ onCardMove?.(activeId, overCard.columnId, targetIndex >= 0 ? targetIndex : 0)
76
+ }
77
+ } else if (isOverColumn) {
78
+ if (draggedCard.columnId !== overId) {
79
+ const columnCards = cards.filter((c) => c.columnId === overId)
80
+ onCardMove?.(activeId, overId, columnCards.length)
81
+ }
82
+ }
83
+ }
84
+
85
+ function handleDragEnd(event: DragEndEvent) {
86
+ const { active, over } = event
87
+ setActiveCard(null)
88
+
89
+ if (!over) return
90
+
91
+ const activeId = String(active.id)
92
+ const overId = String(over.id)
93
+
94
+ const draggedCard = cards.find((c) => c.id === activeId)
95
+ if (!draggedCard) return
96
+
97
+ const isOverCard = over.data.current?.type === 'Card'
98
+ const isOverColumn = over.data.current?.type === 'Column'
99
+
100
+ if (isOverCard) {
101
+ const overCard = cards.find((c) => c.id === overId)
102
+ if (overCard) {
103
+ const columnCards = cards.filter((c) => c.columnId === overCard.columnId)
104
+ const newIndex = columnCards.findIndex((c) => c.id === overId)
105
+ onCardMove?.(activeId, overCard.columnId, newIndex >= 0 ? newIndex : 0)
106
+ }
107
+ } else if (isOverColumn) {
108
+ const columnCards = cards.filter((c) => c.columnId === overId)
109
+ onCardMove?.(activeId, overId, columnCards.length)
110
+ }
111
+ }
112
+
113
+ return (
114
+ <DndContext
115
+ collisionDetection={closestCorners}
116
+ sensors={sensors}
117
+ onDragEnd={handleDragEnd}
118
+ onDragOver={handleDragOver}
119
+ onDragStart={handleDragStart}
120
+ >
121
+ <div
122
+ className={cn('flex h-full w-full gap-4 overflow-x-auto pb-4 scrollbar-thin', className)}
123
+ >
124
+ {columns.map((column) => {
125
+ const columnCards = cards.filter((c) => c.columnId === column.id)
126
+ return (
127
+ <KanbanColumn
128
+ cards={columnCards}
129
+ column={column}
130
+ emptyMessage={emptyColumnMessage}
131
+ key={column.id}
132
+ onAddCard={onAddCard}
133
+ onCardClick={onCardClick}
134
+ renderCardFooter={renderCardFooter}
135
+ showAddCard={showAddCard}
136
+ />
137
+ )
138
+ })}
139
+ </div>
140
+
141
+ <DragOverlay>
142
+ {activeCard ? <KanbanCard card={activeCard} isDragOverlay /> : null}
143
+ </DragOverlay>
144
+ </DndContext>
145
+ )
146
+ }
@@ -0,0 +1,149 @@
1
+ import { useSortable } from '@dnd-kit/sortable'
2
+ import { CSS } from '@dnd-kit/utilities'
3
+ import { Calendar, GripVertical } from 'lucide-react'
4
+ import type { ReactNode } from 'react'
5
+ import { Badge } from '../../components/badge'
6
+ import { cn } from '../../lib/utils'
7
+ import type { KanbanCardItem, KanbanPriority } from './kanban-types'
8
+
9
+ const priorityStyles: Record<KanbanPriority, { badge: string; border: string; label: string }> = {
10
+ low: {
11
+ badge: 'bg-muted text-muted-foreground border-transparent',
12
+ border: 'border-l-muted-foreground/40',
13
+ label: 'Low',
14
+ },
15
+ medium: {
16
+ badge: 'bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/20',
17
+ border: 'border-l-blue-500',
18
+ label: 'Medium',
19
+ },
20
+ high: {
21
+ badge: 'bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/20',
22
+ border: 'border-l-amber-500',
23
+ label: 'High',
24
+ },
25
+ urgent: {
26
+ badge: 'bg-destructive/10 text-destructive border-destructive/20',
27
+ border: 'border-l-destructive',
28
+ label: 'Urgent',
29
+ },
30
+ }
31
+
32
+ export interface KanbanCardProps {
33
+ card: KanbanCardItem
34
+ isDragOverlay?: boolean
35
+ onClick?: (card: KanbanCardItem) => void
36
+ renderFooter?: (card: KanbanCardItem) => ReactNode
37
+ }
38
+
39
+ export function KanbanCard({
40
+ card,
41
+ isDragOverlay = false,
42
+ onClick,
43
+ renderFooter,
44
+ }: KanbanCardProps) {
45
+ const { attributes, isDragging, listeners, setNodeRef, transform, transition } = useSortable({
46
+ data: { card, type: 'Card' },
47
+ id: card.id,
48
+ })
49
+
50
+ const style = {
51
+ transform: CSS.Transform.toString(transform),
52
+ transition,
53
+ }
54
+
55
+ const priority = card.priority ? priorityStyles[card.priority] : null
56
+
57
+ return (
58
+ <div
59
+ className={cn(
60
+ 'group relative flex flex-col gap-2 rounded-xl border border-border/80 bg-card p-3.5 text-card-foreground shadow-xs transition-shadow hover:border-foreground/25 hover:shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50',
61
+ priority?.border ? cn('border-l-4', priority.border) : '',
62
+ isDragging && 'opacity-40 shadow-inner',
63
+ isDragOverlay && 'rotate-1 shadow-lg ring-2 ring-primary/20 cursor-grabbing',
64
+ )}
65
+ ref={setNodeRef}
66
+ style={style}
67
+ tabIndex={0}
68
+ onClick={() => onClick?.(card)}
69
+ onKeyDown={(e) => {
70
+ if (e.key === 'Enter' || e.key === ' ') {
71
+ e.preventDefault()
72
+ onClick?.(card)
73
+ }
74
+ }}
75
+ >
76
+ <div className="flex items-start justify-between gap-2">
77
+ <h4 className="text-sm font-medium leading-snug tracking-tight text-foreground select-none">
78
+ {card.title}
79
+ </h4>
80
+ <button
81
+ aria-label={`Drag card: ${card.title}`}
82
+ className="shrink-0 cursor-grab text-muted-foreground/50 opacity-60 transition-opacity hover:text-foreground hover:opacity-100 group-hover:opacity-100 focus-visible:opacity-100 active:cursor-grabbing"
83
+ type="button"
84
+ {...attributes}
85
+ {...listeners}
86
+ onClick={(e) => e.stopPropagation()}
87
+ >
88
+ <GripVertical className="size-4" />
89
+ </button>
90
+ </div>
91
+
92
+ {card.description && (
93
+ <p className="line-clamp-2 text-xs leading-relaxed text-muted-foreground select-none">
94
+ {card.description}
95
+ </p>
96
+ )}
97
+
98
+ {(card.tags?.length || card.priority) && (
99
+ <div className="flex flex-wrap items-center gap-1.5 pt-1">
100
+ {priority && (
101
+ <Badge className={cn('text-[10px] font-semibold tracking-tight', priority.badge)}>
102
+ {priority.label}
103
+ </Badge>
104
+ )}
105
+ {card.tags?.map((tag) => (
106
+ <span
107
+ className="inline-flex items-center rounded-md bg-muted/60 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground"
108
+ key={tag}
109
+ >
110
+ {tag}
111
+ </span>
112
+ ))}
113
+ </div>
114
+ )}
115
+
116
+ <div className="flex items-center justify-between gap-2 border-t border-border/40 pt-2 text-xs text-muted-foreground">
117
+ {card.dueDate ? (
118
+ <span className="flex items-center gap-1 text-[11px]">
119
+ <Calendar className="size-3" />
120
+ {card.dueDate}
121
+ </span>
122
+ ) : (
123
+ <span />
124
+ )}
125
+
126
+ {card.assignee && (
127
+ <div className="flex items-center gap-1.5" title={`Assigned to ${card.assignee.name}`}>
128
+ {card.assignee.avatarUrl ? (
129
+ <img
130
+ alt={card.assignee.name}
131
+ className="size-4.5 rounded-full object-cover ring-1 ring-border"
132
+ src={card.assignee.avatarUrl}
133
+ />
134
+ ) : (
135
+ <span className="flex size-4.5 items-center justify-center rounded-full bg-primary/10 text-[9px] font-semibold text-primary">
136
+ {card.assignee.name.charAt(0).toUpperCase()}
137
+ </span>
138
+ )}
139
+ <span className="max-w-20 truncate text-[11px] font-medium text-foreground/80">
140
+ {card.assignee.name}
141
+ </span>
142
+ </div>
143
+ )}
144
+ </div>
145
+
146
+ {renderFooter?.(card)}
147
+ </div>
148
+ )
149
+ }
@@ -0,0 +1,102 @@
1
+ import { useDroppable } from '@dnd-kit/core'
2
+ import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'
3
+ import { Plus } from 'lucide-react'
4
+ import type { ReactNode } from 'react'
5
+ import { Badge } from '../../components/badge'
6
+ import { Button } from '../../components/button'
7
+ import { cn } from '../../lib/utils'
8
+ import { KanbanCard } from './kanban-card'
9
+ import type { KanbanCardItem, KanbanColumnItem } from './kanban-types'
10
+
11
+ export interface KanbanColumnProps {
12
+ cards: readonly KanbanCardItem[]
13
+ column: KanbanColumnItem
14
+ emptyMessage?: string
15
+ onAddCard?: (columnId: string) => void
16
+ onCardClick?: (card: KanbanCardItem) => void
17
+ renderCardFooter?: (card: KanbanCardItem) => ReactNode
18
+ showAddCard?: boolean
19
+ }
20
+
21
+ export function KanbanColumn({
22
+ cards,
23
+ column,
24
+ emptyMessage = 'No cards in this column',
25
+ onAddCard,
26
+ onCardClick,
27
+ renderCardFooter,
28
+ showAddCard = true,
29
+ }: KanbanColumnProps) {
30
+ const { isOver, setNodeRef } = useDroppable({
31
+ data: { column, type: 'Column' },
32
+ id: column.id,
33
+ })
34
+
35
+ const cardIds = cards.map((c) => c.id)
36
+ const isOverLimit = column.limit && cards.length > column.limit
37
+
38
+ return (
39
+ <div
40
+ className={cn(
41
+ 'flex w-80 shrink-0 flex-col rounded-2xl border border-border/70 bg-muted/30 p-3 shadow-2xs transition-colors',
42
+ isOver && 'border-primary/50 bg-primary/5 ring-1 ring-primary/20',
43
+ )}
44
+ >
45
+ <header className="mb-3 flex items-center justify-between px-1.5 pt-1">
46
+ <div className="flex items-center gap-2">
47
+ {column.accentColor && (
48
+ <span
49
+ className="size-2.5 rounded-full"
50
+ style={{ backgroundColor: column.accentColor }}
51
+ />
52
+ )}
53
+ <h3 className="text-sm font-semibold tracking-tight text-foreground">{column.title}</h3>
54
+ <Badge
55
+ className={cn(
56
+ 'h-5 px-1.5 text-[11px] font-medium',
57
+ isOverLimit ? 'bg-destructive/15 text-destructive' : 'bg-muted text-muted-foreground',
58
+ )}
59
+ variant="outline"
60
+ >
61
+ {cards.length}
62
+ {column.limit ? ` / ${column.limit}` : ''}
63
+ </Badge>
64
+ </div>
65
+
66
+ {showAddCard && onAddCard && (
67
+ <Button
68
+ aria-label={`Add card to ${column.title}`}
69
+ className="size-7 rounded-lg text-muted-foreground hover:text-foreground"
70
+ size="icon"
71
+ variant="ghost"
72
+ onClick={() => onAddCard(column.id)}
73
+ >
74
+ <Plus className="size-4" />
75
+ </Button>
76
+ )}
77
+ </header>
78
+
79
+ <div
80
+ className="flex flex-1 flex-col gap-2.5 overflow-y-auto min-h-32 px-0.5 pb-1"
81
+ ref={setNodeRef}
82
+ >
83
+ <SortableContext items={cardIds} strategy={verticalListSortingStrategy}>
84
+ {cards.map((card) => (
85
+ <KanbanCard
86
+ card={card}
87
+ key={card.id}
88
+ onClick={onCardClick}
89
+ renderFooter={renderCardFooter}
90
+ />
91
+ ))}
92
+ </SortableContext>
93
+
94
+ {cards.length === 0 && (
95
+ <div className="flex flex-1 items-center justify-center rounded-xl border border-dashed border-border/60 py-8 text-center text-xs text-muted-foreground/70">
96
+ {emptyMessage}
97
+ </div>
98
+ )}
99
+ </div>
100
+ </div>
101
+ )
102
+ }
@@ -0,0 +1,38 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ export type KanbanPriority = 'low' | 'medium' | 'high' | 'urgent'
4
+
5
+ export interface KanbanCardItem {
6
+ assignee?: {
7
+ avatarUrl?: string
8
+ name: string
9
+ }
10
+ columnId: string
11
+ description?: string
12
+ dueDate?: string
13
+ id: string
14
+ priority?: KanbanPriority
15
+ tags?: readonly string[]
16
+ title: string
17
+ }
18
+
19
+ export interface KanbanColumnItem {
20
+ accentColor?: string
21
+ cardIds?: readonly string[]
22
+ description?: string
23
+ id: string
24
+ limit?: number
25
+ title: string
26
+ }
27
+
28
+ export interface KanbanBoardProps {
29
+ cards: readonly KanbanCardItem[]
30
+ className?: string
31
+ columns: readonly KanbanColumnItem[]
32
+ emptyColumnMessage?: string
33
+ onAddCard?: (columnId: string) => void
34
+ onCardClick?: (card: KanbanCardItem) => void
35
+ onCardMove?: (cardId: string, targetColumnId: string, newIndex: number) => void
36
+ renderCardFooter?: (card: KanbanCardItem) => ReactNode
37
+ showAddCard?: boolean
38
+ }
@@ -0,0 +1,90 @@
1
+ import { useEffect, useRef, useState, type ComponentProps } from 'react'
2
+
3
+ import { Spinner } from '@devxcrew/react-ui/components/spinner'
4
+ import { cn } from '@devxcrew/react-ui/lib/utils'
5
+
6
+ type GlobalLoaderProps = ComponentProps<'div'> & {
7
+ active: boolean
8
+ delayMs?: number
9
+ label?: string
10
+ minimumDurationMs?: number
11
+ overlay?: boolean
12
+ }
13
+
14
+ const FADE_DURATION_MS = 160
15
+
16
+ export function GlobalLoader({
17
+ active,
18
+ className,
19
+ delayMs = 120,
20
+ label = 'Loading',
21
+ minimumDurationMs = 220,
22
+ overlay = false,
23
+ ...props
24
+ }: GlobalLoaderProps) {
25
+ const { mounted, visible } = useLoaderPresence(active, delayMs, minimumDurationMs)
26
+
27
+ if (!mounted) {
28
+ return null
29
+ }
30
+
31
+ return (
32
+ <div
33
+ aria-busy={active}
34
+ aria-hidden={!visible}
35
+ aria-live="polite"
36
+ data-slot="global-loader"
37
+ role="status"
38
+ className={cn(
39
+ 'flex min-h-full items-center justify-center gap-3 bg-background px-6 text-sm text-muted-foreground transition-opacity duration-150 ease-out',
40
+ overlay && 'absolute inset-0 z-10',
41
+ visible ? 'opacity-100' : 'pointer-events-none opacity-0',
42
+ className,
43
+ )}
44
+ {...props}
45
+ >
46
+ <Spinner className="size-5 text-foreground" />
47
+ <span>{label}</span>
48
+ </div>
49
+ )
50
+ }
51
+
52
+ function useLoaderPresence(active: boolean, delayMs: number, minimumDurationMs: number) {
53
+ const [mounted, setMounted] = useState(active && delayMs === 0)
54
+ const [visible, setVisible] = useState(active && delayMs === 0)
55
+ const shownAt = useRef<number | undefined>(undefined)
56
+
57
+ useEffect(() => {
58
+ let hideTimer: ReturnType<typeof setTimeout> | undefined
59
+ let showTimer: ReturnType<typeof setTimeout> | undefined
60
+ let unmountTimer: ReturnType<typeof setTimeout> | undefined
61
+
62
+ if (active && !mounted) {
63
+ showTimer = setTimeout(() => {
64
+ shownAt.current = Date.now()
65
+ setMounted(true)
66
+ setVisible(true)
67
+ }, delayMs)
68
+ } else if (active) {
69
+ shownAt.current ??= Date.now()
70
+ setVisible(true)
71
+ } else if (mounted) {
72
+ const elapsed = shownAt.current ? Date.now() - shownAt.current : minimumDurationMs
73
+ hideTimer = setTimeout(
74
+ () => {
75
+ setVisible(false)
76
+ unmountTimer = setTimeout(() => setMounted(false), FADE_DURATION_MS)
77
+ },
78
+ Math.max(0, minimumDurationMs - elapsed),
79
+ )
80
+ }
81
+
82
+ return () => {
83
+ clearTimeout(hideTimer)
84
+ clearTimeout(showTimer)
85
+ clearTimeout(unmountTimer)
86
+ }
87
+ }, [active, delayMs, minimumDurationMs, mounted])
88
+
89
+ return { mounted, visible }
90
+ }
@@ -0,0 +1 @@
1
+ export { GlobalLoader } from './global-loader'
@@ -0,0 +1,21 @@
1
+ # Mascot block
2
+
3
+ The Mascot block provides the shared runtime for a cursor-aware, clickable character.
4
+
5
+ ## Asset contract
6
+
7
+ Each character supplies two aligned 3x3 sprite atlases:
8
+
9
+ - `directions`: nine head directions in clockwise pointer order with `center` in the middle.
10
+ - `reactions`: nine expressions, including `blink`, `heart`, `sparkle`, and `dizzy`.
11
+
12
+ The app owns these served or imported asset URLs. This keeps the package reusable across apps and
13
+ allows each app to select a character or generate its own atlases without changing the component.
14
+ Set `trackPointer={false}` for dense variant galleries where only click reactions are needed.
15
+
16
+ ## Runtime behavior
17
+
18
+ The component selects a direction from the pointer sector, uses a dead zone and hysteresis to avoid
19
+ jitter, and pre-mounts the reaction sheet so the first click does not fetch it late. Clicks cycle
20
+ through payoff reactions, trigger `dizzy` after four quick boops, and respect
21
+ `prefers-reduced-motion` for the squash animation.
@@ -0,0 +1,2 @@
1
+ export { Mascot } from "./mascot";
2
+ export type { MascotProps } from "./mascot";