@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
@@ -1,53 +1,160 @@
1
- import React, { forwardRef } from "react";
2
- import { Box } from "../../primitives/box";
1
+ import React, { forwardRef, useState } from "react";
2
+ import { Search } from "../../form/search";
3
3
  import { VStack } from "../../primitives/stack";
4
- import { Label } from "../../typography";
4
+ import { Detail, Label } from "../../typography";
5
+ import { ListboxGroup } from "../../utils/components/Listbox/group/ListboxGroup";
6
+ import { ListboxItem } from "../../utils/components/Listbox/item/ListboxItem";
7
+ import Listbox from "../../utils/components/Listbox/root/ListboxRoot";
8
+ import { DismissableLayer } from "../../utils/components/dismissablelayer/DismissableLayer";
9
+ import { Floating } from "../../utils/components/floating/Floating";
10
+ import { useMergeRefsN } from "../../utils/hooks";
5
11
  import type { AutoCompleteOption, OptionGroup } from "./AutoSuggest.types";
6
12
 
7
13
  interface AutoSuggestProps {
8
14
  options: OptionGroup<AutoCompleteOption>[];
9
- onSelect: (value: string) => void;
15
+ onSelect: (option: AutoCompleteOption) => boolean;
10
16
  className?: string;
17
+ value: string;
18
+ onChange: (newValue: string) => void;
19
+ open: boolean;
20
+ setOpen: (open: boolean) => void;
11
21
  }
12
22
 
13
- const AutoSuggest = forwardRef<HTMLDivElement, AutoSuggestProps>(
14
- ({ options, onSelect }, ref) => {
23
+ const AutoSuggest = forwardRef<HTMLInputElement, AutoSuggestProps>(
24
+ ({ options, onSelect, value, onChange, open, setOpen }, ref) => {
25
+ const [virtuallyFocusedItemId, setVirtuallyFocusedItemId] = useState("");
26
+
27
+ const [inputRef, setInputRef] = useState<HTMLInputElement | null>(null);
28
+
29
+ /* Unsure why N version works, but not regular here */
30
+ const mergedRef = useMergeRefsN([setInputRef, ref]);
31
+
32
+ const handleClose = () => {
33
+ setOpen(false);
34
+ };
35
+
36
+ const handleChange = (newValue: string) => {
37
+ onChange(newValue);
38
+ setOpen(true);
39
+ };
40
+
41
+ const handleSelectOption = (option: AutoCompleteOption) => {
42
+ const createdNewToken = onSelect(option);
43
+
44
+ if (createdNewToken) {
45
+ inputRef?.focus();
46
+ setOpen(false);
47
+ }
48
+ };
49
+
50
+ return (
51
+ <Floating>
52
+ <Listbox setVirtuallyFocusedItemId={setVirtuallyFocusedItemId}>
53
+ <Floating.Anchor>
54
+ <Listbox.InputSlot>
55
+ <Search
56
+ label="Tabellsøk"
57
+ variant="simple"
58
+ className="aksel-property-filter__input"
59
+ placeholder="Type to filter..."
60
+ ref={mergedRef}
61
+ value={value}
62
+ onChange={handleChange}
63
+ onClick={() => {
64
+ setOpen(true);
65
+ }}
66
+ onFocus={() => setOpen(true)}
67
+ /* onKeyDown={(e) => {
68
+ if (e.key === "Enter") {
69
+ createToken(filterText);
70
+ }
71
+ }} */
72
+ />
73
+ </Listbox.InputSlot>
74
+ </Floating.Anchor>
75
+ {open && (
76
+ <AutoSuggestPopup
77
+ options={options}
78
+ onSelect={handleSelectOption}
79
+ focusedValue={virtuallyFocusedItemId}
80
+ setFocusedValue={setVirtuallyFocusedItemId}
81
+ onClose={handleClose}
82
+ safeZoneAnchor={inputRef}
83
+ />
84
+ )}
85
+ </Listbox>
86
+ </Floating>
87
+ );
88
+ },
89
+ );
90
+
91
+ type AutoSuggestPopupProps = {
92
+ options: OptionGroup<AutoCompleteOption>[];
93
+ onSelect: (option: AutoCompleteOption) => void;
94
+ focusedValue: string;
95
+ setFocusedValue: (value: string) => void;
96
+ onClose: () => void;
97
+ safeZoneAnchor: HTMLInputElement | null;
98
+ };
99
+
100
+ const AutoSuggestPopup = forwardRef<HTMLDivElement, AutoSuggestPopupProps>(
101
+ (
102
+ {
103
+ options,
104
+ onSelect,
105
+ focusedValue,
106
+ setFocusedValue,
107
+ onClose,
108
+ safeZoneAnchor,
109
+ },
110
+ ref,
111
+ ) => {
15
112
  return (
16
- <Box ref={ref} padding="space-6">
17
- {options.map((group) => (
18
- <div key={group.label}>
19
- <Label as="div">{group.label}</Label>
20
- <VStack gap="space-4">
21
- {group.options.map((option) => {
22
- return (
23
- <div key={option.value}>
24
- <button
25
- type="button"
26
- onClick={() =>
27
- /* @ts-expect-error TODO: We need to convert the data properly */
28
- onSelect(option.value ?? option.propertyKey)
29
- }
113
+ <DismissableLayer
114
+ asChild
115
+ onDismiss={onClose}
116
+ safeZone={{ anchor: safeZoneAnchor }}
117
+ >
118
+ <Floating.Content
119
+ ref={ref}
120
+ align="start"
121
+ side="bottom"
122
+ fallbackPlacements={[]}
123
+ sideOffset={8}
124
+ className="aksel-property-filter__popup"
125
+ >
126
+ <div className="aksel-property-filter__popup-inner">
127
+ <Listbox.List setVirtuallyFocusedItemId={setFocusedValue}>
128
+ {options.map((group) => (
129
+ <ListboxGroup key={group.label} label={group.label}>
130
+ {group.options.map((item) => (
131
+ <ListboxItem
132
+ key={item.value}
133
+ id={item.value}
134
+ onClick={() => onSelect(item)}
135
+ hasVirtualFocus={focusedValue === item.value}
30
136
  >
31
- <span>
32
- {/* @ts-expect-error TODO: We need to convert the data properly */}
33
- {option.value ?? option.label ?? option.propertyLabel}
34
- </span>
35
- {option.description && <span>{option.description}</span>}
36
- {option.tags && option.tags.length > 0 && (
137
+ <VStack gap="space-0">
138
+ <Label as="div">{item.label}</Label>
139
+ {item.description && (
140
+ <Detail as="div">{item.description}</Detail>
141
+ )}
142
+ </VStack>
143
+ {/* {item.tags && item.tags.length > 0 && (
37
144
  <div>
38
- {option.tags.map((tag) => (
145
+ {item.tags.map((tag) => (
39
146
  <span key={tag}>{tag}</span>
40
147
  ))}
41
148
  </div>
42
- )}
43
- </button>
44
- </div>
45
- );
46
- })}
47
- </VStack>
149
+ )} */}
150
+ </ListboxItem>
151
+ ))}
152
+ </ListboxGroup>
153
+ ))}
154
+ </Listbox.List>
48
155
  </div>
49
- ))}
50
- </Box>
156
+ </Floating.Content>
157
+ </DismissableLayer>
51
158
  );
52
159
  },
53
160
  );
@@ -7,7 +7,6 @@ interface AutoCompleteOption {
7
7
  value: string;
8
8
  label: string;
9
9
  tags?: string[];
10
- filteringTags?: string[];
11
10
  description?: string;
12
11
  }
13
12
 
@@ -1,51 +1,105 @@
1
1
  import React, { forwardRef, useState } from "react";
2
- import { Popover } from "../../popover";
2
+ import { Chips } from "../../chips";
3
+ import { HStack } from "../../primitives/stack";
3
4
  import { cl } from "../../utils/helpers";
4
5
  import { AutoSuggest } from "./AutoSuggest";
6
+ import { AutoCompleteOption } from "./AutoSuggest.types";
5
7
  import type {
6
- ParsedOption,
7
- ParsedProperty,
8
- QueryFilterQuery,
9
- QueryFilteringOptions,
10
- QueryFilteringProperties,
8
+ ExternalOptions,
9
+ ExternalPropertyDefinitions,
10
+ ExternalQuery,
11
+ ExternalToken,
12
+ InternalPropertyDefinition,
13
+ InternalPropertyOption,
14
+ OperationT,
11
15
  } from "./TokenFilter.types";
12
16
  import { generateAutoCompleteOptions } from "./helpers/generate-autocomplete-options";
13
17
  import { parseQueryText } from "./helpers/parse-query-text";
14
18
 
15
19
  type TokenFilterProps = {
16
- query: QueryFilterQuery;
17
- onChange: (newQuery: QueryFilterQuery) => void;
20
+ query: ExternalQuery;
21
+ onChange: (newQuery: ExternalQuery) => void;
18
22
  className?: string;
19
- filteringOptions: QueryFilteringOptions;
20
- filteringProperties: QueryFilteringProperties;
23
+ propertyDefinitions: ExternalPropertyDefinitions;
24
+ options: ExternalOptions;
21
25
  };
22
26
 
27
+ /**
28
+ * TODO:
29
+ * - Implement onChange handler to update query state when user selects an autocomplete option.
30
+ * - Handle token rendering and editing (e.g., show tokens for matched properties/operators/values, allow deleting tokens).
31
+ */
23
32
  export const TokenFilter = forwardRef<HTMLDivElement, TokenFilterProps>(
24
- ({ query, className, filteringProperties, filteringOptions }, ref) => {
25
- const [inputAnchor, setInputAnchor] = useState<HTMLInputElement | null>(
26
- null,
27
- );
28
-
33
+ ({ query, className, propertyDefinitions, options, onChange }, ref) => {
29
34
  const [filterText, setFilterText] = useState<string>("");
30
- const { properties, options } = derrivedFilterState(
31
- filteringProperties,
32
- filteringOptions,
33
- );
35
+ const [open, setOpen] = useState(false);
34
36
 
35
- const queryState = parseQueryText(filterText, properties);
37
+ const { parsedPropertyDefinitions, parsedPropertyOptions } =
38
+ derrivedFilterState(propertyDefinitions, options);
39
+
40
+ const queryState = parseQueryText(filterText, parsedPropertyDefinitions);
36
41
 
37
42
  const autoCompleteOptions = generateAutoCompleteOptions(
38
43
  queryState,
39
- properties,
40
- options,
44
+ parsedPropertyDefinitions,
45
+ parsedPropertyOptions,
41
46
  );
42
47
 
43
- const handleSelectOption = (value: string) => {
44
- setFilterText(value);
45
- setCustomOpen(false);
48
+ const { addToken, removeToken } = createActionHandlers({
49
+ query,
50
+ onChange,
51
+ });
52
+
53
+ const createToken = (newText: string): boolean => {
54
+ const newQueryState = parseQueryText(newText, parsedPropertyDefinitions);
55
+
56
+ let newToken: ExternalToken | null = null;
57
+
58
+ switch (newQueryState.step) {
59
+ case "property": {
60
+ if (newQueryState.value === "") {
61
+ return false;
62
+ }
63
+ newToken = {
64
+ propertyKey: newQueryState.property.key,
65
+ operator: newQueryState.operator,
66
+ value: newQueryState.value,
67
+ };
68
+ break;
69
+ }
70
+ case "free-text": {
71
+ break;
72
+ }
73
+ case "operator": {
74
+ break;
75
+ }
76
+ }
77
+ if (newToken) {
78
+ addToken(newToken);
79
+ setFilterText("");
80
+ return true;
81
+ }
82
+ return false;
46
83
  };
47
84
 
48
- const [customOpen, setCustomOpen] = useState(false);
85
+ const handleSelectOption = (option: AutoCompleteOption) => {
86
+ const newQueryState = parseQueryText(
87
+ option.value,
88
+ parsedPropertyDefinitions,
89
+ );
90
+
91
+ if (
92
+ (newQueryState.step === "property" && newQueryState.value === "") ||
93
+ newQueryState.step === "operator"
94
+ ) {
95
+ /* Add space after for better formatting */
96
+ /* TODO: Handle this scenario better */
97
+ setFilterText(`${option.value} `);
98
+ return false;
99
+ }
100
+ setFilterText(option.value);
101
+ return createToken(option.value);
102
+ };
49
103
 
50
104
  return (
51
105
  <div
@@ -53,77 +107,121 @@ export const TokenFilter = forwardRef<HTMLDivElement, TokenFilterProps>(
53
107
  className={cl("aksel-property-filter", className)}
54
108
  role="search"
55
109
  >
56
- <input
57
- type="text"
58
- className="aksel-property-filter__input"
59
- placeholder="Type to filter..."
60
- ref={setInputAnchor}
110
+ <AutoSuggest
111
+ onSelect={handleSelectOption}
112
+ options={autoCompleteOptions.options}
61
113
  value={filterText}
62
- onChange={(e) => setFilterText(e.target.value)}
63
- onFocus={() => setCustomOpen(true)}
114
+ onChange={setFilterText}
115
+ open={open}
116
+ setOpen={setOpen}
64
117
  />
65
- <Popover
66
- anchorEl={inputAnchor}
67
- open={customOpen}
68
- onClose={() => {
69
- setFilterText("");
70
- setCustomOpen(false);
71
- }}
72
- >
73
- <AutoSuggest
74
- /* @ts-expect-error TODO: handle conversion better */
75
- options={autoCompleteOptions.options}
76
- onSelect={handleSelectOption}
77
- />
78
- </Popover>
79
- {query.tokens.map((token, index) => {
80
- return (
81
- <div key={index} className="aksel-property-filter__token">
82
- <strong>{token.propertyKey}</strong> {token.operator}{" "}
83
- </div>
84
- );
85
- })}
86
- <ul>
87
- {filteringProperties.map((prop) => (
88
- <li key={prop.key}>{prop.propertyLabel}</li>
89
- ))}
90
- </ul>
91
- <pre>{JSON.stringify(queryState, null, 2)}</pre>
92
- <pre>{JSON.stringify(autoCompleteOptions, null, 2)}</pre>
118
+ <HStack marginBlock="space-8" gap="space-8">
119
+ {query.tokens.map((token, index) => {
120
+ return (
121
+ <React.Fragment
122
+ key={`${token.propertyKey}-${token.operator}-${token.value}-${index}`}
123
+ >
124
+ <Chips.Removable
125
+ key={index}
126
+ onClick={() => {
127
+ removeToken(index);
128
+ }}
129
+ >
130
+ {`${token.propertyKey} ${token.operator} ${token.value}`}
131
+ </Chips.Removable>
132
+ {index < query.tokens.length - 1 && (
133
+ <span>{query.operation}</span>
134
+ )}
135
+ </React.Fragment>
136
+ );
137
+ })}
138
+ </HStack>
93
139
  </div>
94
140
  );
95
141
  },
96
142
  );
97
143
 
98
144
  function derrivedFilterState(
99
- filteringProperties: QueryFilteringProperties,
100
- filteringOptions: QueryFilteringOptions,
101
- /* query: QueryFilterQuery */
145
+ propertyDefinitions: ExternalPropertyDefinitions,
146
+ propteryOptions: ExternalOptions,
102
147
  ): {
103
- properties: ParsedProperty[];
104
- options: ParsedOption[];
148
+ parsedPropertyDefinitions: InternalPropertyDefinition[];
149
+ parsedPropertyOptions: InternalPropertyOption[];
105
150
  } {
106
- const propertyMap = new Map<string, any>();
151
+ const propertyMap = new Map<string, InternalPropertyDefinition>();
107
152
 
108
- for (const property of filteringProperties) {
153
+ for (const property of propertyDefinitions) {
109
154
  propertyMap.set(property.key, {
110
- propertyKey: property.key,
111
- propertyLabel: property?.propertyLabel ?? "",
112
- groupValuesLabel: property?.groupValuesLabel ?? "",
113
- propertyGroup: property?.group,
155
+ key: property.key,
156
+ label: property?.label ?? "",
157
+ groupLabel: property?.groupLabel ?? "",
158
+ group: property?.group ?? "",
114
159
  operators: property?.operators ?? [],
115
- /* defaultOperator: property?.defaultOperator ?? '=', */
116
160
  externalProperty: property,
117
161
  });
118
162
  }
119
163
 
120
- const internalOptions = filteringOptions.map((option) => ({
121
- property: propertyMap.get(option.propertyKey) ?? null,
122
- value: option.value,
123
- label: option.label ?? option.value ?? "",
124
- tags: option.tags ?? [],
125
- filteringTags: option.filteringTags ?? [],
126
- }));
164
+ const internalOptions: InternalPropertyOption[] = [];
165
+
166
+ for (const option of propteryOptions) {
167
+ internalOptions.push({
168
+ property: propertyMap.get(option.propertyKey) ?? null,
169
+ value: option.value,
170
+ label: option.label ?? option.value ?? "",
171
+ tags: option.tags ?? [],
172
+ });
173
+ }
174
+
175
+ return {
176
+ parsedPropertyDefinitions: [...propertyMap.values()],
177
+ parsedPropertyOptions: internalOptions,
178
+ };
179
+ }
180
+
181
+ function createActionHandlers({
182
+ query,
183
+ onChange,
184
+ }: {
185
+ query: ExternalQuery;
186
+ onChange: (newQuery: ExternalQuery) => void;
187
+ }) {
188
+ const handleChange = (newQuery: ExternalQuery) => {
189
+ onChange(newQuery);
190
+ };
191
+
192
+ const addToken = (token: ExternalToken) => {
193
+ handleChange({ ...query, tokens: [...query.tokens, token] });
194
+ };
195
+
196
+ const updateToken = (updateIndex: number, updatedToken: ExternalToken) => {
197
+ handleChange({
198
+ ...query,
199
+ tokens: query.tokens.map((token, index) =>
200
+ index === updateIndex ? updatedToken : token,
201
+ ),
202
+ });
203
+ };
204
+
205
+ const updateOperation = (operation: OperationT) => {
206
+ handleChange({ ...query, operation });
207
+ };
208
+
209
+ const removeToken = (removeIndex: number) => {
210
+ handleChange({
211
+ ...query,
212
+ tokens: query.tokens.filter((_, index) => index !== removeIndex),
213
+ });
214
+ };
215
+
216
+ const removeAllTokens = () => {
217
+ handleChange({ ...query, tokens: [] });
218
+ };
127
219
 
128
- return { properties: [...propertyMap.values()], options: internalOptions };
220
+ return {
221
+ addToken,
222
+ updateToken,
223
+ updateOperation,
224
+ removeToken,
225
+ removeAllTokens,
226
+ };
129
227
  }
@@ -1,4 +1,5 @@
1
- type QueryFilterOperator =
1
+ /* External API */
2
+ type OperatorT =
2
3
  | "<"
3
4
  | "<="
4
5
  | ">"
@@ -11,75 +12,100 @@ type QueryFilterOperator =
11
12
  | "!^"
12
13
  | (string & {});
13
14
 
14
- type QueryFilterOperation = "and" | "or";
15
+ type OperationT = "and" | "or";
15
16
 
16
- type QueryFilterToken = {
17
+ type ExternalToken = {
17
18
  propertyKey: string;
18
- operator: QueryFilterOperator;
19
- value: any;
19
+ operator: OperatorT;
20
+ value: string;
20
21
  };
21
22
 
22
- type QueryFilterQuery = {
23
- tokens: QueryFilterToken[];
24
- operation: QueryFilterOperation;
23
+ type ExternalQuery = {
24
+ tokens: ExternalToken[];
25
+ operation: OperationT;
25
26
  };
26
27
 
27
- type QueryFilteringOption = {
28
+ type ExternalOption = {
28
29
  propertyKey: string;
29
- value: any;
30
+ value: string;
30
31
  label?: string;
31
32
  tags?: string[];
32
- filteringTags?: string[];
33
33
  disabled?: boolean;
34
34
  };
35
35
 
36
- type QueryFilteringOptions = QueryFilteringOption[];
36
+ type ExternalOptions = ExternalOption[];
37
37
 
38
- type QueryFilteringOptionGroup = {
38
+ type ExternalPropertyGroup = {
39
39
  label: string;
40
- options: QueryFilteringOptions;
40
+ options: ExternalOptions;
41
41
  };
42
42
 
43
- type QueryFilteringScopedOperator =
44
- | string
45
- | { operator: string; tokenType: "single" | "multiple" };
46
-
47
- type QueryFilteringProperty = {
43
+ type ExternalPropertyDefinition = {
48
44
  key: string;
49
- propertyLabel: string;
50
- groupValuesLabel?: string;
45
+ label: string;
46
+ groupLabel?: string;
51
47
  group?: string;
52
- operators?: QueryFilteringScopedOperator[];
48
+ operators?: ExternalPropertyOperator[];
53
49
  };
54
50
 
55
- type QueryFilteringProperties = QueryFilteringProperty[];
51
+ type ExternalPropertyDefinitions = ExternalPropertyDefinition[];
56
52
 
57
- type ParsedProperty = {
58
- propertyKey: string;
59
- propertyLabel: string;
60
- groupValuesLabel: string;
61
- propertyGroup: string;
62
- operators: QueryFilteringScopedOperator[];
63
- externalProperty: QueryFilteringProperty;
53
+ type ExternalPropertyOperator =
54
+ | string
55
+ | { operator: string; type: "single" | "multiple" };
56
+
57
+ export type {
58
+ ExternalOption,
59
+ ExternalOptions,
60
+ ExternalPropertyDefinition,
61
+ ExternalPropertyDefinitions,
62
+ ExternalPropertyGroup,
63
+ ExternalQuery,
64
+ ExternalToken,
65
+ OperationT,
66
+ OperatorT,
67
+ };
68
+
69
+ /* Internal API */
70
+ type InternalPropertyDefinition = {
71
+ key: string;
72
+ label: string;
73
+ groupLabel: string;
74
+ group: string;
75
+ operators: ExternalPropertyOperator[];
76
+ externalProperty: ExternalPropertyDefinition;
64
77
  };
65
78
 
66
- type ParsedOption = {
67
- property: ParsedProperty | null;
68
- value: any;
79
+ type InternalPropertyOption = {
80
+ property: InternalPropertyDefinition | null;
81
+ value: string;
69
82
  label: string;
70
83
  tags: string[];
71
- filteringTags: string[];
72
84
  };
73
85
 
86
+ type InternalParsedTextState =
87
+ | {
88
+ /** User has typed property + complete operator + value (e.g., "Status != active") */
89
+ step: "property";
90
+ property: InternalPropertyDefinition;
91
+ operator: OperatorT;
92
+ value: string;
93
+ }
94
+ | {
95
+ /** User is typing the operator after property (e.g., "Status !") */
96
+ step: "operator";
97
+ property: InternalPropertyDefinition;
98
+ operatorPrefix: string;
99
+ }
100
+ | {
101
+ /** No property match; treat as free-text search */
102
+ step: "free-text";
103
+ value: string;
104
+ operator?: OperatorT;
105
+ };
106
+
74
107
  export type {
75
- QueryFilterOperator,
76
- QueryFilterQuery,
77
- QueryFilteringOptions,
78
- QueryFilteringProperty,
79
- QueryFilterOperation,
80
- QueryFilteringProperties,
81
- ParsedProperty,
82
- ParsedOption,
83
- QueryFilteringOption,
84
- QueryFilteringOptionGroup,
108
+ InternalPropertyDefinition,
109
+ InternalPropertyOption,
110
+ InternalParsedTextState,
85
111
  };