@eocrm/design-system 0.0.0-stage → 0.6.17

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 (897) hide show
  1. package/AI-PRIMER.md +341 -0
  2. package/README.md +262 -2
  3. package/docs/components/Accordion.md +114 -0
  4. package/docs/components/AgentAvatar.md +58 -0
  5. package/docs/components/Alert.md +81 -0
  6. package/docs/components/AppLayout.md +70 -0
  7. package/docs/components/Avatar.md +49 -0
  8. package/docs/components/AvatarGroup.md +47 -0
  9. package/docs/components/Badge.md +60 -0
  10. package/docs/components/Banner.md +89 -0
  11. package/docs/components/BrandIcon.md +32 -0
  12. package/docs/components/Breadcrumb.md +72 -0
  13. package/docs/components/Button.md +107 -0
  14. package/docs/components/ButtonGroup.md +78 -0
  15. package/docs/components/Calendar.md +158 -0
  16. package/docs/components/Card.md +165 -0
  17. package/docs/components/CatalogPicker.md +83 -0
  18. package/docs/components/Chart.md +114 -0
  19. package/docs/components/ChatBubble.md +55 -0
  20. package/docs/components/ChatLauncher.md +97 -0
  21. package/docs/components/Checkbox.md +55 -0
  22. package/docs/components/CircularProgress.md +53 -0
  23. package/docs/components/Cluster.md +96 -0
  24. package/docs/components/Code.md +35 -0
  25. package/docs/components/CodeBlock.md +67 -0
  26. package/docs/components/ColorPicker.md +115 -0
  27. package/docs/components/ConfirmationPopover.md +70 -0
  28. package/docs/components/Constrain.md +39 -0
  29. package/docs/components/CurrencySelect.md +57 -0
  30. package/docs/components/CursorPagination.md +43 -0
  31. package/docs/components/DashboardCanvas.md +79 -0
  32. package/docs/components/DashboardWidget.md +131 -0
  33. package/docs/components/DataTable.md +88 -0
  34. package/docs/components/DatePicker.md +68 -0
  35. package/docs/components/DateRangePicker.md +71 -0
  36. package/docs/components/DateStrip.md +56 -0
  37. package/docs/components/DefinitionList.md +82 -0
  38. package/docs/components/Divider.md +57 -0
  39. package/docs/components/Dot.md +26 -0
  40. package/docs/components/Drawer.md +94 -0
  41. package/docs/components/DropdownMenu.md +299 -0
  42. package/docs/components/EmojiPicker.md +84 -0
  43. package/docs/components/EmptyState.md +55 -0
  44. package/docs/components/EntityChip.md +102 -0
  45. package/docs/components/ErrorState.md +72 -0
  46. package/docs/components/Field.md +91 -0
  47. package/docs/components/FileUpload.md +109 -0
  48. package/docs/components/FilterChip.md +88 -0
  49. package/docs/components/FlowCanvas.md +108 -0
  50. package/docs/components/FormRow.md +31 -0
  51. package/docs/components/FormSection.md +37 -0
  52. package/docs/components/Grid.md +103 -0
  53. package/docs/components/Highlight.md +75 -0
  54. package/docs/components/HighlightedText.md +50 -0
  55. package/docs/components/IconPicker.md +134 -0
  56. package/docs/components/IconTile.md +30 -0
  57. package/docs/components/Image.md +84 -0
  58. package/docs/components/ImageCrop.md +163 -0
  59. package/docs/components/Indent.md +31 -0
  60. package/docs/components/InlineDatePicker.md +58 -0
  61. package/docs/components/InlineDateRangePicker.md +57 -0
  62. package/docs/components/Input.md +54 -0
  63. package/docs/components/Kanban.md +122 -0
  64. package/docs/components/Kbd.md +41 -0
  65. package/docs/components/Lightbox.md +105 -0
  66. package/docs/components/Link.md +65 -0
  67. package/docs/components/LinkCard.md +41 -0
  68. package/docs/components/LiquidEditor.md +84 -0
  69. package/docs/components/List.md +68 -0
  70. package/docs/components/LiveRegion.md +29 -0
  71. package/docs/components/LocaleProvider.md +25 -0
  72. package/docs/components/Logo.md +41 -0
  73. package/docs/components/Masonry.md +48 -0
  74. package/docs/components/MediaTile.md +95 -0
  75. package/docs/components/Modal.md +107 -0
  76. package/docs/components/MoneyInput.md +62 -0
  77. package/docs/components/OptionsPicker.md +123 -0
  78. package/docs/components/OtpInput.md +65 -0
  79. package/docs/components/Page.md +35 -0
  80. package/docs/components/PageHeader.md +133 -0
  81. package/docs/components/Pagination.md +44 -0
  82. package/docs/components/Palette.md +32 -0
  83. package/docs/components/PasswordInput.md +45 -0
  84. package/docs/components/PasswordStrengthMeter.md +27 -0
  85. package/docs/components/PersonDisplay.md +153 -0
  86. package/docs/components/PhoneInput.md +54 -0
  87. package/docs/components/PillMenu.md +91 -0
  88. package/docs/components/Popover.md +127 -0
  89. package/docs/components/Progress.md +56 -0
  90. package/docs/components/QrCode.md +34 -0
  91. package/docs/components/Radio.md +37 -0
  92. package/docs/components/RadioGroup.md +56 -0
  93. package/docs/components/Rail.md +191 -0
  94. package/docs/components/RelativeDateInput.md +114 -0
  95. package/docs/components/RichText.md +44 -0
  96. package/docs/components/RichTextEditor.md +112 -0
  97. package/docs/components/Screen.md +37 -0
  98. package/docs/components/ScrollArea.md +39 -0
  99. package/docs/components/Select.md +131 -0
  100. package/docs/components/SettingRow.md +98 -0
  101. package/docs/components/Skeleton.md +68 -0
  102. package/docs/components/Slider.md +92 -0
  103. package/docs/components/SlotGrid.md +55 -0
  104. package/docs/components/SocialButton.md +31 -0
  105. package/docs/components/Sortable.md +127 -0
  106. package/docs/components/SortableGroup.md +52 -0
  107. package/docs/components/Sparkline.md +51 -0
  108. package/docs/components/Split.md +76 -0
  109. package/docs/components/Stack.md +59 -0
  110. package/docs/components/StagePath.md +66 -0
  111. package/docs/components/StatGroup.md +61 -0
  112. package/docs/components/StatTile.md +58 -0
  113. package/docs/components/Sticky.md +69 -0
  114. package/docs/components/Switch.md +89 -0
  115. package/docs/components/Table.md +184 -0
  116. package/docs/components/TableOfContents.md +76 -0
  117. package/docs/components/Tabs.md +94 -0
  118. package/docs/components/Text.md +63 -0
  119. package/docs/components/Textarea.md +59 -0
  120. package/docs/components/Thread.md +52 -0
  121. package/docs/components/TimeField.md +70 -0
  122. package/docs/components/Timeline.md +57 -0
  123. package/docs/components/Title.md +54 -0
  124. package/docs/components/ToastViewport.md +61 -0
  125. package/docs/components/Tooltip.md +63 -0
  126. package/docs/components/TopBar.md +214 -0
  127. package/docs/components/Tour.md +85 -0
  128. package/docs/components/TreeView.md +113 -0
  129. package/docs/components/VisuallyHidden.md +43 -0
  130. package/docs/components/WidgetPreview.md +54 -0
  131. package/docs/components/useBelowBreakpoint.md +11 -0
  132. package/docs/components/useMonth.md +18 -0
  133. package/docs/setup.md +51 -0
  134. package/docs/theming.md +95 -0
  135. package/docs/tokens.md +123 -0
  136. package/docs/transient-state.md +56 -0
  137. package/package.json +82 -4
  138. package/src/_meta/manifest.ts +293 -0
  139. package/src/app/AppProvider.tsx +78 -0
  140. package/src/app/index.ts +3 -0
  141. package/src/app/themeTokens.ts +95 -0
  142. package/src/calendar/dateMath.ts +114 -0
  143. package/src/calendar/formatters.ts +75 -0
  144. package/src/calendar/index.ts +39 -0
  145. package/src/calendar/types.ts +36 -0
  146. package/src/calendar/useAgenda.ts +70 -0
  147. package/src/calendar/useDay.ts +51 -0
  148. package/src/calendar/useMonth.ts +82 -0
  149. package/src/calendar/useWeek.ts +62 -0
  150. package/src/calendar/weekInfo.ts +83 -0
  151. package/src/components/Accordion/Accordion.module.scss +304 -0
  152. package/src/components/Accordion/Accordion.tokens.scss +64 -0
  153. package/src/components/Accordion/Accordion.tsx +272 -0
  154. package/src/components/Accordion/AccordionContent.tsx +36 -0
  155. package/src/components/Accordion/AccordionItem.tsx +66 -0
  156. package/src/components/Accordion/AccordionTrigger.tsx +121 -0
  157. package/src/components/Accordion/context.ts +47 -0
  158. package/src/components/Accordion/index.ts +14 -0
  159. package/src/components/Alert/Alert.module.scss +92 -0
  160. package/src/components/Alert/Alert.tokens.scss +60 -0
  161. package/src/components/Alert/Alert.tsx +136 -0
  162. package/src/components/Alert/index.ts +2 -0
  163. package/src/components/AppLayout/AppLayout.module.scss +130 -0
  164. package/src/components/AppLayout/AppLayout.tokens.scss +27 -0
  165. package/src/components/AppLayout/AppLayout.tsx +211 -0
  166. package/src/components/AppLayout/index.ts +2 -0
  167. package/src/components/Avatar/AgentAvatar.module.scss +117 -0
  168. package/src/components/Avatar/AgentAvatar.tsx +122 -0
  169. package/src/components/Avatar/Avatar.module.scss +181 -0
  170. package/src/components/Avatar/Avatar.tokens.scss +107 -0
  171. package/src/components/Avatar/Avatar.tsx +210 -0
  172. package/src/components/Avatar/AvatarGroup.module.scss +128 -0
  173. package/src/components/Avatar/AvatarGroup.tsx +119 -0
  174. package/src/components/Avatar/AvatarGroupContext.tsx +20 -0
  175. package/src/components/Avatar/index.ts +6 -0
  176. package/src/components/Badge/Badge.module.scss +124 -0
  177. package/src/components/Badge/Badge.tokens.scss +74 -0
  178. package/src/components/Badge/Badge.tsx +164 -0
  179. package/src/components/Badge/index.ts +2 -0
  180. package/src/components/Banner/Banner.module.scss +73 -0
  181. package/src/components/Banner/Banner.tokens.scss +51 -0
  182. package/src/components/Banner/Banner.tsx +138 -0
  183. package/src/components/Banner/index.ts +2 -0
  184. package/src/components/BrandIcon/BrandIcon.module.scss +5 -0
  185. package/src/components/BrandIcon/BrandIcon.tsx +99 -0
  186. package/src/components/BrandIcon/index.ts +2 -0
  187. package/src/components/Breadcrumb/Breadcrumb.module.scss +32 -0
  188. package/src/components/Breadcrumb/Breadcrumb.tokens.scss +9 -0
  189. package/src/components/Breadcrumb/Breadcrumb.tsx +146 -0
  190. package/src/components/Breadcrumb/index.ts +2 -0
  191. package/src/components/Button/Button.module.scss +167 -0
  192. package/src/components/Button/Button.tokens.scss +90 -0
  193. package/src/components/Button/Button.tsx +205 -0
  194. package/src/components/Button/index.ts +2 -0
  195. package/src/components/ButtonGroup/ButtonGroup.module.scss +134 -0
  196. package/src/components/ButtonGroup/ButtonGroup.tokens.scss +42 -0
  197. package/src/components/ButtonGroup/ButtonGroup.tsx +278 -0
  198. package/src/components/ButtonGroup/ButtonGroupItem.tsx +74 -0
  199. package/src/components/ButtonGroup/context.ts +45 -0
  200. package/src/components/ButtonGroup/index.ts +2 -0
  201. package/src/components/Calendar/AgendaView.module.scss +231 -0
  202. package/src/components/Calendar/AgendaView.tsx +214 -0
  203. package/src/components/Calendar/AllDayBand.module.scss +18 -0
  204. package/src/components/Calendar/AllDayBand.tsx +75 -0
  205. package/src/components/Calendar/Calendar.module.scss +23 -0
  206. package/src/components/Calendar/Calendar.tokens.scss +262 -0
  207. package/src/components/Calendar/Calendar.tsx +370 -0
  208. package/src/components/Calendar/DayCell.module.scss +30 -0
  209. package/src/components/Calendar/DayCell.tsx +50 -0
  210. package/src/components/Calendar/DayView.module.scss +10 -0
  211. package/src/components/Calendar/DayView.tsx +163 -0
  212. package/src/components/Calendar/EventChip.module.scss +171 -0
  213. package/src/components/Calendar/EventChip.tsx +115 -0
  214. package/src/components/Calendar/HourGrid.module.scss +123 -0
  215. package/src/components/Calendar/HourGrid.tsx +381 -0
  216. package/src/components/Calendar/MonthView.module.scss +99 -0
  217. package/src/components/Calendar/MonthView.tsx +296 -0
  218. package/src/components/Calendar/TimedEvent.module.scss +205 -0
  219. package/src/components/Calendar/TimedEvent.tsx +247 -0
  220. package/src/components/Calendar/ViewSwitcher.module.scss +3 -0
  221. package/src/components/Calendar/ViewSwitcher.tsx +47 -0
  222. package/src/components/Calendar/WeekView.module.scss +10 -0
  223. package/src/components/Calendar/WeekView.tsx +137 -0
  224. package/src/components/Calendar/eventColor.ts +60 -0
  225. package/src/components/Calendar/index.ts +18 -0
  226. package/src/components/Calendar/types.ts +451 -0
  227. package/src/components/Calendar/useEventDrag.ts +783 -0
  228. package/src/components/Calendar/utils.ts +622 -0
  229. package/src/components/Card/Card.module.scss +264 -0
  230. package/src/components/Card/Card.tokens.scss +55 -0
  231. package/src/components/Card/Card.tsx +170 -0
  232. package/src/components/Card/CardBody.tsx +45 -0
  233. package/src/components/Card/CardHeader.tsx +113 -0
  234. package/src/components/Card/CardList.tsx +24 -0
  235. package/src/components/Card/CardListRow.tsx +24 -0
  236. package/src/components/Card/index.ts +7 -0
  237. package/src/components/CatalogPicker/CatalogPicker.module.scss +193 -0
  238. package/src/components/CatalogPicker/CatalogPicker.tokens.scss +26 -0
  239. package/src/components/CatalogPicker/CatalogPicker.tsx +339 -0
  240. package/src/components/CatalogPicker/index.ts +2 -0
  241. package/src/components/Chart/Chart.module.scss +287 -0
  242. package/src/components/Chart/Chart.tokens.scss +63 -0
  243. package/src/components/Chart/Chart.tsx +607 -0
  244. package/src/components/Chart/ChartLegend.tsx +99 -0
  245. package/src/components/Chart/ChartTable.tsx +62 -0
  246. package/src/components/Chart/ChartTooltip.tsx +91 -0
  247. package/src/components/Chart/chartModel.ts +689 -0
  248. package/src/components/Chart/index.ts +2 -0
  249. package/src/components/ChatBubble/ChatBubble.module.scss +93 -0
  250. package/src/components/ChatBubble/ChatBubble.tokens.scss +17 -0
  251. package/src/components/ChatBubble/ChatBubble.tsx +61 -0
  252. package/src/components/ChatBubble/ChatBubbleList.tsx +27 -0
  253. package/src/components/ChatBubble/index.ts +3 -0
  254. package/src/components/ChatLauncher/ChatLauncher.module.scss +68 -0
  255. package/src/components/ChatLauncher/ChatLauncher.tokens.scss +27 -0
  256. package/src/components/ChatLauncher/ChatLauncher.tsx +202 -0
  257. package/src/components/ChatLauncher/index.ts +2 -0
  258. package/src/components/ChatLauncher/isAtBottom.ts +12 -0
  259. package/src/components/Checkbox/Checkbox.module.scss +147 -0
  260. package/src/components/Checkbox/Checkbox.tokens.scss +44 -0
  261. package/src/components/Checkbox/Checkbox.tsx +188 -0
  262. package/src/components/Checkbox/index.ts +2 -0
  263. package/src/components/CircularProgress/CircularProgress.module.scss +133 -0
  264. package/src/components/CircularProgress/CircularProgress.tokens.scss +40 -0
  265. package/src/components/CircularProgress/CircularProgress.tsx +139 -0
  266. package/src/components/CircularProgress/index.ts +7 -0
  267. package/src/components/Cluster/Cluster.module.scss +145 -0
  268. package/src/components/Cluster/Cluster.tokens.scss +11 -0
  269. package/src/components/Cluster/Cluster.tsx +215 -0
  270. package/src/components/Cluster/index.ts +2 -0
  271. package/src/components/Code/Code.module.scss +28 -0
  272. package/src/components/Code/Code.tokens.scss +15 -0
  273. package/src/components/Code/Code.tsx +44 -0
  274. package/src/components/Code/index.ts +2 -0
  275. package/src/components/CodeBlock/CodeBlock.module.scss +146 -0
  276. package/src/components/CodeBlock/CodeBlock.tokens.scss +27 -0
  277. package/src/components/CodeBlock/CodeBlock.tsx +274 -0
  278. package/src/components/CodeBlock/highlight.ts +179 -0
  279. package/src/components/CodeBlock/index.ts +8 -0
  280. package/src/components/CodeBlock/sampleSync.ts +42 -0
  281. package/src/components/ColorPicker/ColorPicker.module.scss +286 -0
  282. package/src/components/ColorPicker/ColorPicker.tokens.scss +60 -0
  283. package/src/components/ColorPicker/ColorPicker.tsx +367 -0
  284. package/src/components/ColorPicker/ColorPickerPanel.tsx +265 -0
  285. package/src/components/ColorPicker/SVSquare.tsx +189 -0
  286. package/src/components/ColorPicker/colorMath.ts +141 -0
  287. package/src/components/ColorPicker/index.ts +6 -0
  288. package/src/components/ConfirmationPopover/ConfirmationPopover.module.scss +39 -0
  289. package/src/components/ConfirmationPopover/ConfirmationPopover.tokens.scss +13 -0
  290. package/src/components/ConfirmationPopover/ConfirmationPopover.tsx +395 -0
  291. package/src/components/ConfirmationPopover/index.ts +2 -0
  292. package/src/components/Constrain/Constrain.module.scss +99 -0
  293. package/src/components/Constrain/Constrain.tsx +79 -0
  294. package/src/components/Constrain/index.ts +2 -0
  295. package/src/components/CurrencySelect/CurrencySelect.module.scss +7 -0
  296. package/src/components/CurrencySelect/CurrencySelect.tsx +167 -0
  297. package/src/components/CurrencySelect/index.ts +3 -0
  298. package/src/components/CursorPagination/CursorPagination.module.scss +69 -0
  299. package/src/components/CursorPagination/CursorPagination.tokens.scss +32 -0
  300. package/src/components/CursorPagination/CursorPagination.tsx +121 -0
  301. package/src/components/CursorPagination/index.ts +2 -0
  302. package/src/components/DashboardCanvas/DashboardCanvas.module.scss +360 -0
  303. package/src/components/DashboardCanvas/DashboardCanvas.tokens.scss +48 -0
  304. package/src/components/DashboardCanvas/DashboardCanvas.tsx +1270 -0
  305. package/src/components/DashboardCanvas/DashboardCanvasItem.tsx +140 -0
  306. package/src/components/DashboardCanvas/DashboardCanvasSection.tsx +174 -0
  307. package/src/components/DashboardCanvas/engine.ts +363 -0
  308. package/src/components/DashboardCanvas/index.ts +9 -0
  309. package/src/components/DashboardWidget/DashboardWidget.module.scss +100 -0
  310. package/src/components/DashboardWidget/DashboardWidget.tokens.scss +16 -0
  311. package/src/components/DashboardWidget/DashboardWidget.tsx +188 -0
  312. package/src/components/DashboardWidget/index.ts +7 -0
  313. package/src/components/DataTable/BodyRow.tsx +197 -0
  314. package/src/components/DataTable/ColumnVisibilityTrigger.module.scss +7 -0
  315. package/src/components/DataTable/ColumnVisibilityTrigger.tsx +105 -0
  316. package/src/components/DataTable/DataTable.module.scss +511 -0
  317. package/src/components/DataTable/DataTable.tokens.scss +80 -0
  318. package/src/components/DataTable/DataTable.tsx +734 -0
  319. package/src/components/DataTable/HeaderCell.module.scss +215 -0
  320. package/src/components/DataTable/HeaderCell.tsx +440 -0
  321. package/src/components/DataTable/columnShift.ts +117 -0
  322. package/src/components/DataTable/index.ts +23 -0
  323. package/src/components/DataTable/pinStyle.ts +47 -0
  324. package/src/components/DataTable/reorderColumns.ts +66 -0
  325. package/src/components/DataTable/types.ts +289 -0
  326. package/src/components/DataTable/useColumnDragShift.ts +216 -0
  327. package/src/components/DataTable/useDataTable.ts +372 -0
  328. package/src/components/DataTable/useResizeHandle.ts +97 -0
  329. package/src/components/DatePicker/DatePicker.module.scss +180 -0
  330. package/src/components/DatePicker/DatePicker.tokens.scss +152 -0
  331. package/src/components/DatePicker/DatePicker.tsx +664 -0
  332. package/src/components/DatePicker/DatePickerGrid.module.scss +172 -0
  333. package/src/components/DatePicker/DatePickerGrid.tsx +341 -0
  334. package/src/components/DatePicker/InlineDatePicker.module.scss +32 -0
  335. package/src/components/DatePicker/InlineDatePicker.tsx +201 -0
  336. package/src/components/DatePicker/index.ts +4 -0
  337. package/src/components/DatePicker/utils.ts +364 -0
  338. package/src/components/DateRangePicker/DateRangePicker.module.scss +237 -0
  339. package/src/components/DateRangePicker/DateRangePicker.tokens.scss +78 -0
  340. package/src/components/DateRangePicker/DateRangePicker.tsx +720 -0
  341. package/src/components/DateRangePicker/InlineDateRangePicker.module.scss +94 -0
  342. package/src/components/DateRangePicker/InlineDateRangePicker.tsx +376 -0
  343. package/src/components/DateRangePicker/index.ts +5 -0
  344. package/src/components/DateRangePicker/utils.ts +151 -0
  345. package/src/components/DateStrip/DateStrip.module.scss +78 -0
  346. package/src/components/DateStrip/DateStrip.tokens.scss +23 -0
  347. package/src/components/DateStrip/DateStrip.tsx +205 -0
  348. package/src/components/DateStrip/index.ts +2 -0
  349. package/src/components/DefinitionList/DefinitionList.module.scss +102 -0
  350. package/src/components/DefinitionList/DefinitionList.tokens.scss +29 -0
  351. package/src/components/DefinitionList/DefinitionList.tsx +198 -0
  352. package/src/components/DefinitionList/index.ts +9 -0
  353. package/src/components/Divider/Divider.module.scss +144 -0
  354. package/src/components/Divider/Divider.tokens.scss +21 -0
  355. package/src/components/Divider/Divider.tsx +103 -0
  356. package/src/components/Divider/index.ts +2 -0
  357. package/src/components/Dot/Dot.module.scss +40 -0
  358. package/src/components/Dot/Dot.tokens.scss +4 -0
  359. package/src/components/Dot/Dot.tsx +45 -0
  360. package/src/components/Dot/index.ts +2 -0
  361. package/src/components/Drawer/Body.tsx +23 -0
  362. package/src/components/Drawer/Close.tsx +35 -0
  363. package/src/components/Drawer/Content.tsx +93 -0
  364. package/src/components/Drawer/Drawer.module.scss +232 -0
  365. package/src/components/Drawer/Drawer.tokens.scss +38 -0
  366. package/src/components/Drawer/Drawer.tsx +23 -0
  367. package/src/components/Drawer/DrawerRoot.tsx +190 -0
  368. package/src/components/Drawer/Footer.tsx +24 -0
  369. package/src/components/Drawer/Header.tsx +64 -0
  370. package/src/components/Drawer/Overlay.tsx +82 -0
  371. package/src/components/Drawer/context.ts +44 -0
  372. package/src/components/Drawer/index.ts +12 -0
  373. package/src/components/Drawer/useDragToClose.ts +149 -0
  374. package/src/components/DropdownMenu/CheckboxItem.tsx +144 -0
  375. package/src/components/DropdownMenu/Content.tsx +419 -0
  376. package/src/components/DropdownMenu/DropdownMenu.module.scss +280 -0
  377. package/src/components/DropdownMenu/DropdownMenu.tokens.scss +88 -0
  378. package/src/components/DropdownMenu/DropdownMenu.tsx +43 -0
  379. package/src/components/DropdownMenu/Group.tsx +76 -0
  380. package/src/components/DropdownMenu/Item.tsx +148 -0
  381. package/src/components/DropdownMenu/ItemIndicator.tsx +30 -0
  382. package/src/components/DropdownMenu/Radio.tsx +178 -0
  383. package/src/components/DropdownMenu/Root.tsx +100 -0
  384. package/src/components/DropdownMenu/Sub.tsx +332 -0
  385. package/src/components/DropdownMenu/Trigger.tsx +76 -0
  386. package/src/components/DropdownMenu/context.ts +65 -0
  387. package/src/components/DropdownMenu/index.ts +20 -0
  388. package/src/components/EmojiPicker/EmojiPicker.module.scss +84 -0
  389. package/src/components/EmojiPicker/EmojiPicker.tokens.scss +24 -0
  390. package/src/components/EmojiPicker/EmojiPicker.tsx +341 -0
  391. package/src/components/EmojiPicker/emojiData.ts +245 -0
  392. package/src/components/EmojiPicker/index.ts +3 -0
  393. package/src/components/EmptyState/EmptyState.module.scss +102 -0
  394. package/src/components/EmptyState/EmptyState.tokens.scss +28 -0
  395. package/src/components/EmptyState/EmptyState.tsx +112 -0
  396. package/src/components/EmptyState/index.ts +7 -0
  397. package/src/components/EntityChip/EntityChip.module.scss +275 -0
  398. package/src/components/EntityChip/EntityChip.tokens.scss +66 -0
  399. package/src/components/EntityChip/EntityChip.tsx +649 -0
  400. package/src/components/EntityChip/index.ts +10 -0
  401. package/src/components/ErrorState/ErrorState.module.scss +105 -0
  402. package/src/components/ErrorState/ErrorState.tokens.scss +32 -0
  403. package/src/components/ErrorState/ErrorState.tsx +125 -0
  404. package/src/components/ErrorState/index.ts +8 -0
  405. package/src/components/Field/Field.module.scss +46 -0
  406. package/src/components/Field/Field.tsx +185 -0
  407. package/src/components/Field/index.ts +2 -0
  408. package/src/components/FileUpload/FileUpload.module.scss +187 -0
  409. package/src/components/FileUpload/FileUpload.tokens.scss +81 -0
  410. package/src/components/FileUpload/FileUpload.tsx +569 -0
  411. package/src/components/FileUpload/formatBytes.ts +34 -0
  412. package/src/components/FileUpload/iconForFile.ts +40 -0
  413. package/src/components/FileUpload/index.ts +2 -0
  414. package/src/components/FilterChip/FilterChip.module.scss +61 -0
  415. package/src/components/FilterChip/FilterChip.tokens.scss +25 -0
  416. package/src/components/FilterChip/FilterChip.tsx +208 -0
  417. package/src/components/FilterChip/index.ts +5 -0
  418. package/src/components/FlowCanvas/FlowCanvas.module.scss +329 -0
  419. package/src/components/FlowCanvas/FlowCanvas.tokens.scss +45 -0
  420. package/src/components/FlowCanvas/FlowCanvas.tsx +1867 -0
  421. package/src/components/FlowCanvas/FlowControls.tsx +46 -0
  422. package/src/components/FlowCanvas/FlowEdge.tsx +58 -0
  423. package/src/components/FlowCanvas/FlowNode.tsx +76 -0
  424. package/src/components/FlowCanvas/edgePath.ts +85 -0
  425. package/src/components/FlowCanvas/index.ts +4 -0
  426. package/src/components/FlowCanvas/layout.ts +148 -0
  427. package/src/components/FlowCanvas/spatialNav.ts +76 -0
  428. package/src/components/FlowCanvas/types.ts +59 -0
  429. package/src/components/FlowCanvas/useViewport.ts +122 -0
  430. package/src/components/FormRow/FormRow.module.scss +2 -0
  431. package/src/components/FormRow/FormRow.tsx +37 -0
  432. package/src/components/FormRow/index.ts +2 -0
  433. package/src/components/FormSection/FormSection.module.scss +24 -0
  434. package/src/components/FormSection/FormSection.tsx +46 -0
  435. package/src/components/FormSection/index.ts +2 -0
  436. package/src/components/Grid/Grid.module.scss +162 -0
  437. package/src/components/Grid/Grid.tokens.scss +13 -0
  438. package/src/components/Grid/Grid.tsx +226 -0
  439. package/src/components/Grid/GridItem.tsx +77 -0
  440. package/src/components/Grid/index.ts +11 -0
  441. package/src/components/Highlight/Highlight.module.scss +43 -0
  442. package/src/components/Highlight/Highlight.tokens.scss +35 -0
  443. package/src/components/Highlight/Highlight.tsx +164 -0
  444. package/src/components/Highlight/HighlightOverlay.tsx +100 -0
  445. package/src/components/Highlight/index.ts +2 -0
  446. package/src/components/HighlightedText/HighlightedText.module.scss +11 -0
  447. package/src/components/HighlightedText/HighlightedText.tokens.scss +9 -0
  448. package/src/components/HighlightedText/HighlightedText.tsx +50 -0
  449. package/src/components/HighlightedText/highlightRanges.ts +44 -0
  450. package/src/components/HighlightedText/index.ts +2 -0
  451. package/src/components/IconPicker/IconPicker.module.scss +175 -0
  452. package/src/components/IconPicker/IconPicker.tokens.scss +40 -0
  453. package/src/components/IconPicker/IconPicker.tsx +629 -0
  454. package/src/components/IconPicker/index.ts +9 -0
  455. package/src/components/IconTile/IconTile.module.scss +57 -0
  456. package/src/components/IconTile/IconTile.tokens.scss +16 -0
  457. package/src/components/IconTile/IconTile.tsx +91 -0
  458. package/src/components/IconTile/index.ts +2 -0
  459. package/src/components/Image/Image.module.scss +266 -0
  460. package/src/components/Image/Image.tokens.scss +55 -0
  461. package/src/components/Image/Image.tsx +342 -0
  462. package/src/components/Image/index.ts +2 -0
  463. package/src/components/ImageCrop/ImageCrop.module.scss +274 -0
  464. package/src/components/ImageCrop/ImageCrop.tokens.scss +33 -0
  465. package/src/components/ImageCrop/ImageCrop.tsx +542 -0
  466. package/src/components/ImageCrop/cropGeometry.ts +120 -0
  467. package/src/components/ImageCrop/extractCropBlob.ts +140 -0
  468. package/src/components/ImageCrop/index.ts +11 -0
  469. package/src/components/ImageCrop/useCropPreview.ts +161 -0
  470. package/src/components/Indent/Indent.module.scss +33 -0
  471. package/src/components/Indent/Indent.tokens.scss +10 -0
  472. package/src/components/Indent/Indent.tsx +46 -0
  473. package/src/components/Indent/index.ts +2 -0
  474. package/src/components/Input/Input.module.scss +99 -0
  475. package/src/components/Input/Input.tokens.scss +54 -0
  476. package/src/components/Input/Input.tsx +105 -0
  477. package/src/components/Input/Suggestions.tsx +260 -0
  478. package/src/components/Input/index.ts +2 -0
  479. package/src/components/Kanban/Kanban.module.scss +39 -0
  480. package/src/components/Kanban/Kanban.tokens.scss +15 -0
  481. package/src/components/Kanban/Kanban.tsx +951 -0
  482. package/src/components/Kanban/index.ts +7 -0
  483. package/src/components/Kbd/Kbd.module.scss +47 -0
  484. package/src/components/Kbd/Kbd.tokens.scss +26 -0
  485. package/src/components/Kbd/Kbd.tsx +73 -0
  486. package/src/components/Kbd/index.ts +2 -0
  487. package/src/components/Lightbox/Lightbox.module.scss +283 -0
  488. package/src/components/Lightbox/Lightbox.tokens.scss +36 -0
  489. package/src/components/Lightbox/Lightbox.tsx +506 -0
  490. package/src/components/Lightbox/index.ts +2 -0
  491. package/src/components/Link/Link.module.scss +78 -0
  492. package/src/components/Link/Link.tokens.scss +28 -0
  493. package/src/components/Link/Link.tsx +114 -0
  494. package/src/components/Link/index.ts +2 -0
  495. package/src/components/LinkCard/LinkCard.module.scss +92 -0
  496. package/src/components/LinkCard/LinkCard.tokens.scss +9 -0
  497. package/src/components/LinkCard/LinkCard.tsx +72 -0
  498. package/src/components/LinkCard/index.ts +2 -0
  499. package/src/components/LiquidEditor/AutocompleteMenu.tsx +133 -0
  500. package/src/components/LiquidEditor/HighlightLayer.tsx +53 -0
  501. package/src/components/LiquidEditor/InsertVariableMenu.tsx +81 -0
  502. package/src/components/LiquidEditor/LiquidEditor.module.scss +339 -0
  503. package/src/components/LiquidEditor/LiquidEditor.tokens.scss +81 -0
  504. package/src/components/LiquidEditor/LiquidEditor.tsx +412 -0
  505. package/src/components/LiquidEditor/PreviewPane.tsx +29 -0
  506. package/src/components/LiquidEditor/caretCoordinates.ts +97 -0
  507. package/src/components/LiquidEditor/index.ts +7 -0
  508. package/src/components/LiquidEditor/liquidFilters.ts +23 -0
  509. package/src/components/LiquidEditor/liquidTokenizer.ts +235 -0
  510. package/src/components/LiquidEditor/types.ts +101 -0
  511. package/src/components/LiquidEditor/useLiquidAutocomplete.ts +178 -0
  512. package/src/components/List/List.module.scss +105 -0
  513. package/src/components/List/List.tokens.scss +42 -0
  514. package/src/components/List/List.tsx +124 -0
  515. package/src/components/List/index.ts +9 -0
  516. package/src/components/LiveRegion/LiveRegion.module.scss +3 -0
  517. package/src/components/LiveRegion/LiveRegion.tsx +116 -0
  518. package/src/components/LiveRegion/index.ts +2 -0
  519. package/src/components/Logo/Logo.module.scss +139 -0
  520. package/src/components/Logo/Logo.tokens.scss +14 -0
  521. package/src/components/Logo/Logo.tsx +126 -0
  522. package/src/components/Logo/index.ts +2 -0
  523. package/src/components/Masonry/Masonry.module.scss +42 -0
  524. package/src/components/Masonry/Masonry.tokens.scss +8 -0
  525. package/src/components/Masonry/Masonry.tsx +136 -0
  526. package/src/components/Masonry/index.ts +2 -0
  527. package/src/components/Masonry/masonryUtils.ts +42 -0
  528. package/src/components/MediaTile/MediaTile.module.scss +183 -0
  529. package/src/components/MediaTile/MediaTile.tokens.scss +60 -0
  530. package/src/components/MediaTile/MediaTile.tsx +208 -0
  531. package/src/components/MediaTile/index.ts +8 -0
  532. package/src/components/Modal/Body.tsx +27 -0
  533. package/src/components/Modal/Close.tsx +45 -0
  534. package/src/components/Modal/Content.tsx +98 -0
  535. package/src/components/Modal/Footer.tsx +28 -0
  536. package/src/components/Modal/Header.tsx +51 -0
  537. package/src/components/Modal/Modal.module.scss +262 -0
  538. package/src/components/Modal/Modal.tokens.scss +50 -0
  539. package/src/components/Modal/Modal.tsx +23 -0
  540. package/src/components/Modal/ModalRoot.tsx +299 -0
  541. package/src/components/Modal/Overlay.tsx +101 -0
  542. package/src/components/Modal/context.ts +60 -0
  543. package/src/components/Modal/index.ts +11 -0
  544. package/src/components/MoneyInput/MoneyInput.module.scss +57 -0
  545. package/src/components/MoneyInput/MoneyInput.tsx +433 -0
  546. package/src/components/MoneyInput/index.ts +2 -0
  547. package/src/components/OptionsPicker/OptionsPicker.module.scss +216 -0
  548. package/src/components/OptionsPicker/OptionsPicker.tokens.scss +68 -0
  549. package/src/components/OptionsPicker/OptionsPicker.tsx +936 -0
  550. package/src/components/OptionsPicker/index.ts +9 -0
  551. package/src/components/OtpInput/OtpInput.module.scss +69 -0
  552. package/src/components/OtpInput/OtpInput.tokens.scss +45 -0
  553. package/src/components/OtpInput/OtpInput.tsx +368 -0
  554. package/src/components/OtpInput/index.ts +2 -0
  555. package/src/components/Page/Page.module.scss +30 -0
  556. package/src/components/Page/Page.tokens.scss +11 -0
  557. package/src/components/Page/Page.tsx +53 -0
  558. package/src/components/Page/index.ts +2 -0
  559. package/src/components/PageHeader/PageHeader.module.scss +197 -0
  560. package/src/components/PageHeader/PageHeader.tokens.scss +41 -0
  561. package/src/components/PageHeader/PageHeader.tsx +310 -0
  562. package/src/components/PageHeader/index.ts +20 -0
  563. package/src/components/Pagination/Pagination.module.scss +117 -0
  564. package/src/components/Pagination/Pagination.tokens.scss +54 -0
  565. package/src/components/Pagination/Pagination.tsx +165 -0
  566. package/src/components/Pagination/index.ts +4 -0
  567. package/src/components/Pagination/paginationRange.ts +72 -0
  568. package/src/components/PasswordInput/PasswordInput.module.scss +120 -0
  569. package/src/components/PasswordInput/PasswordInput.tokens.scss +44 -0
  570. package/src/components/PasswordInput/PasswordInput.tsx +210 -0
  571. package/src/components/PasswordInput/index.ts +2 -0
  572. package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.module.scss +80 -0
  573. package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.tokens.scss +40 -0
  574. package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.tsx +96 -0
  575. package/src/components/PasswordStrengthMeter/index.ts +2 -0
  576. package/src/components/PersonDisplay/PersonDisplay.module.scss +180 -0
  577. package/src/components/PersonDisplay/PersonDisplay.tokens.scss +35 -0
  578. package/src/components/PersonDisplay/PersonDisplay.tsx +420 -0
  579. package/src/components/PersonDisplay/index.ts +10 -0
  580. package/src/components/PhoneInput/PhoneInput.module.scss +23 -0
  581. package/src/components/PhoneInput/PhoneInput.tsx +271 -0
  582. package/src/components/PhoneInput/index.ts +4 -0
  583. package/src/components/PhoneInput/phone.ts +128 -0
  584. package/src/components/PillMenu/PillMenu.module.scss +117 -0
  585. package/src/components/PillMenu/PillMenu.tokens.scss +22 -0
  586. package/src/components/PillMenu/PillMenu.tsx +324 -0
  587. package/src/components/PillMenu/index.ts +2 -0
  588. package/src/components/Popover/Anchor.tsx +37 -0
  589. package/src/components/Popover/Close.tsx +45 -0
  590. package/src/components/Popover/Content.tsx +213 -0
  591. package/src/components/Popover/Heading.tsx +33 -0
  592. package/src/components/Popover/Popover.module.scss +177 -0
  593. package/src/components/Popover/Popover.tokens.scss +22 -0
  594. package/src/components/Popover/Popover.tsx +23 -0
  595. package/src/components/Popover/PopoverRoot.tsx +75 -0
  596. package/src/components/Popover/Trigger.tsx +86 -0
  597. package/src/components/Popover/context.ts +22 -0
  598. package/src/components/Popover/index.ts +7 -0
  599. package/src/components/Progress/Progress.module.scss +110 -0
  600. package/src/components/Progress/Progress.tokens.scss +44 -0
  601. package/src/components/Progress/Progress.tsx +129 -0
  602. package/src/components/Progress/index.ts +2 -0
  603. package/src/components/QrCode/QrCode.module.scss +97 -0
  604. package/src/components/QrCode/QrCode.tokens.scss +17 -0
  605. package/src/components/QrCode/QrCode.tsx +233 -0
  606. package/src/components/QrCode/index.ts +2 -0
  607. package/src/components/QrCode/qr.ts +161 -0
  608. package/src/components/Radio/Radio.module.scss +175 -0
  609. package/src/components/Radio/Radio.tokens.scss +59 -0
  610. package/src/components/Radio/Radio.tsx +127 -0
  611. package/src/components/Radio/RadioGroup.module.scss +42 -0
  612. package/src/components/Radio/RadioGroup.tsx +120 -0
  613. package/src/components/Radio/RadioGroupContext.tsx +30 -0
  614. package/src/components/Radio/index.ts +4 -0
  615. package/src/components/Rail/Rail.module.scss +510 -0
  616. package/src/components/Rail/Rail.tokens.scss +92 -0
  617. package/src/components/Rail/Rail.tsx +287 -0
  618. package/src/components/Rail/RailCollapseToggle.tsx +57 -0
  619. package/src/components/Rail/RailFooter.tsx +17 -0
  620. package/src/components/Rail/RailGroup.tsx +728 -0
  621. package/src/components/Rail/RailGroupContext.ts +9 -0
  622. package/src/components/Rail/RailHeader.tsx +23 -0
  623. package/src/components/Rail/RailItem.tsx +119 -0
  624. package/src/components/Rail/RailSection.tsx +39 -0
  625. package/src/components/Rail/RailSpacer.tsx +19 -0
  626. package/src/components/Rail/index.ts +9 -0
  627. package/src/components/RelativeDateInput/RelativeDateInput.module.scss +15 -0
  628. package/src/components/RelativeDateInput/RelativeDateInput.tokens.scss +6 -0
  629. package/src/components/RelativeDateInput/RelativeDateInput.tsx +474 -0
  630. package/src/components/RelativeDateInput/index.ts +10 -0
  631. package/src/components/RelativeDateInput/relativeDate.ts +127 -0
  632. package/src/components/RichText/RichText.module.scss +5 -0
  633. package/src/components/RichText/RichText.tsx +70 -0
  634. package/src/components/RichText/RichTextAttachment.module.scss +82 -0
  635. package/src/components/RichText/RichTextAttachment.tsx +122 -0
  636. package/src/components/RichText/_prose.scss +79 -0
  637. package/src/components/RichText/attachmentIcon.tsx +24 -0
  638. package/src/components/RichText/engine/attachment.ts +154 -0
  639. package/src/components/RichText/engine/autolink.ts +64 -0
  640. package/src/components/RichText/engine/blockUnit.ts +174 -0
  641. package/src/components/RichText/engine/colorMarks.ts +85 -0
  642. package/src/components/RichText/engine/escape.ts +11 -0
  643. package/src/components/RichText/engine/fromHtml.ts +353 -0
  644. package/src/components/RichText/engine/fromMarkdown.ts +20 -0
  645. package/src/components/RichText/engine/inlines.ts +82 -0
  646. package/src/components/RichText/engine/listDepths.ts +32 -0
  647. package/src/components/RichText/engine/marks.ts +80 -0
  648. package/src/components/RichText/engine/mdToHtml.ts +178 -0
  649. package/src/components/RichText/engine/model.ts +157 -0
  650. package/src/components/RichText/engine/position.ts +118 -0
  651. package/src/components/RichText/engine/renderDoc.tsx +395 -0
  652. package/src/components/RichText/engine/renderLink.ts +15 -0
  653. package/src/components/RichText/engine/renderMention.ts +16 -0
  654. package/src/components/RichText/engine/safeHref.ts +22 -0
  655. package/src/components/RichText/engine/toHtml.ts +154 -0
  656. package/src/components/RichText/engine/toMarkdown.ts +97 -0
  657. package/src/components/RichText/engine/transforms.ts +434 -0
  658. package/src/components/RichText/imageMaxHeightStyle.ts +10 -0
  659. package/src/components/RichText/index.ts +31 -0
  660. package/src/components/RichTextEditor/RichTextAttachmentConfig.tsx +264 -0
  661. package/src/components/RichTextEditor/RichTextBlockControls.tsx +365 -0
  662. package/src/components/RichTextEditor/RichTextBlockMenu.tsx +162 -0
  663. package/src/components/RichTextEditor/RichTextColorMenu.tsx +115 -0
  664. package/src/components/RichTextEditor/RichTextEditor.module.scss +298 -0
  665. package/src/components/RichTextEditor/RichTextEditor.tsx +1910 -0
  666. package/src/components/RichTextEditor/RichTextImageResizer.tsx +131 -0
  667. package/src/components/RichTextEditor/RichTextLinkEditor.tsx +151 -0
  668. package/src/components/RichTextEditor/RichTextMentionMenu.tsx +99 -0
  669. package/src/components/RichTextEditor/RichTextToolbar.tsx +379 -0
  670. package/src/components/RichTextEditor/autolinkInput.ts +65 -0
  671. package/src/components/RichTextEditor/blockDrop.ts +9 -0
  672. package/src/components/RichTextEditor/blockReflow.ts +62 -0
  673. package/src/components/RichTextEditor/commands.ts +300 -0
  674. package/src/components/RichTextEditor/history.ts +93 -0
  675. package/src/components/RichTextEditor/icons.tsx +152 -0
  676. package/src/components/RichTextEditor/imageSize.ts +70 -0
  677. package/src/components/RichTextEditor/index.ts +5 -0
  678. package/src/components/RichTextEditor/input.ts +209 -0
  679. package/src/components/RichTextEditor/inputRules.ts +71 -0
  680. package/src/components/RichTextEditor/links.ts +103 -0
  681. package/src/components/RichTextEditor/mentionContext.ts +37 -0
  682. package/src/components/RichTextEditor/mentions.ts +28 -0
  683. package/src/components/RichTextEditor/preventSelectionLoss.ts +15 -0
  684. package/src/components/RichTextEditor/selection.ts +415 -0
  685. package/src/components/RichTextEditor/shortcuts.ts +44 -0
  686. package/src/components/RichTextEditor/useAnchoredFloating.ts +50 -0
  687. package/src/components/RichTextEditor/useAttachmentConfig.ts +134 -0
  688. package/src/components/RichTextEditor/useDismissOnOutsidePointerDown.ts +26 -0
  689. package/src/components/RichTextEditor/useLinkEditor.ts +107 -0
  690. package/src/components/RichTextEditor/useMention.ts +226 -0
  691. package/src/components/RichTextEditor/useOverlayRect.ts +73 -0
  692. package/src/components/RichTextEditor/useUpload.ts +246 -0
  693. package/src/components/Screen/Screen.module.scss +46 -0
  694. package/src/components/Screen/Screen.tokens.scss +20 -0
  695. package/src/components/Screen/Screen.tsx +97 -0
  696. package/src/components/Screen/index.ts +2 -0
  697. package/src/components/ScrollArea/ScrollArea.module.scss +39 -0
  698. package/src/components/ScrollArea/ScrollArea.tokens.scss +15 -0
  699. package/src/components/ScrollArea/ScrollArea.tsx +154 -0
  700. package/src/components/ScrollArea/index.ts +2 -0
  701. package/src/components/Select/Chip.tsx +45 -0
  702. package/src/components/Select/Empty.tsx +39 -0
  703. package/src/components/Select/Error.tsx +51 -0
  704. package/src/components/Select/HiddenInputs.tsx +44 -0
  705. package/src/components/Select/Listbox.tsx +557 -0
  706. package/src/components/Select/Loading.tsx +30 -0
  707. package/src/components/Select/Select.module.scss +512 -0
  708. package/src/components/Select/Select.tokens.scss +140 -0
  709. package/src/components/Select/Select.tsx +685 -0
  710. package/src/components/Select/Trigger.tsx +952 -0
  711. package/src/components/Select/context.ts +101 -0
  712. package/src/components/Select/emptyStateText.ts +19 -0
  713. package/src/components/Select/index.ts +9 -0
  714. package/src/components/Select/useSelectState.ts +157 -0
  715. package/src/components/Select/utils-types.ts +7 -0
  716. package/src/components/Select/utils.ts +122 -0
  717. package/src/components/SettingRow/SettingRow.module.scss +166 -0
  718. package/src/components/SettingRow/SettingRow.tokens.scss +37 -0
  719. package/src/components/SettingRow/SettingRow.tsx +242 -0
  720. package/src/components/SettingRow/index.ts +7 -0
  721. package/src/components/Skeleton/Skeleton.module.scss +55 -0
  722. package/src/components/Skeleton/Skeleton.tokens.scss +17 -0
  723. package/src/components/Skeleton/Skeleton.tsx +131 -0
  724. package/src/components/Skeleton/index.ts +4 -0
  725. package/src/components/Skeleton/useSkeletonVisibility.ts +95 -0
  726. package/src/components/Slider/Slider.module.scss +293 -0
  727. package/src/components/Slider/Slider.tokens.scss +98 -0
  728. package/src/components/Slider/Slider.tsx +624 -0
  729. package/src/components/Slider/index.ts +9 -0
  730. package/src/components/SlotGrid/SlotGrid.module.scss +61 -0
  731. package/src/components/SlotGrid/SlotGrid.tokens.scss +21 -0
  732. package/src/components/SlotGrid/SlotGrid.tsx +111 -0
  733. package/src/components/SlotGrid/index.ts +2 -0
  734. package/src/components/SocialButton/SocialButton.module.scss +4 -0
  735. package/src/components/SocialButton/SocialButton.tsx +40 -0
  736. package/src/components/SocialButton/index.ts +2 -0
  737. package/src/components/Sortable/Sortable.module.scss +177 -0
  738. package/src/components/Sortable/Sortable.tokens.scss +27 -0
  739. package/src/components/Sortable/Sortable.tsx +572 -0
  740. package/src/components/Sortable/containerAwareCollision.ts +125 -0
  741. package/src/components/Sortable/index.ts +13 -0
  742. package/src/components/SortableGroup/SortableGroup.module.scss +23 -0
  743. package/src/components/SortableGroup/SortableGroup.tsx +315 -0
  744. package/src/components/SortableGroup/index.ts +4 -0
  745. package/src/components/SortableGroup/moveSortableItem.ts +39 -0
  746. package/src/components/Sparkline/Sparkline.module.scss +28 -0
  747. package/src/components/Sparkline/Sparkline.tokens.scss +7 -0
  748. package/src/components/Sparkline/Sparkline.tsx +133 -0
  749. package/src/components/Sparkline/index.ts +2 -0
  750. package/src/components/Sparkline/sparklineModel.ts +119 -0
  751. package/src/components/Split/Split.module.scss +158 -0
  752. package/src/components/Split/Split.tokens.scss +10 -0
  753. package/src/components/Split/Split.tsx +231 -0
  754. package/src/components/Split/index.ts +2 -0
  755. package/src/components/Stack/Stack.module.scss +67 -0
  756. package/src/components/Stack/Stack.tokens.scss +11 -0
  757. package/src/components/Stack/Stack.tsx +100 -0
  758. package/src/components/Stack/index.ts +2 -0
  759. package/src/components/StagePath/StagePath.module.scss +255 -0
  760. package/src/components/StagePath/StagePath.tokens.scss +43 -0
  761. package/src/components/StagePath/StagePath.tsx +176 -0
  762. package/src/components/StagePath/index.ts +2 -0
  763. package/src/components/StatGroup/StatGroup.module.scss +6 -0
  764. package/src/components/StatGroup/StatGroup.tsx +70 -0
  765. package/src/components/StatGroup/StatGroupContext.ts +10 -0
  766. package/src/components/StatGroup/StatTile.module.scss +99 -0
  767. package/src/components/StatGroup/StatTile.tokens.scss +35 -0
  768. package/src/components/StatGroup/StatTile.tsx +143 -0
  769. package/src/components/StatGroup/index.ts +4 -0
  770. package/src/components/Sticky/Sticky.module.scss +98 -0
  771. package/src/components/Sticky/Sticky.tokens.scss +17 -0
  772. package/src/components/Sticky/Sticky.tsx +103 -0
  773. package/src/components/Sticky/index.ts +2 -0
  774. package/src/components/Switch/Switch.module.scss +182 -0
  775. package/src/components/Switch/Switch.tokens.scss +59 -0
  776. package/src/components/Switch/Switch.tsx +214 -0
  777. package/src/components/Switch/index.ts +2 -0
  778. package/src/components/Table/Table.module.scss +308 -0
  779. package/src/components/Table/Table.tokens.scss +79 -0
  780. package/src/components/Table/Table.tsx +381 -0
  781. package/src/components/Table/index.ts +15 -0
  782. package/src/components/TableOfContents/TableOfContents.module.scss +83 -0
  783. package/src/components/TableOfContents/TableOfContents.tokens.scss +26 -0
  784. package/src/components/TableOfContents/TableOfContents.tsx +192 -0
  785. package/src/components/TableOfContents/activeSection.ts +18 -0
  786. package/src/components/TableOfContents/index.ts +2 -0
  787. package/src/components/Tabs/Tabs.module.scss +359 -0
  788. package/src/components/Tabs/Tabs.tokens.scss +50 -0
  789. package/src/components/Tabs/Tabs.tsx +538 -0
  790. package/src/components/Tabs/index.ts +2 -0
  791. package/src/components/Text/Text.module.scss +115 -0
  792. package/src/components/Text/Text.tokens.scss +55 -0
  793. package/src/components/Text/Text.tsx +197 -0
  794. package/src/components/Text/index.ts +2 -0
  795. package/src/components/Textarea/Textarea.module.scss +94 -0
  796. package/src/components/Textarea/Textarea.tokens.scss +48 -0
  797. package/src/components/Textarea/Textarea.tsx +246 -0
  798. package/src/components/Textarea/index.ts +2 -0
  799. package/src/components/Thread/Thread.module.scss +161 -0
  800. package/src/components/Thread/Thread.tokens.scss +32 -0
  801. package/src/components/Thread/Thread.tsx +176 -0
  802. package/src/components/Thread/index.ts +2 -0
  803. package/src/components/TimeField/TimeField.module.scss +232 -0
  804. package/src/components/TimeField/TimeField.tsx +793 -0
  805. package/src/components/TimeField/index.ts +2 -0
  806. package/src/components/Timeline/Timeline.module.scss +58 -0
  807. package/src/components/Timeline/Timeline.tokens.scss +13 -0
  808. package/src/components/Timeline/Timeline.tsx +53 -0
  809. package/src/components/Timeline/index.ts +2 -0
  810. package/src/components/Title/Title.module.scss +84 -0
  811. package/src/components/Title/Title.tokens.scss +37 -0
  812. package/src/components/Title/Title.tsx +150 -0
  813. package/src/components/Title/index.ts +2 -0
  814. package/src/components/Toast/Toast.module.scss +299 -0
  815. package/src/components/Toast/Toast.tokens.scss +65 -0
  816. package/src/components/Toast/Toast.tsx +122 -0
  817. package/src/components/Toast/ToastViewport.tsx +166 -0
  818. package/src/components/Toast/api.ts +175 -0
  819. package/src/components/Toast/index.ts +5 -0
  820. package/src/components/Toast/store.ts +133 -0
  821. package/src/components/Toast/useToastTimer.ts +94 -0
  822. package/src/components/Tooltip/Tooltip.module.scss +99 -0
  823. package/src/components/Tooltip/Tooltip.tokens.scss +23 -0
  824. package/src/components/Tooltip/Tooltip.tsx +341 -0
  825. package/src/components/Tooltip/index.ts +2 -0
  826. package/src/components/TopBar/TopBar.module.scss +235 -0
  827. package/src/components/TopBar/TopBar.tokens.scss +79 -0
  828. package/src/components/TopBar/TopBar.tsx +87 -0
  829. package/src/components/TopBar/TopBarEnd.tsx +20 -0
  830. package/src/components/TopBar/TopBarIconButton.tsx +86 -0
  831. package/src/components/TopBar/TopBarSearch.tsx +319 -0
  832. package/src/components/TopBar/TopBarSearchPanel.tsx +268 -0
  833. package/src/components/TopBar/TopBarStart.tsx +20 -0
  834. package/src/components/TopBar/index.ts +11 -0
  835. package/src/components/Tour/Spotlight.tsx +121 -0
  836. package/src/components/Tour/Tour.module.scss +210 -0
  837. package/src/components/Tour/Tour.tokens.scss +58 -0
  838. package/src/components/Tour/Tour.tsx +617 -0
  839. package/src/components/Tour/index.ts +2 -0
  840. package/src/components/Tour/useTourTarget.ts +115 -0
  841. package/src/components/TreeView/TreeView.module.scss +268 -0
  842. package/src/components/TreeView/TreeView.tokens.scss +36 -0
  843. package/src/components/TreeView/TreeView.tsx +536 -0
  844. package/src/components/TreeView/index.ts +3 -0
  845. package/src/components/TreeView/treeModel.ts +224 -0
  846. package/src/components/VisuallyHidden/VisuallyHidden.module.scss +7 -0
  847. package/src/components/VisuallyHidden/VisuallyHidden.tsx +29 -0
  848. package/src/components/VisuallyHidden/index.ts +2 -0
  849. package/src/components/WidgetPreview/WidgetPreview.module.scss +11 -0
  850. package/src/components/WidgetPreview/WidgetPreview.tokens.scss +26 -0
  851. package/src/components/WidgetPreview/WidgetPreview.tsx +36 -0
  852. package/src/components/WidgetPreview/WidgetShape.module.scss +140 -0
  853. package/src/components/WidgetPreview/WidgetShape.tsx +159 -0
  854. package/src/components/WidgetPreview/index.ts +2 -0
  855. package/src/components/_internal/Trend.tsx +27 -0
  856. package/src/components/_internal/choiceTile.scss +74 -0
  857. package/src/components/_internal/collapse.scss +10 -0
  858. package/src/components/_internal/collapse.ts +58 -0
  859. package/src/components/_internal/currency.ts +257 -0
  860. package/src/components/_internal/dragAnnouncements.ts +200 -0
  861. package/src/components/_internal/fieldWiring.ts +225 -0
  862. package/src/components/_internal/gridSpan.ts +54 -0
  863. package/src/components/_internal/isoDay.ts +35 -0
  864. package/src/components/_internal/letterKey.ts +26 -0
  865. package/src/components/_internal/overlay/focusRestore.ts +21 -0
  866. package/src/components/_internal/overlay/index.ts +12 -0
  867. package/src/components/_internal/overlay/tourHold.ts +23 -0
  868. package/src/components/_internal/overlay/useFocusTrap.ts +120 -0
  869. package/src/components/_internal/overlay/useInOverlay.ts +81 -0
  870. package/src/components/_internal/overlay/useOverlayStack.ts +232 -0
  871. package/src/components/_internal/overlay/useScrollLock.ts +112 -0
  872. package/src/components/_internal/refs.ts +48 -0
  873. package/src/components/_internal/statusColor.ts +26 -0
  874. package/src/components/_internal/timeUtils.ts +23 -0
  875. package/src/components/_internal/useAsyncOptions.ts +145 -0
  876. package/src/components/_internal/useClippedTooltip.ts +50 -0
  877. package/src/components/_internal/useControllableState.ts +50 -0
  878. package/src/components.manifest.json +1141 -0
  879. package/src/hooks/useBelowBreakpoint.ts +58 -0
  880. package/src/i18n/I18nProvider.tsx +52 -0
  881. package/src/i18n/LocaleProvider.tsx +17 -0
  882. package/src/i18n/en.ts +648 -0
  883. package/src/i18n/format.ts +71 -0
  884. package/src/i18n/index.ts +13 -0
  885. package/src/i18n/messages.ts +1275 -0
  886. package/src/i18n/ru.ts +677 -0
  887. package/src/i18n/useLocale.ts +19 -0
  888. package/src/i18n/useTranslation.ts +58 -0
  889. package/src/index.ts +913 -0
  890. package/src/palette/index.ts +2 -0
  891. package/src/palette/palette.ts +108 -0
  892. package/src/styles/dark.scss +4 -0
  893. package/src/styles/global.scss +7 -0
  894. package/src/styles/mixins.scss +143 -0
  895. package/src/styles/reset.scss +78 -0
  896. package/src/styles/tokens.scss +18 -0
  897. package/src/styles/typography.scss +44 -0
@@ -0,0 +1,951 @@
1
+ import {
2
+ Children,
3
+ cloneElement,
4
+ createContext,
5
+ forwardRef,
6
+ isValidElement,
7
+ useCallback,
8
+ useContext,
9
+ useEffect,
10
+ useLayoutEffect,
11
+ useMemo,
12
+ useRef,
13
+ useState,
14
+ type HTMLAttributes,
15
+ type ReactElement,
16
+ type ReactNode,
17
+ } from 'react';
18
+ import {
19
+ DndContext,
20
+ KeyboardSensor,
21
+ PointerSensor,
22
+ getScrollableAncestors,
23
+ useDroppable,
24
+ useSensor,
25
+ useSensors,
26
+ type CollisionDetection,
27
+ type DragEndEvent,
28
+ type DragOverEvent,
29
+ type DragStartEvent,
30
+ } from '@dnd-kit/core';
31
+ import {
32
+ SortableContext,
33
+ sortableKeyboardCoordinates,
34
+ useSortable,
35
+ verticalListSortingStrategy,
36
+ } from '@dnd-kit/sortable';
37
+ import { CSS, type Transform } from '@dnd-kit/utilities';
38
+ import clsx from 'clsx';
39
+ import {
40
+ SortableHandle,
41
+ SortableItemContext,
42
+ type SortableItemContextValue,
43
+ } from '../Sortable/Sortable';
44
+ import {
45
+ containerName,
46
+ dragLabelOf,
47
+ useDragAccessibility,
48
+ type DescribeDragTarget,
49
+ } from '../_internal/dragAnnouncements';
50
+ import { useTranslation } from '../../i18n/useTranslation';
51
+ import {
52
+ containerAwareClosestCorners,
53
+ isPointerOutsideContainers,
54
+ } from '../Sortable/containerAwareCollision';
55
+ import styles from './Kanban.module.scss';
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Internal context: Root → Column, passes effective cardIds for SortableContext
59
+ // ---------------------------------------------------------------------------
60
+
61
+ const KanbanColumnItemsContext = createContext<readonly (string | number)[] | null>(null);
62
+
63
+ // ---------------------------------------------------------------------------
64
+ // Public interfaces
65
+ // ---------------------------------------------------------------------------
66
+
67
+ /**
68
+ * Payload fired by `onMove` after a successful drag-drop across or within
69
+ * a column. Consumer applies the move by updating their items state.
70
+ */
71
+ export interface KanbanMoveEvent {
72
+ /** Source position — where the card was before the drag. */
73
+ from: { columnId: string | number; index: number };
74
+ /** Destination position — where the card landed after the drop. */
75
+ to: { columnId: string | number; index: number };
76
+ /** The id of the card that was dragged. */
77
+ cardId: string | number;
78
+ }
79
+
80
+ export interface KanbanProps extends HTMLAttributes<HTMLDivElement> {
81
+ /**
82
+ * Fires once per drag (on drop) with the from/to positions and the cardId.
83
+ * NOT fired on drag cancel. NOT fired when from === to (no-op drop).
84
+ *
85
+ * Consumer should update their items state immutably, e.g.:
86
+ * ```ts
87
+ * const [cols, setCols] = useState({ todo: ['a', 'b'], done: ['c'] });
88
+ * const handleMove = ({ from, to, cardId }) => {
89
+ * setCols((prev) => {
90
+ * const next = { ...prev };
91
+ * next[from.columnId] = [...prev[from.columnId]];
92
+ * next[from.columnId].splice(from.index, 1);
93
+ * next[to.columnId] = [...prev[to.columnId]];
94
+ * next[to.columnId].splice(to.index, 0, cardId);
95
+ * return next;
96
+ * });
97
+ * };
98
+ * ```
99
+ */
100
+ onMove?: (event: KanbanMoveEvent) => void;
101
+ }
102
+
103
+ export interface KanbanColumnProps extends Omit<HTMLAttributes<HTMLDivElement>, 'id'> {
104
+ /**
105
+ * Stable identifier for this column. Must be unique within a `<Kanban>`.
106
+ * Used internally to track which cards belong to which column.
107
+ */
108
+ id: string | number;
109
+ children?: ReactNode;
110
+ }
111
+
112
+ export interface KanbanCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'id'> {
113
+ /**
114
+ * Stable identifier for this card. Must be unique across ALL columns in
115
+ * the `<Kanban>`. Used by dnd-kit to track the card during drag.
116
+ */
117
+ id: string | number;
118
+ children: ReactNode;
119
+ }
120
+
121
+ // ---------------------------------------------------------------------------
122
+ // containsHandle — mirrors Sortable's implementation; checks for KanbanHandle
123
+ // ---------------------------------------------------------------------------
124
+
125
+ /**
126
+ * Recursively walk the children subtree looking for a `Kanban.Handle`
127
+ * (`SortableHandle`). Mirrors Sortable's `containsHandle` function.
128
+ *
129
+ * Limitation: only walks `React.Children` and `props.children` — a Handle
130
+ * wrapped inside a custom component is invisible to this walk.
131
+ */
132
+ function containsHandle(children: ReactNode): boolean {
133
+ let found = false;
134
+ function walk(node: ReactNode) {
135
+ if (found) return;
136
+ if (!isValidElement(node)) return;
137
+ if (node.type === SortableHandle) {
138
+ found = true;
139
+ return;
140
+ }
141
+ const { children: nested } = node.props as { children?: ReactNode };
142
+ if (nested != null) Children.forEach(nested, walk);
143
+ }
144
+ Children.forEach(children, walk);
145
+ return found;
146
+ }
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // Drag bounds — the dragged card may not leave the board's scrollable content
150
+ // ---------------------------------------------------------------------------
151
+
152
+ /**
153
+ * Where the dragged card sits inside its scroll container, and how big that
154
+ * container's content is. Everything is expressed in the container's CONTENT
155
+ * space (origin = scroll offset 0), which is scroll-invariant: the numbers stay
156
+ * valid while auto-scroll moves the board under the card.
157
+ *
158
+ * @internal exported for unit tests; not part of the public API.
159
+ */
160
+ export interface KanbanDragBounds {
161
+ /** Card centre, x, in content space. */
162
+ centerX: number;
163
+ /** Card centre, y, in content space. */
164
+ centerY: number;
165
+ /** Card layout width (untransformed). */
166
+ width: number;
167
+ /** Card layout height (untransformed). */
168
+ height: number;
169
+ /** `scrollWidth` of the container, measured with the card untransformed. */
170
+ contentWidth: number;
171
+ /** `scrollHeight` of the container, measured with the card untransformed. */
172
+ contentHeight: number;
173
+ }
174
+
175
+ /**
176
+ * Measure `node` against its nearest scrollable ancestor.
177
+ *
178
+ * The card's own transform (and transition) are stripped for the duration of
179
+ * the measurement — both readings would otherwise be poisoned by the transform:
180
+ * `getBoundingClientRect()` returns the TRANSFORMED box, and a card already
181
+ * hanging past the content edge inflates the very `scrollWidth` we are about to
182
+ * use as the bound. Stripping makes the measurement a fixed point, so repeated
183
+ * measurements during one drag cannot ratchet the bound outwards. The
184
+ * transition goes too: a keyboard drag leaves a real `transition` on the drag
185
+ * source (a pointer drag does not), and strip-then-restore would otherwise read
186
+ * as a computed-value change and animate the card back from identity. Both are
187
+ * restored before the browser paints (this runs in a layout effect), so nothing
188
+ * is visible.
189
+ *
190
+ * Which container: only the NEAREST scrollable ancestor matters — it clips
191
+ * everything below it, so no ancestor further up ever sees the overhang. Inside
192
+ * a `<Kanban>` that is always the board. It is whatever else the consumer made
193
+ * scrollable, though: a `<Kanban.Column>` given `overflow-y: auto` becomes the
194
+ * nearest scroller for its own cards.
195
+ *
196
+ * Returns null when nothing is measurable — no node, or a zero-sized scroll
197
+ * area, which is every element under jsdom (it lays nothing out). A null bound
198
+ * means "don't clamp", i.e. the pre-#373 behaviour. Note there is no
199
+ * "unscrollable" case to bail out of in a real browser:
200
+ * `getScrollableAncestors` falls back to `document.scrollingElement`, so a card
201
+ * with no scrollable ancestor is bounded by the page instead.
202
+ *
203
+ * @internal exported for unit tests; not part of the public API.
204
+ */
205
+ export function measureKanbanDragBounds(node: HTMLElement | null): KanbanDragBounds | null {
206
+ if (!node) return null;
207
+ const [scroller] = getScrollableAncestors(node, 1);
208
+ if (!scroller) return null;
209
+
210
+ const previousTransform = node.style.transform;
211
+ const previousTransition = node.style.transition;
212
+ node.style.transform = 'none';
213
+ node.style.transition = 'none';
214
+ const rect = node.getBoundingClientRect();
215
+ const box = scroller.getBoundingClientRect();
216
+ const { scrollLeft, scrollTop, scrollWidth, scrollHeight, clientLeft, clientTop } = scroller;
217
+ node.style.transform = previousTransform;
218
+ node.style.transition = previousTransition;
219
+
220
+ if (scrollWidth === 0 || scrollHeight === 0) return null;
221
+
222
+ // Content-space origin: the padding-box corner, un-scrolled. The root
223
+ // scrolling element is the exception — its OWN box already moves with page
224
+ // scroll, so `box` is the un-scrolled origin and subtracting the scroll
225
+ // offset again would count it twice.
226
+ const isRootScroller = scroller === scroller.ownerDocument.scrollingElement;
227
+ const originX = box.left + clientLeft - (isRootScroller ? 0 : scrollLeft);
228
+ const originY = box.top + clientTop - (isRootScroller ? 0 : scrollTop);
229
+
230
+ return {
231
+ centerX: rect.left + rect.width / 2 - originX,
232
+ centerY: rect.top + rect.height / 2 - originY,
233
+ width: rect.width,
234
+ height: rect.height,
235
+ contentWidth: scrollWidth,
236
+ contentHeight: scrollHeight,
237
+ };
238
+ }
239
+
240
+ /**
241
+ * Bound `transform` so the transformed card stays inside the scroll
242
+ * container's content box. Pass-through when either argument is null.
243
+ *
244
+ * This is what stops horizontal auto-scroll from running away (#373). The card
245
+ * is rendered IN FLOW (there is no `DragOverlay`), so a transform that carries
246
+ * it past the content edge extends the container's `scrollWidth`; dnd-kit's
247
+ * auto-scroll then chases the edge it just created, which moves the card
248
+ * further out, which extends the edge again — measured at 8910px of `scrollLeft`
249
+ * against a real maximum of 597. Clamping the APPLIED transform breaks the loop
250
+ * at its source: `scrollWidth` never changes, so `scrollLeft` plateaus at the
251
+ * real maximum and the last column becomes reachable.
252
+ *
253
+ * It has to happen here, where the transform is written to the DOM — a
254
+ * `DndContext` modifier is not enough, because dnd-kit computes
255
+ * `scrollAdjustedTranslate = modifiedTranslate + scrollAdjustment` and adds the
256
+ * scroll adjustment AFTER modifiers run (same reason `DataTable` clamps
257
+ * `event.delta` rather than only its modifier — see `useColumnDragShift`).
258
+ *
259
+ * `scaleX` / `scaleY` are honoured because dnd-kit scales the drag source to
260
+ * the `over` rect; the scaled half-extent, not the layout half-extent, is what
261
+ * reaches the edge.
262
+ *
263
+ * @internal exported for unit tests; not part of the public API.
264
+ */
265
+ export function clampKanbanTransform(
266
+ transform: Transform | null,
267
+ bounds: KanbanDragBounds | null,
268
+ ): Transform | null {
269
+ if (!transform || !bounds) return transform;
270
+ const halfWidth = (bounds.width * transform.scaleX) / 2;
271
+ const halfHeight = (bounds.height * transform.scaleY) / 2;
272
+ return {
273
+ ...transform,
274
+ x: clamp(
275
+ transform.x,
276
+ halfWidth - bounds.centerX,
277
+ bounds.contentWidth - halfWidth - bounds.centerX,
278
+ ),
279
+ y: clamp(
280
+ transform.y,
281
+ halfHeight - bounds.centerY,
282
+ bounds.contentHeight - halfHeight - bounds.centerY,
283
+ ),
284
+ };
285
+ }
286
+
287
+ const clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max);
288
+
289
+ // ---------------------------------------------------------------------------
290
+ // KanbanCard
291
+ // ---------------------------------------------------------------------------
292
+
293
+ /**
294
+ * One draggable card inside a `<Kanban.Column>`; needs a stable `id`.
295
+ * @see docs/components/Kanban.md
296
+ */
297
+ export const KanbanCard = forwardRef<HTMLDivElement, KanbanCardProps>(function KanbanCard(
298
+ { id, className, children, ...rest },
299
+ ref,
300
+ ) {
301
+ // Announcement label: the consumer's `aria-label`, else the card's RENDERED
302
+ // text (so a closed menu inside the card contributes nothing — see
303
+ // `dragLabelOf`). The same ref is the drag-bounds node ref below.
304
+ const cardRef = useRef<HTMLDivElement | null>(null);
305
+ const dragLabel = rest['aria-label'];
306
+ const dragData = useMemo(() => ({ dragLabel, dragNode: cardRef }), [dragLabel]);
307
+
308
+ const {
309
+ setNodeRef,
310
+ setActivatorNodeRef,
311
+ listeners,
312
+ attributes,
313
+ transform,
314
+ transition,
315
+ isDragging,
316
+ index,
317
+ items,
318
+ } = useSortable({ id, data: dragData });
319
+
320
+ const hasHandle = useMemo(() => containsHandle(children), [children]);
321
+
322
+ const ctxValue = useMemo<SortableItemContextValue>(
323
+ () => ({ listeners, attributes, setActivatorNodeRef }),
324
+ [listeners, attributes, setActivatorNodeRef],
325
+ );
326
+
327
+ // Travel available to the dragged card, in its scroll container's content
328
+ // space (#373). Re-measured only when the card's LAYOUT position can have
329
+ // changed, which is never a plain pointer move — while the card only
330
+ // translates, its layout position and the content size are both constant, so
331
+ // measuring per move would force a full layout per move for no gain. It
332
+ // changes in exactly two ways:
333
+ // - a live re-seat WITHIN a column (`index` / `items` change, same parent —
334
+ // React moves the node), and
335
+ // - a live re-seat ACROSS columns, where `KanbanRoot` re-parents the card
336
+ // into another `KanbanColumnItemsContext` subtree. That is a remount, not
337
+ // a move: this component gets a fresh instance with `boundsRef` back to
338
+ // null, so its FIRST render clamps against nothing at all.
339
+ //
340
+ // The write-back below exists for that first render. Measuring alone can't
341
+ // fix it: React's mutation phase writes the frame's transform BEFORE refs
342
+ // attach and layout effects run, so by then the unbounded value is already on
343
+ // the node and nothing re-reads `boundsRef` until the next render. Only
344
+ // writing the bounded value back changes what that frame paints, and a layout
345
+ // effect does it pre-paint. `transform` is deliberately NOT a dependency —
346
+ // this effect runs only on the renders that (re)measure, and on those the
347
+ // closed-over `transform` is exactly the one just committed.
348
+ //
349
+ // Be clear about what it is: on today's dnd-kit it is a NO-OP, not a fix.
350
+ // `transform` is null on precisely these frames — a re-seat mutates the
351
+ // SortableContext items, so `itemsHaveChanged` forces `displaceItem` false;
352
+ // and on a transit `over` is the column, so `overIndex` is -1 and it's false
353
+ // again. A null transform short-circuits every guard below, so nothing is
354
+ // written. Matching that, the frame could not be made to inflate
355
+ // `scrollWidth` in the browser: 635 rAF-sampled frames over 14 transits at
356
+ // max scroll with the pointer held outside the board, write-back disabled,
357
+ // yielded a single distinct `scrollWidth`.
358
+ //
359
+ // It is kept as insurance against exactly that internal changing. If a
360
+ // dnd-kit bump ever emits a non-null transform on a re-seat frame, deleting
361
+ // these lines silently reinstates the runaway — and no test can catch it,
362
+ // because jsdom lays nothing out, so the clamp's only evidence is a manual
363
+ // browser drag. Two inert guarded lines against a hand-only regression.
364
+ const boundsRef = useRef<KanbanDragBounds | null>(null);
365
+ useLayoutEffect(() => {
366
+ const node = cardRef.current;
367
+ if (!isDragging || !node) {
368
+ boundsRef.current = null;
369
+ return;
370
+ }
371
+ boundsRef.current = measureKanbanDragBounds(node);
372
+ const bounded = CSS.Transform.toString(clampKanbanTransform(transform, boundsRef.current));
373
+ if (bounded) node.style.transform = bounded;
374
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- transform is deliberately not a dependency, see above
375
+ }, [isDragging, index, items]);
376
+
377
+ const setRef = (node: HTMLDivElement | null) => {
378
+ cardRef.current = node;
379
+ setNodeRef(node);
380
+ // When no Handle is present, the Card itself is the drag activator.
381
+ if (!hasHandle) setActivatorNodeRef(node);
382
+ if (typeof ref === 'function') ref(node);
383
+ else if (ref) ref.current = node;
384
+ };
385
+
386
+ return (
387
+ <SortableItemContext.Provider value={ctxValue}>
388
+ <div
389
+ ref={setRef}
390
+ // Reading the ref during render is deliberate: the bound is written by
391
+ // the layout effect above and must be applied on the very next pointer
392
+ // move, without a re-render of its own.
393
+ style={{
394
+ transform: CSS.Transform.toString(clampKanbanTransform(transform, boundsRef.current)),
395
+ transition,
396
+ }}
397
+ data-dragging={isDragging ? 'true' : undefined}
398
+ className={clsx(styles.card, className)}
399
+ // dnd-kit drag attrs BEFORE {...rest} so consumer rest wins.
400
+ {...(hasHandle ? {} : listeners)}
401
+ {...(hasHandle ? {} : attributes)}
402
+ {...rest}
403
+ // role="article" AFTER {...rest} so it wins, but only when a Handle
404
+ // is present. Without a Handle, dnd-kit's role="button" is correct
405
+ // (Card IS the drag activator).
406
+ {...(hasHandle ? { role: 'article' } : {})}
407
+ >
408
+ {children}
409
+ </div>
410
+ </SortableItemContext.Provider>
411
+ );
412
+ });
413
+ KanbanCard.displayName = 'KanbanCard';
414
+
415
+ // ---------------------------------------------------------------------------
416
+ // KanbanColumn
417
+ // ---------------------------------------------------------------------------
418
+
419
+ /**
420
+ * One swimlane in a `<Kanban>`; needs a stable `id`.
421
+ * @see docs/components/Kanban.md
422
+ */
423
+ export const KanbanColumn = forwardRef<HTMLDivElement, KanbanColumnProps>(function KanbanColumn(
424
+ { id, className, children, ...rest },
425
+ ref,
426
+ ) {
427
+ const { setNodeRef } = useDroppable({ id });
428
+
429
+ // Effective card ids are injected by KanbanRoot via context so that this
430
+ // column's SortableContext sees the live-reordered order.
431
+ const ctxCardIds = useContext(KanbanColumnItemsContext);
432
+
433
+ // Fallback: derive card ids from children if not in context (standalone use).
434
+ const fallbackCardIds = useMemo(() => {
435
+ if (ctxCardIds !== null) return null; // skip computation
436
+ const ids: (string | number)[] = [];
437
+ Children.forEach(children, (card) => {
438
+ if (!isValidElement(card) || card.type !== KanbanCard) return;
439
+ ids.push((card.props as KanbanCardProps).id);
440
+ });
441
+ return ids;
442
+ }, [children, ctxCardIds]);
443
+
444
+ const cardIds = ctxCardIds ?? fallbackCardIds ?? [];
445
+
446
+ const setRef = (node: HTMLDivElement | null) => {
447
+ setNodeRef(node);
448
+ if (typeof ref === 'function') ref(node);
449
+ else if (ref) ref.current = node;
450
+ };
451
+
452
+ return (
453
+ <div
454
+ ref={setRef}
455
+ className={clsx(styles.column, className)}
456
+ {...rest}
457
+ // A name on a role-less <div> is inert, so a labelled column gets a role
458
+ // to hang it on. AFTER {...rest} — but only when the consumer didn't
459
+ // already pick a role — so labelling can't silently do nothing.
460
+ {...((rest['aria-label'] || rest['aria-labelledby']) && rest.role == null
461
+ ? { role: 'group' }
462
+ : {})}
463
+ >
464
+ <SortableContext
465
+ items={cardIds as (string | number)[]}
466
+ strategy={verticalListSortingStrategy}
467
+ >
468
+ {children}
469
+ </SortableContext>
470
+ </div>
471
+ );
472
+ });
473
+ KanbanColumn.displayName = 'KanbanColumn';
474
+
475
+ // ---------------------------------------------------------------------------
476
+ // KanbanRoot
477
+ // ---------------------------------------------------------------------------
478
+
479
+ /**
480
+ * Multi-column drag-and-drop board with live cross-column reflow (`Kanban`, `Kanban.Column`, `Kanban.Card`, `Kanban.Handle`).
481
+ * @see docs/components/Kanban.md
482
+ */
483
+ const KanbanRoot = forwardRef<HTMLDivElement, KanbanProps>(function KanbanRoot(
484
+ { onMove, className, children, ...rest },
485
+ ref,
486
+ ) {
487
+ const t = useTranslation();
488
+ // ------------------------------------------------------------------
489
+ // Parse children: extract column order, initial items, card elements,
490
+ // and the before/after non-card slices for each column.
491
+ // columnElements is built in the same pass so render-time lookup is O(1)
492
+ // instead of O(N) per column (i.e., O(N²) total per render).
493
+ // ------------------------------------------------------------------
494
+ const { columnOrder, columnElements, initialItems, cardElements, columnChildrenSlices } =
495
+ useMemo(() => {
496
+ const colOrder: (string | number)[] = [];
497
+ const colElems = new Map<string | number, ReactElement<KanbanColumnProps>>();
498
+ const initItems = new Map<string | number, (string | number)[]>();
499
+ const cardElems = new Map<string | number, ReactElement<KanbanCardProps>>();
500
+ const colSlices = new Map<string | number, { before: ReactNode[]; after: ReactNode[] }>();
501
+
502
+ Children.forEach(children, (child) => {
503
+ if (!isValidElement(child) || child.type !== KanbanColumn) return;
504
+ const columnElement = child as ReactElement<KanbanColumnProps>;
505
+ const colProps = columnElement.props;
506
+ const colId = colProps.id;
507
+ colOrder.push(colId);
508
+ colElems.set(colId, columnElement);
509
+
510
+ const colCardIds: (string | number)[] = [];
511
+ let firstCardIdx = -1;
512
+ let lastCardIdx = -1;
513
+ const colChildren = Children.toArray(colProps.children);
514
+
515
+ colChildren.forEach((grandchild, idx) => {
516
+ if (!isValidElement(grandchild) || grandchild.type !== KanbanCard) return;
517
+ const cardProps = grandchild.props as KanbanCardProps;
518
+ colCardIds.push(cardProps.id);
519
+ cardElems.set(cardProps.id, grandchild as ReactElement<KanbanCardProps>);
520
+ if (firstCardIdx < 0) firstCardIdx = idx;
521
+ lastCardIdx = idx;
522
+ });
523
+
524
+ // Split into before/after slices (non-card children outside the card block).
525
+ const before: ReactNode[] =
526
+ firstCardIdx >= 0 ? colChildren.slice(0, firstCardIdx) : colChildren;
527
+ const after: ReactNode[] = lastCardIdx >= 0 ? colChildren.slice(lastCardIdx + 1) : [];
528
+
529
+ initItems.set(colId, colCardIds);
530
+ colSlices.set(colId, { before, after });
531
+ });
532
+
533
+ return {
534
+ columnOrder: colOrder,
535
+ columnElements: colElems,
536
+ initialItems: initItems,
537
+ cardElements: cardElems,
538
+ columnChildrenSlices: colSlices,
539
+ };
540
+ }, [children]);
541
+
542
+ // ------------------------------------------------------------------
543
+ // Stable serialization of initialItems to detect consumer state changes.
544
+ // JSON.stringify is robust against ids that contain `:` or `,` characters,
545
+ // unlike a hand-rolled delimiter-join.
546
+ // ------------------------------------------------------------------
547
+ const initialItemsKey = useMemo(
548
+ () => JSON.stringify(Array.from(initialItems.entries())),
549
+ [initialItems],
550
+ );
551
+
552
+ // ------------------------------------------------------------------
553
+ // Internal drag state: null when idle, populated on drag start
554
+ // ------------------------------------------------------------------
555
+ const [liveItems, setLiveItems] = useState<Map<string | number, (string | number)[]> | null>(
556
+ null,
557
+ );
558
+
559
+ // Reset live items when consumer state mutates (e.g. external update mid-drag).
560
+ useEffect(() => {
561
+ setLiveItems(null);
562
+ }, [initialItemsKey]);
563
+
564
+ const effectiveItems = liveItems ?? initialItems;
565
+
566
+ // ------------------------------------------------------------------
567
+ // Sensors
568
+ // ------------------------------------------------------------------
569
+ const sensors = useSensors(
570
+ useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
571
+ useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
572
+ );
573
+
574
+ // ------------------------------------------------------------------
575
+ // Container lookup: given an id (card or column), find which column owns it
576
+ // ------------------------------------------------------------------
577
+ const findContainer = useCallback(
578
+ (
579
+ id: string | number,
580
+ items: Map<string | number, (string | number)[]>,
581
+ ): string | number | null => {
582
+ if (items.has(id)) return id; // id is a column id itself (drop on empty column)
583
+ for (const [colId, cards] of items) {
584
+ if (cards.includes(id)) return colId;
585
+ }
586
+ return null;
587
+ },
588
+ [],
589
+ );
590
+
591
+ // ------------------------------------------------------------------
592
+ // Drag handlers
593
+ // ------------------------------------------------------------------
594
+ const originRef = useRef<string | number | null>(null);
595
+
596
+ // Was the pointer outside every column the last time a drop target was
597
+ // resolved? `DragEndEvent` carries no pointer position — only `delta`, which
598
+ // is `translate - nodeRectDelta + scrollAdjustment` and so drifts from the
599
+ // real pointer by however far the board auto-scrolled and however far a live
600
+ // re-seat moved the card. Collision detection, on the other hand, is handed
601
+ // dnd-kit's own `pointerCoordinates` alongside the very rects `over` was
602
+ // ranked from, so answering the question there and remembering the answer
603
+ // makes the cancel decision agree with `over` by construction (#387).
604
+ //
605
+ // Written during render (collision detection runs in `DndContext`'s render);
606
+ // that is safe because it is a pure function of the arguments, so a double
607
+ // render under StrictMode writes the same value.
608
+ const outsideBoardRef = useRef(false);
609
+ const collisionDetection = useCallback<CollisionDetection>((args) => {
610
+ const isOutsideBoard = isPointerOutsideContainers(args);
611
+ outsideBoardRef.current = isOutsideBoard;
612
+ // Force an `over` transition to null at the board boundary. dnd-kit's
613
+ // announcement callback only runs when `over.id` changes, so retaining the
614
+ // nearest collision here would leave screen readers naming a stale column.
615
+ if (isOutsideBoard) return [];
616
+ return containerAwareClosestCorners(args);
617
+ }, []);
618
+
619
+ const handleDragStart = useCallback(
620
+ (event: DragStartEvent) => {
621
+ outsideBoardRef.current = false;
622
+ // The column the card started in. Used by handleDragOver to tell a
623
+ // genuine same-column reorder apart from "already transited, still
624
+ // moving inside the destination".
625
+ originRef.current = findContainer(event.active.id as string | number, initialItems);
626
+ // Snapshot: shallow-copy each cards array so mutations don't leak into initialItems.
627
+ const snapshot = new Map<string | number, (string | number)[]>();
628
+ for (const [colId, cards] of initialItems) snapshot.set(colId, [...cards]);
629
+ setLiveItems(snapshot);
630
+ },
631
+ [initialItems, findContainer],
632
+ );
633
+
634
+ const handleDragOver = useCallback(
635
+ (event: DragOverEvent) => {
636
+ const { active, over } = event;
637
+ if (!over) return;
638
+ const activeId = active.id as string | number;
639
+ const overId = over.id as string | number;
640
+ if (activeId === overId) return;
641
+
642
+ setLiveItems((prev) => {
643
+ if (!prev) return prev;
644
+ const activeContainer = findContainer(activeId, prev);
645
+ const overContainer = findContainer(overId, prev);
646
+ if (activeContainer == null || overContainer == null) return prev;
647
+
648
+ const isTransit = activeContainer !== overContainer;
649
+ // Pointer in the padding of the column the card has already transited
650
+ // into (no card under it). `over` is the column, so the sortable
651
+ // strategy computes overIndex === -1 and applies NO displacement —
652
+ // re-appending here is therefore stable (it can't fight a transform)
653
+ // and it's exactly what the user sees. Cheap to keep in sync.
654
+ const isForeignAppend =
655
+ !isTransit && overId === overContainer && overContainer !== originRef.current;
656
+ // Everything else with active and over in the same column is finalized
657
+ // at dragEnd from `over` (see handleDragEnd). Re-seating the card here
658
+ // on every dragOver would oscillate: the insertion re-measures the
659
+ // rects, which changes `over`, which re-fires this handler — and the
660
+ // strategy would still displace siblings on top of the new order, so
661
+ // the commit would drift from the preview again.
662
+ if (!isTransit && !isForeignAppend) return prev;
663
+
664
+ const activeCards = prev.get(activeContainer) ?? [];
665
+ if (!activeCards.includes(activeId)) return prev;
666
+ if (isForeignAppend && activeCards[activeCards.length - 1] === activeId) return prev;
667
+
668
+ // Slot math runs against the destination WITHOUT the active card, so
669
+ // the card's own slot is never counted (off-by-one when moving down).
670
+ const overCards = (prev.get(overContainer) ?? []).filter((id) => id !== activeId);
671
+
672
+ // Resolve the target slot inside overContainer:
673
+ // - over.id is a sibling card → use that card's index, plus 1 if
674
+ // the cursor is past the over card's vertical midpoint (active's
675
+ // translated rect tracks the cursor centered around it).
676
+ // - over.id is the column itself (empty drop / padding) → append
677
+ //
678
+ // The `isBelow` +1 rarely survives to the commit: the strategy
679
+ // immediately re-renders the card at `overIndex` and dragEnd commits
680
+ // from `over`, so both sides of the +1 produce the same drop. It still
681
+ // decides the seeded slot the frame after transit (while dnd-kit has
682
+ // transforms disabled to re-measure), and dragEnd's fallbacks — `over`
683
+ // resolving to the column or to the dragged card itself — commit that
684
+ // seeded slot verbatim. Keep it; without it the card visibly jumps a
685
+ // slot on entry.
686
+ let insertAt: number;
687
+ if (overCards.includes(overId)) {
688
+ const overIdx = overCards.indexOf(overId);
689
+ const activeRect = active.rect.current.translated;
690
+ const overRect = over.rect;
691
+ const isBelow =
692
+ activeRect != null &&
693
+ activeRect.top + activeRect.height / 2 > overRect.top + overRect.height / 2;
694
+ insertAt = overIdx + (isBelow ? 1 : 0);
695
+ } else if (overId === overContainer) {
696
+ insertAt = overCards.length;
697
+ } else {
698
+ return prev;
699
+ }
700
+
701
+ const next = new Map(prev);
702
+ if (isTransit) {
703
+ next.set(
704
+ activeContainer,
705
+ activeCards.filter((id) => id !== activeId),
706
+ );
707
+ }
708
+ const newOver = [...overCards];
709
+ newOver.splice(insertAt, 0, activeId);
710
+ next.set(overContainer, newOver);
711
+ return next;
712
+ });
713
+ },
714
+ [findContainer],
715
+ );
716
+
717
+ const handleDragEnd = useCallback(
718
+ (event: DragEndEvent) => {
719
+ const { active, over } = event;
720
+ const activeId = active.id as string | number;
721
+
722
+ // Two ways a drag ends in nothing, and `over` only reports the first:
723
+ // - `over == null` — collision detection returned nothing at all, i.e.
724
+ // no droppable was measured.
725
+ // - the pointer left the board. `containerAwareClosestCorners` falls
726
+ // back to `closestCorners`, which ranks every measured droppable and
727
+ // rejects none, so `over` still names the nearest column after a
728
+ // release over unrelated page content — a column the user never aimed
729
+ // at and never saw highlighted. Committing it silently reassigns the
730
+ // card's column (#387), so the pointer test decides instead.
731
+ // Both snap the card back: dropping `liveItems` re-renders every column
732
+ // from the consumer's untouched state, and no `onMove` fires.
733
+ if (!over || outsideBoardRef.current) {
734
+ setLiveItems(null);
735
+ return;
736
+ }
737
+
738
+ const overId = over.id as string | number;
739
+ const finalItems = liveItems ?? initialItems;
740
+
741
+ // Source (from-position): the card's pre-drag location.
742
+ let fromColumn: string | number | null = null;
743
+ let fromIndex = -1;
744
+ for (const [colId, cards] of initialItems) {
745
+ const idx = cards.indexOf(activeId);
746
+ if (idx >= 0) {
747
+ fromColumn = colId;
748
+ fromIndex = idx;
749
+ break;
750
+ }
751
+ }
752
+
753
+ if (fromColumn == null) {
754
+ setLiveItems(null);
755
+ return;
756
+ }
757
+
758
+ // Where does active currently live in liveItems? handleDragOver seats
759
+ // it in the destination on transit; the exact slot is resolved HERE,
760
+ // from `over`, because that's what the user was looking at:
761
+ // `verticalListSortingStrategy` renders each column as
762
+ // `arrayMove(liveItems[col], activeIndex, overIndex)` with
763
+ // `overIndex = liveItems[col].indexOf(over.id)`, so the previewed slot
764
+ // is `overIndex` whenever `over` is a sibling card, and the card's live
765
+ // slot when it isn't (`overIndex === -1` → the strategy displaces
766
+ // nothing). Committing the live slot alone would pin the drop to the
767
+ // boundary-crossing point (#376).
768
+ let liveColumn: string | number | null = null;
769
+ let liveIdx = -1;
770
+ for (const [colId, cards] of finalItems) {
771
+ const idx = cards.indexOf(activeId);
772
+ if (idx >= 0) {
773
+ liveColumn = colId;
774
+ liveIdx = idx;
775
+ break;
776
+ }
777
+ }
778
+
779
+ let toColumn: string | number;
780
+ let toIndex: number;
781
+
782
+ if (liveColumn != null && liveColumn !== fromColumn) {
783
+ // Cross-column drop: the previewed slot wins (see the comment above).
784
+ toColumn = liveColumn;
785
+ const destCards = finalItems.get(liveColumn) ?? [];
786
+ const overIdx = overId === activeId ? -1 : destCards.indexOf(overId);
787
+ toIndex = overIdx >= 0 ? overIdx : liveIdx;
788
+ } else if (initialItems.has(overId)) {
789
+ // Within-source-column drop on the column's padding (no card target).
790
+ // Default to "drop at end" so the card lands where the user dragged
791
+ // it, rather than snapping back to its original slot.
792
+ toColumn = fromColumn;
793
+ const fromCol = initialItems.get(fromColumn) ?? [];
794
+ toIndex = fromCol.length - 1;
795
+ } else if (overId === activeId) {
796
+ // Cursor over the dragged card itself. The strategy displaces nothing
797
+ // (overIndex === activeIndex), so the preview IS the live order —
798
+ // commit that. A barely-moved drag resolves to its own starting index
799
+ // and gets dropped by the no-op guard below; a card that came back to
800
+ // its origin column lands where the preview put it.
801
+ toColumn = fromColumn;
802
+ toIndex = liveIdx;
803
+ } else {
804
+ // Within-column drop on a sibling card. arrayMove semantics:
805
+ // toIndex = sibling's index in the live column — same rule as the
806
+ // cross-column branch. For a plain same-column drag liveItems is
807
+ // untouched, so this is the sibling's index in the consumer's
808
+ // pre-move state; if the card visited another column and came back,
809
+ // the live order is what the preview showed, so it wins.
810
+ // For cross-column drops where the transit didn't commit (rare
811
+ // stale-closure case at the very first dragOver), the sibling may
812
+ // live in a different column — resolve the column from the card.
813
+ let overCol: string | number | null = null;
814
+ for (const [colId, cards] of finalItems) {
815
+ if (cards.includes(overId)) {
816
+ overCol = colId;
817
+ break;
818
+ }
819
+ }
820
+ if (overCol == null) {
821
+ setLiveItems(null);
822
+ return;
823
+ }
824
+ toColumn = overCol;
825
+ toIndex = (finalItems.get(toColumn) ?? []).indexOf(overId);
826
+ }
827
+
828
+ setLiveItems(null);
829
+
830
+ if (toIndex < 0) return;
831
+ if (fromColumn === toColumn && fromIndex === toIndex) return;
832
+
833
+ onMove?.({
834
+ from: { columnId: fromColumn, index: fromIndex },
835
+ to: { columnId: toColumn, index: toIndex },
836
+ cardId: activeId,
837
+ });
838
+ },
839
+ [liveItems, initialItems, onMove],
840
+ );
841
+
842
+ const handleDragCancel = useCallback(() => {
843
+ setLiveItems(null);
844
+ }, []);
845
+
846
+ // ------------------------------------------------------------------
847
+ // Screen-reader announcements (Hard rule 9, #390)
848
+ // ------------------------------------------------------------------
849
+ // Names a card by its own text and a column by the `aria-label` the consumer
850
+ // put on `<Kanban.Column>` (falling back to its board position), so a drag
851
+ // says "Acme — 40 seats, position 2 of 3 in Qualified" rather than dnd-kit's
852
+ // "deal-1 was moved over droppable area col-2". Slots are read from
853
+ // `effectiveItems` — the order the user is actually looking at mid-drag.
854
+ // `||`, not `??`, for the reason SortableGroup's twin site states: already
855
+ // safe, because `containerName` rejects a blank `aria-label` itself, but
856
+ // aligned so the operator is not the thing a reader has to verify.
857
+ const columnLabel = (colId: string | number): string =>
858
+ containerName(columnElements.get(colId)?.props ?? {}) ||
859
+ t('kanban.unnamedColumn', {
860
+ index: columnOrder.indexOf(colId) + 1,
861
+ total: columnOrder.length,
862
+ });
863
+ const describeDrag: DescribeDragTarget = (entry, activeId) => {
864
+ if (!entry) return null;
865
+ const id = entry.id as string | number;
866
+ const isColumn = effectiveItems.has(id);
867
+ const colId = isColumn ? id : findContainer(id, effectiveItems);
868
+ if (colId == null) return null;
869
+ const cards = effectiveItems.get(colId) ?? [];
870
+ const label = dragLabelOf(entry.data.current);
871
+ const container = columnLabel(colId);
872
+ if (!isColumn) return { label, index: cards.indexOf(id) + 1, total: cards.length, container };
873
+ // `over` is the column itself (empty column, or the padding below the
874
+ // cards). If the dragged card has already been live-seated here it keeps
875
+ // its slot; otherwise it would be appended, so count it as one more.
876
+ const seated = cards.indexOf(activeId as string | number);
877
+ return seated >= 0
878
+ ? { label, index: seated + 1, total: cards.length, container }
879
+ : { label, index: cards.length + 1, total: cards.length + 1, container };
880
+ };
881
+ // A release outside the board is a cancel, not a drop — `over` is non-null on
882
+ // that path (closestCorners rejects nothing), so without this dnd-kit would
883
+ // announce a drop that handleDragEnd deliberately threw away (#387).
884
+ const accessibility = useDragAccessibility(describeDrag, () => outsideBoardRef.current);
885
+
886
+ // ------------------------------------------------------------------
887
+ // Render
888
+ // ------------------------------------------------------------------
889
+ return (
890
+ <DndContext
891
+ accessibility={accessibility}
892
+ sensors={sensors}
893
+ collisionDetection={collisionDetection}
894
+ onDragStart={handleDragStart}
895
+ onDragOver={handleDragOver}
896
+ onDragEnd={handleDragEnd}
897
+ onDragCancel={handleDragCancel}
898
+ >
899
+ {/*
900
+ aria-label BEFORE {...rest} so consumer can pass a more specific
901
+ label (e.g. "Q3 sales pipeline") and have it win.
902
+ role="region" AFTER {...rest} so it stays locked — non-overridable
903
+ semantic guarantee.
904
+ */}
905
+ <div
906
+ ref={ref}
907
+ aria-label={t('kanban.board')}
908
+ className={clsx(styles.board, className)}
909
+ {...rest}
910
+ role="region"
911
+ >
912
+ {columnOrder.map((colId) => {
913
+ const columnElement = columnElements.get(colId);
914
+ if (!columnElement) return null;
915
+
916
+ const cardIds = effectiveItems.get(colId) ?? [];
917
+ const cards = cardIds
918
+ .map((id) => cardElements.get(id))
919
+ .filter((c): c is ReactElement<KanbanCardProps> => c != null);
920
+
921
+ const slice = columnChildrenSlices.get(colId) ?? { before: [], after: [] };
922
+
923
+ // Re-clone the column with re-arranged children. KanbanColumn reads
924
+ // cardIds via KanbanColumnItemsContext so its SortableContext.items
925
+ // matches what's rendered.
926
+ return (
927
+ <KanbanColumnItemsContext.Provider key={colId} value={cardIds}>
928
+ {cloneElement(columnElement, {}, ...slice.before, ...cards, ...slice.after)}
929
+ </KanbanColumnItemsContext.Provider>
930
+ );
931
+ })}
932
+ </div>
933
+ </DndContext>
934
+ );
935
+ });
936
+ KanbanRoot.displayName = 'Kanban';
937
+
938
+ // ---------------------------------------------------------------------------
939
+ // Compound export
940
+ // ---------------------------------------------------------------------------
941
+
942
+ export const Kanban = Object.assign(KanbanRoot, {
943
+ Column: KanbanColumn,
944
+ Card: KanbanCard,
945
+ /**
946
+ * Re-exported from `<Sortable.Handle>`. Attaches drag listeners when placed
947
+ * inside a `<Kanban.Card>`. When present, only the Handle initiates drag
948
+ * (the Card div gets `role="article"`).
949
+ */
950
+ Handle: SortableHandle,
951
+ });