@rebasepro/ui 0.24.0 → 0.24.1-canary.gcb918fc

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 (241) hide show
  1. package/dist/components/Alert.d.ts +6 -0
  2. package/dist/components/Alert.es.js +82 -0
  3. package/dist/components/Alert.es.js.map +1 -0
  4. package/dist/components/Autocomplete.es.js +53 -0
  5. package/dist/components/Autocomplete.es.js.map +1 -0
  6. package/dist/components/Avatar.d.ts +3 -1
  7. package/dist/components/Avatar.es.js +37 -0
  8. package/dist/components/Avatar.es.js.map +1 -0
  9. package/dist/components/Badge.es.js +27 -0
  10. package/dist/components/Badge.es.js.map +1 -0
  11. package/dist/components/BooleanSwitch.es.js +75 -0
  12. package/dist/components/BooleanSwitch.es.js.map +1 -0
  13. package/dist/components/BooleanSwitchWithLabel.es.js +55 -0
  14. package/dist/components/BooleanSwitchWithLabel.es.js.map +1 -0
  15. package/dist/components/Button.es.js +58 -0
  16. package/dist/components/Button.es.js.map +1 -0
  17. package/dist/components/Card.es.js +26 -0
  18. package/dist/components/Card.es.js.map +1 -0
  19. package/dist/components/CenteredView.es.js +23 -0
  20. package/dist/components/CenteredView.es.js.map +1 -0
  21. package/dist/components/Checkbox.es.js +62 -0
  22. package/dist/components/Checkbox.es.js.map +1 -0
  23. package/dist/components/Chip.es.js +72 -0
  24. package/dist/components/Chip.es.js.map +1 -0
  25. package/dist/components/CircularProgress.es.js +28 -0
  26. package/dist/components/CircularProgress.es.js.map +1 -0
  27. package/dist/components/CircularProgressCenter.es.js +30 -0
  28. package/dist/components/CircularProgressCenter.es.js.map +1 -0
  29. package/dist/components/Collapse.es.js +57 -0
  30. package/dist/components/Collapse.es.js.map +1 -0
  31. package/dist/components/ColorPicker.es.js +89 -0
  32. package/dist/components/ColorPicker.es.js.map +1 -0
  33. package/dist/components/Container.es.js +31 -0
  34. package/dist/components/Container.es.js.map +1 -0
  35. package/dist/components/DateTimeField.d.ts +11 -1
  36. package/dist/components/DateTimeField.es.js +253 -0
  37. package/dist/components/DateTimeField.es.js.map +1 -0
  38. package/dist/components/DebouncedTextField.es.js +93 -0
  39. package/dist/components/DebouncedTextField.es.js.map +1 -0
  40. package/dist/components/Dialog.es.js +98 -0
  41. package/dist/components/Dialog.es.js.map +1 -0
  42. package/dist/components/DialogActions.es.js +15 -0
  43. package/dist/components/DialogActions.es.js.map +1 -0
  44. package/dist/components/DialogContent.es.js +18 -0
  45. package/dist/components/DialogContent.es.js.map +1 -0
  46. package/dist/components/DialogTitle.es.js +26 -0
  47. package/dist/components/DialogTitle.es.js.map +1 -0
  48. package/dist/components/ErrorBoundary.es.js +158 -0
  49. package/dist/components/ErrorBoundary.es.js.map +1 -0
  50. package/dist/components/ExpandablePanel.es.js +91 -0
  51. package/dist/components/ExpandablePanel.es.js.map +1 -0
  52. package/dist/components/FileUpload.es.js +58 -0
  53. package/dist/components/FileUpload.es.js.map +1 -0
  54. package/dist/components/FilterChip.es.js +31 -0
  55. package/dist/components/FilterChip.es.js.map +1 -0
  56. package/dist/components/IconButton.es.js +52 -0
  57. package/dist/components/IconButton.es.js.map +1 -0
  58. package/dist/components/InfoLabel.es.js +18 -0
  59. package/dist/components/InfoLabel.es.js.map +1 -0
  60. package/dist/components/InputLabel.es.js +27 -0
  61. package/dist/components/InputLabel.es.js.map +1 -0
  62. package/dist/components/Label.es.js +18 -0
  63. package/dist/components/Label.es.js.map +1 -0
  64. package/dist/components/LoadingButton.es.js +21 -0
  65. package/dist/components/LoadingButton.es.js.map +1 -0
  66. package/dist/components/Markdown.es.js +88 -0
  67. package/dist/components/Markdown.es.js.map +1 -0
  68. package/dist/components/Menu.es.js +46 -0
  69. package/dist/components/Menu.es.js.map +1 -0
  70. package/dist/components/Menubar.es.js +142 -0
  71. package/dist/components/Menubar.es.js.map +1 -0
  72. package/dist/components/MultiSelect.d.ts +1 -7
  73. package/dist/components/MultiSelect.es.js +271 -0
  74. package/dist/components/MultiSelect.es.js.map +1 -0
  75. package/dist/components/Paper.es.js +16 -0
  76. package/dist/components/Paper.es.js.map +1 -0
  77. package/dist/components/Popover.es.js +113 -0
  78. package/dist/components/Popover.es.js.map +1 -0
  79. package/dist/components/RadioGroup.es.js +30 -0
  80. package/dist/components/RadioGroup.es.js.map +1 -0
  81. package/dist/components/ResizablePanels.d.ts +3 -1
  82. package/dist/components/ResizablePanels.es.js +137 -0
  83. package/dist/components/ResizablePanels.es.js.map +1 -0
  84. package/dist/components/SearchBar.es.js +82 -0
  85. package/dist/components/SearchBar.es.js.map +1 -0
  86. package/dist/components/Select.d.ts +8 -1
  87. package/dist/components/Select.es.js +300 -0
  88. package/dist/components/Select.es.js.map +1 -0
  89. package/dist/components/Separator.es.js +20 -0
  90. package/dist/components/Separator.es.js.map +1 -0
  91. package/dist/components/Sheet.es.js +73 -0
  92. package/dist/components/Sheet.es.js.map +1 -0
  93. package/dist/components/Skeleton.es.js +33 -0
  94. package/dist/components/Skeleton.es.js.map +1 -0
  95. package/dist/components/Slider.es.js +56 -0
  96. package/dist/components/Slider.es.js.map +1 -0
  97. package/dist/components/Table.d.ts +18 -3
  98. package/dist/components/Table.es.js +69 -0
  99. package/dist/components/Table.es.js.map +1 -0
  100. package/dist/components/Tabs.es.js +101 -0
  101. package/dist/components/Tabs.es.js.map +1 -0
  102. package/dist/components/TextField.d.ts +0 -8
  103. package/dist/components/TextField.es.js +111 -0
  104. package/dist/components/TextField.es.js.map +1 -0
  105. package/dist/components/TextareaAutosize.es.js +187 -0
  106. package/dist/components/TextareaAutosize.es.js.map +1 -0
  107. package/dist/components/ToggleButtonGroup.d.ts +6 -1
  108. package/dist/components/ToggleButtonGroup.es.js +35 -0
  109. package/dist/components/ToggleButtonGroup.es.js.map +1 -0
  110. package/dist/components/Tooltip.d.ts +24 -0
  111. package/dist/components/Tooltip.es.js +165 -0
  112. package/dist/components/Tooltip.es.js.map +1 -0
  113. package/dist/components/Typography.es.js +85 -0
  114. package/dist/components/Typography.es.js.map +1 -0
  115. package/dist/components/VirtualTable/VirtualTable.es.js +516 -0
  116. package/dist/components/VirtualTable/VirtualTable.es.js.map +1 -0
  117. package/dist/components/VirtualTable/VirtualTableCell.d.ts +1 -1
  118. package/dist/components/VirtualTable/VirtualTableCell.es.js +40 -0
  119. package/dist/components/VirtualTable/VirtualTableCell.es.js.map +1 -0
  120. package/dist/components/VirtualTable/VirtualTableHeader.es.js +209 -0
  121. package/dist/components/VirtualTable/VirtualTableHeader.es.js.map +1 -0
  122. package/dist/components/VirtualTable/VirtualTableHeaderRow.es.js +166 -0
  123. package/dist/components/VirtualTable/VirtualTableHeaderRow.es.js.map +1 -0
  124. package/dist/components/VirtualTable/VirtualTableProps.d.ts +13 -2
  125. package/dist/components/VirtualTable/VirtualTableRow.d.ts +6 -7
  126. package/dist/components/VirtualTable/VirtualTableRow.es.js +41 -0
  127. package/dist/components/VirtualTable/VirtualTableRow.es.js.map +1 -0
  128. package/dist/components/VirtualTable/fields/VirtualTableDateField.d.ts +2 -1
  129. package/dist/components/VirtualTable/fields/VirtualTableDateField.es.js +71 -0
  130. package/dist/components/VirtualTable/fields/VirtualTableDateField.es.js.map +1 -0
  131. package/dist/components/VirtualTable/fields/VirtualTableInput.es.js +73 -0
  132. package/dist/components/VirtualTable/fields/VirtualTableInput.es.js.map +1 -0
  133. package/dist/components/VirtualTable/fields/VirtualTableNumberInput.es.js +47 -0
  134. package/dist/components/VirtualTable/fields/VirtualTableNumberInput.es.js.map +1 -0
  135. package/dist/components/VirtualTable/fields/VirtualTableSelect.es.js +83 -0
  136. package/dist/components/VirtualTable/fields/VirtualTableSelect.es.js.map +1 -0
  137. package/dist/components/VirtualTable/fields/VirtualTableSwitch.es.js +22 -0
  138. package/dist/components/VirtualTable/fields/VirtualTableSwitch.es.js.map +1 -0
  139. package/dist/components/VirtualTable/selection/SelectionContext.es.js +23 -0
  140. package/dist/components/VirtualTable/selection/SelectionContext.es.js.map +1 -0
  141. package/dist/components/VirtualTable/selection/SelectionStore.es.js +43 -0
  142. package/dist/components/VirtualTable/selection/SelectionStore.es.js.map +1 -0
  143. package/dist/components/VirtualTable/types.d.ts +1 -1
  144. package/dist/components/common/SelectInputLabel.es.js +28 -0
  145. package/dist/components/common/SelectInputLabel.es.js.map +1 -0
  146. package/dist/components/index.es.js +64 -0
  147. package/dist/hooks/PortalContainerContext.es.js +40 -0
  148. package/dist/hooks/PortalContainerContext.es.js.map +1 -0
  149. package/dist/hooks/UiLabelsContext.d.ts +63 -0
  150. package/dist/hooks/UiLabelsContext.es.js +59 -0
  151. package/dist/hooks/UiLabelsContext.es.js.map +1 -0
  152. package/dist/hooks/useDebounceCallback.es.js +15 -0
  153. package/dist/hooks/useDebounceCallback.es.js.map +1 -0
  154. package/dist/hooks/useDebounceValue.es.js +19 -0
  155. package/dist/hooks/useDebounceValue.es.js.map +1 -0
  156. package/dist/hooks/useDebouncedCallback.es.js +36 -0
  157. package/dist/hooks/useDebouncedCallback.es.js.map +1 -0
  158. package/dist/hooks/useInjectStyles.es.js +27 -0
  159. package/dist/hooks/useInjectStyles.es.js.map +1 -0
  160. package/dist/hooks/useIsDarkMode.es.js +61 -0
  161. package/dist/hooks/useIsDarkMode.es.js.map +1 -0
  162. package/dist/hooks/useModalFocus.es.js +81 -0
  163. package/dist/hooks/useModalFocus.es.js.map +1 -0
  164. package/dist/hooks/useOutsideAlerter.es.js +41 -0
  165. package/dist/hooks/useOutsideAlerter.es.js.map +1 -0
  166. package/dist/hooks/useRestoreInterruptedFocus.es.js +61 -0
  167. package/dist/hooks/useRestoreInterruptedFocus.es.js.map +1 -0
  168. package/dist/icons/GitHubIcon.es.js +23 -0
  169. package/dist/icons/GitHubIcon.es.js.map +1 -0
  170. package/dist/icons/HandleIcon.es.js +60 -0
  171. package/dist/icons/HandleIcon.es.js.map +1 -0
  172. package/dist/icons/Icon.es.js +25 -0
  173. package/dist/icons/Icon.es.js.map +1 -0
  174. package/dist/icons/LucideIconByName.d.ts +12 -9
  175. package/dist/icons/LucideIconByName.es.js +83 -0
  176. package/dist/icons/LucideIconByName.es.js.map +1 -0
  177. package/dist/icons/cool_icon_keys.es.js +55 -0
  178. package/dist/icons/cool_icon_keys.es.js.map +1 -0
  179. package/dist/icons/icon_keys.es.js +1941 -0
  180. package/dist/icons/icon_keys.es.js.map +1 -0
  181. package/dist/icons/index.d.ts +4 -3
  182. package/dist/icons/index.es.js +8 -0
  183. package/dist/icons/lucide_icon_map.d.ts +12 -0
  184. package/dist/icons/lucide_icon_map.es.js +2 -0
  185. package/dist/index.d.ts +2 -0
  186. package/dist/index.es.js +87 -9563
  187. package/dist/styles.es.js +158 -0
  188. package/dist/styles.es.js.map +1 -0
  189. package/dist/util/chip_colors.es.js +290 -0
  190. package/dist/util/chip_colors.es.js.map +1 -0
  191. package/dist/util/cls.es.js +10 -0
  192. package/dist/util/cls.es.js.map +1 -0
  193. package/dist/util/debounce.es.js +22 -0
  194. package/dist/util/debounce.es.js.map +1 -0
  195. package/dist/util/hash.es.js +16 -0
  196. package/dist/util/hash.es.js.map +1 -0
  197. package/dist/util/keys.es.js +37 -0
  198. package/dist/util/keys.es.js.map +1 -0
  199. package/dist/util/lazy_chunk.es.js +96 -0
  200. package/dist/util/lazy_chunk.es.js.map +1 -0
  201. package/dist/views/CardView.d.ts +26 -2
  202. package/dist/views/CardView.es.js +435 -0
  203. package/dist/views/CardView.es.js.map +1 -0
  204. package/dist/views/CollectionView/CollectionCardView.es.js +179 -0
  205. package/dist/views/CollectionView/CollectionCardView.es.js.map +1 -0
  206. package/dist/views/CollectionView/CollectionKanbanView.es.js +172 -0
  207. package/dist/views/CollectionView/CollectionKanbanView.es.js.map +1 -0
  208. package/dist/views/CollectionView/CollectionListView.es.js +156 -0
  209. package/dist/views/CollectionView/CollectionListView.es.js.map +1 -0
  210. package/dist/views/CollectionView/CollectionTableView.es.js +149 -0
  211. package/dist/views/CollectionView/CollectionTableView.es.js.map +1 -0
  212. package/dist/views/CollectionView/CollectionView.es.js +197 -0
  213. package/dist/views/CollectionView/CollectionView.es.js.map +1 -0
  214. package/dist/views/CollectionView/CollectionViewToolbar.es.js +155 -0
  215. package/dist/views/CollectionView/CollectionViewToolbar.es.js.map +1 -0
  216. package/dist/views/CollectionView/DefaultCellRenderer.es.js +302 -0
  217. package/dist/views/CollectionView/DefaultCellRenderer.es.js.map +1 -0
  218. package/dist/views/CollectionView/resolveTitleProperty.es.js +56 -0
  219. package/dist/views/CollectionView/resolveTitleProperty.es.js.map +1 -0
  220. package/dist/views/CollectionView/utils.es.js +19 -0
  221. package/dist/views/CollectionView/utils.es.js.map +1 -0
  222. package/dist/views/Kanban/Board.es.js +237 -0
  223. package/dist/views/Kanban/Board.es.js.map +1 -0
  224. package/dist/views/Kanban/BoardColumn.es.js +81 -0
  225. package/dist/views/Kanban/BoardColumn.es.js.map +1 -0
  226. package/dist/views/Kanban/BoardColumnTitle.es.js +27 -0
  227. package/dist/views/Kanban/BoardColumnTitle.es.js.map +1 -0
  228. package/dist/views/Kanban/BoardSortableList.es.js +98 -0
  229. package/dist/views/Kanban/BoardSortableList.es.js.map +1 -0
  230. package/dist/views/Kanban/placement.es.js +27 -0
  231. package/dist/views/Kanban/placement.es.js.map +1 -0
  232. package/dist/views/KanbanView.es.js +11 -0
  233. package/dist/views/KanbanView.es.js.map +1 -0
  234. package/dist/views/ListView.d.ts +15 -2
  235. package/dist/views/ListView.es.js +245 -0
  236. package/dist/views/ListView.es.js.map +1 -0
  237. package/dist/views/useScrollParentRestoration.d.ts +8 -1
  238. package/dist/views/useScrollParentRestoration.es.js +75 -0
  239. package/dist/views/useScrollParentRestoration.es.js.map +1 -0
  240. package/package.json +2 -2
  241. package/dist/index.es.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Board.es.js","names":[],"sources":["../../../src/views/Kanban/Board.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport {\n closestCorners,\n DndContext,\n DragEndEvent,\n DragOverEvent,\n DragOverlay,\n DragStartEvent,\n PointerSensor,\n useSensor,\n useSensors\n} from \"@dnd-kit/core\";\nimport { arrayMove, SortableContext } from \"@dnd-kit/sortable\";\nimport { BoardColumn } from \"./BoardColumn\";\nimport { placeDroppedCard } from \"./placement\";\nimport { BoardItem, BoardItemMap, BoardProps } from \"./board_types\";\nimport { cls } from \"../../util\";\n\nexport function Board<T, COLUMN extends string>({\n data,\n columns: columnsProp,\n columnLabels,\n columnColors,\n className,\n assignColumn,\n allowColumnReorder = false,\n onColumnReorder,\n onItemsReorder,\n ItemComponent,\n columnLoadingState,\n onLoadMoreColumn,\n onAddItemToColumn,\n AddColumnComponent\n}: BoardProps<T, COLUMN>) {\n\n const [activeItem, setActiveItem] = useState<BoardItem<T> | null>(null);\n const [isDragging, setIsDragging] = useState(false);\n const [dragOverColumnId, setDragOverColumnId] = useState<string | null>(null);\n\n /**\n * Which column the card was picked up from.\n *\n * `handleDragOver` moves the card between columns in `itemMapState` while\n * the pointer is still down — that is what makes the gap open up under the\n * cursor. So by the time the drop is handled, looking the card up by id\n * finds it in its *destination*, and the board was reporting the move as\n * source === target: a cross-column drop looked like a plain reorder and\n * the column property was never written. The card snapped back on the next\n * fetch.\n */\n const dragSourceColumnRef = useRef<COLUMN | null>(null);\n\n const [itemMapState, setItemMapState] = useState<BoardItemMap<T>>(() => {\n const dataColumnMap: Record<string, COLUMN> = data.reduce((prev, item: BoardItem<T>) => ({\n ...prev,\n [item.id]: assignColumn(item)\n }), {});\n return columnsProp.reduce(\n (previous: BoardItemMap<T>, column: COLUMN) => ({\n ...previous,\n [column]: data.filter((item: BoardItem<T>) => dataColumnMap[item.id] === column)\n }),\n {} as BoardItemMap<T>\n );\n });\n\n const sensors = useSensors(\n useSensor(PointerSensor, {\n activationConstraint: {\n distance: 5\n }\n })\n );\n\n useEffect(() => {\n if (isDragging) return;\n\n const dataColumnMap: Record<string, COLUMN> = data.reduce((prev, item) => ({\n ...prev,\n [item.id]: assignColumn(item)\n }), {});\n\n const newItemMap = columnsProp.reduce(\n (previous: BoardItemMap<T>, column: COLUMN) => ({\n ...previous,\n [column]: data.filter((item: BoardItem<T>) => dataColumnMap[item.id] === column)\n }),\n {} as BoardItemMap<T>\n );\n\n setItemMapState(prevMap => {\n let changed = false;\n\n for (const col of columnsProp) {\n const prevItems = prevMap[col] ?? [];\n const newItems = newItemMap[col] ?? [];\n\n if (prevItems.length !== newItems.length) {\n changed = true;\n continue;\n }\n for (let i = 0; i < prevItems.length; i++) {\n if (prevItems[i].id !== newItems[i].id || prevItems[i].data !== newItems[i].data) {\n changed = true;\n break;\n }\n }\n }\n\n if (!changed) {\n return prevMap;\n }\n\n const updated: BoardItemMap<T> = {};\n for (const col of columnsProp) {\n const prevItems = prevMap[col] ?? [];\n const newItems = newItemMap[col] ?? [];\n\n const prevById = new Map<string, BoardItem<T>>();\n for (const item of prevItems) {\n prevById.set(item.id, item);\n }\n\n updated[col] = newItems.map(newItem => {\n const prev = prevById.get(newItem.id);\n if (prev && prev.data === newItem.data) {\n return prev;\n }\n return newItem;\n });\n }\n return updated;\n });\n }, [data, columnsProp, assignColumn]);\n\n const findColumnByItemId = (id: string): string | undefined => {\n return Object.keys(itemMapState).find(col => itemMapState[col]?.some(i => i.id === id));\n };\n\n const handleDragStart = (event: DragStartEvent) => {\n setIsDragging(true);\n setDragOverColumnId(null);\n const { active } = event;\n\n if (active.data.current?.type === \"ITEM\") {\n const columnId = findColumnByItemId(active.id as string);\n dragSourceColumnRef.current = (columnId as COLUMN | undefined) ?? null;\n if (columnId) {\n const item = itemMapState[columnId]?.find(i => i.id === active.id);\n setActiveItem(item || null);\n }\n }\n };\n\n const handleDragOver = (event: DragOverEvent) => {\n const {\n active,\n over\n } = event;\n\n if (!over) {\n setDragOverColumnId(null);\n return;\n }\n\n let currentHoveredColumnId: string | null = null;\n const overId = over.id as string;\n const overDataType = over.data.current?.type as string | undefined;\n\n if (overDataType === \"ITEM-LIST\" || overDataType === \"COLUMN\") {\n currentHoveredColumnId = overId;\n } else if (overDataType === \"ITEM\") {\n currentHoveredColumnId = findColumnByItemId(overId) || null;\n } else if (columnsProp.includes(overId as COLUMN)) {\n currentHoveredColumnId = overId;\n }\n\n setDragOverColumnId(currentHoveredColumnId);\n\n if (active.data.current?.type !== \"ITEM\") {\n return;\n }\n\n const activeId = active.id as string;\n const activeItemColumn = findColumnByItemId(activeId);\n let overColumnForMove = findColumnByItemId(overId);\n\n if (!overColumnForMove && overDataType === \"ITEM-LIST\") {\n overColumnForMove = overId;\n }\n if (!overColumnForMove && columnsProp.includes(overId as COLUMN)) {\n overColumnForMove = overId;\n }\n\n if (!activeItemColumn || !overColumnForMove) return;\n if (activeItemColumn === overColumnForMove) return;\n\n if (itemMapState[overColumnForMove]?.some(i => i.id === activeId)) {\n return;\n }\n\n setItemMapState(currentMap => {\n const activeItems = [...(currentMap[activeItemColumn] || [])];\n const overItems = [...(currentMap[overColumnForMove!] || [])];\n const activeIndex = activeItems.findIndex(i => i.id === activeId);\n\n if (activeIndex === -1) return currentMap;\n\n let overIndex;\n if (overDataType === \"ITEM-LIST\" || (columnsProp.includes(overId as COLUMN) && !findColumnByItemId(overId))) {\n overIndex = overItems.length;\n } else {\n overIndex = overItems.findIndex(i => i.id === overId);\n if (overIndex !== -1) {\n const activeTop = active.rect.current.translated?.top ?? 0;\n const activeHeight = active.rect.current.translated?.height ?? 0;\n const activeCenter = activeTop + activeHeight / 2;\n\n const overTop = over?.rect.top ?? 0;\n const overHeight = over?.rect.height ?? 0;\n const overCenter = overTop + overHeight / 2;\n\n const isBelowOverItem = activeCenter > overCenter;\n\n const modifier = isBelowOverItem ? 1 : 0;\n overIndex = overIndex >= 0 ? overIndex + modifier : overItems.length;\n } else {\n overIndex = overItems.length;\n }\n }\n\n const newItemMap = { ...currentMap };\n const [moved] = activeItems.splice(activeIndex, 1);\n overItems.splice(overIndex, 0, moved);\n newItemMap[activeItemColumn] = activeItems;\n newItemMap[overColumnForMove!] = overItems;\n return newItemMap;\n });\n };\n\n // Escape, or a drop outside the board. `handleDragOver` may already have\n // moved the card between columns, so the next `data` effect has to be\n // allowed to put it back.\n const handleDragCancel = () => {\n setIsDragging(false);\n setActiveItem(null);\n setDragOverColumnId(null);\n dragSourceColumnRef.current = null;\n };\n\n const handleDragEnd = (event: DragEndEvent) => {\n const {\n active,\n over\n } = event;\n\n const sourceColumn = dragSourceColumnRef.current;\n\n setIsDragging(false);\n setActiveItem(null);\n setDragOverColumnId(null);\n dragSourceColumnRef.current = null;\n\n if (!over) return;\n\n const activeId = active.id as string;\n const overId = over.id as string;\n\n if (active.data.current?.type === \"COLUMN\") {\n if (activeId !== overId) {\n const oldIndex = columnsProp.indexOf(activeId as COLUMN);\n const newIndex = columnsProp.indexOf(overId as COLUMN);\n if (oldIndex !== -1 && newIndex !== -1) {\n const reordered = arrayMove(columnsProp, oldIndex, newIndex);\n onColumnReorder?.(reordered);\n }\n }\n return;\n }\n\n // Where the card sits right now — `handleDragOver` has already put it in\n // the column being hovered, so this is the destination, not the origin.\n const currentCol = findColumnByItemId(activeId) as COLUMN | undefined;\n let targetCol = findColumnByItemId(overId) as COLUMN | undefined;\n\n if (!targetCol) {\n const overDataType = over.data.current?.type;\n if (overDataType === \"ITEM-LIST\" || columnsProp.includes(overId as COLUMN)) {\n targetCol = overId as COLUMN;\n }\n }\n targetCol = targetCol ?? currentCol;\n\n if (!currentCol || !targetCol) return;\n\n const targetItems = itemMapState[targetCol] || [];\n const movedItem = (itemMapState[currentCol] || []).find(i => i.id === activeId);\n if (!movedItem) return;\n\n const finalTargetItems = placeDroppedCard({\n targetItems,\n movedItem,\n overId,\n changedColumn: (sourceColumn ?? currentCol) !== targetCol\n });\n\n onItemsReorder?.(finalTargetItems, {\n itemId: activeId,\n // The column the pointer went down in. `currentCol` would report a\n // cross-column move as a same-column reorder.\n sourceColumn: (sourceColumn ?? currentCol),\n targetColumn: targetCol\n });\n };\n\n return (\n <DndContext\n sensors={sensors}\n // `rectIntersection`, the default, only reports a target while the\n // dragged rect physically overlaps it, and prefers whichever\n // overlap is largest — on a board of tall columns that means a card\n // held over a gap reports nothing, and one held near a column edge\n // reports the neighbour. `closestCorners` is the sortable-list\n // recommendation and is what makes the drop land where it looks\n // like it will.\n collisionDetection={closestCorners}\n onDragStart={handleDragStart}\n onDragOver={handleDragOver}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n >\n <div className={cls(\"flex flex-row h-full w-full overflow-x-auto p-4 select-none items-start\", className)}>\n <SortableContext items={columnsProp}>\n {columnsProp.map((col, index) => {\n const colItems = itemMapState[col] || [];\n const loadingState = columnLoadingState?.[col];\n return (\n <BoardColumn\n key={String(col)}\n id={String(col)}\n title={columnLabels?.[col] ?? String(col)}\n items={colItems}\n index={index}\n ItemComponent={ItemComponent}\n isDragging={isDragging}\n isDragOverColumn={dragOverColumnId === String(col)}\n allowReorder={allowColumnReorder}\n loading={loadingState?.loading}\n hasMore={loadingState?.hasMore}\n error={loadingState?.error}\n totalCount={loadingState?.totalCount}\n color={columnColors?.[col]}\n onLoadMore={onLoadMoreColumn ? () => onLoadMoreColumn(col) : undefined}\n onAddItem={onAddItemToColumn ? () => onAddItemToColumn(col) : undefined}\n />\n );\n })}\n </SortableContext>\n {AddColumnComponent}\n </div>\n\n {/* The overlay used to be positioned by hand, from `mousemove`\n listeners on `window` and a grab offset measured off whatever\n `[role='button']` the pointer went down on. dnd-kit already\n tracks the pointer and already knows the grab offset — the\n hand-rolled copy drifted from the position collision detection\n was actually using, so the card you saw and the slot you got\n were two different things. */}\n {typeof document !== \"undefined\" && createPortal(\n <DragOverlay dropAnimation={null} zIndex={9999}>\n {activeItem && (\n <ItemComponent\n item={activeItem}\n isDragging={true}\n isClone={true}\n />\n )}\n </DragOverlay>,\n document.body\n )}\n </DndContext>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import { cls } from "../../util/cls.es.js";
2
+ import { iconSize } from "../../icons/Icon.es.js";
3
+ import { PlusIcon } from "../../icons/index.es.js";
4
+ import { IconButton } from "../../components/IconButton.es.js";
5
+ import { BoardSortableList } from "./BoardSortableList.es.js";
6
+ import { BoardColumnTitle } from "./BoardColumnTitle.es.js";
7
+ import { memo, useMemo } from "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { SortableContext, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
10
+ import { CSS } from "@dnd-kit/utilities";
11
+ //#region src/views/Kanban/BoardColumn.tsx
12
+ var BoardColumn = memo(function BoardColumn({ id, title, items, ItemComponent, isDragging, isDragOverColumn, allowReorder = false, loading = false, hasMore = false, error, onLoadMore, onAddItem, totalCount, color, style }) {
13
+ const { setNodeRef, attributes, listeners, isDragging: isColumnBeingDragged, transform, transition } = useSortable({
14
+ id,
15
+ data: { type: "COLUMN" },
16
+ disabled: !allowReorder
17
+ });
18
+ const combinedStyle = useMemo(() => ({
19
+ ...style,
20
+ transform: CSS.Translate.toString(transform),
21
+ transition,
22
+ zIndex: isColumnBeingDragged ? 2 : 1
23
+ }), [
24
+ style,
25
+ transform,
26
+ transition,
27
+ isColumnBeingDragged
28
+ ]);
29
+ const dragListeners = allowReorder ? listeners : {};
30
+ const columnClassName = useMemo(() => cls("border h-full w-80 min-w-80 mx-2 flex flex-col rounded-md border-hairline", isColumnBeingDragged ? "ring-2 ring-primary" : ""), [isColumnBeingDragged]);
31
+ const headerClassName = useMemo(() => cls("flex items-center justify-between px-2 rounded-t-md transition-colors duration-200 ease-in-out", isColumnBeingDragged ? "bg-surface-active" : "bg-surface-field hover:bg-surface-field-hover", allowReorder ? "cursor-grab" : ""), [isColumnBeingDragged, allowReorder]);
32
+ const itemIds = useMemo(() => items.map((i) => i.id), [items]);
33
+ return /* @__PURE__ */ jsxs("div", {
34
+ ref: setNodeRef,
35
+ style: combinedStyle,
36
+ ...attributes,
37
+ className: columnClassName,
38
+ children: [/* @__PURE__ */ jsxs("div", {
39
+ ...dragListeners,
40
+ className: headerClassName,
41
+ children: [/* @__PURE__ */ jsxs("div", {
42
+ className: "flex items-center gap-2",
43
+ children: [/* @__PURE__ */ jsx(BoardColumnTitle, {
44
+ "aria-label": `${title} item list`,
45
+ color,
46
+ children: title
47
+ }), totalCount !== void 0 && /* @__PURE__ */ jsx("span", {
48
+ className: "text-xs text-surface-500 dark:text-surface-400",
49
+ children: totalCount
50
+ })]
51
+ }), onAddItem && /* @__PURE__ */ jsx(IconButton, {
52
+ "aria-label": `Add to ${title}`,
53
+ size: "small",
54
+ onClick: (e) => {
55
+ e.stopPropagation();
56
+ onAddItem();
57
+ },
58
+ className: "opacity-60 hover:opacity-100",
59
+ children: /* @__PURE__ */ jsx(PlusIcon, { size: iconSize.small })
60
+ })]
61
+ }), /* @__PURE__ */ jsx(SortableContext, {
62
+ items: itemIds,
63
+ strategy: verticalListSortingStrategy,
64
+ children: /* @__PURE__ */ jsx(BoardSortableList, {
65
+ columnId: id,
66
+ items,
67
+ ItemComponent,
68
+ isDragging,
69
+ isDragOverColumn,
70
+ loading,
71
+ hasMore,
72
+ error,
73
+ onLoadMore
74
+ })
75
+ })]
76
+ });
77
+ });
78
+ //#endregion
79
+ export { BoardColumn };
80
+
81
+ //# sourceMappingURL=BoardColumn.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BoardColumn.es.js","names":[],"sources":["../../../src/views/Kanban/BoardColumn.tsx"],"sourcesContent":["import React, { memo, useMemo } from \"react\";\nimport { SortableContext, useSortable, verticalListSortingStrategy } from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport { BoardSortableList } from \"./BoardSortableList\";\nimport { BoardColumnTitle } from \"./BoardColumnTitle\";\nimport { BoardItem, BoardItemViewProps } from \"./board_types\";\nimport {\n IconButton\n} from \"../../components\";\nimport { cls } from \"../../util\";\nimport { iconSize, PlusIcon } from \"../../icons\";\n\nexport interface BoardColumnProps<T> {\n id: string;\n title: string;\n items: BoardItem<T>[];\n index: number;\n ItemComponent: React.ComponentType<BoardItemViewProps<T>>;\n isDragging: boolean;\n isDragOverColumn: boolean;\n allowReorder?: boolean;\n loading?: boolean;\n hasMore?: boolean;\n error?: Error;\n onLoadMore?: () => void;\n onAddItem?: () => void;\n totalCount?: number;\n color?: any;\n style?: React.CSSProperties;\n}\n\nexport const BoardColumn = memo(function BoardColumn<T>({\n id,\n title,\n items,\n ItemComponent,\n isDragging,\n isDragOverColumn,\n allowReorder = false,\n loading = false,\n hasMore = false,\n error,\n onLoadMore,\n onAddItem,\n totalCount,\n color,\n style\n}: BoardColumnProps<T>) {\n const {\n setNodeRef,\n attributes,\n listeners,\n isDragging: isColumnBeingDragged,\n transform,\n transition\n } = useSortable({\n id,\n data: { type: \"COLUMN\" },\n disabled: !allowReorder\n });\n\n const combinedStyle = useMemo(() => ({\n ...style,\n transform: CSS.Translate.toString(transform),\n transition,\n zIndex: isColumnBeingDragged ? 2 : 1\n }), [style, transform, transition, isColumnBeingDragged]);\n\n const dragListeners = allowReorder ? listeners : {};\n\n const columnClassName = useMemo(() => cls(\n \"border h-full w-80 min-w-80 mx-2 flex flex-col rounded-md border-hairline\",\n isColumnBeingDragged ? \"ring-2 ring-primary\" : \"\"\n ), [isColumnBeingDragged]);\n\n const headerClassName = useMemo(() => cls(\n \"flex items-center justify-between px-2 rounded-t-md transition-colors duration-200 ease-in-out\",\n isColumnBeingDragged\n ? \"bg-surface-active\"\n : \"bg-surface-field hover:bg-surface-field-hover\",\n allowReorder ? \"cursor-grab\" : \"\"\n ), [isColumnBeingDragged, allowReorder]);\n\n const itemIds = useMemo(() => items.map(i => i.id), [items]);\n\n return (\n <div\n ref={setNodeRef}\n style={combinedStyle}\n {...attributes}\n className={columnClassName}\n >\n <div\n {...dragListeners}\n className={headerClassName}\n >\n <div className=\"flex items-center gap-2\">\n <BoardColumnTitle aria-label={`${title} item list`} color={color}>\n {title}\n </BoardColumnTitle>\n {totalCount !== undefined && (\n <span className=\"text-xs text-surface-500 dark:text-surface-400\">\n {totalCount}\n </span>\n )}\n </div>\n {onAddItem && (\n <IconButton\n aria-label={`Add to ${title}`}\n size=\"small\"\n onClick={(e: React.MouseEvent) => {\n e.stopPropagation();\n onAddItem();\n }}\n className=\"opacity-60 hover:opacity-100\"\n >\n <PlusIcon size={iconSize.small}/>\n </IconButton>\n )}\n </div>\n <SortableContext\n items={itemIds}\n strategy={verticalListSortingStrategy}\n >\n <BoardSortableList\n columnId={id}\n items={items}\n ItemComponent={ItemComponent}\n isDragging={isDragging}\n isDragOverColumn={isDragOverColumn}\n loading={loading}\n hasMore={hasMore}\n error={error}\n onLoadMore={onLoadMore}\n />\n </SortableContext>\n </div>\n );\n}) as <T>(props: BoardColumnProps<T>) => React.ReactElement;\n"],"mappings":";;;;;;;;;;;AA+BA,IAAa,cAAc,KAAK,SAAS,YAAe,EACpD,IACA,OACA,OACA,eACA,YACA,kBACA,eAAe,OACf,UAAU,OACV,UAAU,OACV,OACA,YACA,WACA,YACA,OACA,SACoB;CACpB,MAAM,EACF,YACA,YACA,WACA,YAAY,sBACZ,WACA,eACA,YAAY;EACZ;EACA,MAAM,EAAE,MAAM,SAAS;EACvB,UAAU,CAAC;CACf,CAAC;CAED,MAAM,gBAAgB,eAAe;EACjC,GAAG;EACH,WAAW,IAAI,UAAU,SAAS,SAAS;EAC3C;EACA,QAAQ,uBAAuB,IAAI;CACvC,IAAI;EAAC;EAAO;EAAW;EAAY;CAAoB,CAAC;CAExD,MAAM,gBAAgB,eAAe,YAAY,CAAC;CAElD,MAAM,kBAAkB,cAAc,IAClC,6EACA,uBAAuB,wBAAwB,EACnD,GAAG,CAAC,oBAAoB,CAAC;CAEzB,MAAM,kBAAkB,cAAc,IAClC,kGACA,uBACM,sBACA,iDACN,eAAe,gBAAgB,EACnC,GAAG,CAAC,sBAAsB,YAAY,CAAC;CAEvC,MAAM,UAAU,cAAc,MAAM,KAAI,MAAK,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC;CAE3D,OACI,qBAAC,OAAD;EACI,KAAK;EACL,OAAO;EACP,GAAI;EACJ,WAAW;YAJf,CAMI,qBAAC,OAAD;GACI,GAAI;GACJ,WAAW;aAFf,CAII,qBAAC,OAAD;IAAK,WAAU;cAAf,CACI,oBAAC,kBAAD;KAAkB,cAAY,GAAG,MAAM;KAAoB;eACtD;IACa,CAAA,GACjB,eAAe,KAAA,KACZ,oBAAC,QAAD;KAAM,WAAU;eACX;IACC,CAAA,CAET;OACJ,aACG,oBAAC,YAAD;IACI,cAAY,UAAU;IACtB,MAAK;IACL,UAAU,MAAwB;KAC9B,EAAE,gBAAgB;KAClB,UAAU;IACd;IACA,WAAU;cAEV,oBAAC,UAAD,EAAU,MAAM,SAAS,MAAO,CAAA;GACxB,CAAA,CAEf;MACL,oBAAC,iBAAD;GACI,OAAO;GACP,UAAU;aAEV,oBAAC,mBAAD;IACI,UAAU;IACH;IACQ;IACH;IACM;IACT;IACA;IACF;IACK;GACf,CAAA;EACY,CAAA,CAChB;;AAEb,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { cls } from "../../util/cls.es.js";
2
+ import { getColorSchemeForKey } from "../../util/chip_colors.es.js";
3
+ import { Typography } from "../../components/Typography.es.js";
4
+ import { useMemo } from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/views/Kanban/BoardColumnTitle.tsx
7
+ function BoardColumnTitle({ children, className, color, ...props }) {
8
+ const colorScheme = useMemo(() => {
9
+ if (!color) return void 0;
10
+ if (typeof color === "string") return getColorSchemeForKey(color);
11
+ return color;
12
+ }, [color]);
13
+ return /* @__PURE__ */ jsxs(Typography, {
14
+ variant: "subtitle2",
15
+ component: "h4",
16
+ className: cls("py-3 px-3 transition-colors duration-200 flex-grow select-none relative outline-none focus:outline focus:outline-2 focus:outline-offset-2 flex items-center gap-3", className),
17
+ ...props,
18
+ children: [colorScheme && /* @__PURE__ */ jsx("div", {
19
+ className: "w-3 h-3 rounded-full flex-shrink-0",
20
+ style: { backgroundColor: colorScheme.darkColor ?? colorScheme.color }
21
+ }), children]
22
+ });
23
+ }
24
+ //#endregion
25
+ export { BoardColumnTitle };
26
+
27
+ //# sourceMappingURL=BoardColumnTitle.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BoardColumnTitle.es.js","names":[],"sources":["../../../src/views/Kanban/BoardColumnTitle.tsx"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { Typography } from \"../../components\";\nimport { cls, getColorSchemeForKey } from \"../../util\";\n\nexport interface BoardColumnTitleProps {\n children: React.ReactNode;\n className?: string;\n \"aria-label\"?: string;\n color?: any;\n}\n\nexport function BoardColumnTitle({\n children,\n className,\n color,\n ...props\n}: BoardColumnTitleProps) {\n const colorScheme = useMemo(() => {\n if (!color) return undefined;\n if (typeof color === \"string\") {\n return getColorSchemeForKey(color);\n }\n return color;\n }, [color]);\n\n return (\n <Typography\n variant=\"subtitle2\"\n component=\"h4\"\n className={\n cls(\"py-3 px-3 transition-colors duration-200 flex-grow select-none relative outline-none focus:outline focus:outline-2 focus:outline-offset-2 flex items-center gap-3\",\n className)\n }\n {...props}\n >\n {colorScheme && (\n <div\n className=\"w-3 h-3 rounded-full flex-shrink-0\"\n style={{\n backgroundColor: colorScheme.darkColor ?? colorScheme.color\n }}\n />\n )}\n {children}\n </Typography>\n );\n}\n"],"mappings":";;;;;;AAWA,SAAgB,iBAAiB,EAC7B,UACA,WACA,OACA,GAAG,SACmB;CACtB,MAAM,cAAc,cAAc;EAC9B,IAAI,CAAC,OAAO,OAAO,KAAA;EACnB,IAAI,OAAO,UAAU,UACjB,OAAO,qBAAqB,KAAK;EAErC,OAAO;CACX,GAAG,CAAC,KAAK,CAAC;CAEV,OACI,qBAAC,YAAD;EACI,SAAQ;EACR,WAAU;EACV,WACI,IAAI,qKACA,SAAS;EAEjB,GAAI;YAPR,CASK,eACG,oBAAC,OAAD;GACI,WAAU;GACV,OAAO,EACH,iBAAiB,YAAY,aAAa,YAAY,MAC1D;EACH,CAAA,GAEJ,QACO;;AAEpB"}
@@ -0,0 +1,98 @@
1
+ import { cls } from "../../util/cls.es.js";
2
+ import { CircularProgress } from "../../components/CircularProgress.es.js";
3
+ import { memo, useEffect, useMemo, useRef } from "react";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import { SortableContext, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
6
+ import { useDroppable } from "@dnd-kit/core";
7
+ import { CSS } from "@dnd-kit/utilities";
8
+ //#region src/views/Kanban/BoardSortableList.tsx
9
+ function BoardSortableList({ columnId, items, ItemComponent, isDragging, isDragOverColumn, loading = false, hasMore = false, error, onLoadMore }) {
10
+ const { setNodeRef } = useDroppable({
11
+ id: columnId,
12
+ data: { type: "ITEM-LIST" }
13
+ });
14
+ const sentinelRef = useRef(null);
15
+ const isLoadingRef = useRef(loading);
16
+ isLoadingRef.current = loading;
17
+ const lastLoadTimeRef = useRef(0);
18
+ useEffect(() => {
19
+ if (!sentinelRef.current || !hasMore || !onLoadMore) return;
20
+ const sentinel = sentinelRef.current;
21
+ const observer = new IntersectionObserver((entries) => {
22
+ const now = Date.now();
23
+ if (entries[0].isIntersecting && hasMore && !isLoadingRef.current && now - lastLoadTimeRef.current > 500) {
24
+ lastLoadTimeRef.current = now;
25
+ onLoadMore();
26
+ }
27
+ }, { threshold: .1 });
28
+ observer.observe(sentinel);
29
+ const rect = sentinel.getBoundingClientRect();
30
+ const containerRect = sentinel.parentElement?.getBoundingClientRect();
31
+ if (containerRect && rect.top < containerRect.bottom && rect.bottom > containerRect.top) {
32
+ const now = Date.now();
33
+ if (hasMore && !isLoadingRef.current && now - lastLoadTimeRef.current > 500) {
34
+ lastLoadTimeRef.current = now;
35
+ onLoadMore();
36
+ }
37
+ }
38
+ return () => observer.disconnect();
39
+ }, [hasMore, onLoadMore]);
40
+ return /* @__PURE__ */ jsx("div", {
41
+ ref: setNodeRef,
42
+ className: useMemo(() => cls("flex flex-col p-2 transition-opacity duration-100 transition-bg ease-linear w-full overflow-y-auto flex-1 min-h-0 rounded-md", isDragging && isDragOverColumn ? "bg-surface-active" : isDragging ? "bg-surface-field hover:bg-surface-hover" : "bg-surface-field"), [isDragging, isDragOverColumn]),
43
+ style: { minHeight: 80 },
44
+ children: /* @__PURE__ */ jsx(SortableContext, {
45
+ items: items.map((i) => i.id),
46
+ strategy: verticalListSortingStrategy,
47
+ children: items.length === 0 && !loading ? /* @__PURE__ */ jsx("div", {
48
+ className: "flex-1 flex items-center justify-center px-3 text-center",
49
+ children: /* @__PURE__ */ jsx("span", {
50
+ className: cls("text-xs", error ? "text-red-600 dark:text-red-400" : "text-surface-400 dark:text-surface-500"),
51
+ children: error ? "Could not load this column" : "No items"
52
+ })
53
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [items.map((item, index) => /* @__PURE__ */ jsx(SortableItem, {
54
+ item,
55
+ index,
56
+ columnId,
57
+ ItemComponent
58
+ }, item.id)), (loading || hasMore) && /* @__PURE__ */ jsx("div", {
59
+ ref: sentinelRef,
60
+ className: "flex items-center justify-center py-2 min-h-6",
61
+ children: loading && /* @__PURE__ */ jsx(CircularProgress, { size: "smallest" })
62
+ })] })
63
+ })
64
+ });
65
+ }
66
+ var SortableItem = memo(function SortableItem({ item, index, columnId, ItemComponent }) {
67
+ const { setNodeRef, attributes, listeners, isDragging: isItemBeingDragged, transform, transition } = useSortable({
68
+ id: item.id,
69
+ data: {
70
+ type: "ITEM",
71
+ columnId
72
+ }
73
+ });
74
+ return /* @__PURE__ */ jsx("div", {
75
+ ref: setNodeRef,
76
+ style: useMemo(() => ({
77
+ transform: CSS.Transform.toString(transform),
78
+ transition,
79
+ zIndex: isItemBeingDragged ? 2 : 1,
80
+ opacity: isItemBeingDragged ? 0 : 1
81
+ }), [
82
+ transform,
83
+ transition,
84
+ isItemBeingDragged
85
+ ]),
86
+ ...attributes,
87
+ ...listeners,
88
+ children: /* @__PURE__ */ jsx(ItemComponent, {
89
+ item,
90
+ isDragging: isItemBeingDragged,
91
+ index
92
+ })
93
+ });
94
+ });
95
+ //#endregion
96
+ export { BoardSortableList };
97
+
98
+ //# sourceMappingURL=BoardSortableList.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BoardSortableList.es.js","names":[],"sources":["../../../src/views/Kanban/BoardSortableList.tsx"],"sourcesContent":["import React, { memo, useEffect, useMemo, useRef } from \"react\";\nimport { useDroppable } from \"@dnd-kit/core\";\nimport { useSortable, SortableContext, verticalListSortingStrategy } from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport { CircularProgress } from \"../../components\";\nimport { cls } from \"../../util\";\nimport { BoardItem, BoardItemViewProps } from \"./board_types\";\n\ninterface BoardSortableListProps<T> {\n columnId: string;\n items: BoardItem<T>[];\n ItemComponent: React.ComponentType<BoardItemViewProps<T>>;\n isDragging: boolean;\n isDragOverColumn: boolean;\n loading?: boolean;\n hasMore?: boolean;\n error?: Error;\n onLoadMore?: () => void;\n}\n\nexport function BoardSortableList<T>({\n columnId,\n items,\n ItemComponent,\n isDragging,\n isDragOverColumn,\n loading = false,\n hasMore = false,\n error,\n onLoadMore\n}: BoardSortableListProps<T>) {\n const {\n setNodeRef\n } = useDroppable({\n id: columnId,\n data: { type: \"ITEM-LIST\" }\n });\n\n const sentinelRef = useRef<HTMLDivElement>(null);\n const isLoadingRef = useRef(loading);\n isLoadingRef.current = loading;\n const lastLoadTimeRef = useRef(0);\n\n useEffect(() => {\n if (!sentinelRef.current || !hasMore || !onLoadMore) return;\n\n const sentinel = sentinelRef.current;\n\n const observer = new IntersectionObserver(\n (entries) => {\n const now = Date.now();\n if (\n entries[0].isIntersecting &&\n hasMore &&\n !isLoadingRef.current &&\n now - lastLoadTimeRef.current > 500\n ) {\n lastLoadTimeRef.current = now;\n onLoadMore();\n }\n },\n { threshold: 0.1 }\n );\n\n observer.observe(sentinel);\n\n const rect = sentinel.getBoundingClientRect();\n const containerRect = sentinel.parentElement?.getBoundingClientRect();\n if (containerRect && rect.top < containerRect.bottom && rect.bottom > containerRect.top) {\n const now = Date.now();\n if (hasMore && !isLoadingRef.current && now - lastLoadTimeRef.current > 500) {\n lastLoadTimeRef.current = now;\n onLoadMore();\n }\n }\n\n return () => observer.disconnect();\n }, [hasMore, onLoadMore]);\n\n const containerClassName = useMemo(() => cls(\n // The scrollbar stays: this is the only thing on the page that scrolls\n // vertically, and hiding it left a column of cards with no sign that\n // there were more below the fold.\n \"flex flex-col p-2 transition-opacity duration-100 transition-bg ease-linear w-full overflow-y-auto flex-1 min-h-0 rounded-md\",\n isDragging && isDragOverColumn\n ? \"bg-surface-active\"\n : isDragging\n ? \"bg-surface-field hover:bg-surface-hover\"\n : \"bg-surface-field\"\n ), [isDragging, isDragOverColumn]);\n\n return (\n <div\n ref={setNodeRef}\n className={containerClassName}\n style={{ minHeight: 80 }}\n >\n <SortableContext\n items={items.map(i => i.id)}\n strategy={verticalListSortingStrategy}\n >\n {items.length === 0 && !loading ? (\n <div className=\"flex-1 flex items-center justify-center px-3 text-center\">\n {/* \"No items\" over a header counting eleven of them is\n the wrong thing to say when the read failed. */}\n <span className={cls(\n \"text-xs\",\n error\n ? \"text-red-600 dark:text-red-400\"\n : \"text-surface-400 dark:text-surface-500\"\n )}>\n {error ? \"Could not load this column\" : \"No items\"}\n </span>\n </div>\n ) : (\n <>\n {items.map((item, index) => (\n <SortableItem\n key={item.id}\n item={item}\n index={index}\n columnId={columnId}\n ItemComponent={ItemComponent}\n />\n ))}\n {(loading || hasMore) && (\n <div ref={sentinelRef} className=\"flex items-center justify-center py-2 min-h-6\">\n {loading && <CircularProgress size=\"smallest\"/>}\n </div>\n )}\n </>\n )}\n </SortableContext>\n </div>\n );\n}\n\ninterface SortableItemProps<T> {\n item: BoardItem<T>;\n index: number;\n columnId: string;\n ItemComponent: React.ComponentType<BoardItemViewProps<T>>;\n}\n\nconst SortableItem = memo(function SortableItem<T>({\n item,\n index,\n columnId,\n ItemComponent\n}: SortableItemProps<T>) {\n const {\n setNodeRef,\n attributes,\n listeners,\n isDragging: isItemBeingDragged,\n transform,\n transition\n } = useSortable({\n id: item.id,\n data: {\n type: \"ITEM\",\n columnId\n }\n });\n\n const sortableStyle = useMemo(() => ({\n transform: CSS.Transform.toString(transform),\n transition,\n zIndex: isItemBeingDragged ? 2 : 1,\n opacity: isItemBeingDragged ? 0 : 1\n }), [transform, transition, isItemBeingDragged]);\n\n return (\n <div ref={setNodeRef} style={sortableStyle} {...attributes} {...listeners}>\n <ItemComponent\n item={item}\n isDragging={isItemBeingDragged}\n index={index}\n />\n </div>\n );\n}) as <T>(props: SortableItemProps<T>) => React.ReactElement;\n"],"mappings":";;;;;;;;AAoBA,SAAgB,kBAAqB,EACjC,UACA,OACA,eACA,YACA,kBACA,UAAU,OACV,UAAU,OACV,OACA,cAC0B;CAC1B,MAAM,EACF,eACA,aAAa;EACb,IAAI;EACJ,MAAM,EAAE,MAAM,YAAY;CAC9B,CAAC;CAED,MAAM,cAAc,OAAuB,IAAI;CAC/C,MAAM,eAAe,OAAO,OAAO;CACnC,aAAa,UAAU;CACvB,MAAM,kBAAkB,OAAO,CAAC;CAEhC,gBAAgB;EACZ,IAAI,CAAC,YAAY,WAAW,CAAC,WAAW,CAAC,YAAY;EAErD,MAAM,WAAW,YAAY;EAE7B,MAAM,WAAW,IAAI,sBAChB,YAAY;GACT,MAAM,MAAM,KAAK,IAAI;GACrB,IACI,QAAQ,EAAE,CAAC,kBACX,WACA,CAAC,aAAa,WACd,MAAM,gBAAgB,UAAU,KAClC;IACE,gBAAgB,UAAU;IAC1B,WAAW;GACf;EACJ,GACA,EAAE,WAAW,GAAI,CACrB;EAEA,SAAS,QAAQ,QAAQ;EAEzB,MAAM,OAAO,SAAS,sBAAsB;EAC5C,MAAM,gBAAgB,SAAS,eAAe,sBAAsB;EACpE,IAAI,iBAAiB,KAAK,MAAM,cAAc,UAAU,KAAK,SAAS,cAAc,KAAK;GACrF,MAAM,MAAM,KAAK,IAAI;GACrB,IAAI,WAAW,CAAC,aAAa,WAAW,MAAM,gBAAgB,UAAU,KAAK;IACzE,gBAAgB,UAAU;IAC1B,WAAW;GACf;EACJ;EAEA,aAAa,SAAS,WAAW;CACrC,GAAG,CAAC,SAAS,UAAU,CAAC;CAcxB,OACI,oBAAC,OAAD;EACI,KAAK;EACL,WAfmB,cAAc,IAIrC,gIACA,cAAc,mBACR,sBACA,aACI,4CACA,kBACd,GAAG,CAAC,YAAY,gBAAgB,CAKb;EACX,OAAO,EAAE,WAAW,GAAG;YAEvB,oBAAC,iBAAD;GACI,OAAO,MAAM,KAAI,MAAK,EAAE,EAAE;GAC1B,UAAU;aAET,MAAM,WAAW,KAAK,CAAC,UACpB,oBAAC,OAAD;IAAK,WAAU;cAGX,oBAAC,QAAD;KAAM,WAAW,IACb,WACA,QACM,mCACA,wCACV;eACK,QAAQ,+BAA+B;IACtC,CAAA;GACL,CAAA,IAEL,qBAAA,UAAA,EAAA,UAAA,CACK,MAAM,KAAK,MAAM,UACd,oBAAC,cAAD;IAEU;IACC;IACG;IACK;GAClB,GALQ,KAAK,EAKb,CACJ,IACC,WAAW,YACT,oBAAC,OAAD;IAAK,KAAK;IAAa,WAAU;cAC5B,WAAW,oBAAC,kBAAD,EAAkB,MAAK,WAAW,CAAA;GAC7C,CAAA,CAEX,EAAA,CAAA;EAEO,CAAA;CAChB,CAAA;AAEb;AASA,IAAM,eAAe,KAAK,SAAS,aAAgB,EAC/C,MACA,OACA,UACA,iBACqB;CACrB,MAAM,EACF,YACA,YACA,WACA,YAAY,oBACZ,WACA,eACA,YAAY;EACZ,IAAI,KAAK;EACT,MAAM;GACF,MAAM;GACN;EACJ;CACJ,CAAC;CASD,OACI,oBAAC,OAAD;EAAK,KAAK;EAAY,OARJ,eAAe;GACjC,WAAW,IAAI,UAAU,SAAS,SAAS;GAC3C;GACA,QAAQ,qBAAqB,IAAI;GACjC,SAAS,qBAAqB,IAAI;EACtC,IAAI;GAAC;GAAW;GAAY;EAAkB,CAGb;EAAe,GAAI;EAAY,GAAI;YAC5D,oBAAC,eAAD;GACU;GACN,YAAY;GACL;EACV,CAAA;CACA,CAAA;AAEb,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { arrayMove } from "@dnd-kit/sortable";
2
+ //#region src/views/Kanban/placement.ts
3
+ /**
4
+ * Where a dropped card ends up in its destination column.
5
+ *
6
+ * Pulled out of the drop handler because it is the part that was wrong twice:
7
+ * once appending a card that had been dropped into the middle of a column, and
8
+ * once refusing to place a card at all when it was released over an empty
9
+ * column rather than over another card. It is pure, so it can be checked
10
+ * without driving a pointer.
11
+ */
12
+ function placeDroppedCard({ targetItems, movedItem, overId, changedColumn }) {
13
+ const indexInTarget = targetItems.findIndex((i) => i.id === movedItem.id);
14
+ const overIndex = targetItems.findIndex((i) => i.id === overId);
15
+ if (indexInTarget === -1) {
16
+ const placed = [...targetItems];
17
+ placed.splice(overIndex === -1 ? targetItems.length : overIndex, 0, movedItem);
18
+ return placed;
19
+ }
20
+ if (overIndex !== -1) return arrayMove(targetItems, indexInTarget, overIndex);
21
+ if (changedColumn) return targetItems;
22
+ return arrayMove(targetItems, indexInTarget, targetItems.length - 1);
23
+ }
24
+ //#endregion
25
+ export { placeDroppedCard };
26
+
27
+ //# sourceMappingURL=placement.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placement.es.js","names":[],"sources":["../../../src/views/Kanban/placement.ts"],"sourcesContent":["import { arrayMove } from \"@dnd-kit/sortable\";\nimport { BoardItem } from \"./board_types\";\n\nexport interface PlaceDroppedCardParams<T> {\n /**\n * The destination column as it stands at the moment of the drop. When the\n * card came from another column it is normally already in here:\n * `handleDragOver` moves it while the pointer is still down, which is what\n * opens the gap under the cursor.\n */\n targetItems: BoardItem<T>[];\n /** The card being dropped. */\n movedItem: BoardItem<T>;\n /** What the pointer was over — a card id, or the column's own droppable id. */\n overId: string;\n /** Whether the card was picked up in a different column than it landed in. */\n changedColumn: boolean;\n}\n\n/**\n * Where a dropped card ends up in its destination column.\n *\n * Pulled out of the drop handler because it is the part that was wrong twice:\n * once appending a card that had been dropped into the middle of a column, and\n * once refusing to place a card at all when it was released over an empty\n * column rather than over another card. It is pure, so it can be checked\n * without driving a pointer.\n */\nexport function placeDroppedCard<T>({\n targetItems,\n movedItem,\n overId,\n changedColumn\n}: PlaceDroppedCardParams<T>): BoardItem<T>[] {\n\n const indexInTarget = targetItems.findIndex(i => i.id === movedItem.id);\n const overIndex = targetItems.findIndex(i => i.id === overId);\n\n // Not placed during the drag — drop straight into the slot. `-1` means the\n // pointer was over the column itself (an empty one, or the space below the\n // last card), which appends.\n if (indexInTarget === -1) {\n const placed = [...targetItems];\n placed.splice(overIndex === -1 ? targetItems.length : overIndex, 0, movedItem);\n return placed;\n }\n\n // Released over another card: that card's slot is the one the user chose.\n if (overIndex !== -1) {\n return arrayMove(targetItems, indexInTarget, overIndex);\n }\n\n // Released over the column rather than a card. The card is already sitting\n // in the gap the drag opened, so its position is the one on screen —\n // appending here is what used to send it to the bottom.\n if (changedColumn) {\n return targetItems;\n }\n\n // Same column, released below the last card.\n return arrayMove(targetItems, indexInTarget, targetItems.length - 1);\n}\n"],"mappings":";;;;;;;;;;;AA4BA,SAAgB,iBAAoB,EAChC,aACA,WACA,QACA,iBAC0C;CAE1C,MAAM,gBAAgB,YAAY,WAAU,MAAK,EAAE,OAAO,UAAU,EAAE;CACtE,MAAM,YAAY,YAAY,WAAU,MAAK,EAAE,OAAO,MAAM;CAK5D,IAAI,kBAAkB,IAAI;EACtB,MAAM,SAAS,CAAC,GAAG,WAAW;EAC9B,OAAO,OAAO,cAAc,KAAK,YAAY,SAAS,WAAW,GAAG,SAAS;EAC7E,OAAO;CACX;CAGA,IAAI,cAAc,IACd,OAAO,UAAU,aAAa,eAAe,SAAS;CAM1D,IAAI,eACA,OAAO;CAIX,OAAO,UAAU,aAAa,eAAe,YAAY,SAAS,CAAC;AACvE"}
@@ -0,0 +1,11 @@
1
+ import { Board } from "./Kanban/Board.es.js";
2
+ import "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/views/KanbanView.tsx
5
+ function KanbanView(props) {
6
+ return /* @__PURE__ */ jsx(Board, { ...props });
7
+ }
8
+ //#endregion
9
+ export { KanbanView };
10
+
11
+ //# sourceMappingURL=KanbanView.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KanbanView.es.js","names":[],"sources":["../../src/views/KanbanView.tsx"],"sourcesContent":["import React from \"react\";\nimport { Board } from \"./Kanban/Board\";\nimport { BoardItem, BoardItemViewProps, ColumnLoadingState } from \"./Kanban/board_types\";\n\nexport type KanbanViewProps<T, COLUMN extends string> = {\n columns: COLUMN[];\n columnLabels?: Record<COLUMN, string>;\n columnColors?: Record<COLUMN, any>;\n className?: string;\n assignColumn: (item: BoardItem<T>) => COLUMN;\n allowColumnReorder?: boolean;\n onColumnReorder?: (columns: COLUMN[]) => void;\n onItemsReorder?: (\n items: BoardItem<T>[],\n moveInfo?: {\n itemId: string;\n sourceColumn: COLUMN;\n targetColumn: COLUMN;\n }\n ) => void;\n ItemComponent: React.ComponentType<BoardItemViewProps<T>>;\n columnLoadingState?: ColumnLoadingState;\n onLoadMoreColumn?: (column: COLUMN) => void;\n onAddItemToColumn?: (column: COLUMN) => void;\n AddColumnComponent?: React.ReactNode;\n data: BoardItem<T>[];\n};\n\nexport function KanbanView<T, COLUMN extends string>(props: KanbanViewProps<T, COLUMN>) {\n return <Board {...props} />;\n}\nexport type { BoardItem, BoardItemViewProps, ColumnLoadingState };\n"],"mappings":";;;;AA4BA,SAAgB,WAAqC,OAAmC;CACpF,OAAO,oBAAC,OAAD,EAAO,GAAI,MAAQ,CAAA;AAC9B"}
@@ -10,7 +10,11 @@ export type ListViewColumnDef<T> = {
10
10
  isTitle?: boolean;
11
11
  };
12
12
  export type ListViewProps<T> = {
13
- data: T[];
13
+ /**
14
+ * The rows, in order. A position holding `undefined` is a row that exists
15
+ * but has not been read yet: a placeholder is drawn in its place.
16
+ */
17
+ data: (T | undefined)[];
14
18
  dataLoading?: boolean;
15
19
  noMoreToLoad?: boolean;
16
20
  dataLoadingError?: Error;
@@ -31,6 +35,15 @@ export type ListViewProps<T> = {
31
35
  */
32
36
  onScroll?: (props: ViewScrollEvent) => void;
33
37
  initialScroll?: number;
38
+ /**
39
+ * Called with the positions in `data` of the rows on screen — not the
40
+ * ones drawn ahead of them — end exclusive, each time they change. While
41
+ * the list is still scrolling to `initialScroll`, the rows there.
42
+ */
43
+ onRowsRendered?: (range: {
44
+ start: number;
45
+ end: number;
46
+ }) => void;
34
47
  size?: CollectionSize;
35
48
  selectedEntityId?: string | number;
36
49
  /**
@@ -51,4 +64,4 @@ export type ListViewProps<T> = {
51
64
  onSelectionChange: (selected: boolean) => void;
52
65
  }) => React.ReactNode;
53
66
  };
54
- export declare function ListView<T>({ data, dataLoading, noMoreToLoad, dataLoadingError, itemCount, setItemCount, pageSize, paginationEnabled, onItemClick, selectedIds, highlightedIds, selectionEnabled, onSelectionChange, emptyComponent, onScroll, initialScroll, size, selectedEntityId, header, renderRow }: ListViewProps<T>): React.JSX.Element;
67
+ export declare function ListView<T>({ data, dataLoading, noMoreToLoad, dataLoadingError, itemCount, setItemCount, pageSize, paginationEnabled, onItemClick, selectedIds, highlightedIds, selectionEnabled, onSelectionChange, emptyComponent, onScroll, initialScroll, onRowsRendered, size, selectedEntityId, header, renderRow }: ListViewProps<T>): React.JSX.Element;
@@ -0,0 +1,245 @@
1
+ import { cls } from "../util/cls.es.js";
2
+ import { CircularProgress } from "../components/CircularProgress.es.js";
3
+ import { Typography } from "../components/Typography.es.js";
4
+ import { useUiLabels } from "../hooks/UiLabelsContext.es.js";
5
+ import { Skeleton } from "../components/Skeleton.es.js";
6
+ import { getScrollParent, useScrollParentRestoration } from "./useScrollParentRestoration.es.js";
7
+ import React, { useCallback, useEffect, useRef, useState } from "react";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ //#region src/views/ListView.tsx
10
+ function getEstimatedRowHeight(size) {
11
+ switch (size) {
12
+ case "xs": return 44;
13
+ case "s": return 52;
14
+ case "m": return 64;
15
+ case "l": return 76;
16
+ case "xl": return 88;
17
+ default: return 64;
18
+ }
19
+ }
20
+ var OVERSCAN_COUNT = 8;
21
+ var LOAD_MORE_THRESHOLD = 400;
22
+ function ListView({ data, dataLoading = false, noMoreToLoad = false, dataLoadingError, itemCount, setItemCount, pageSize = 50, paginationEnabled = true, onItemClick, selectedIds, highlightedIds, selectionEnabled = true, onSelectionChange, emptyComponent, onScroll, initialScroll, onRowsRendered, size = "m", selectedEntityId, header, renderRow }) {
23
+ const labels = useUiLabels();
24
+ const containerRef = useRef(null);
25
+ const rowsRef = useRef(null);
26
+ const isLoadingMore = useRef(false);
27
+ useEffect(() => {
28
+ if (!dataLoading) isLoadingMore.current = false;
29
+ }, [dataLoading]);
30
+ const estimatedRowHeight = getEstimatedRowHeight(size);
31
+ const [effectiveScrollTop, setEffectiveScrollTop] = useState(0);
32
+ const [viewportHeight, setViewportHeight] = useState(800);
33
+ const [reportedScrollTop, setReportedScrollTop] = useState(0);
34
+ const [reportedHeight, setReportedHeight] = useState(800);
35
+ const paginationStateRef = useRef({
36
+ paginationEnabled,
37
+ noMoreToLoad,
38
+ itemCount,
39
+ pageSize
40
+ });
41
+ useEffect(() => {
42
+ paginationStateRef.current = {
43
+ paginationEnabled,
44
+ noMoreToLoad,
45
+ itemCount,
46
+ pageSize
47
+ };
48
+ }, [
49
+ paginationEnabled,
50
+ noMoreToLoad,
51
+ itemCount,
52
+ pageSize
53
+ ]);
54
+ useEffect(() => {
55
+ const el = containerRef.current;
56
+ if (!el) return;
57
+ const scrollEl = getScrollParent(el);
58
+ if (!scrollEl) return;
59
+ let rafId = null;
60
+ const update = () => {
61
+ rafId = null;
62
+ const scrollRect = scrollEl.getBoundingClientRect();
63
+ const listTopRelative = (rowsRef.current ?? el).getBoundingClientRect().top - scrollRect.top;
64
+ setEffectiveScrollTop(Math.max(0, -listTopRelative));
65
+ setViewportHeight(scrollRect.height);
66
+ const restoring = pendingScroll.current;
67
+ const top = restoring ? restoring - (scrollEl.scrollTop + listTopRelative) : -listTopRelative;
68
+ setReportedScrollTop(Math.max(0, top));
69
+ setReportedHeight(Math.max(0, scrollRect.height + Math.min(0, top)));
70
+ const { paginationEnabled: pe, noMoreToLoad: nm, itemCount: ic, pageSize: ps } = paginationStateRef.current;
71
+ if (pe && !nm && !isLoadingMore.current && scrollEl.scrollHeight - scrollEl.scrollTop - scrollEl.clientHeight < LOAD_MORE_THRESHOLD) {
72
+ isLoadingMore.current = true;
73
+ setItemCount?.((ic ?? ps) + ps);
74
+ }
75
+ };
76
+ const onScrollEvent = () => {
77
+ if (rafId === null) rafId = requestAnimationFrame(update);
78
+ };
79
+ scrollEl.addEventListener("scroll", onScrollEvent, { passive: true });
80
+ const ro = new ResizeObserver(() => update());
81
+ ro.observe(scrollEl);
82
+ update();
83
+ return () => {
84
+ scrollEl.removeEventListener("scroll", onScrollEvent);
85
+ ro.disconnect();
86
+ if (rafId !== null) cancelAnimationFrame(rafId);
87
+ };
88
+ }, [setItemCount]);
89
+ const { pendingScroll } = useScrollParentRestoration({
90
+ containerRef,
91
+ initialScroll,
92
+ onScroll,
93
+ dataLength: data.length
94
+ });
95
+ const visibleStart = Math.min(data.length, Math.floor(reportedScrollTop / estimatedRowHeight));
96
+ const visibleEnd = Math.min(data.length, Math.ceil((reportedScrollTop + reportedHeight) / estimatedRowHeight));
97
+ useEffect(() => {
98
+ if (onRowsRendered && data.length > 0) onRowsRendered({
99
+ start: visibleStart,
100
+ end: visibleEnd
101
+ });
102
+ }, [
103
+ visibleStart,
104
+ visibleEnd,
105
+ onRowsRendered
106
+ ]);
107
+ const totalHeight = data.length * estimatedRowHeight;
108
+ const startIndex = Math.max(0, Math.floor(effectiveScrollTop / estimatedRowHeight) - OVERSCAN_COUNT);
109
+ const endIndex = Math.min(data.length, Math.ceil((effectiveScrollTop + viewportHeight) / estimatedRowHeight) + OVERSCAN_COUNT);
110
+ const visibleData = data.slice(startIndex, endIndex);
111
+ const offsetY = startIndex * estimatedRowHeight;
112
+ const footerHeight = dataLoading ? 48 : !dataLoading && noMoreToLoad && data.length > 0 ? 32 : 0;
113
+ const isInitialLoading = dataLoading && data.length === 0;
114
+ const isEmpty = !dataLoading && data.length === 0 && !dataLoadingError;
115
+ const getItemId = useCallback((item) => {
116
+ if (item && typeof item === "object" && "id" in item) {
117
+ const id = item.id;
118
+ return typeof id === "number" ? id : String(id);
119
+ }
120
+ return String(item);
121
+ }, []);
122
+ return /* @__PURE__ */ jsx("div", {
123
+ ref: containerRef,
124
+ className: cls("w-full", selectedEntityId === void 0 && `rounded-lg overflow-hidden border border-hairline`),
125
+ children: dataLoadingError && data.length === 0 ? /* @__PURE__ */ jsx("div", {
126
+ className: "flex items-center justify-center p-8",
127
+ children: /* @__PURE__ */ jsx(Typography, {
128
+ className: "text-red-500",
129
+ children: labels.errorLoadingData(dataLoadingError.message)
130
+ })
131
+ }) : isInitialLoading ? /* @__PURE__ */ jsx("div", {
132
+ className: "flex items-center justify-center py-12 px-8",
133
+ children: /* @__PURE__ */ jsx(CircularProgress, { size: "small" })
134
+ }) : isEmpty ? /* @__PURE__ */ jsx("div", {
135
+ className: "w-full flex items-center justify-center py-12 px-8",
136
+ children: emptyComponent ?? /* @__PURE__ */ jsx(Typography, {
137
+ variant: "label",
138
+ color: "secondary",
139
+ children: labels.noEntriesFound
140
+ })
141
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [header, /* @__PURE__ */ jsxs("div", {
142
+ ref: rowsRef,
143
+ className: "my-1.5",
144
+ style: {
145
+ height: totalHeight + footerHeight,
146
+ position: "relative"
147
+ },
148
+ children: [
149
+ /* @__PURE__ */ jsx("div", {
150
+ style: {
151
+ position: "absolute",
152
+ top: offsetY,
153
+ left: 0,
154
+ right: 0
155
+ },
156
+ children: visibleData.map((item, i) => {
157
+ const actualIndex = startIndex + i;
158
+ if (item === void 0) return /* @__PURE__ */ jsx(UnreadRow, { height: estimatedRowHeight }, `__unread_${actualIndex}`);
159
+ const isLast = actualIndex === data.length - 1;
160
+ const id = getItemId(item);
161
+ const selected = selectedIds?.has(id) ?? false;
162
+ const highlighted = highlightedIds?.has(id) ?? false;
163
+ const handleClick = (e) => {
164
+ if ((e.metaKey || e.ctrlKey) && selectionEnabled) {
165
+ e.preventDefault();
166
+ onSelectionChange?.(item, !selected);
167
+ return;
168
+ }
169
+ onItemClick?.(item);
170
+ };
171
+ const handleSelectionChange = (val) => {
172
+ onSelectionChange?.(item, val);
173
+ };
174
+ return /* @__PURE__ */ jsx(React.Fragment, { children: renderRow({
175
+ item,
176
+ index: actualIndex,
177
+ style: {
178
+ height: estimatedRowHeight,
179
+ overflow: "hidden"
180
+ },
181
+ className: "py-0.5",
182
+ selected,
183
+ highlighted,
184
+ isLast,
185
+ onClick: handleClick,
186
+ onSelectionChange: handleSelectionChange
187
+ }) }, id || actualIndex);
188
+ })
189
+ }),
190
+ dataLoading && /* @__PURE__ */ jsx("div", {
191
+ className: "flex items-center justify-center py-3",
192
+ style: {
193
+ position: "absolute",
194
+ top: totalHeight,
195
+ left: 0,
196
+ right: 0
197
+ },
198
+ children: /* @__PURE__ */ jsx(CircularProgress, { size: "small" })
199
+ }),
200
+ !dataLoading && noMoreToLoad && data.length > 0 && /* @__PURE__ */ jsx("div", {
201
+ className: "flex items-center justify-center py-2",
202
+ style: {
203
+ position: "absolute",
204
+ top: totalHeight,
205
+ left: 0,
206
+ right: 0
207
+ },
208
+ children: /* @__PURE__ */ jsx(Typography, {
209
+ variant: "caption",
210
+ color: "secondary",
211
+ children: labels.allEntriesLoaded(data.length)
212
+ })
213
+ })
214
+ ]
215
+ })] })
216
+ });
217
+ }
218
+ /** A row that exists but has not been read yet. */
219
+ function UnreadRow({ height }) {
220
+ return /* @__PURE__ */ jsxs("div", {
221
+ className: "py-0.5 flex items-center gap-3 px-4",
222
+ style: {
223
+ height,
224
+ overflow: "hidden"
225
+ },
226
+ "aria-busy": true,
227
+ children: [/* @__PURE__ */ jsx(Skeleton, {
228
+ width: 32,
229
+ height: 32
230
+ }), /* @__PURE__ */ jsxs("div", {
231
+ className: "flex flex-col gap-1.5 grow",
232
+ children: [/* @__PURE__ */ jsx(Skeleton, {
233
+ width: 160,
234
+ height: 12
235
+ }), /* @__PURE__ */ jsx(Skeleton, {
236
+ width: 96,
237
+ height: 10
238
+ })]
239
+ })]
240
+ });
241
+ }
242
+ //#endregion
243
+ export { ListView };
244
+
245
+ //# sourceMappingURL=ListView.es.js.map