@helsenorge/designsystem-react 16.0.0 → 16.2.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 (136) hide show
  1. package/lib/Button.js +8 -9
  2. package/lib/Button.js.map +1 -1
  3. package/lib/CHANGELOG.md +24 -0
  4. package/lib/Checkbox.js +0 -2
  5. package/lib/Checkbox.js.map +1 -1
  6. package/lib/Drawer.js +30 -30
  7. package/lib/Drawer.js.map +1 -1
  8. package/lib/Expander.js +1 -0
  9. package/lib/Expander.js.map +1 -1
  10. package/lib/HorizontalScroll.js +6 -10
  11. package/lib/HorizontalScroll.js.map +1 -1
  12. package/lib/PopOver.js +2 -3
  13. package/lib/PopOver.js.map +1 -1
  14. package/lib/Radio.js +0 -2
  15. package/lib/Radio.js.map +1 -1
  16. package/lib/StackedCellContent.js +15 -0
  17. package/lib/StackedCellContent.js.map +1 -0
  18. package/lib/TabPanel.js +1 -0
  19. package/lib/TabPanel.js.map +1 -1
  20. package/lib/TableContext.js +26 -0
  21. package/lib/TableContext.js.map +1 -0
  22. package/lib/UNSAFE_TableBody.js +22 -0
  23. package/lib/UNSAFE_TableBody.js.map +1 -0
  24. package/lib/UNSAFE_TableCell.js +26 -0
  25. package/lib/UNSAFE_TableCell.js.map +1 -0
  26. package/lib/UNSAFE_TableDetail.js +17 -0
  27. package/lib/UNSAFE_TableDetail.js.map +1 -0
  28. package/lib/UNSAFE_TableExpandedRow.js +46 -0
  29. package/lib/UNSAFE_TableExpandedRow.js.map +1 -0
  30. package/lib/UNSAFE_TableHead.js +25 -0
  31. package/lib/UNSAFE_TableHead.js.map +1 -0
  32. package/lib/UNSAFE_TableHeadCell.js +182 -0
  33. package/lib/UNSAFE_TableHeadCell.js.map +1 -0
  34. package/lib/UNSAFE_TableRow.js +130 -0
  35. package/lib/UNSAFE_TableRow.js.map +1 -0
  36. package/lib/__mocks__/ResizeObserver.js +4 -1
  37. package/lib/__mocks__/ResizeObserver.js.map +1 -1
  38. package/lib/components/Button/Button.d.ts +1 -1
  39. package/lib/components/Button/styles.module.scss +0 -4
  40. package/lib/components/Button/styles.module.scss.d.ts +0 -1
  41. package/lib/components/Checkbox/styles.module.scss +5 -5
  42. package/lib/components/Checkbox/styles.module.scss.d.ts +12 -1
  43. package/lib/components/Drawer/DrawerHeaderContent.module.scss +10 -1
  44. package/lib/components/Drawer/styles.module.scss +6 -2
  45. package/lib/components/Duolist/styles.module.scss +3 -7
  46. package/lib/components/Expander/styles.module.scss.d.ts +0 -1
  47. package/lib/components/ExpanderHierarchy/index.js +1 -0
  48. package/lib/components/ExpanderHierarchy/index.js.map +1 -1
  49. package/lib/components/ExpanderList/index.js +1 -0
  50. package/lib/components/ExpanderList/index.js.map +1 -1
  51. package/lib/components/ExpanderList/styles.module.scss +1 -0
  52. package/lib/components/HorizontalScroll/HorizontalScroll.d.ts +0 -4
  53. package/lib/components/HorizontalScroll/styles.module.scss +1 -4
  54. package/lib/components/HorizontalScroll/styles.module.scss.d.ts +0 -1
  55. package/lib/components/Label/Label.d.ts +2 -0
  56. package/lib/components/Label/styles.module.scss +16 -0
  57. package/lib/components/Label/utils.d.ts +0 -1
  58. package/lib/components/Modal/index.js +5 -2
  59. package/lib/components/Modal/index.js.map +1 -1
  60. package/lib/components/Panel/styles.module.scss.d.ts +0 -1
  61. package/lib/components/PopMenu/PopMenu.d.ts +4 -0
  62. package/lib/components/PopMenu/index.js +3 -1
  63. package/lib/components/PopMenu/index.js.map +1 -1
  64. package/lib/components/Radio/styles.module.scss +6 -6
  65. package/lib/components/Radio/styles.module.scss.d.ts +12 -1
  66. package/lib/components/Table/index.js +0 -1
  67. package/lib/components/Table/index.js.map +1 -1
  68. package/lib/components/UNSAFE_Table/StackedCellContent.d.ts +9 -0
  69. package/lib/components/UNSAFE_Table/TableCaption.d.ts +10 -0
  70. package/lib/components/UNSAFE_Table/TableContext.d.ts +22 -0
  71. package/lib/components/UNSAFE_Table/UNSAFE_Table.d.ts +37 -0
  72. package/lib/components/UNSAFE_Table/UNSAFE_TableBody/UNSAFE_TableBody.d.ts +10 -0
  73. package/lib/components/UNSAFE_Table/UNSAFE_TableBody/index.d.ts +3 -0
  74. package/lib/components/UNSAFE_Table/UNSAFE_TableBody/index.js +2 -0
  75. package/lib/components/UNSAFE_Table/UNSAFE_TableCell/UNSAFE_TableCell.d.ts +15 -0
  76. package/lib/components/UNSAFE_Table/UNSAFE_TableCell/index.d.ts +3 -0
  77. package/lib/components/UNSAFE_Table/UNSAFE_TableCell/index.js +7 -0
  78. package/lib/components/UNSAFE_Table/UNSAFE_TableCell/index.js.map +1 -0
  79. package/lib/components/UNSAFE_Table/UNSAFE_TableDetail/UNSAFE_TableDetail.d.ts +12 -0
  80. package/lib/components/UNSAFE_Table/UNSAFE_TableDetail/index.d.ts +3 -0
  81. package/lib/components/UNSAFE_Table/UNSAFE_TableDetail/index.js +7 -0
  82. package/lib/components/UNSAFE_Table/UNSAFE_TableDetail/index.js.map +1 -0
  83. package/lib/components/UNSAFE_Table/UNSAFE_TableExpandedRow/UNSAFE_TableExpandedRow.d.ts +17 -0
  84. package/lib/components/UNSAFE_Table/UNSAFE_TableExpandedRow/index.d.ts +3 -0
  85. package/lib/components/UNSAFE_Table/UNSAFE_TableExpandedRow/index.js +7 -0
  86. package/lib/components/UNSAFE_Table/UNSAFE_TableExpandedRow/index.js.map +1 -0
  87. package/lib/components/UNSAFE_Table/UNSAFE_TableExpanderCell/UNSAFE_TableExpanderCellMobile.d.ts +4 -0
  88. package/lib/components/UNSAFE_Table/UNSAFE_TableHead/UNSAFE_TableHead.d.ts +8 -0
  89. package/lib/components/UNSAFE_Table/UNSAFE_TableHead/index.d.ts +3 -0
  90. package/lib/components/UNSAFE_Table/UNSAFE_TableHead/index.js +7 -0
  91. package/lib/components/UNSAFE_Table/UNSAFE_TableHead/index.js.map +1 -0
  92. package/lib/components/UNSAFE_Table/UNSAFE_TableHeadCell/UNSAFE_TableHeadCell.d.ts +15 -0
  93. package/lib/components/UNSAFE_Table/UNSAFE_TableHeadCell/index.d.ts +3 -0
  94. package/lib/components/UNSAFE_Table/UNSAFE_TableHeadCell/index.js +2 -0
  95. package/lib/components/UNSAFE_Table/UNSAFE_TablePopMenuCell/UNSAFE_TablePopMenuCell.d.ts +8 -0
  96. package/lib/components/UNSAFE_Table/UNSAFE_TableRow/UNSAFE_TableRow.d.ts +20 -0
  97. package/lib/components/UNSAFE_Table/UNSAFE_TableRow/index.d.ts +3 -0
  98. package/lib/components/UNSAFE_Table/UNSAFE_TableRow/index.js +2 -0
  99. package/lib/components/UNSAFE_Table/constants.d.ts +29 -0
  100. package/lib/components/UNSAFE_Table/index.d.ts +19 -0
  101. package/lib/components/UNSAFE_Table/index.js +182 -0
  102. package/lib/components/UNSAFE_Table/index.js.map +1 -0
  103. package/lib/components/UNSAFE_Table/resourceHelper.d.ts +3 -0
  104. package/lib/components/UNSAFE_Table/styles.module.scss +621 -0
  105. package/lib/components/UNSAFE_Table/styles.module.scss.d.ts +62 -0
  106. package/lib/components/UNSAFE_Table/useTableExpandedRows.d.ts +12 -0
  107. package/lib/components/UNSAFE_Table/useTableShowMore.d.ts +25 -0
  108. package/lib/components/UNSAFE_Table/utils.d.ts +47 -0
  109. package/lib/constants3.js +42 -0
  110. package/lib/constants3.js.map +1 -0
  111. package/lib/floating-ui.react.js +2 -2
  112. package/lib/floating-ui.react.js.map +1 -1
  113. package/lib/hooks/useSort.d.ts +88 -0
  114. package/lib/hooks/useSort.js +91 -0
  115. package/lib/hooks/useSort.js.map +1 -0
  116. package/lib/hooks/useSort.test.js +11739 -0
  117. package/lib/hooks/useSort.test.js.map +1 -0
  118. package/lib/hooks/useSynchronousResizeObserver.d.ts +11 -0
  119. package/lib/hooks/useSynchronousResizeObserver.js +29 -0
  120. package/lib/hooks/useSynchronousResizeObserver.js.map +1 -0
  121. package/lib/resources/HN.Designsystem.Table.nb-NO.json.d.ts +9 -0
  122. package/lib/resources/Resources.d.ts +19 -0
  123. package/lib/rolldown-runtime.js +33 -0
  124. package/lib/scss/_font-mixins.scss +28 -1
  125. package/lib/scss/typography.module.scss +8 -0
  126. package/lib/scss/typography.module.scss.d.ts +2 -0
  127. package/lib/scss/typography.stories.tsx +8 -0
  128. package/lib/utils/uuid.js +1 -12
  129. package/lib/utils/uuid.js.map +1 -1
  130. package/lib/utils2.js +18 -14
  131. package/lib/utils2.js.map +1 -1
  132. package/package.json +54 -2
  133. package/scss/_font-mixins.scss +28 -1
  134. package/scss/typography.module.scss +8 -0
  135. package/scss/typography.module.scss.d.ts +2 -0
  136. package/scss/typography.stories.tsx +8 -0
@@ -0,0 +1,88 @@
1
+ import { SortDirection } from '../components/UNSAFE_Table/constants';
2
+ /** Values that should never be treated as "an object to sort one level deeper into" */
3
+ type SortLeaf = string | number | boolean | bigint | symbol | null | undefined | Date | readonly unknown[] | ((...args: never[]) => unknown);
4
+ /**
5
+ * A sort key for TData: rejects unknown property names (so typos are caught), while still allowing
6
+ * one level of dot-notation into a nested property (e.g. "kontor.navn"). Nested properties with dynamic
7
+ * keys (e.g. Record<string, number>) accept any suffix after the dot, since their keys aren't statically known.
8
+ */
9
+ export type SortKey<TData> = {
10
+ [K in Extract<keyof TData, string>]: NonNullable<TData[K]> extends SortLeaf ? K : K | `${K}.${Extract<keyof NonNullable<TData[K]>, string>}`;
11
+ }[Extract<keyof TData, string>];
12
+ export type SortValueGetter<TData> = (item: TData, sortKey: SortKey<TData>) => unknown;
13
+ /** A comparator function with the same signature as Array.prototype.sort */
14
+ export type SortComparer<TData> = (a: TData, b: TData) => number;
15
+ /** A named sort option, e.g. for a FilterSort dropdown, mapping an option key to a column key + direction. */
16
+ export interface SortOption<TData> {
17
+ columnKey: SortKey<TData>;
18
+ direction?: SortDirection;
19
+ }
20
+ /** Decides whether a sortOptions entry matches the current sort state. Default: compareSortOption. */
21
+ export type SortOptionMatcher<TData> = (option: SortOption<TData>, columnKey: SortKey<TData> | undefined, direction: SortDirection | undefined) => boolean;
22
+ export interface SortableHeadCellProps {
23
+ /** Sort direction, for displaying the current sort state on the column header (e.g. aria-sort) */
24
+ sortDir?: SortDirection;
25
+ }
26
+ export interface UseSortOptions<TData> {
27
+ /** The items to sort */
28
+ data: TData[];
29
+ /** Key to sort by on mount (uncontrolled mode) */
30
+ initialSortColumnKey?: SortKey<TData>;
31
+ /** Sort direction on mount (uncontrolled mode). Default: SortDirection.asc */
32
+ initialSortDirection?: SortDirection;
33
+ /** Currently sorted key (controlled mode). When set, the consumer owns the sort state and must update it based on onSortChange. */
34
+ sortColumnKey?: SortKey<TData>;
35
+ /** Current sort direction (controlled mode). Default: SortDirection.asc */
36
+ sortDirection?: SortDirection;
37
+ /** Disable internal sorting of data, e.g. when sorting happens server-side. requestSort still updates state and calls onSortChange. */
38
+ disableInternalSort?: boolean;
39
+ /**
40
+ * Get the value to sort an item by. Default: reads the sort key as a property from the item, with support for dot-notation paths like "person.name".
41
+ * Memoize this (e.g. with useCallback) if provided inline, otherwise sortedData recomputes on every render.
42
+ */
43
+ getSortValue?: SortValueGetter<TData>;
44
+ /**
45
+ * Custom comparators per sort key, for keys that need their own ordering (e.g. status ranking). The comparator should sort ascending; direction is applied by the hook.
46
+ * Memoize this (e.g. with useMemo) if provided inline, otherwise sortedData recomputes on every render.
47
+ */
48
+ sorters?: Partial<Record<SortKey<TData>, SortComparer<TData>>>;
49
+ /** Called whenever the sort changes */
50
+ onSortChange?: (columnKey: SortKey<TData>, sortDirection: SortDirection) => void;
51
+ /**
52
+ * Named sort options (e.g. for a FilterSort dropdown), each mapping an option key to a column key + direction.
53
+ * When provided, the hook exposes `sortOptionKey` (the option matching the current sort, if any) and
54
+ * `setSortOption` (set the sort directly from an option's key, e.g. straight from a <select>'s onChange).
55
+ */
56
+ sortOptions?: Record<string, SortOption<TData>>;
57
+ /**
58
+ * Custom matcher for deciding which sortOptions entry matches the current sort. Default: matches on exact
59
+ * columnKey, treating a missing direction as SortDirection.asc. Only needed for non-standard matching.
60
+ */
61
+ matchSortOption?: SortOptionMatcher<TData>;
62
+ }
63
+ export interface UseSortReturn<TData> {
64
+ /** The items, sorted by the current sort (or as-is when disableInternalSort is set) */
65
+ sortedData: TData[];
66
+ /** The key currently sorted by */
67
+ sortColumnKey: SortKey<TData> | undefined;
68
+ /** The current sort direction */
69
+ sortDirection: SortDirection | undefined;
70
+ /** Sort by a key. Toggles direction when the key is already sorted. */
71
+ requestSort: (columnKey: SortKey<TData>) => void;
72
+ /** Set both sort key and direction directly, without toggling. Useful when a control (e.g. a FilterSort dropdown) maps its own options to a fixed (key, direction) pair. */
73
+ setSort: (columnKey: SortKey<TData>, direction?: SortDirection) => void;
74
+ /** Get props for TableHeadCell to display the current sort state (aria-sort). Sorting itself is triggered by the consumer, e.g. via requestSort/onSortChange from a FilterSort control. */
75
+ getSortProps: (columnKey: SortKey<TData>) => SortableHeadCellProps;
76
+ /** The sortOptions key matching the current sort state, if any. undefined if sortOptions wasn't provided or none match. */
77
+ sortOptionKey: string | undefined;
78
+ /** Set the sort from a sortOptions key, e.g. straight from a <select>'s onChange. No-ops if the key isn't found in sortOptions. */
79
+ setSortOption: (optionKey: string) => void;
80
+ }
81
+ /**
82
+ * Sort state and type-aware sorting for any list (tables, filtered result lists etc.).
83
+ * Strings sort with Norwegian locale and numeric collation, numbers/booleans/Dates sort natively,
84
+ * and null/undefined always sort last. Supports uncontrolled mode (internal state),
85
+ * controlled mode (sortColumnKey/sortDirection props) and server-side sorting (disableInternalSort).
86
+ */
87
+ export declare const useSort: <TData>(options: UseSortOptions<TData>) => UseSortReturn<TData>;
88
+ export {};
@@ -0,0 +1,91 @@
1
+ import { n as SortDirection } from "../constants3.js";
2
+ import { useMemo, useState } from "react";
3
+ //#region src/hooks/useSort.ts
4
+ var getValueByPath = (item, path) => path.split(".").reduce((value, key) => value !== null && typeof value === "object" ? value[key] : void 0, item);
5
+ var compareSortValues = (a, b) => {
6
+ if (a === null || a === void 0) return b === null || b === void 0 ? 0 : 1;
7
+ if (b === null || b === void 0) return -1;
8
+ if (typeof a === "number" && typeof b === "number") return a - b;
9
+ if (typeof a === "boolean" && typeof b === "boolean") return Number(a) - Number(b);
10
+ if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
11
+ return String(a).localeCompare(String(b), "nb", {
12
+ numeric: true,
13
+ sensitivity: "base"
14
+ });
15
+ };
16
+ var sortBy = (accessor, direction = SortDirection.asc) => (a, b) => {
17
+ const aValue = accessor(a);
18
+ const bValue = accessor(b);
19
+ if (aValue === null || aValue === void 0 || bValue === null || bValue === void 0) return compareSortValues(aValue, bValue);
20
+ const result = compareSortValues(aValue, bValue);
21
+ return direction === SortDirection.desc ? -result : result;
22
+ };
23
+ var defaultMatchSortOption = (option, columnKey, direction) => option.columnKey === columnKey && (option.direction ?? SortDirection.asc) === (direction ?? SortDirection.asc);
24
+ /**
25
+ * Sort state and type-aware sorting for any list (tables, filtered result lists etc.).
26
+ * Strings sort with Norwegian locale and numeric collation, numbers/booleans/Dates sort natively,
27
+ * and null/undefined always sort last. Supports uncontrolled mode (internal state),
28
+ * controlled mode (sortColumnKey/sortDirection props) and server-side sorting (disableInternalSort).
29
+ */
30
+ var useSort = (options) => {
31
+ const { data, initialSortColumnKey, initialSortDirection, sortColumnKey, sortDirection, disableInternalSort, getSortValue, sorters, onSortChange, sortOptions, matchSortOption = defaultMatchSortOption } = options;
32
+ const [internalSort, setInternalSort] = useState(() => initialSortColumnKey ? {
33
+ columnKey: initialSortColumnKey,
34
+ direction: initialSortDirection ?? SortDirection.asc
35
+ } : void 0);
36
+ const isControlled = sortColumnKey !== void 0;
37
+ const sort = isControlled ? {
38
+ columnKey: sortColumnKey,
39
+ direction: sortDirection ?? SortDirection.asc
40
+ } : internalSort;
41
+ const requestSort = (columnKey) => {
42
+ const direction = sort?.columnKey === columnKey && sort.direction === SortDirection.asc ? SortDirection.desc : SortDirection.asc;
43
+ if (!isControlled) setInternalSort({
44
+ columnKey,
45
+ direction
46
+ });
47
+ onSortChange?.(columnKey, direction);
48
+ };
49
+ const setSort = (columnKey, direction = SortDirection.asc) => {
50
+ if (!isControlled) setInternalSort({
51
+ columnKey,
52
+ direction
53
+ });
54
+ onSortChange?.(columnKey, direction);
55
+ };
56
+ const currentSortColumnKey = sort?.columnKey;
57
+ const currentSortDirection = sort?.direction;
58
+ const sortedData = useMemo(() => {
59
+ if (!currentSortColumnKey || disableInternalSort) return data;
60
+ const customComparer = sorters?.[currentSortColumnKey];
61
+ const comparer = customComparer ? (a, b) => currentSortDirection === SortDirection.desc ? -customComparer(a, b) : customComparer(a, b) : sortBy((item) => (getSortValue ?? getValueByPath)(item, currentSortColumnKey), currentSortDirection);
62
+ return [...data].sort(comparer);
63
+ }, [
64
+ data,
65
+ currentSortColumnKey,
66
+ currentSortDirection,
67
+ disableInternalSort,
68
+ getSortValue,
69
+ sorters
70
+ ]);
71
+ const getSortProps = (columnKey) => ({ sortDir: sort?.columnKey === columnKey ? sort.direction : void 0 });
72
+ const sortOptionKey = sortOptions ? Object.entries(sortOptions).find(([, option]) => matchSortOption(option, currentSortColumnKey, currentSortDirection))?.[0] : void 0;
73
+ const setSortOption = (optionKey) => {
74
+ const option = sortOptions?.[optionKey];
75
+ if (option) setSort(option.columnKey, option.direction);
76
+ };
77
+ return {
78
+ sortedData,
79
+ sortColumnKey: sort?.columnKey,
80
+ sortDirection: sort?.direction,
81
+ requestSort,
82
+ setSort,
83
+ getSortProps,
84
+ sortOptionKey,
85
+ setSortOption
86
+ };
87
+ };
88
+ //#endregion
89
+ export { useSort };
90
+
91
+ //# sourceMappingURL=useSort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSort.js","names":[],"sources":["../../src/hooks/useSort.ts"],"sourcesContent":["import { useMemo, useState } from 'react';\n\nimport { SortDirection } from '../components/UNSAFE_Table/constants';\n\n/** Values that should never be treated as \"an object to sort one level deeper into\" */\ntype SortLeaf =\n string | number | boolean | bigint | symbol | null | undefined | Date | readonly unknown[] | ((...args: never[]) => unknown);\n\n/**\n * A sort key for TData: rejects unknown property names (so typos are caught), while still allowing\n * one level of dot-notation into a nested property (e.g. \"kontor.navn\"). Nested properties with dynamic\n * keys (e.g. Record<string, number>) accept any suffix after the dot, since their keys aren't statically known.\n */\nexport type SortKey<TData> = {\n [K in Extract<keyof TData, string>]: NonNullable<TData[K]> extends SortLeaf\n ? K\n : K | `${K}.${Extract<keyof NonNullable<TData[K]>, string>}`;\n}[Extract<keyof TData, string>];\n\nexport type SortValueGetter<TData> = (item: TData, sortKey: SortKey<TData>) => unknown;\n\n/** A comparator function with the same signature as Array.prototype.sort */\nexport type SortComparer<TData> = (a: TData, b: TData) => number;\n\n/** A named sort option, e.g. for a FilterSort dropdown, mapping an option key to a column key + direction. */\nexport interface SortOption<TData> {\n columnKey: SortKey<TData>;\n direction?: SortDirection;\n}\n\n/** Decides whether a sortOptions entry matches the current sort state. Default: compareSortOption. */\nexport type SortOptionMatcher<TData> = (\n option: SortOption<TData>,\n columnKey: SortKey<TData> | undefined,\n direction: SortDirection | undefined\n) => boolean;\n\nexport interface SortableHeadCellProps {\n /** Sort direction, for displaying the current sort state on the column header (e.g. aria-sort) */\n sortDir?: SortDirection;\n}\n\nexport interface UseSortOptions<TData> {\n /** The items to sort */\n data: TData[];\n /** Key to sort by on mount (uncontrolled mode) */\n initialSortColumnKey?: SortKey<TData>;\n /** Sort direction on mount (uncontrolled mode). Default: SortDirection.asc */\n initialSortDirection?: SortDirection;\n /** Currently sorted key (controlled mode). When set, the consumer owns the sort state and must update it based on onSortChange. */\n sortColumnKey?: SortKey<TData>;\n /** Current sort direction (controlled mode). Default: SortDirection.asc */\n sortDirection?: SortDirection;\n /** Disable internal sorting of data, e.g. when sorting happens server-side. requestSort still updates state and calls onSortChange. */\n disableInternalSort?: boolean;\n /**\n * Get the value to sort an item by. Default: reads the sort key as a property from the item, with support for dot-notation paths like \"person.name\".\n * Memoize this (e.g. with useCallback) if provided inline, otherwise sortedData recomputes on every render.\n */\n getSortValue?: SortValueGetter<TData>;\n /**\n * Custom comparators per sort key, for keys that need their own ordering (e.g. status ranking). The comparator should sort ascending; direction is applied by the hook.\n * Memoize this (e.g. with useMemo) if provided inline, otherwise sortedData recomputes on every render.\n */\n sorters?: Partial<Record<SortKey<TData>, SortComparer<TData>>>;\n /** Called whenever the sort changes */\n onSortChange?: (columnKey: SortKey<TData>, sortDirection: SortDirection) => void;\n /**\n * Named sort options (e.g. for a FilterSort dropdown), each mapping an option key to a column key + direction.\n * When provided, the hook exposes `sortOptionKey` (the option matching the current sort, if any) and\n * `setSortOption` (set the sort directly from an option's key, e.g. straight from a <select>'s onChange).\n */\n sortOptions?: Record<string, SortOption<TData>>;\n /**\n * Custom matcher for deciding which sortOptions entry matches the current sort. Default: matches on exact\n * columnKey, treating a missing direction as SortDirection.asc. Only needed for non-standard matching.\n */\n matchSortOption?: SortOptionMatcher<TData>;\n}\n\nexport interface UseSortReturn<TData> {\n /** The items, sorted by the current sort (or as-is when disableInternalSort is set) */\n sortedData: TData[];\n /** The key currently sorted by */\n sortColumnKey: SortKey<TData> | undefined;\n /** The current sort direction */\n sortDirection: SortDirection | undefined;\n /** Sort by a key. Toggles direction when the key is already sorted. */\n requestSort: (columnKey: SortKey<TData>) => void;\n /** Set both sort key and direction directly, without toggling. Useful when a control (e.g. a FilterSort dropdown) maps its own options to a fixed (key, direction) pair. */\n setSort: (columnKey: SortKey<TData>, direction?: SortDirection) => void;\n /** Get props for TableHeadCell to display the current sort state (aria-sort). Sorting itself is triggered by the consumer, e.g. via requestSort/onSortChange from a FilterSort control. */\n getSortProps: (columnKey: SortKey<TData>) => SortableHeadCellProps;\n /** The sortOptions key matching the current sort state, if any. undefined if sortOptions wasn't provided or none match. */\n sortOptionKey: string | undefined;\n /** Set the sort from a sortOptions key, e.g. straight from a <select>'s onChange. No-ops if the key isn't found in sortOptions. */\n setSortOption: (optionKey: string) => void;\n}\n\ninterface SortState<TData> {\n columnKey: SortKey<TData>;\n direction: SortDirection;\n}\n\ntype SortableValue = string | number | boolean | Date | null | undefined;\n\nconst getValueByPath = (item: unknown, path: string): unknown =>\n path\n .split('.')\n .reduce<unknown>(\n (value, key) => (value !== null && typeof value === 'object' ? (value as Record<string, unknown>)[key] : undefined),\n item\n );\n\nconst compareSortValues = (a: SortableValue, b: SortableValue): number => {\n if (a === null || a === undefined) {\n return b === null || b === undefined ? 0 : 1;\n }\n if (b === null || b === undefined) {\n return -1;\n }\n if (typeof a === 'number' && typeof b === 'number') {\n return a - b;\n }\n if (typeof a === 'boolean' && typeof b === 'boolean') {\n return Number(a) - Number(b);\n }\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() - b.getTime();\n }\n return String(a).localeCompare(String(b), 'nb', { numeric: true, sensitivity: 'base' });\n};\n\nconst sortBy =\n <TData>(accessor: (item: TData) => SortableValue, direction: SortDirection = SortDirection.asc): SortComparer<TData> =>\n (a: TData, b: TData): number => {\n const aValue = accessor(a);\n const bValue = accessor(b);\n // null/undefined skal alltid sist, også ved desc\n if (aValue === null || aValue === undefined || bValue === null || bValue === undefined) {\n return compareSortValues(aValue, bValue);\n }\n const result = compareSortValues(aValue, bValue);\n return direction === SortDirection.desc ? -result : result;\n };\n\nconst defaultMatchSortOption = <TData>(\n option: SortOption<TData>,\n columnKey: SortKey<TData> | undefined,\n direction: SortDirection | undefined\n): boolean => option.columnKey === columnKey && (option.direction ?? SortDirection.asc) === (direction ?? SortDirection.asc);\n\n/**\n * Sort state and type-aware sorting for any list (tables, filtered result lists etc.).\n * Strings sort with Norwegian locale and numeric collation, numbers/booleans/Dates sort natively,\n * and null/undefined always sort last. Supports uncontrolled mode (internal state),\n * controlled mode (sortColumnKey/sortDirection props) and server-side sorting (disableInternalSort).\n */\nexport const useSort = <TData>(options: UseSortOptions<TData>): UseSortReturn<TData> => {\n const {\n data,\n initialSortColumnKey,\n initialSortDirection,\n sortColumnKey,\n sortDirection,\n disableInternalSort,\n getSortValue,\n sorters,\n onSortChange,\n sortOptions,\n matchSortOption = defaultMatchSortOption,\n } = options;\n\n const [internalSort, setInternalSort] = useState<SortState<TData> | undefined>(() =>\n initialSortColumnKey ? { columnKey: initialSortColumnKey, direction: initialSortDirection ?? SortDirection.asc } : undefined\n );\n\n const isControlled = sortColumnKey !== undefined;\n const sort: SortState<TData> | undefined = isControlled\n ? { columnKey: sortColumnKey, direction: sortDirection ?? SortDirection.asc }\n : internalSort;\n\n const requestSort = (columnKey: SortKey<TData>): void => {\n const direction = sort?.columnKey === columnKey && sort.direction === SortDirection.asc ? SortDirection.desc : SortDirection.asc;\n\n if (!isControlled) {\n setInternalSort({ columnKey, direction });\n }\n onSortChange?.(columnKey, direction);\n };\n\n const setSort = (columnKey: SortKey<TData>, direction: SortDirection = SortDirection.asc): void => {\n if (!isControlled) {\n setInternalSort({ columnKey, direction });\n }\n onSortChange?.(columnKey, direction);\n };\n\n const currentSortColumnKey = sort?.columnKey;\n const currentSortDirection = sort?.direction;\n\n const sortedData = useMemo(() => {\n if (!currentSortColumnKey || disableInternalSort) {\n return data;\n }\n\n const customComparer = sorters?.[currentSortColumnKey];\n const comparer: SortComparer<TData> = customComparer\n ? (a, b): number => (currentSortDirection === SortDirection.desc ? -customComparer(a, b) : customComparer(a, b))\n : sortBy(item => (getSortValue ?? getValueByPath)(item, currentSortColumnKey) as SortableValue, currentSortDirection);\n\n return [...data].sort(comparer);\n }, [data, currentSortColumnKey, currentSortDirection, disableInternalSort, getSortValue, sorters]);\n\n const getSortProps = (columnKey: SortKey<TData>): SortableHeadCellProps => ({\n sortDir: sort?.columnKey === columnKey ? sort.direction : undefined,\n });\n\n const sortOptionKey = sortOptions\n ? Object.entries(sortOptions).find(([, option]) => matchSortOption(option, currentSortColumnKey, currentSortDirection))?.[0]\n : undefined;\n\n const setSortOption = (optionKey: string): void => {\n const option = sortOptions?.[optionKey];\n if (option) {\n setSort(option.columnKey, option.direction);\n }\n };\n\n return {\n sortedData,\n sortColumnKey: sort?.columnKey,\n sortDirection: sort?.direction,\n requestSort,\n setSort,\n getSortProps,\n sortOptionKey,\n setSortOption,\n };\n};\n"],"mappings":"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"}