@navikt/ds-react 8.5.2 → 8.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/cjs/data/drag-and-drop/item/DataDragAndDropItem.d.ts +27 -0
  2. package/cjs/data/drag-and-drop/item/DataDragAndDropItem.js +91 -0
  3. package/cjs/data/drag-and-drop/item/DataDragAndDropItem.js.map +1 -0
  4. package/cjs/data/drag-and-drop/root/DataDragAndDrop.context.d.ts +5 -0
  5. package/cjs/data/drag-and-drop/root/DataDragAndDrop.context.js +6 -0
  6. package/cjs/data/drag-and-drop/root/DataDragAndDrop.context.js.map +1 -0
  7. package/cjs/data/drag-and-drop/root/DataDragAndDropRoot.d.ts +24 -0
  8. package/cjs/data/drag-and-drop/root/DataDragAndDropRoot.js +111 -0
  9. package/cjs/data/drag-and-drop/root/DataDragAndDropRoot.js.map +1 -0
  10. package/cjs/data/table/helpers/table-keyboard.d.ts +1 -0
  11. package/cjs/data/table/helpers/table-keyboard.js +5 -3
  12. package/cjs/data/table/helpers/table-keyboard.js.map +1 -1
  13. package/cjs/data/table/root/DataTableRoot.context.d.ts +8 -0
  14. package/cjs/data/table/root/DataTableRoot.context.js +11 -0
  15. package/cjs/data/table/root/DataTableRoot.context.js.map +1 -0
  16. package/cjs/data/table/root/DataTableRoot.js +5 -3
  17. package/cjs/data/table/root/DataTableRoot.js.map +1 -1
  18. package/cjs/data/table/th/DataTableTh.d.ts +18 -2
  19. package/cjs/data/table/th/DataTableTh.js +45 -20
  20. package/cjs/data/table/th/DataTableTh.js.map +1 -1
  21. package/cjs/data/table/tr/DataTableTr.js +9 -2
  22. package/cjs/data/table/tr/DataTableTr.js.map +1 -1
  23. package/cjs/data/token-filter/AutoSuggest.d.ts +6 -2
  24. package/cjs/data/token-filter/AutoSuggest.js +46 -14
  25. package/cjs/data/token-filter/AutoSuggest.js.map +1 -1
  26. package/cjs/data/token-filter/AutoSuggest.types.d.ts +0 -1
  27. package/cjs/data/token-filter/TokenFilter.d.ts +10 -5
  28. package/cjs/data/token-filter/TokenFilter.js +110 -48
  29. package/cjs/data/token-filter/TokenFilter.js.map +1 -1
  30. package/cjs/data/token-filter/TokenFilter.types.d.ts +51 -35
  31. package/cjs/data/token-filter/helpers/generate-autocomplete-options.d.ts +3 -6
  32. package/cjs/data/token-filter/helpers/generate-autocomplete-options.js +24 -37
  33. package/cjs/data/token-filter/helpers/generate-autocomplete-options.js.map +1 -1
  34. package/cjs/data/token-filter/helpers/operators.d.ts +6 -6
  35. package/cjs/data/token-filter/helpers/operators.js +3 -4
  36. package/cjs/data/token-filter/helpers/operators.js.map +1 -1
  37. package/cjs/data/token-filter/helpers/parse-query-text.d.ts +2 -20
  38. package/cjs/data/token-filter/helpers/parse-query-text.js +1 -1
  39. package/cjs/data/token-filter/helpers/parse-query-text.js.map +1 -1
  40. package/cjs/data/token-filter/helpers/query-builder.d.ts +2 -2
  41. package/cjs/data/token-filter/helpers/query-builder.js.map +1 -1
  42. package/cjs/date/Date.Dialog.d.ts +5 -1
  43. package/cjs/date/Date.Dialog.js +6 -2
  44. package/cjs/date/Date.Dialog.js.map +1 -1
  45. package/cjs/date/datepicker/DatePicker.js +3 -2
  46. package/cjs/date/datepicker/DatePicker.js.map +1 -1
  47. package/cjs/date/datepicker/hooks/useDatepicker.js +5 -2
  48. package/cjs/date/datepicker/hooks/useDatepicker.js.map +1 -1
  49. package/cjs/date/datepicker/hooks/useRangeDatepicker.js +3 -1
  50. package/cjs/date/datepicker/hooks/useRangeDatepicker.js.map +1 -1
  51. package/cjs/date/datepicker/parts/DatePicker.Months.d.ts +2 -1
  52. package/cjs/date/datepicker/parts/DatePicker.Months.js +3 -3
  53. package/cjs/date/datepicker/parts/DatePicker.Months.js.map +1 -1
  54. package/cjs/date/datepicker/parts/DatePicker.RDP.d.ts +5 -1
  55. package/cjs/date/datepicker/parts/DatePicker.RDP.js +2 -2
  56. package/cjs/date/datepicker/parts/DatePicker.RDP.js.map +1 -1
  57. package/cjs/date/monthpicker/MonthPicker.js +3 -2
  58. package/cjs/date/monthpicker/MonthPicker.js.map +1 -1
  59. package/cjs/date/monthpicker/hooks/useMonthPicker.js +3 -1
  60. package/cjs/date/monthpicker/hooks/useMonthPicker.js.map +1 -1
  61. package/cjs/date/monthpicker/parts/MonthPicker.Caption.d.ts +4 -1
  62. package/cjs/date/monthpicker/parts/MonthPicker.Caption.js +3 -2
  63. package/cjs/date/monthpicker/parts/MonthPicker.Caption.js.map +1 -1
  64. package/cjs/dropdown/Toggle.js +5 -12
  65. package/cjs/dropdown/Toggle.js.map +1 -1
  66. package/cjs/form/combobox/Input/Input.js +1 -1
  67. package/cjs/form/combobox/Input/Input.js.map +1 -1
  68. package/cjs/inline-message/root/InlineMessage.js +2 -2
  69. package/cjs/inline-message/root/InlineMessage.js.map +1 -1
  70. package/cjs/provider/Provider.d.ts +2 -2
  71. package/cjs/toggle-group/useToggleGroup.js +5 -3
  72. package/cjs/toggle-group/useToggleGroup.js.map +1 -1
  73. package/cjs/tooltip/Tooltip.js +1 -3
  74. package/cjs/tooltip/Tooltip.js.map +1 -1
  75. package/cjs/utils/components/HighlightText/HighlightText.d.ts +8 -0
  76. package/cjs/utils/components/HighlightText/HighlightText.js +27 -0
  77. package/cjs/utils/components/HighlightText/HighlightText.js.map +1 -0
  78. package/cjs/utils/components/Listbox/group/ListboxGroup.d.ts +7 -0
  79. package/cjs/utils/components/Listbox/group/ListboxGroup.js +15 -0
  80. package/cjs/utils/components/Listbox/group/ListboxGroup.js.map +1 -0
  81. package/cjs/utils/components/Listbox/input-slot/ListboxInputSlot.d.ts +7 -0
  82. package/cjs/utils/components/Listbox/input-slot/ListboxInputSlot.js +15 -0
  83. package/cjs/utils/components/Listbox/input-slot/ListboxInputSlot.js.map +1 -0
  84. package/cjs/utils/components/Listbox/item/ListboxItem.d.ts +24 -0
  85. package/cjs/utils/components/Listbox/item/ListboxItem.js +33 -0
  86. package/cjs/utils/components/Listbox/item/ListboxItem.js.map +1 -0
  87. package/cjs/utils/components/Listbox/list/ListboxList.d.ts +8 -0
  88. package/cjs/utils/components/Listbox/list/ListboxList.js +32 -0
  89. package/cjs/utils/components/Listbox/list/ListboxList.js.map +1 -0
  90. package/cjs/utils/components/Listbox/root/ListboxRoot.d.ts +20 -0
  91. package/cjs/utils/components/Listbox/root/ListboxRoot.js +84 -0
  92. package/cjs/utils/components/Listbox/root/ListboxRoot.js.map +1 -0
  93. package/cjs/utils/components/Listbox/root/domHelpers.d.ts +3 -0
  94. package/cjs/utils/components/Listbox/root/domHelpers.js +53 -0
  95. package/cjs/utils/components/Listbox/root/domHelpers.js.map +1 -0
  96. package/cjs/utils/components/focus-boundary/FocusBoundary.js +9 -64
  97. package/cjs/utils/components/focus-boundary/FocusBoundary.js.map +1 -1
  98. package/cjs/utils/helpers/focus.d.ts +14 -0
  99. package/cjs/utils/helpers/focus.js +63 -0
  100. package/cjs/utils/helpers/focus.js.map +1 -0
  101. package/cjs/utils/hooks/useDeferredValue.d.ts +1 -0
  102. package/cjs/utils/hooks/useDeferredValue.js +14 -0
  103. package/cjs/utils/hooks/useDeferredValue.js.map +1 -0
  104. package/esm/data/drag-and-drop/item/DataDragAndDropItem.d.ts +27 -0
  105. package/esm/data/drag-and-drop/item/DataDragAndDropItem.js +55 -0
  106. package/esm/data/drag-and-drop/item/DataDragAndDropItem.js.map +1 -0
  107. package/esm/data/drag-and-drop/root/DataDragAndDrop.context.d.ts +5 -0
  108. package/esm/data/drag-and-drop/root/DataDragAndDrop.context.js +3 -0
  109. package/esm/data/drag-and-drop/root/DataDragAndDrop.context.js.map +1 -0
  110. package/esm/data/drag-and-drop/root/DataDragAndDropRoot.d.ts +24 -0
  111. package/esm/data/drag-and-drop/root/DataDragAndDropRoot.js +71 -0
  112. package/esm/data/drag-and-drop/root/DataDragAndDropRoot.js.map +1 -0
  113. package/esm/data/table/helpers/table-keyboard.d.ts +1 -0
  114. package/esm/data/table/helpers/table-keyboard.js +5 -3
  115. package/esm/data/table/helpers/table-keyboard.js.map +1 -1
  116. package/esm/data/table/root/DataTableRoot.context.d.ts +8 -0
  117. package/esm/data/table/root/DataTableRoot.context.js +7 -0
  118. package/esm/data/table/root/DataTableRoot.context.js.map +1 -0
  119. package/esm/data/table/root/DataTableRoot.js +5 -3
  120. package/esm/data/table/root/DataTableRoot.js.map +1 -1
  121. package/esm/data/table/th/DataTableTh.d.ts +18 -2
  122. package/esm/data/table/th/DataTableTh.js +46 -21
  123. package/esm/data/table/th/DataTableTh.js.map +1 -1
  124. package/esm/data/table/tr/DataTableTr.js +9 -2
  125. package/esm/data/table/tr/DataTableTr.js.map +1 -1
  126. package/esm/data/token-filter/AutoSuggest.d.ts +6 -2
  127. package/esm/data/token-filter/AutoSuggest.js +45 -16
  128. package/esm/data/token-filter/AutoSuggest.js.map +1 -1
  129. package/esm/data/token-filter/AutoSuggest.types.d.ts +0 -1
  130. package/esm/data/token-filter/TokenFilter.d.ts +10 -5
  131. package/esm/data/token-filter/TokenFilter.js +110 -48
  132. package/esm/data/token-filter/TokenFilter.js.map +1 -1
  133. package/esm/data/token-filter/TokenFilter.types.d.ts +51 -35
  134. package/esm/data/token-filter/helpers/generate-autocomplete-options.d.ts +3 -6
  135. package/esm/data/token-filter/helpers/generate-autocomplete-options.js +24 -37
  136. package/esm/data/token-filter/helpers/generate-autocomplete-options.js.map +1 -1
  137. package/esm/data/token-filter/helpers/operators.d.ts +6 -6
  138. package/esm/data/token-filter/helpers/operators.js +3 -4
  139. package/esm/data/token-filter/helpers/operators.js.map +1 -1
  140. package/esm/data/token-filter/helpers/parse-query-text.d.ts +2 -20
  141. package/esm/data/token-filter/helpers/parse-query-text.js +1 -1
  142. package/esm/data/token-filter/helpers/parse-query-text.js.map +1 -1
  143. package/esm/data/token-filter/helpers/query-builder.d.ts +2 -2
  144. package/esm/data/token-filter/helpers/query-builder.js.map +1 -1
  145. package/esm/date/Date.Dialog.d.ts +5 -1
  146. package/esm/date/Date.Dialog.js +6 -2
  147. package/esm/date/Date.Dialog.js.map +1 -1
  148. package/esm/date/datepicker/DatePicker.js +3 -2
  149. package/esm/date/datepicker/DatePicker.js.map +1 -1
  150. package/esm/date/datepicker/hooks/useDatepicker.js +5 -2
  151. package/esm/date/datepicker/hooks/useDatepicker.js.map +1 -1
  152. package/esm/date/datepicker/hooks/useRangeDatepicker.js +3 -1
  153. package/esm/date/datepicker/hooks/useRangeDatepicker.js.map +1 -1
  154. package/esm/date/datepicker/parts/DatePicker.Months.d.ts +2 -1
  155. package/esm/date/datepicker/parts/DatePicker.Months.js +3 -3
  156. package/esm/date/datepicker/parts/DatePicker.Months.js.map +1 -1
  157. package/esm/date/datepicker/parts/DatePicker.RDP.d.ts +5 -1
  158. package/esm/date/datepicker/parts/DatePicker.RDP.js +2 -2
  159. package/esm/date/datepicker/parts/DatePicker.RDP.js.map +1 -1
  160. package/esm/date/monthpicker/MonthPicker.js +3 -2
  161. package/esm/date/monthpicker/MonthPicker.js.map +1 -1
  162. package/esm/date/monthpicker/hooks/useMonthPicker.js +3 -1
  163. package/esm/date/monthpicker/hooks/useMonthPicker.js.map +1 -1
  164. package/esm/date/monthpicker/parts/MonthPicker.Caption.d.ts +4 -1
  165. package/esm/date/monthpicker/parts/MonthPicker.Caption.js +3 -2
  166. package/esm/date/monthpicker/parts/MonthPicker.Caption.js.map +1 -1
  167. package/esm/dropdown/Toggle.js +5 -12
  168. package/esm/dropdown/Toggle.js.map +1 -1
  169. package/esm/form/combobox/Input/Input.js +1 -1
  170. package/esm/form/combobox/Input/Input.js.map +1 -1
  171. package/esm/inline-message/root/InlineMessage.js +3 -3
  172. package/esm/inline-message/root/InlineMessage.js.map +1 -1
  173. package/esm/provider/Provider.d.ts +2 -2
  174. package/esm/toggle-group/useToggleGroup.js +6 -4
  175. package/esm/toggle-group/useToggleGroup.js.map +1 -1
  176. package/esm/tooltip/Tooltip.js +1 -3
  177. package/esm/tooltip/Tooltip.js.map +1 -1
  178. package/esm/utils/components/HighlightText/HighlightText.d.ts +8 -0
  179. package/esm/utils/components/HighlightText/HighlightText.js +21 -0
  180. package/esm/utils/components/HighlightText/HighlightText.js.map +1 -0
  181. package/esm/utils/components/Listbox/group/ListboxGroup.d.ts +7 -0
  182. package/esm/utils/components/Listbox/group/ListboxGroup.js +10 -0
  183. package/esm/utils/components/Listbox/group/ListboxGroup.js.map +1 -0
  184. package/esm/utils/components/Listbox/input-slot/ListboxInputSlot.d.ts +7 -0
  185. package/esm/utils/components/Listbox/input-slot/ListboxInputSlot.js +9 -0
  186. package/esm/utils/components/Listbox/input-slot/ListboxInputSlot.js.map +1 -0
  187. package/esm/utils/components/Listbox/item/ListboxItem.d.ts +24 -0
  188. package/esm/utils/components/Listbox/item/ListboxItem.js +27 -0
  189. package/esm/utils/components/Listbox/item/ListboxItem.js.map +1 -0
  190. package/esm/utils/components/Listbox/list/ListboxList.d.ts +8 -0
  191. package/esm/utils/components/Listbox/list/ListboxList.js +27 -0
  192. package/esm/utils/components/Listbox/list/ListboxList.js.map +1 -0
  193. package/esm/utils/components/Listbox/root/ListboxRoot.d.ts +20 -0
  194. package/esm/utils/components/Listbox/root/ListboxRoot.js +79 -0
  195. package/esm/utils/components/Listbox/root/ListboxRoot.js.map +1 -0
  196. package/esm/utils/components/Listbox/root/domHelpers.d.ts +3 -0
  197. package/esm/utils/components/Listbox/root/domHelpers.js +50 -0
  198. package/esm/utils/components/Listbox/root/domHelpers.js.map +1 -0
  199. package/esm/utils/components/focus-boundary/FocusBoundary.js +8 -63
  200. package/esm/utils/components/focus-boundary/FocusBoundary.js.map +1 -1
  201. package/esm/utils/helpers/focus.d.ts +14 -0
  202. package/esm/utils/helpers/focus.js +60 -0
  203. package/esm/utils/helpers/focus.js.map +1 -0
  204. package/esm/utils/hooks/useDeferredValue.d.ts +1 -0
  205. package/esm/utils/hooks/useDeferredValue.js +7 -0
  206. package/esm/utils/hooks/useDeferredValue.js.map +1 -0
  207. package/package.json +7 -7
  208. package/src/data/drag-and-drop/item/DataDragAndDropItem.tsx +101 -0
  209. package/src/data/drag-and-drop/root/DataDragAndDrop.context.tsx +9 -0
  210. package/src/data/drag-and-drop/root/DataDragAndDropRoot.tsx +98 -0
  211. package/src/data/table/helpers/table-keyboard.ts +7 -3
  212. package/src/data/table/root/DataTableRoot.context.ts +13 -0
  213. package/src/data/table/root/DataTableRoot.tsx +16 -13
  214. package/src/data/table/th/DataTableTh.tsx +110 -54
  215. package/src/data/table/tr/DataTableTr.tsx +13 -2
  216. package/src/data/token-filter/AutoSuggest.tsx +142 -35
  217. package/src/data/token-filter/AutoSuggest.types.ts +0 -1
  218. package/src/data/token-filter/TokenFilter.tsx +179 -81
  219. package/src/data/token-filter/TokenFilter.types.ts +70 -44
  220. package/src/data/token-filter/helpers/generate-autocomplete-options.test.ts +97 -157
  221. package/src/data/token-filter/helpers/generate-autocomplete-options.ts +56 -53
  222. package/src/data/token-filter/helpers/operators.test.ts +29 -29
  223. package/src/data/token-filter/helpers/operators.ts +16 -16
  224. package/src/data/token-filter/helpers/parse-query-text.test.ts +37 -35
  225. package/src/data/token-filter/helpers/parse-query-text.ts +7 -26
  226. package/src/data/token-filter/helpers/query-builder.ts +2 -2
  227. package/src/date/Date.Dialog.tsx +15 -0
  228. package/src/date/datepicker/DatePicker.tsx +3 -0
  229. package/src/date/datepicker/hooks/useDatepicker.tsx +7 -2
  230. package/src/date/datepicker/hooks/useRangeDatepicker.tsx +5 -1
  231. package/src/date/datepicker/parts/DatePicker.Months.tsx +9 -1
  232. package/src/date/datepicker/parts/DatePicker.RDP.tsx +7 -1
  233. package/src/date/monthpicker/MonthPicker.tsx +3 -1
  234. package/src/date/monthpicker/hooks/useMonthPicker.tsx +5 -1
  235. package/src/date/monthpicker/parts/MonthPicker.Caption.tsx +20 -2
  236. package/src/dropdown/Toggle.tsx +6 -12
  237. package/src/form/combobox/Input/Input.tsx +2 -2
  238. package/src/inline-message/root/InlineMessage.tsx +5 -5
  239. package/src/provider/Provider.tsx +2 -2
  240. package/src/toggle-group/useToggleGroup.ts +6 -5
  241. package/src/tooltip/Tooltip.tsx +1 -3
  242. package/src/utils/components/HighlightText/HighlightText.tsx +34 -0
  243. package/src/utils/components/Listbox/group/ListboxGroup.tsx +26 -0
  244. package/src/utils/components/Listbox/input-slot/ListboxInputSlot.tsx +22 -0
  245. package/src/utils/components/Listbox/item/ListboxItem.tsx +57 -0
  246. package/src/utils/components/Listbox/list/ListboxList.tsx +38 -0
  247. package/src/utils/components/Listbox/root/ListboxRoot.tsx +104 -0
  248. package/src/utils/components/Listbox/root/domHelpers.ts +59 -0
  249. package/src/utils/components/focus-boundary/FocusBoundary.tsx +8 -78
  250. package/src/utils/helpers/focus.ts +75 -0
  251. package/src/utils/hooks/useDeferredValue.ts +12 -0
  252. package/cjs/data/table/th/DataTableThSortHandle.d.ts +0 -6
  253. package/cjs/data/table/th/DataTableThSortHandle.js +0 -82
  254. package/cjs/data/table/th/DataTableThSortHandle.js.map +0 -1
  255. package/esm/data/table/th/DataTableThSortHandle.d.ts +0 -6
  256. package/esm/data/table/th/DataTableThSortHandle.js +0 -47
  257. package/esm/data/table/th/DataTableThSortHandle.js.map +0 -1
  258. package/src/data/table/th/DataTableThSortHandle.tsx +0 -67
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@navikt/ds-react",
3
- "version": "8.5.2",
3
+ "version": "8.7.0",
4
4
  "description": "React components from the Norwegian Labour and Welfare Administration.",
5
5
  "author": "Aksel, a team part of the Norwegian Labour and Welfare Administration.",
6
6
  "license": "MIT",
@@ -705,8 +705,8 @@
705
705
  "dependencies": {
706
706
  "@floating-ui/react": "0.27.8",
707
707
  "@floating-ui/react-dom": "^2.1.6",
708
- "@navikt/aksel-icons": "^8.5.2",
709
- "@navikt/ds-tokens": "^8.5.2",
708
+ "@navikt/aksel-icons": "^8.7.0",
709
+ "@navikt/ds-tokens": "^8.7.0",
710
710
  "date-fns": "^4.0.0",
711
711
  "react-day-picker": "9.7.0"
712
712
  },
@@ -716,7 +716,7 @@
716
716
  "@testing-library/react": "^16.3.0",
717
717
  "@testing-library/user-event": "^14.5.2",
718
718
  "@types/jscodeshift": "^0.11.11",
719
- "@types/react": "19.2.9",
719
+ "@types/react": "19.2.14",
720
720
  "@types/react-dom": "19.2.3",
721
721
  "aksel": "workspace:^",
722
722
  "concurrently": "9.2.1",
@@ -724,10 +724,10 @@
724
724
  "fast-glob": "3.3.3",
725
725
  "jscodeshift": "17.3.0",
726
726
  "jsdom": "27.1.0",
727
- "react": "19.2.3",
728
- "react-dom": "19.2.3",
727
+ "react": "19.2.4",
728
+ "react-dom": "19.2.4",
729
729
  "react-router": "^7.13.0",
730
- "rimraf": "6.1.2",
730
+ "rimraf": "6.1.3",
731
731
  "swr": "^2.3.6",
732
732
  "tsc-alias": "1.8.16",
733
733
  "tsx": "^4.20.6",
@@ -0,0 +1,101 @@
1
+ import { useSortable } from "@dnd-kit/react/sortable";
2
+ import React, { useRef } from "react";
3
+ import {
4
+ CaretDownCircleFillIcon,
5
+ CaretUpCircleFillIcon,
6
+ DragVerticalIcon,
7
+ } from "@navikt/aksel-icons";
8
+ import { HStack } from "../../../primitives/stack";
9
+ import { cl } from "../../../utils/helpers";
10
+ import { useMergeRefs } from "../../../utils/hooks";
11
+ import { DataDragAndDropContext } from "../root/DataDragAndDrop.context";
12
+
13
+ interface DataDragAndDropItemProps extends React.HTMLAttributes<HTMLDivElement> {
14
+ children: React.ReactNode;
15
+ /**
16
+ * Unique id
17
+ */
18
+ id: string;
19
+ /**
20
+ * Index of the item being dragged
21
+ */
22
+ index: number;
23
+ }
24
+
25
+ /**
26
+ * TODO
27
+ *
28
+ * @see 🏷️ {@link DataDragAndDropItemProps}
29
+ * @example
30
+ * ```tsx
31
+ * <DragAndDrop.Item numOfSelectedRows={selectedRows.length} onClear={handleClear}>
32
+ * TODO
33
+ * </DragAndDrop.Item>
34
+ * ```
35
+ */
36
+ const DataDragAndDropItem = React.forwardRef<
37
+ HTMLDivElement,
38
+ DataDragAndDropItemProps
39
+ >(({ children, id, index, className, ...rest }, forwardedRef) => {
40
+ const handleRef = useRef<HTMLDivElement>(null);
41
+ const { ref, isDragging, isDropTarget } = useSortable({
42
+ id,
43
+ index,
44
+ handle: handleRef,
45
+ });
46
+ const mergedRef = useMergeRefs(ref, forwardedRef);
47
+ const context = React.useContext(DataDragAndDropContext);
48
+ const mouseDragging = isDragging && context?.inputMethod === "mouse";
49
+ const mouseDropTarget = isDropTarget && context?.inputMethod === "mouse";
50
+ const keyboardDragging = isDragging && context?.inputMethod === "keyboard";
51
+
52
+ return (
53
+ <HStack gap="space-8" align="center" wrap={false} asChild>
54
+ {/* TODO Should this be a <li>? */}
55
+ <div
56
+ ref={mergedRef}
57
+ {...rest}
58
+ className={cl("aksel-data-table__drag-and-drop-item", className)}
59
+ data-dragging={isDragging}
60
+ data-mouse-dragging={mouseDragging}
61
+ data-keyboard-dragging={keyboardDragging}
62
+ data-drop-target={mouseDropTarget}
63
+ tabIndex={-1}
64
+ >
65
+ <div
66
+ className="aksel-data-table__drag-and-drop-item-drag-handler"
67
+ ref={handleRef}
68
+ // TODO Consider moving this to its own component
69
+ // TODO Perhaps make it a button where clicking also enables arrow icons?
70
+ >
71
+ {keyboardDragging && (
72
+ <span
73
+ className="aksel-data-table__drag-and-drop-item-keyboard-drag-icon"
74
+ data-direction="up"
75
+ >
76
+ <CaretUpCircleFillIcon aria-hidden fontSize="1.2rem" />
77
+ </span>
78
+ )}
79
+ <DragVerticalIcon
80
+ aria-hidden
81
+ title="Dra for å flytte"
82
+ fontSize="1.5rem"
83
+ />
84
+ {keyboardDragging && (
85
+ <span
86
+ className="aksel-data-table__drag-and-drop-item-keyboard-drag-icon"
87
+ data-direction="down"
88
+ >
89
+ <CaretDownCircleFillIcon aria-hidden fontSize="1.2rem" />
90
+ </span>
91
+ )}
92
+ </div>
93
+ <div>{children}</div>
94
+ </div>
95
+ </HStack>
96
+ );
97
+ });
98
+
99
+ export default DataDragAndDropItem;
100
+ export { DataDragAndDropItem };
101
+ export type { DataDragAndDropItemProps };
@@ -0,0 +1,9 @@
1
+ import { createContext } from "react";
2
+
3
+ interface DataDragAndDropContextType {
4
+ inputMethod: "mouse" | "keyboard" | null;
5
+ }
6
+
7
+ export const DataDragAndDropContext = createContext<
8
+ DataDragAndDropContextType | undefined
9
+ >(undefined);
@@ -0,0 +1,98 @@
1
+ import { DragDropProvider, DragOverlay } from "@dnd-kit/react";
2
+ import { isSortable } from "@dnd-kit/react/sortable";
3
+ import React, { forwardRef, isValidElement } from "react";
4
+ import { VStack } from "../../../primitives/stack";
5
+ import DataDragAndDropItem, {
6
+ DataDragAndDropItemProps,
7
+ } from "../item/DataDragAndDropItem";
8
+ import { DataDragAndDropContext } from "./DataDragAndDrop.context";
9
+
10
+ interface DataDragAndDropProps extends React.HTMLAttributes<HTMLTableElement> {
11
+ children: any[];
12
+ setItems: React.Dispatch<React.SetStateAction<any[]>>;
13
+ }
14
+
15
+ interface DataDragAndDropRootComponent extends React.ForwardRefExoticComponent<
16
+ DataDragAndDropProps & React.RefAttributes<HTMLTableElement>
17
+ > {
18
+ /**
19
+ * @see 🏷️ {@link DataDragAndDropItemProps}
20
+ * * @example
21
+ * ```jsx
22
+ * <DragAndDrop>
23
+ * <DragAndDrop.Item id="1" index={0}>
24
+ * ...
25
+ * </DragAndDrop.Item>
26
+ * </DragAndDrop>
27
+ * ```
28
+ */
29
+ Item: typeof DataDragAndDropItem;
30
+ }
31
+
32
+ const DataDragAndDrop = forwardRef<HTMLDivElement, DataDragAndDropProps>(
33
+ ({ setItems, children, ...rest }, forwardedRef) => {
34
+ const [inputMethod, setInputMethod] = React.useState<
35
+ "mouse" | "keyboard" | null
36
+ >(null);
37
+
38
+ const setItemOrder = (initalIndex: number, targetIndex: number) => {
39
+ setItems((items) => {
40
+ const newItems = [...items];
41
+ const [movedItem] = newItems.splice(initalIndex, 1);
42
+ newItems.splice(targetIndex, 0, movedItem);
43
+ return newItems;
44
+ });
45
+ };
46
+
47
+ return (
48
+ <DataDragAndDropContext.Provider value={{ inputMethod }}>
49
+ <DragDropProvider
50
+ // TODO Look into overriding default keybinds, might eliminate context need
51
+ onBeforeDragStart={(event) =>
52
+ setInputMethod(
53
+ event.operation.activatorEvent?.type === "pointerdown"
54
+ ? "mouse"
55
+ : "keyboard",
56
+ )
57
+ }
58
+ onDragOver={(event) => {
59
+ if (event.operation.activatorEvent?.type === "pointerdown") {
60
+ // Prevents items to rearrange while dragging with mouse, but allows keyboard dragging to work as intended
61
+ event.preventDefault();
62
+ }
63
+ }}
64
+ onDragEnd={(event) => {
65
+ if (event.operation.activatorEvent?.type === "pointerdown") {
66
+ const { source, target } = event.operation;
67
+ if (!isSortable(source) || !isSortable(target)) return;
68
+ setItemOrder(source.initialIndex, target.index);
69
+ }
70
+ }}
71
+ >
72
+ <VStack asChild gap="space-12">
73
+ <div {...rest} ref={forwardedRef}>
74
+ {children}
75
+ </div>
76
+ </VStack>
77
+ <DragOverlay>
78
+ {(source) => {
79
+ // Overlay not needed and causes glitching when using keyboard
80
+ if (inputMethod === "keyboard") return null;
81
+ if (!isSortable(source)) return null;
82
+ if (isValidElement(children[source.initialIndex])) {
83
+ return children[source.initialIndex];
84
+ }
85
+ return null;
86
+ }}
87
+ </DragOverlay>
88
+ </DragDropProvider>
89
+ </DataDragAndDropContext.Provider>
90
+ );
91
+ },
92
+ ) as DataDragAndDropRootComponent;
93
+
94
+ DataDragAndDrop.Item = DataDragAndDropItem;
95
+
96
+ export { DataDragAndDrop, DataDragAndDropItem };
97
+ export default DataDragAndDrop;
98
+ export type { DataDragAndDropItemProps, DataDragAndDropProps };
@@ -52,6 +52,7 @@ function getNavigationAction(event: KeyboardEvent): NavigationAction | null {
52
52
  * - Select arrow down/up for opening popup
53
53
  * - User is navigating inside multiline textarea
54
54
  * - contenteditable attrb is in use
55
+ * - element has attribute data-block-keyboard-nav="true"
55
56
  */
56
57
  function shouldBlockNavigation(event: KeyboardEvent): boolean {
57
58
  const key = event.key;
@@ -68,9 +69,9 @@ function shouldBlockNavigation(event: KeyboardEvent): boolean {
68
69
  return true;
69
70
  }
70
71
 
71
- /* If not any of these elements, assueme "safe" to navigate */
72
+ /* If not any of these elements, assume "safe" to navigate */
72
73
  const editable = el.closest(
73
- 'input, textarea, select, [contenteditable="true"]',
74
+ 'input, textarea, select, [contenteditable="true"], [data-block-keyboard-nav="true"]',
74
75
  );
75
76
 
76
77
  if (!editable) {
@@ -92,7 +93,10 @@ function shouldBlockNavigation(event: KeyboardEvent): boolean {
92
93
  return false;
93
94
  }
94
95
 
95
- return editable.hasAttribute("contenteditable");
96
+ return (
97
+ editable.hasAttribute("contenteditable") ||
98
+ editable.hasAttribute("data-block-keyboard-nav")
99
+ );
96
100
  }
97
101
 
98
102
  function shouldBlockInputArrow(input: HTMLInputElement, key: string): boolean {
@@ -0,0 +1,13 @@
1
+ import { createStrictContext } from "../../../utils/helpers";
2
+
3
+ interface DataTableContextProps {
4
+ layout: "fixed" | "auto";
5
+ }
6
+
7
+ const { Provider: DataTableContextProvider, useContext: useDataTableContext } =
8
+ createStrictContext<DataTableContextProps>({
9
+ name: "DataTableContext",
10
+ errorMessage: "useDataTableContext must be used within DataTable",
11
+ });
12
+
13
+ export { DataTableContextProvider, useDataTableContext };
@@ -20,6 +20,7 @@ import {
20
20
  type DataTableTheadProps,
21
21
  } from "../thead/DataTableThead";
22
22
  import { DataTableTr, type DataTableTrProps } from "../tr/DataTableTr";
23
+ import { DataTableContextProvider } from "./DataTableRoot.context";
23
24
  import { useTableKeyboardNav } from "./useTableKeyboardNav";
24
25
 
25
26
  interface DataTableProps extends React.HTMLAttributes<HTMLTableElement> {
@@ -193,20 +194,22 @@ const DataTable = forwardRef<HTMLTableElement, DataTableProps>(
193
194
  });
194
195
 
195
196
  return (
196
- <div className="aksel-data-table__border-wrapper">
197
- <div className="aksel-data-table__scroll-wrapper">
198
- <table
199
- {...rest}
200
- ref={mergedRef}
201
- className={cl("aksel-data-table", className)}
202
- data-zebra-stripes={zebraStripes}
203
- data-truncate-content={truncateContent}
204
- data-density={rowDensity}
205
- data-layout={layout}
206
- tabIndex={tabIndex}
207
- />
197
+ <DataTableContextProvider layout={layout}>
198
+ <div className="aksel-data-table__border-wrapper">
199
+ <div className="aksel-data-table__scroll-wrapper">
200
+ <table
201
+ {...rest}
202
+ ref={mergedRef}
203
+ className={cl("aksel-data-table", className)}
204
+ data-zebra-stripes={zebraStripes}
205
+ data-truncate-content={truncateContent}
206
+ data-density={rowDensity}
207
+ data-layout={layout}
208
+ tabIndex={tabIndex}
209
+ />
210
+ </div>
208
211
  </div>
209
- </div>
212
+ </DataTableContextProvider>
210
213
  );
211
214
  },
212
215
  ) as DataTableRootComponent;
@@ -1,10 +1,14 @@
1
1
  import React, { forwardRef } from "react";
2
- import { FunnelIcon, SortDownIcon, SortUpIcon } from "@navikt/aksel-icons";
3
- import { ActionMenu } from "../../../action-menu";
4
- import { HStack, Spacer } from "../../../primitives/stack";
2
+ import {
3
+ ArrowsUpDownIcon,
4
+ CaretLeftCircleFillIcon,
5
+ CaretRightCircleFillIcon,
6
+ SortDownIcon,
7
+ SortUpIcon,
8
+ } from "@navikt/aksel-icons";
5
9
  import { cl } from "../../../utils/helpers";
6
- import { DataTableThActions } from "./DataTableThActions";
7
- import { DataTableThSortHandle } from "./DataTableThSortHandle";
10
+
11
+ type SortDirection = "asc" | "desc" | "none";
8
12
 
9
13
  interface DataTableThProps extends React.HTMLAttributes<HTMLTableCellElement> {
10
14
  resizeHandler?: (
@@ -13,8 +17,22 @@ interface DataTableThProps extends React.HTMLAttributes<HTMLTableCellElement> {
13
17
  | React.TouchEvent<HTMLButtonElement>,
14
18
  ) => void;
15
19
  size?: number; // TODO: size should be required when resizeHandler is set
16
- sortDirection?: "asc" | "desc" | "none" | false;
17
- onSortChange?: (direction: "asc" | "desc" | "none", event: Event) => void;
20
+ /**
21
+ * Makes the column header sortable. The entire header cell content becomes
22
+ * a clickable button when true.
23
+ */
24
+ sortable?: boolean;
25
+ /**
26
+ * Current sort direction. Only relevant when `sortable` is true.
27
+ * Uses values matching the `aria-sort` attribute directly.
28
+ * @default "none"
29
+ */
30
+ sortDirection?: SortDirection;
31
+ /**
32
+ * Called when the user clicks the sortable header.
33
+ * The consumer is responsible for determining and setting the next sort state.
34
+ */
35
+ onSortClick?: (event: React.MouseEvent<HTMLElement>) => void;
18
36
  render?: {
19
37
  filterMenu?: {
20
38
  title: string;
@@ -26,8 +44,15 @@ interface DataTableThProps extends React.HTMLAttributes<HTMLTableCellElement> {
26
44
  */
27
45
  colSpan?: number;
28
46
  rowSpan?: number;
47
+ keyboardResizingHandler?: (size: number) => void;
29
48
  }
30
49
 
50
+ const SORT_ICON: Record<SortDirection, React.ElementType | null> = {
51
+ asc: SortUpIcon,
52
+ desc: SortDownIcon,
53
+ none: ArrowsUpDownIcon,
54
+ };
55
+
31
56
  /**
32
57
  * TODO:
33
58
  * - Plan for pinning: Move it into "settings" dialog like here: https://cloudscape.design/examples/react/table.html
@@ -39,79 +64,110 @@ const DataTableTh = forwardRef<HTMLTableCellElement, DataTableThProps>(
39
64
  children,
40
65
  resizeHandler,
41
66
  size,
42
- sortDirection,
43
- onSortChange,
67
+ sortable = false,
68
+ sortDirection = "none",
69
+ onSortClick,
44
70
  style,
45
- render,
71
+ keyboardResizingHandler,
46
72
  ...rest
47
73
  },
48
74
  forwardedRef,
49
75
  ) => {
50
- const { filterMenu } = render || {};
76
+ const [resizeHandlerActive, setResizeHandlerActive] = React.useState(false);
77
+ const [isOverflowing, setIsOverflowing] = React.useState(false);
78
+ const contentRef = React.useRef<HTMLDivElement>(null);
79
+
80
+ const keyDownHandler = (event: React.KeyboardEvent<HTMLButtonElement>) => {
81
+ if (keyboardResizingHandler) {
82
+ if (event.key === "Enter" || event.key === " ") {
83
+ event.preventDefault();
84
+ setResizeHandlerActive((active) => !active);
85
+ } else if (
86
+ resizeHandlerActive &&
87
+ (event.key === "ArrowLeft" || event.key === "ArrowRight")
88
+ ) {
89
+ event.preventDefault();
90
+ keyboardResizingHandler(event.key === "ArrowRight" ? 10 : -10);
91
+ }
92
+ }
93
+ };
94
+
95
+ const SortIcon = sortable ? SORT_ICON[sortDirection] : null;
51
96
 
52
97
  return (
53
98
  <th
54
99
  {...rest}
55
100
  ref={forwardedRef}
56
101
  className={cl("aksel-data-table__th", className)}
102
+ data-sortable={sortable}
57
103
  style={{ width: size, ...style }}
104
+ aria-sort={sortable ? getAriaSort(sortDirection) : undefined}
105
+ onPointerEnter={() => {
106
+ const el = contentRef.current;
107
+ setIsOverflowing(el ? el.scrollWidth > el.offsetWidth : false);
108
+ console.info("is overflowing", isOverflowing);
109
+ }}
110
+ onPointerLeave={() => setIsOverflowing(false)}
58
111
  >
59
- <HStack align="center" gap="space-8" wrap={false}>
60
- <div className="aksel-data-table__th-content">{children}</div>
61
- {/* TODO: If the column is too narrow, the sort button will move when hovering b.c. the actions menu button slides in */}
62
- <DataTableThSortHandle
63
- sortDirection={sortDirection}
64
- onSortChange={onSortChange}
65
- />
66
- <Spacer />
67
-
68
- <DataTableThActions>
69
- {/* TODO: onSortChange just rotates between the three states now */}
70
- {/* TODO: Sorting texts do not handle different data-types now */}
71
- {sortDirection && (
72
- <ActionMenu.Group label="Sortering">
73
- <ActionMenu.Item
74
- onSelect={(event) => onSortChange?.("desc", event)}
75
- icon={<SortUpIcon aria-hidden />}
76
- >
77
- {sortDirection === "desc" ? "Fjern sortering" : "A-Z"}
78
- </ActionMenu.Item>
79
- <ActionMenu.Item
80
- onSelect={(event) => onSortChange?.("asc", event)}
81
- icon={<SortDownIcon aria-hidden />}
82
- >
83
- {sortDirection === "asc" ? "Fjern sortering" : "Z-A"}
84
- </ActionMenu.Item>
85
- </ActionMenu.Group>
86
- )}
87
- {filterMenu && (
88
- <ActionMenu.Group label="Filter">
89
- <ActionMenu.Sub>
90
- <ActionMenu.SubTrigger icon={<FunnelIcon aria-hidden />}>
91
- {filterMenu.title}
92
- </ActionMenu.SubTrigger>
93
- <ActionMenu.SubContent>
94
- {/* TODO: ActionMenu stops tab from working, so user cant tab "into" filter now even when wrapper has focus */}
95
- {filterMenu.content}
96
- </ActionMenu.SubContent>
97
- </ActionMenu.Sub>
98
- </ActionMenu.Group>
112
+ {sortable ? (
113
+ <button
114
+ className="aksel-data-table__th-sort-button"
115
+ onClick={sortable ? onSortClick : undefined}
116
+ >
117
+ {SortIcon && (
118
+ <SortIcon
119
+ aria-hidden
120
+ data-sort-direction={sortDirection}
121
+ className="aksel-data-table__th-sort-icon"
122
+ />
99
123
  )}
100
- </DataTableThActions>
101
- </HStack>
124
+ <div ref={contentRef} className="aksel-data-table__th-content">
125
+ {children}
126
+ </div>
127
+ </button>
128
+ ) : (
129
+ <div ref={contentRef} className="aksel-data-table__th-content">
130
+ {children}
131
+ </div>
132
+ )}
102
133
 
103
134
  {resizeHandler && (
104
135
  <button
105
136
  // TODO: Should probably not be a button since it doesn't have onClick
106
137
  onMouseDown={resizeHandler}
107
138
  onTouchStart={resizeHandler}
139
+ onBlur={() => setResizeHandlerActive(false)}
108
140
  className="aksel-data-table__th-resize-handle"
109
- />
141
+ data-active={resizeHandlerActive}
142
+ // TODO Very open to a better name for this
143
+ data-block-keyboard-nav
144
+ onKeyDown={keyDownHandler}
145
+ >
146
+ {resizeHandlerActive && (
147
+ <>
148
+ <span className="aksel-data-table__th-resize-handle-indicator aksel-data-table__th-resize-handle-indicator--start">
149
+ <CaretLeftCircleFillIcon aria-hidden fontSize="1.5rem" />
150
+ </span>
151
+ <span className="aksel-data-table__th-resize-handle-indicator aksel-data-table__th-resize-handle-indicator--end">
152
+ <CaretRightCircleFillIcon aria-hidden fontSize="1.5rem" />
153
+ </span>
154
+ </>
155
+ )}
156
+ </button>
110
157
  )}
111
158
  </th>
112
159
  );
113
160
  },
114
161
  );
115
162
 
163
+ function getAriaSort(
164
+ sortDirection: SortDirection | undefined,
165
+ ): "ascending" | "descending" | "none" | undefined {
166
+ if (sortDirection === "asc") return "ascending";
167
+ if (sortDirection === "desc") return "descending";
168
+ if (sortDirection === "none") return "none";
169
+ return undefined;
170
+ }
171
+
116
172
  export { DataTableTh };
117
173
  export type { DataTableThProps };
@@ -1,12 +1,17 @@
1
1
  import React, { forwardRef } from "react";
2
2
  import { cl } from "../../../utils/helpers";
3
+ import { useDataTableContext } from "../root/DataTableRoot.context";
3
4
 
4
5
  type DataTableTrProps = React.HTMLAttributes<HTMLTableRowElement> & {
5
6
  selected?: boolean;
6
7
  };
7
8
 
8
9
  const DataTableTr = forwardRef<HTMLTableRowElement, DataTableTrProps>(
9
- ({ className, selected = false, ...rest }, forwardedRef) => {
10
+ ({ className, children, selected = false, ...rest }, forwardedRef) => {
11
+ const rootContext = useDataTableContext();
12
+
13
+ const renderFillerCell = rootContext.layout === "fixed" && children;
14
+
10
15
  return (
11
16
  <tr
12
17
  {...rest}
@@ -14,7 +19,13 @@ const DataTableTr = forwardRef<HTMLTableRowElement, DataTableTrProps>(
14
19
  className={cl("aksel-data-table__tr", className, {
15
20
  "aksel-data-table__tr--selected": selected,
16
21
  })}
17
- />
22
+ >
23
+ {children}
24
+ {renderFillerCell && (
25
+ /* TODO: Consider chaning between th and td based on context */
26
+ <div className="aksel-data-table__th aksel-data-table__filler-cell" />
27
+ )}
28
+ </tr>
18
29
  );
19
30
  },
20
31
  );