@navikt/ds-react 8.10.4 → 8.10.6

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 (244) hide show
  1. package/cjs/data/data-grid/index.d.ts +2 -0
  2. package/cjs/data/data-grid/index.js +7 -0
  3. package/cjs/data/data-grid/index.js.map +1 -0
  4. package/cjs/data/data-grid/root/DataGridRoot.context.d.ts +11 -0
  5. package/cjs/data/data-grid/root/DataGridRoot.context.js +11 -0
  6. package/cjs/data/data-grid/root/DataGridRoot.context.js.map +1 -0
  7. package/cjs/data/data-grid/root/DataGridRoot.d.ts +38 -0
  8. package/cjs/data/data-grid/root/DataGridRoot.js +68 -0
  9. package/cjs/data/data-grid/root/DataGridRoot.js.map +1 -0
  10. package/cjs/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js +11 -13
  11. package/cjs/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js.map +1 -1
  12. package/cjs/data/drag-and-drop/root/DragAndDrop.context.d.ts +4 -2
  13. package/cjs/data/drag-and-drop/root/DragAndDrop.context.js.map +1 -1
  14. package/cjs/data/drag-and-drop/root/DragAndDropRoot.js +44 -46
  15. package/cjs/data/drag-and-drop/root/DragAndDropRoot.js.map +1 -1
  16. package/cjs/data/drag-and-drop/types.d.ts +0 -4
  17. package/cjs/data/stories/Data.test-data.d.ts +2 -5
  18. package/cjs/data/stories/Data.test-data.js +30 -39
  19. package/cjs/data/stories/Data.test-data.js.map +1 -1
  20. package/cjs/data/table/base-cell/DataTableBaseCell.d.ts +15 -15
  21. package/cjs/data/table/base-cell/DataTableBaseCell.js +4 -8
  22. package/cjs/data/table/base-cell/DataTableBaseCell.js.map +1 -1
  23. package/cjs/data/table/column-header/DataTableColumnHeader.d.ts +24 -6
  24. package/cjs/data/table/column-header/DataTableColumnHeader.js +22 -27
  25. package/cjs/data/table/column-header/DataTableColumnHeader.js.map +1 -1
  26. package/cjs/data/table/column-header/useTableColumnResize.d.ts +19 -29
  27. package/cjs/data/table/column-header/useTableColumnResize.js +24 -22
  28. package/cjs/data/table/column-header/useTableColumnResize.js.map +1 -1
  29. package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.d.ts +1 -1
  30. package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.js +2 -2
  31. package/cjs/data/table/details-panel-row/DataTableDetailsPanelRow.js.map +1 -1
  32. package/cjs/data/table/helpers/collectTableRowEntries.d.ts +9 -7
  33. package/cjs/data/table/helpers/collectTableRowEntries.js +18 -10
  34. package/cjs/data/table/helpers/collectTableRowEntries.js.map +1 -1
  35. package/cjs/data/table/helpers/selection/getMultipleSelectProps.d.ts +13 -11
  36. package/cjs/data/table/helpers/selection/getMultipleSelectProps.js +43 -53
  37. package/cjs/data/table/helpers/selection/getMultipleSelectProps.js.map +1 -1
  38. package/cjs/data/table/helpers/selection/getSingleSelectProps.d.ts +9 -8
  39. package/cjs/data/table/helpers/selection/getSingleSelectProps.js +23 -10
  40. package/cjs/data/table/helpers/selection/getSingleSelectProps.js.map +1 -1
  41. package/cjs/data/table/helpers/selection/selection.types.d.ts +19 -19
  42. package/cjs/data/table/helpers/selection/selection.utils.d.ts +21 -0
  43. package/cjs/data/table/helpers/selection/selection.utils.js +46 -0
  44. package/cjs/data/table/helpers/selection/selection.utils.js.map +1 -0
  45. package/cjs/data/table/helpers/table-focus.d.ts +0 -3
  46. package/cjs/data/table/helpers/table-focus.js +38 -8
  47. package/cjs/data/table/helpers/table-focus.js.map +1 -1
  48. package/cjs/data/table/hooks/useColumnOptions.d.ts +16 -5
  49. package/cjs/data/table/hooks/useColumnOptions.js +26 -8
  50. package/cjs/data/table/hooks/useColumnOptions.js.map +1 -1
  51. package/cjs/data/table/hooks/useGridCache.js +2 -2
  52. package/cjs/data/table/hooks/useGridCache.js.map +1 -1
  53. package/cjs/data/table/hooks/useTableDetailsPanel.d.ts +10 -13
  54. package/cjs/data/table/hooks/useTableDetailsPanel.js +7 -6
  55. package/cjs/data/table/hooks/useTableDetailsPanel.js.map +1 -1
  56. package/cjs/data/table/hooks/useTableItems.d.ts +31 -17
  57. package/cjs/data/table/hooks/useTableItems.js +10 -20
  58. package/cjs/data/table/hooks/useTableItems.js.map +1 -1
  59. package/cjs/data/table/hooks/useTableKeyboardNav.d.ts +1 -6
  60. package/cjs/data/table/hooks/useTableKeyboardNav.js +6 -5
  61. package/cjs/data/table/hooks/useTableKeyboardNav.js.map +1 -1
  62. package/cjs/data/table/hooks/useTableSelection.d.ts +6 -6
  63. package/cjs/data/table/hooks/useTableSelection.js +13 -13
  64. package/cjs/data/table/hooks/useTableSelection.js.map +1 -1
  65. package/cjs/data/table/hooks/useTableSort.d.ts +2 -2
  66. package/cjs/data/table/hooks/useTableSort.js +4 -5
  67. package/cjs/data/table/hooks/useTableSort.js.map +1 -1
  68. package/cjs/data/table/root/DataTable.types.d.ts +22 -13
  69. package/cjs/data/table/root/DataTableRoot.context.d.ts +13 -7
  70. package/cjs/data/table/root/DataTableRoot.context.js.map +1 -1
  71. package/cjs/data/table/root/DataTableRoot.d.ts +49 -72
  72. package/cjs/data/table/root/DataTableRoot.js +56 -72
  73. package/cjs/data/table/root/DataTableRoot.js.map +1 -1
  74. package/cjs/data/table/root/DataTableRoot.legacy.d.ts +2 -7
  75. package/cjs/data/table/root/DataTableRoot.legacy.js +17 -3
  76. package/cjs/data/table/root/DataTableRoot.legacy.js.map +1 -1
  77. package/cjs/data/table/sub-row-toggle/DataTableSubRowToggle.js +4 -4
  78. package/cjs/data/table/sub-row-toggle/DataTableSubRowToggle.js.map +1 -1
  79. package/cjs/data/table/tbody/DataTableTbody.js +4 -2
  80. package/cjs/data/table/tbody/DataTableTbody.js.map +1 -1
  81. package/cjs/data/table/tr/DataTableTr.d.ts +5 -3
  82. package/cjs/data/table/tr/DataTableTr.js +36 -23
  83. package/cjs/data/table/tr/DataTableTr.js.map +1 -1
  84. package/cjs/table/ColumnHeader.js +2 -1
  85. package/cjs/table/ColumnHeader.js.map +1 -1
  86. package/esm/data/data-grid/index.d.ts +2 -0
  87. package/esm/data/data-grid/index.js +3 -0
  88. package/esm/data/data-grid/index.js.map +1 -0
  89. package/esm/data/data-grid/root/DataGridRoot.context.d.ts +11 -0
  90. package/esm/data/data-grid/root/DataGridRoot.context.js +7 -0
  91. package/esm/data/data-grid/root/DataGridRoot.context.js.map +1 -0
  92. package/esm/data/data-grid/root/DataGridRoot.d.ts +38 -0
  93. package/esm/data/data-grid/root/DataGridRoot.js +32 -0
  94. package/esm/data/data-grid/root/DataGridRoot.js.map +1 -0
  95. package/esm/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js +11 -13
  96. package/esm/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js.map +1 -1
  97. package/esm/data/drag-and-drop/root/DragAndDrop.context.d.ts +4 -2
  98. package/esm/data/drag-and-drop/root/DragAndDrop.context.js.map +1 -1
  99. package/esm/data/drag-and-drop/root/DragAndDropRoot.js +44 -46
  100. package/esm/data/drag-and-drop/root/DragAndDropRoot.js.map +1 -1
  101. package/esm/data/drag-and-drop/types.d.ts +0 -4
  102. package/esm/data/stories/Data.test-data.d.ts +2 -5
  103. package/esm/data/stories/Data.test-data.js +30 -39
  104. package/esm/data/stories/Data.test-data.js.map +1 -1
  105. package/esm/data/table/base-cell/DataTableBaseCell.d.ts +15 -15
  106. package/esm/data/table/base-cell/DataTableBaseCell.js +4 -8
  107. package/esm/data/table/base-cell/DataTableBaseCell.js.map +1 -1
  108. package/esm/data/table/column-header/DataTableColumnHeader.d.ts +24 -6
  109. package/esm/data/table/column-header/DataTableColumnHeader.js +23 -28
  110. package/esm/data/table/column-header/DataTableColumnHeader.js.map +1 -1
  111. package/esm/data/table/column-header/useTableColumnResize.d.ts +19 -29
  112. package/esm/data/table/column-header/useTableColumnResize.js +24 -22
  113. package/esm/data/table/column-header/useTableColumnResize.js.map +1 -1
  114. package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.d.ts +1 -1
  115. package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.js +2 -2
  116. package/esm/data/table/details-panel-row/DataTableDetailsPanelRow.js.map +1 -1
  117. package/esm/data/table/helpers/collectTableRowEntries.d.ts +9 -7
  118. package/esm/data/table/helpers/collectTableRowEntries.js +18 -10
  119. package/esm/data/table/helpers/collectTableRowEntries.js.map +1 -1
  120. package/esm/data/table/helpers/selection/getMultipleSelectProps.d.ts +13 -11
  121. package/esm/data/table/helpers/selection/getMultipleSelectProps.js +43 -53
  122. package/esm/data/table/helpers/selection/getMultipleSelectProps.js.map +1 -1
  123. package/esm/data/table/helpers/selection/getSingleSelectProps.d.ts +9 -8
  124. package/esm/data/table/helpers/selection/getSingleSelectProps.js +23 -10
  125. package/esm/data/table/helpers/selection/getSingleSelectProps.js.map +1 -1
  126. package/esm/data/table/helpers/selection/selection.types.d.ts +19 -19
  127. package/esm/data/table/helpers/selection/selection.utils.d.ts +21 -0
  128. package/esm/data/table/helpers/selection/selection.utils.js +43 -0
  129. package/esm/data/table/helpers/selection/selection.utils.js.map +1 -0
  130. package/esm/data/table/helpers/table-focus.d.ts +0 -3
  131. package/esm/data/table/helpers/table-focus.js +38 -8
  132. package/esm/data/table/helpers/table-focus.js.map +1 -1
  133. package/esm/data/table/hooks/useColumnOptions.d.ts +16 -5
  134. package/esm/data/table/hooks/useColumnOptions.js +26 -8
  135. package/esm/data/table/hooks/useColumnOptions.js.map +1 -1
  136. package/esm/data/table/hooks/useGridCache.js +2 -2
  137. package/esm/data/table/hooks/useGridCache.js.map +1 -1
  138. package/esm/data/table/hooks/useTableDetailsPanel.d.ts +10 -13
  139. package/esm/data/table/hooks/useTableDetailsPanel.js +7 -6
  140. package/esm/data/table/hooks/useTableDetailsPanel.js.map +1 -1
  141. package/esm/data/table/hooks/useTableItems.d.ts +31 -17
  142. package/esm/data/table/hooks/useTableItems.js +11 -18
  143. package/esm/data/table/hooks/useTableItems.js.map +1 -1
  144. package/esm/data/table/hooks/useTableKeyboardNav.d.ts +1 -6
  145. package/esm/data/table/hooks/useTableKeyboardNav.js +7 -6
  146. package/esm/data/table/hooks/useTableKeyboardNav.js.map +1 -1
  147. package/esm/data/table/hooks/useTableSelection.d.ts +6 -6
  148. package/esm/data/table/hooks/useTableSelection.js +13 -13
  149. package/esm/data/table/hooks/useTableSelection.js.map +1 -1
  150. package/esm/data/table/hooks/useTableSort.d.ts +2 -2
  151. package/esm/data/table/hooks/useTableSort.js +4 -5
  152. package/esm/data/table/hooks/useTableSort.js.map +1 -1
  153. package/esm/data/table/root/DataTable.types.d.ts +22 -13
  154. package/esm/data/table/root/DataTableRoot.context.d.ts +13 -7
  155. package/esm/data/table/root/DataTableRoot.context.js.map +1 -1
  156. package/esm/data/table/root/DataTableRoot.d.ts +49 -72
  157. package/esm/data/table/root/DataTableRoot.js +58 -74
  158. package/esm/data/table/root/DataTableRoot.js.map +1 -1
  159. package/esm/data/table/root/DataTableRoot.legacy.d.ts +2 -7
  160. package/esm/data/table/root/DataTableRoot.legacy.js +17 -3
  161. package/esm/data/table/root/DataTableRoot.legacy.js.map +1 -1
  162. package/esm/data/table/sub-row-toggle/DataTableSubRowToggle.js +4 -4
  163. package/esm/data/table/sub-row-toggle/DataTableSubRowToggle.js.map +1 -1
  164. package/esm/data/table/tbody/DataTableTbody.js +4 -2
  165. package/esm/data/table/tbody/DataTableTbody.js.map +1 -1
  166. package/esm/data/table/tr/DataTableTr.d.ts +5 -3
  167. package/esm/data/table/tr/DataTableTr.js +35 -23
  168. package/esm/data/table/tr/DataTableTr.js.map +1 -1
  169. package/esm/table/ColumnHeader.js +2 -1
  170. package/esm/table/ColumnHeader.js.map +1 -1
  171. package/package.json +8 -7
  172. package/src/data/data-grid/index.ts +3 -0
  173. package/src/data/data-grid/root/DataGridRoot.context.ts +16 -0
  174. package/src/data/data-grid/root/DataGridRoot.tsx +71 -0
  175. package/src/data/drag-and-drop/drag-handler/DragAndDropDragHandler.tsx +11 -17
  176. package/src/data/drag-and-drop/root/DragAndDrop.context.tsx +4 -2
  177. package/src/data/drag-and-drop/root/DragAndDropRoot.tsx +63 -52
  178. package/src/data/drag-and-drop/types.ts +0 -5
  179. package/src/data/stories/Data.test-data.tsx +52 -44
  180. package/src/data/table/agent-component-review.md +175 -0
  181. package/src/data/table/base-cell/DataTableBaseCell.tsx +31 -21
  182. package/src/data/table/column-header/DataTableColumnHeader.tsx +63 -58
  183. package/src/data/table/column-header/useTableColumnResize.ts +55 -71
  184. package/src/data/table/details-panel-row/DataTableDetailsPanelRow.tsx +7 -3
  185. package/src/data/table/helpers/collectTableRowEntries.ts +32 -19
  186. package/src/data/table/helpers/selection/getMultipleSelectProps.ts +65 -85
  187. package/src/data/table/helpers/selection/getSingleSelectProps.ts +35 -17
  188. package/src/data/table/helpers/selection/selection.types.ts +19 -19
  189. package/src/data/table/helpers/selection/selection.utils.test.ts +161 -0
  190. package/src/data/table/helpers/selection/selection.utils.ts +73 -0
  191. package/src/data/table/helpers/table-focus.ts +63 -9
  192. package/src/data/table/hooks/__tests__/useTableItems.test.ts +48 -8
  193. package/src/data/table/hooks/useColumnOptions.ts +48 -14
  194. package/src/data/table/hooks/useGridCache.ts +3 -2
  195. package/src/data/table/hooks/useTableDetailsPanel.tsx +25 -25
  196. package/src/data/table/hooks/useTableItems.ts +51 -42
  197. package/src/data/table/hooks/useTableKeyboardNav.ts +7 -15
  198. package/src/data/table/hooks/useTableSelection.ts +26 -31
  199. package/src/data/table/hooks/useTableSort.ts +10 -9
  200. package/src/data/table/root/DataTable.types.ts +30 -25
  201. package/src/data/table/root/DataTableRoot.context.ts +19 -7
  202. package/src/data/table/root/DataTableRoot.legacy.tsx +22 -14
  203. package/src/data/table/root/DataTableRoot.tsx +271 -320
  204. package/src/data/table/sub-row-toggle/DataTableSubRowToggle.tsx +5 -4
  205. package/src/data/table/tbody/DataTableTbody.tsx +6 -2
  206. package/src/data/table/tr/DataTableTr.tsx +98 -35
  207. package/src/table/ColumnHeader.tsx +2 -1
  208. package/cjs/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.d.ts +0 -22
  209. package/cjs/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.js +0 -35
  210. package/cjs/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.js.map +0 -1
  211. package/cjs/data/drag-and-drop-old/item/DataDragAndDropItem.d.ts +0 -27
  212. package/cjs/data/drag-and-drop-old/item/DataDragAndDropItem.js +0 -86
  213. package/cjs/data/drag-and-drop-old/item/DataDragAndDropItem.js.map +0 -1
  214. package/cjs/data/drag-and-drop-old/root/DataDragAndDrop.context.d.ts +0 -5
  215. package/cjs/data/drag-and-drop-old/root/DataDragAndDrop.context.js +0 -6
  216. package/cjs/data/drag-and-drop-old/root/DataDragAndDrop.context.js.map +0 -1
  217. package/cjs/data/drag-and-drop-old/root/DataDragAndDropRoot.d.ts +0 -24
  218. package/cjs/data/drag-and-drop-old/root/DataDragAndDropRoot.js +0 -108
  219. package/cjs/data/drag-and-drop-old/root/DataDragAndDropRoot.js.map +0 -1
  220. package/cjs/data/table/helpers/selection/SelectionSubtreeHelper.d.ts +0 -46
  221. package/cjs/data/table/helpers/selection/SelectionSubtreeHelper.js +0 -112
  222. package/cjs/data/table/helpers/selection/SelectionSubtreeHelper.js.map +0 -1
  223. package/esm/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.d.ts +0 -22
  224. package/esm/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.js +0 -29
  225. package/esm/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.js.map +0 -1
  226. package/esm/data/drag-and-drop-old/item/DataDragAndDropItem.d.ts +0 -27
  227. package/esm/data/drag-and-drop-old/item/DataDragAndDropItem.js +0 -50
  228. package/esm/data/drag-and-drop-old/item/DataDragAndDropItem.js.map +0 -1
  229. package/esm/data/drag-and-drop-old/root/DataDragAndDrop.context.d.ts +0 -5
  230. package/esm/data/drag-and-drop-old/root/DataDragAndDrop.context.js +0 -3
  231. package/esm/data/drag-and-drop-old/root/DataDragAndDrop.context.js.map +0 -1
  232. package/esm/data/drag-and-drop-old/root/DataDragAndDropRoot.d.ts +0 -24
  233. package/esm/data/drag-and-drop-old/root/DataDragAndDropRoot.js +0 -68
  234. package/esm/data/drag-and-drop-old/root/DataDragAndDropRoot.js.map +0 -1
  235. package/esm/data/table/helpers/selection/SelectionSubtreeHelper.d.ts +0 -46
  236. package/esm/data/table/helpers/selection/SelectionSubtreeHelper.js +0 -109
  237. package/esm/data/table/helpers/selection/SelectionSubtreeHelper.js.map +0 -1
  238. package/src/data/drag-and-drop-old/drag-handler/DataDragAndDropDragHandler.tsx +0 -104
  239. package/src/data/drag-and-drop-old/item/DataDragAndDropItem.tsx +0 -74
  240. package/src/data/drag-and-drop-old/root/DataDragAndDrop.context.tsx +0 -11
  241. package/src/data/drag-and-drop-old/root/DataDragAndDropRoot.tsx +0 -96
  242. package/src/data/table/helpers/selection/SelectionSubtreeHelper.test.ts +0 -66
  243. package/src/data/table/helpers/selection/SelectionSubtreeHelper.ts +0 -162
  244. package/src/data/table/hooks/__tests__/useTableSelection.test.ts +0 -488
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, useRef } from "react";
1
+ import React, { forwardRef, useMemo, useRef } from "react";
2
2
  import {
3
3
  ArrowsUpDownIcon,
4
4
  CaretLeftCircleFillIcon,
@@ -13,33 +13,51 @@ import {
13
13
  type DataTableBaseCellProps,
14
14
  } from "../base-cell/DataTableBaseCell";
15
15
  import type { SortDirection } from "../root/DataTable.types";
16
+ import { useDataTableContext } from "../root/DataTableRoot.context";
16
17
  import { type ResizeProps, useTableColumnResize } from "./useTableColumnResize";
17
18
 
18
- interface DataTableColumnHeaderProps
19
- extends ResizeProps, DataTableBaseCellProps {
19
+ interface DataTableColumnHeaderProps extends DataTableBaseCellProps {
20
+ /**
21
+ * Unique identifier for the column. Required for sortable columns to identify which column is being sorted.
22
+ */
23
+ id?: string;
20
24
  /**
21
25
  * Accessible name of the column.
22
26
  */
23
27
  label: string;
24
28
  /**
25
- * Makes the column header sortable. The entire header cell content becomes
26
- * a clickable button when true.
29
+ * Makes the column sortable by clicking on the header.
30
+ * The entire header cell content becomes a clickable button when true.
27
31
  */
28
- sortable?: boolean;
32
+ sortable?: boolean; // TODO: Consider merging sortable, sortDirection and onSortClick into a single "sort" object prop
29
33
  /**
30
34
  * Current sort direction. Only relevant when `sortable` is true.
31
- * Uses values matching the `aria-sort` attribute directly.
35
+ * Uses values matching the `aria-sort` attribute directly. // TODO: What does this mean? (Can we just remove it?)
32
36
  * @default "none"
33
37
  */
34
- sortDirection?: SortDirection;
38
+ sortDirection?: SortDirection; // TODO Not in use???
39
+ /**
40
+ * Called when the user clicks the header. Only relevant when `sortable` is true.
41
+ * The consumer is responsible for determining and setting the next sort state. // TODO: We don't use the term "consumer" in JSDoc anywhere else
42
+ */
43
+ onSortClick?: (event: React.MouseEvent<HTMLElement>) => void; // TODO Not in use???
35
44
  /**
36
- * Called when the user clicks the sortable header.
37
- * The consumer is responsible for determining and setting the next sort state.
45
+ * Object with props related to column width and resizing. Summary:
46
+ *
47
+ * - `resizable?: boolean` - Whether the column should be resizable by the user.
48
+ * - `autoResizeOnce?: boolean` - Whether the column should automatically resize to fit its content.
49
+ * - `resizeMin?: number` - Minimum width of the column when resizing.
50
+ * - `resizeMax?: number` - Maximum width of the column when resizing.
51
+ * - `value?: number | string` - Controlled width of the column.
52
+ * - `default?: number | string` - Initial width of the column.
53
+ * - `onChange?: (width: number) => void` - Called when the column width changes.
54
+ *
55
+ * See individual props for details and defaults.
38
56
  */
39
- onSortClick?: (event: React.MouseEvent<HTMLElement>) => void;
57
+ width?: ResizeProps;
40
58
  }
41
59
 
42
- const SORT_ICON: Record<SortDirection, React.ElementType | null> = {
60
+ const SORT_ICON: Record<SortDirection, React.ElementType> = {
43
61
  asc: SortUpIcon,
44
62
  desc: SortDownIcon,
45
63
  none: ArrowsUpDownIcon,
@@ -56,45 +74,37 @@ const DataTableColumnHeader = forwardRef<
56
74
  >(
57
75
  (
58
76
  {
59
- className,
60
- children,
77
+ id,
61
78
  label,
62
79
  sortable = false,
63
- sortDirection = "none",
64
- onSortClick,
65
- resizable = true,
66
- style,
67
80
  width,
68
- defaultWidth,
69
- autoWidth,
70
- minWidth,
71
- maxWidth,
72
- onWidthChange,
73
- colSpan,
74
- rowSpan,
75
- UNSAFE_isSelection,
81
+ cellType,
82
+ className,
83
+ children,
84
+ style,
76
85
  ...rest
77
86
  },
78
87
  forwardedRef,
79
88
  ) => {
80
- const contentRef = React.useRef<HTMLDivElement>(null);
81
89
  const thRef = useRef<HTMLTableCellElement>(null);
82
90
  const mergedRef = useMergeRefs(forwardedRef, thRef);
91
+ const { sortingState } = useDataTableContext();
92
+ const { onSortClick, sortState } = sortingState;
83
93
 
84
94
  const resizeResult = useTableColumnResize({
85
- resizable,
95
+ ...width,
86
96
  thRef,
87
- width,
88
- defaultWidth,
89
- autoWidth,
90
- minWidth,
91
- maxWidth,
92
- onWidthChange,
93
- style,
94
- colSpan,
97
+ colSpan: rest.colSpan,
95
98
  });
96
99
 
97
- const SortIcon = sortable ? SORT_ICON[sortDirection] : null;
100
+ const sortDirection = useMemo(() => {
101
+ const sortEntry = sortState.find((s) => s.columnId === id);
102
+ return sortEntry?.direction ?? "none";
103
+ }, [id, sortState]);
104
+
105
+ const canSort = sortable && id !== undefined;
106
+
107
+ const SortIcon = canSort ? SORT_ICON[sortDirection] : null;
98
108
 
99
109
  return (
100
110
  <DataTableBaseCell
@@ -102,21 +112,18 @@ const DataTableColumnHeader = forwardRef<
102
112
  {...rest}
103
113
  ref={mergedRef}
104
114
  className={cl("aksel-data-table__column-header", className)}
105
- data-sortable={sortable}
106
- style={resizeResult.style}
107
- aria-sort={sortable ? getAriaSort(sortDirection) : undefined}
108
- UNSAFE_isSelection={UNSAFE_isSelection}
109
- colSpan={colSpan}
110
- rowSpan={rowSpan}
115
+ data-sortable={canSort}
116
+ style={{ ...style, width: resizeResult.width }}
117
+ aria-sort={canSort ? getAriaSort(sortDirection) : undefined}
118
+ cellType={cellType}
111
119
  >
112
- {sortable ? (
120
+ {canSort ? (
113
121
  <button
122
+ type="button"
114
123
  className="aksel-data-table__th-sort-button"
115
- onClick={onSortClick}
124
+ onClick={(event) => onSortClick(id, event)}
116
125
  >
117
- <div ref={contentRef} className="aksel-data-table__th-content">
118
- {children}
119
- </div>
126
+ <div className="aksel-data-table__th-content">{children}</div>
120
127
  {SortIcon && (
121
128
  <SortIcon
122
129
  aria-hidden
@@ -128,18 +135,18 @@ const DataTableColumnHeader = forwardRef<
128
135
  </button>
129
136
  ) : (
130
137
  <div
131
- ref={contentRef}
132
138
  className={cl({
133
- "aksel-data-table__th-content": !UNSAFE_isSelection,
139
+ "aksel-data-table__th-content": cellType !== "action",
134
140
  })}
135
141
  >
136
142
  {children}
137
143
  </div>
138
144
  )}
139
145
 
140
- {resizeResult.enabled && !UNSAFE_isSelection && (
146
+ {resizeResult.enabled && cellType !== "action" && (
141
147
  <button
142
148
  {...resizeResult.resizeHandlerProps}
149
+ type="button"
143
150
  className="aksel-data-table__th-resize-handle"
144
151
  aria-label={
145
152
  resizeResult.isResizingWithKeyboard
@@ -151,23 +158,21 @@ const DataTableColumnHeader = forwardRef<
151
158
  data-block-keyboard-nav
152
159
  role="slider"
153
160
  aria-valuenow={
154
- typeof resizeResult.style.width === "number"
155
- ? resizeResult.style.width
156
- : 0
161
+ typeof resizeResult.width === "number" ? resizeResult.width : 0
157
162
  }
158
163
  aria-valuetext={
159
- typeof resizeResult.style.width === "number" &&
164
+ typeof resizeResult.width === "number" &&
160
165
  resizeResult.isResizingWithKeyboard
161
- ? resizeResult.style.width.toString()
166
+ ? resizeResult.width.toString()
162
167
  : "" // Needs to be blank when not in keyboard resizing mode to avoid NVDA announcing the value as part of the column heading
163
168
  } // Need either this or aria-valuemax to get SR (at least NVDA) to announce the value
164
169
  >
165
170
  {resizeResult.isResizingWithKeyboard && (
166
171
  <>
167
- <span className="aksel-data-table__th-resize-handle-indicator aksel-data-table__th-resize-handle-indicator--start">
172
+ <span className="aksel-data-table__th-resize-handle-indicator">
168
173
  <CaretLeftCircleFillIcon aria-hidden fontSize="1.5rem" />
169
174
  </span>
170
- <span className="aksel-data-table__th-resize-handle-indicator aksel-data-table__th-resize-handle-indicator--end">
175
+ <span className="aksel-data-table__th-resize-handle-indicator">
171
176
  <CaretRightCircleFillIcon aria-hidden fontSize="1.5rem" />
172
177
  </span>
173
178
  </>
@@ -26,61 +26,51 @@ type ResizeProps = {
26
26
  * consider using `layout="auto"` on the root instead for better performance.
27
27
  *
28
28
  * **NB:** This can cause a layout shift. Set a good initial width with `width` or `defaultWidth` to mitigate this.
29
+ *
30
+ * **NB:** Does not work with block content.
29
31
  */
30
- autoWidth?: boolean;
32
+ autoResizeOnce?: boolean;
31
33
  /**
32
- * Minimum width of the column when resizing. Only used when `resizable` or `autoWidth` is enabled.
34
+ * Minimum width of the column when resizing. Only used when `resizable` or `autoResizeOnce` is enabled.
33
35
  * @default 40
34
36
  */
35
- minWidth?: number;
37
+ resizeMin?: number;
36
38
  /**
37
- * Maximum width of the column when resizing. Only used when `resizable` or `autoWidth` is enabled.
39
+ * Maximum width of the column when resizing. Only used when `resizable` or `autoResizeOnce` is enabled.
38
40
  */
39
- maxWidth?: number;
41
+ resizeMax?: number;
40
42
  // TODO: Consider "allowing" %-width on last column, if we find a solution to the overflow issue (width becomes 0px).
41
43
  /**
42
- * Controlled width of the column. Does not respect `minWidth` and `maxWidth`.
44
+ * Controlled width of the column. (Does not respect `resizeMin` and `resizeMax`.)
43
45
  *
44
- * Should only be used to fully control column width state. Otherwise, use `defaultWidth` and let the component handle resizing.
46
+ * Should only be used to fully control column width state. Otherwise, use `default` and let the component handle resizing.
45
47
  *
46
48
  * **NB:** Percentage as initial width does not work well with resizing.
47
49
  */
48
- width?: number | string;
50
+ value?: number | string;
49
51
  /**
50
- * Initial width of the column. Only used when `width` is not set and `resizable` is true.
51
- * Does not respect `minWidth` and `maxWidth`.
52
+ * Initial width of the column. Only used when `value` is not set.
53
+ * (Does not respect `resizeMin` and `resizeMax`.)
52
54
  *
53
55
  * **NB:** Percentage as initial width does not work well with resizing.
54
56
  * @default 140px
55
57
  */
56
- defaultWidth?: number | string;
58
+ default?: number | string;
57
59
  /**
58
60
  * Called when the column width changes.
59
61
  * @param width New width in pixels.
60
62
  */
61
- onWidthChange?: (width: number) => void;
62
- /**
63
- * Forwarded styles
64
- */
65
- style?: React.CSSProperties;
66
- /**
67
- * Forwarded colSpan
68
- */
69
- colSpan?: number;
70
- };
71
-
72
- type WithUndefined<T> = {
73
- [K in keyof T]: T[K] | undefined;
63
+ onChange?: (width: number) => void;
74
64
  };
75
- type Unomittable<T> = WithUndefined<Required<T>>;
76
65
 
77
- type TableColumnResizeArgs = Unomittable<ResizeProps> & {
66
+ type TableColumnResizeArgs = ResizeProps & {
78
67
  thRef: React.RefObject<HTMLTableCellElement | null>;
68
+ colSpan: number | undefined;
79
69
  };
80
70
 
81
71
  type TableColumnResizeResult =
82
72
  | {
83
- style: React.CSSProperties;
73
+ width: number | string;
84
74
  resizeHandlerProps: {
85
75
  onMouseDown: DOMAttributes<HTMLButtonElement>["onMouseDown"];
86
76
  onTouchStart: DOMAttributes<HTMLButtonElement>["onTouchStart"];
@@ -93,59 +83,53 @@ type TableColumnResizeResult =
93
83
  enabled: true;
94
84
  }
95
85
  | {
96
- style?: React.CSSProperties;
86
+ width?: number | string;
97
87
  enabled: false;
98
88
  };
99
89
 
100
90
  /**
101
91
  * TODO:
102
- * - Do we allow % widths?
103
92
  * - Auto-width mode is hard now since that might cause layout-shifts on mount. But would be preferable to
104
93
  * be able to set "1fr" or similar and have it fill remaining space.
105
94
  */
106
- function useTableColumnResize(
107
- args: TableColumnResizeArgs,
108
- ): TableColumnResizeResult {
109
- const {
110
- resizable,
111
- thRef,
112
- width: userWidth,
113
- defaultWidth,
114
- autoWidth,
115
- onWidthChange,
116
- maxWidth = Infinity,
117
- minWidth = 40,
118
- style,
119
- colSpan,
120
- } = args;
121
-
95
+ function useTableColumnResize({
96
+ resizable = true,
97
+ autoResizeOnce,
98
+ resizeMin = 40,
99
+ resizeMax = Infinity,
100
+ value,
101
+ default: defaultProp,
102
+ onChange,
103
+ thRef,
104
+ colSpan,
105
+ }: TableColumnResizeArgs): TableColumnResizeResult {
122
106
  const tableContext = useDataTableContext();
123
107
 
124
108
  const [isResizingWithKeyboard, setIsResizingWithKeyboard] = useState(false);
125
109
  const ignoreNextOnClick = useRef(false);
126
110
 
127
111
  const [width, setWidth] = useControllableState({
128
- value: userWidth,
129
- defaultValue: defaultWidth ?? (colSpan ?? 1) * 140,
112
+ value,
113
+ defaultValue: defaultProp ?? (colSpan ?? 1) * 140,
130
114
  /**
131
115
  * TODO:
132
116
  * - Potential optimization: Only call when width as "stopped" changing, e.g. on mouse up or after a debounce when resizing with keyboard.
133
117
  * Otherwise, this could cause excessive calls when resizing quickly.
134
118
  */
135
- onChange: onWidthChange,
119
+ onChange,
136
120
  });
137
121
 
138
122
  const setClampedWidth = useCallback(
139
123
  (newWidth: number) => {
140
- setWidth(Math.min(Math.max(newWidth, minWidth), maxWidth));
124
+ setWidth(Math.min(Math.max(newWidth, resizeMin), resizeMax));
141
125
  },
142
- [minWidth, maxWidth, setWidth],
126
+ [resizeMin, resizeMax, setWidth],
143
127
  );
144
128
 
145
- // biome-ignore lint/correctness/useExhaustiveDependencies: We only want to run this on mount and when autoWidth changes
129
+ // biome-ignore lint/correctness/useExhaustiveDependencies: We only want to run this on mount and when autoResizeOnce changes
146
130
  useEffect(
147
131
  function autoResizeColumn() {
148
- if (!autoWidth) {
132
+ if (!autoResizeOnce) {
149
133
  return;
150
134
  }
151
135
 
@@ -154,20 +138,19 @@ function useTableColumnResize(
154
138
  setClampedWidth(newColumnWidth);
155
139
  }
156
140
  },
157
- [autoWidth], // eslint-disable-line react-hooks/exhaustive-deps
141
+ [autoResizeOnce], // eslint-disable-line react-hooks/exhaustive-deps
158
142
  );
159
143
 
160
144
  const handleOnClick: DOMAttributes<HTMLButtonElement>["onClick"] =
161
145
  useCallback(() => {
162
146
  // We need to use the onClick event in order to support screen readers properly,
163
- // since some of them only send a mouse click when pressing enter/space.
147
+ // since some of them only send a mouse click (no kbd events) when pressing enter/space.
164
148
  // We detect a "screen reader click" by checking if we had a mouseUp event right before.
165
149
 
166
150
  if (ignoreNextOnClick.current) {
167
151
  ignoreNextOnClick.current = false;
168
152
  return;
169
153
  }
170
-
171
154
  setIsResizingWithKeyboard((prev) => !prev);
172
155
  }, []);
173
156
 
@@ -213,11 +196,11 @@ function useTableColumnResize(
213
196
  const currentWidth = thRef.current?.offsetWidth ?? 0;
214
197
  const newWidth = startWidth + (clientX - startX);
215
198
 
216
- if (newWidth > maxWidth) {
199
+ if (newWidth > resizeMax) {
217
200
  setWidth(newWidth < currentWidth ? newWidth : currentWidth);
218
201
  return;
219
202
  }
220
- if (newWidth < minWidth) {
203
+ if (newWidth < resizeMin) {
221
204
  setWidth(newWidth > currentWidth ? newWidth : currentWidth);
222
205
  return;
223
206
  }
@@ -250,7 +233,7 @@ function useTableColumnResize(
250
233
  document.addEventListener("touchend", cleanup, { once: true });
251
234
  document.addEventListener("touchcancel", cleanup, { once: true });
252
235
  },
253
- [maxWidth, minWidth, setWidth, setClampedWidth, thRef],
236
+ [resizeMax, resizeMin, setWidth, setClampedWidth, thRef],
254
237
  );
255
238
 
256
239
  const handleMouseDown: DOMAttributes<HTMLButtonElement>["onMouseDown"] =
@@ -269,7 +252,7 @@ function useTableColumnResize(
269
252
  [startResize],
270
253
  );
271
254
 
272
- // Auto-size column to fit content on double click. NB: Doesn't work with block content!
255
+ // Auto-size column to fit content on double click
273
256
  const handleDoubleClick: DOMAttributes<HTMLButtonElement>["onDoubleClick"] =
274
257
  useCallback(() => {
275
258
  const newColumnWidth = getAutoColumnWidth(thRef);
@@ -280,26 +263,20 @@ function useTableColumnResize(
280
263
 
281
264
  if (tableContext.layout !== "fixed") {
282
265
  return {
283
- style,
284
266
  enabled: false,
285
267
  };
286
268
  }
287
269
 
288
270
  if (!resizable) {
289
271
  return {
290
- style: {
291
- ...style,
292
- width,
293
- },
272
+ width,
273
+
294
274
  enabled: false,
295
275
  };
296
276
  }
297
277
 
298
278
  return {
299
- style: {
300
- ...style,
301
- width,
302
- },
279
+ width,
303
280
  resizeHandlerProps: {
304
281
  onMouseDown: handleMouseDown,
305
282
  onTouchStart: handleTouchStart,
@@ -313,12 +290,16 @@ function useTableColumnResize(
313
290
  };
314
291
  }
315
292
 
293
+ /**
294
+ * Figures out how wide the column needs to be to fit all the content without truncation.
295
+ * NB: Does not work with block content!
296
+ */
316
297
  function getAutoColumnWidth(
317
298
  thRef: React.RefObject<HTMLTableCellElement | null>,
318
299
  ) {
319
300
  const th = thRef.current!;
320
301
  const thContent = th.querySelector(".aksel-data-table__th-content");
321
- const thPaddingEl = th.querySelector("div");
302
+ const thPaddingEl = th.querySelector(".aksel-data-table__cell-content");
322
303
  const rows = th.closest("table")?.querySelectorAll("tbody tr, tfoot tr");
323
304
  if (!thContent || !thPaddingEl || !rows) {
324
305
  return;
@@ -361,15 +342,18 @@ function getAutoColumnWidth(
361
342
  skipRows = cell.rowSpan - 1;
362
343
 
363
344
  // Find needed width
364
- const cellContent = cell.firstChild as HTMLElement;
345
+ const cellContent = cell.querySelector(
346
+ ".aksel-data-table__cell-content",
347
+ ) as HTMLElement;
365
348
  range.selectNodeContents(cellContent);
366
349
  const cellContentWidth = range.getBoundingClientRect().width;
367
350
  const contentElStyle = window.getComputedStyle(cellContent);
368
351
  const inlinePadding =
369
352
  parseInt(contentElStyle.paddingLeft, 10) +
370
353
  parseInt(contentElStyle.paddingRight, 10);
354
+ const marginLeft = parseInt(contentElStyle.marginLeft, 10); // We don't have right margin for now
371
355
  const widthNeededForThisCell =
372
- (cellContentWidth + inlinePadding) / cell.colSpan;
356
+ (cellContentWidth + inlinePadding + marginLeft) / cell.colSpan;
373
357
  if (widthNeededForThisCell > newColumnWidth) {
374
358
  newColumnWidth = widthNeededForThisCell;
375
359
  }
@@ -9,10 +9,10 @@ function DataTableDetailsPanelRow<T>({
9
9
  rowId,
10
10
  rowData,
11
11
  }: {
12
- rowId: string | number;
12
+ rowId: string;
13
13
  rowData: T;
14
14
  }) {
15
- const { tableId, fullWidthColSpan } = useDataTableContext();
15
+ const { tableId, totalColSpan } = useDataTableContext();
16
16
  const {
17
17
  enableDetailsPanel,
18
18
  isExpanded,
@@ -43,7 +43,11 @@ function DataTableDetailsPanelRow<T>({
43
43
 
44
44
  return (
45
45
  <tr className="aksel-data-table__details-panel-row">
46
- <td id={expansionId} colSpan={fullWidthColSpan}>
46
+ <td
47
+ id={expansionId}
48
+ colSpan={totalColSpan}
49
+ className="aksel-data-table__details-panel-row-cell"
50
+ >
47
51
  <div style={style}>{content}</div>
48
52
  </td>
49
53
  </tr>
@@ -1,21 +1,23 @@
1
- type TableRowEntryId = string | number;
1
+ import type { TableRowEntryId } from "../root/DataTable.types";
2
2
 
3
3
  type CollectTableRowEntriesArgs<T> = {
4
4
  items: T[];
5
- getRowId: (rowData: T, index: number) => TableRowEntryId;
5
+ getRowId?: (rowData: T, index: number) => TableRowEntryId;
6
6
  getRows?: (rowData: T) => T[];
7
7
  isRowExpandable?: (rowData: T) => boolean;
8
8
  };
9
9
 
10
10
  interface ItemDetail<T> {
11
- id: string | number;
11
+ id: TableRowEntryId;
12
+ rowData: T;
12
13
  level: number;
13
- parent: null | T;
14
- children: readonly T[];
14
+ parentId: TableRowEntryId | null;
15
+ children: readonly TableRowEntryId[];
15
16
  }
16
17
 
17
18
  type CollectTableRowEntriesReturn<T> = {
18
- itemDetails: Map<T, ItemDetail<T>>;
19
+ itemDetails: Map<TableRowEntryId, ItemDetail<T>>;
20
+ rootRowIds: TableRowEntryId[];
19
21
  /**
20
22
  * Direct child ids for each row, used to traverse nested selection groups
21
23
  * without storing every descendant list on each ancestor.
@@ -29,54 +31,65 @@ function collectTableRowEntries<T>({
29
31
  getRows,
30
32
  isRowExpandable,
31
33
  }: CollectTableRowEntriesArgs<T>): CollectTableRowEntriesReturn<T> {
32
- const itemDetailsMap = new Map<T, ItemDetail<T>>();
34
+ const itemDetailsMap = new Map<TableRowEntryId, ItemDetail<T>>();
33
35
  const childRowIdsById = new Map<TableRowEntryId, TableRowEntryId[]>();
36
+ const rootRowIds: TableRowEntryId[] = [];
34
37
 
35
38
  const traverseRow = (
36
39
  rowData: T,
37
40
  rowIndex: number,
38
41
  level: number,
39
- parent: T | null,
42
+ parentRowId: TableRowEntryId | null,
40
43
  ): TableRowEntryId => {
41
- const rowId = getRowId(rowData, rowIndex);
44
+ const rowId = getRowId
45
+ ? getRowId(rowData, rowIndex)
46
+ : getFallbackTableRowId(rowIndex, parentRowId);
42
47
 
43
48
  const children =
44
49
  ((isRowExpandable?.(rowData) ?? true) ? getRows?.(rowData) : []) ?? [];
45
50
 
46
- itemDetailsMap.set(rowData, {
47
- id: rowId,
48
- level,
49
- parent,
50
- children,
51
- });
52
-
53
51
  const childRowIds: TableRowEntryId[] = [];
54
52
 
55
53
  for (let childIndex = 0; childIndex < children.length; childIndex++) {
56
54
  const childRow = children[childIndex];
57
- const childRowId = traverseRow(childRow, childIndex, level + 1, rowData);
55
+ const childRowId = traverseRow(childRow, childIndex, level + 1, rowId);
58
56
  childRowIds.push(childRowId);
59
57
  }
60
58
 
59
+ itemDetailsMap.set(rowId, {
60
+ id: rowId,
61
+ rowData,
62
+ level,
63
+ parentId: parentRowId,
64
+ children: childRowIds,
65
+ });
66
+
61
67
  childRowIdsById.set(rowId, childRowIds);
62
68
 
63
69
  return rowId;
64
70
  };
65
71
 
66
72
  for (let rowIndex = 0; rowIndex < items.length; rowIndex++) {
67
- traverseRow(items[rowIndex], rowIndex, 0, null);
73
+ rootRowIds.push(traverseRow(items[rowIndex], rowIndex, 0, null));
68
74
  }
69
75
 
70
76
  return {
71
77
  itemDetails: itemDetailsMap,
78
+ rootRowIds,
72
79
  childRowIdsById,
73
80
  };
74
81
  }
75
82
 
83
+ function getFallbackTableRowId(
84
+ rowIndex: number,
85
+ parentRowId: TableRowEntryId | null,
86
+ ): string {
87
+ return parentRowId == null ? String(rowIndex) : `${parentRowId}.${rowIndex}`;
88
+ }
89
+
76
90
  export { collectTableRowEntries };
77
91
  export type {
78
92
  CollectTableRowEntriesArgs,
79
93
  CollectTableRowEntriesReturn,
80
- TableRowEntryId,
81
94
  ItemDetail,
82
95
  };