@navikt/ds-react 8.11.0 → 8.11.1

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 (79) hide show
  1. package/cjs/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js +5 -1
  2. package/cjs/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js.map +1 -1
  3. package/cjs/data/drag-and-drop/root/DragAndDropRoot.js +48 -18
  4. package/cjs/data/drag-and-drop/root/DragAndDropRoot.js.map +1 -1
  5. package/cjs/data/table/column-header/useTableColumnResize.d.ts +0 -2
  6. package/cjs/data/table/column-header/useTableColumnResize.js +6 -2
  7. package/cjs/data/table/column-header/useTableColumnResize.js.map +1 -1
  8. package/cjs/data/table/helpers/table-focus.js +1 -1
  9. package/cjs/data/table/helpers/table-focus.js.map +1 -1
  10. package/cjs/data/table/hooks/useColumnOptions.d.ts +5 -2
  11. package/cjs/data/table/hooks/useColumnOptions.js +24 -9
  12. package/cjs/data/table/hooks/useColumnOptions.js.map +1 -1
  13. package/cjs/data/table/root/DataGridTableRoot.js +2 -1
  14. package/cjs/data/table/root/DataGridTableRoot.js.map +1 -1
  15. package/cjs/data/table/tr/DataTableTr.js +2 -4
  16. package/cjs/data/table/tr/DataTableTr.js.map +1 -1
  17. package/cjs/data-grid/root/DataGrid.types.d.ts +13 -0
  18. package/cjs/data-grid/root/DataGridRoot.js +2 -0
  19. package/cjs/data-grid/root/DataGridRoot.js.map +1 -1
  20. package/cjs/primitives/bleed/Bleed.d.ts +0 -2
  21. package/cjs/primitives/bleed/Bleed.js.map +1 -1
  22. package/cjs/tabs/Tabs.context.d.ts +1 -1
  23. package/cjs/tabs/useTabs.d.ts +1 -1
  24. package/cjs/toggle-group/ToggleGroup.context.d.ts +1 -1
  25. package/cjs/toggle-group/useToggleGroup.d.ts +1 -1
  26. package/cjs/utils/components/dismissablelayer/DismissableLayer.js +1 -0
  27. package/cjs/utils/components/dismissablelayer/DismissableLayer.js.map +1 -1
  28. package/cjs/utils/components/dismissablelayer/util/useFocusOutside.d.ts +1 -0
  29. package/cjs/utils/components/dismissablelayer/util/useFocusOutside.js +11 -1
  30. package/cjs/utils/components/dismissablelayer/util/useFocusOutside.js.map +1 -1
  31. package/cjs/utils/hooks/useControllableState.d.ts +1 -1
  32. package/cjs/utils/hooks/useControllableState.js.map +1 -1
  33. package/esm/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js +5 -1
  34. package/esm/data/drag-and-drop/drag-handler/DragAndDropDragHandler.js.map +1 -1
  35. package/esm/data/drag-and-drop/root/DragAndDropRoot.js +49 -19
  36. package/esm/data/drag-and-drop/root/DragAndDropRoot.js.map +1 -1
  37. package/esm/data/table/column-header/useTableColumnResize.d.ts +0 -2
  38. package/esm/data/table/column-header/useTableColumnResize.js +6 -2
  39. package/esm/data/table/column-header/useTableColumnResize.js.map +1 -1
  40. package/esm/data/table/helpers/table-focus.js +1 -1
  41. package/esm/data/table/helpers/table-focus.js.map +1 -1
  42. package/esm/data/table/hooks/useColumnOptions.d.ts +5 -2
  43. package/esm/data/table/hooks/useColumnOptions.js +24 -9
  44. package/esm/data/table/hooks/useColumnOptions.js.map +1 -1
  45. package/esm/data/table/root/DataGridTableRoot.js +2 -1
  46. package/esm/data/table/root/DataGridTableRoot.js.map +1 -1
  47. package/esm/data/table/tr/DataTableTr.js +2 -4
  48. package/esm/data/table/tr/DataTableTr.js.map +1 -1
  49. package/esm/data-grid/root/DataGrid.types.d.ts +13 -0
  50. package/esm/data-grid/root/DataGridRoot.js +2 -0
  51. package/esm/data-grid/root/DataGridRoot.js.map +1 -1
  52. package/esm/primitives/bleed/Bleed.d.ts +0 -2
  53. package/esm/primitives/bleed/Bleed.js.map +1 -1
  54. package/esm/tabs/Tabs.context.d.ts +1 -1
  55. package/esm/tabs/useTabs.d.ts +1 -1
  56. package/esm/toggle-group/ToggleGroup.context.d.ts +1 -1
  57. package/esm/toggle-group/useToggleGroup.d.ts +1 -1
  58. package/esm/utils/components/dismissablelayer/DismissableLayer.js +1 -0
  59. package/esm/utils/components/dismissablelayer/DismissableLayer.js.map +1 -1
  60. package/esm/utils/components/dismissablelayer/util/useFocusOutside.d.ts +1 -0
  61. package/esm/utils/components/dismissablelayer/util/useFocusOutside.js +12 -2
  62. package/esm/utils/components/dismissablelayer/util/useFocusOutside.js.map +1 -1
  63. package/esm/utils/hooks/useControllableState.d.ts +1 -1
  64. package/esm/utils/hooks/useControllableState.js.map +1 -1
  65. package/package.json +5 -5
  66. package/src/data/drag-and-drop/drag-handler/DragAndDropDragHandler.tsx +5 -1
  67. package/src/data/drag-and-drop/root/DragAndDropRoot.tsx +52 -22
  68. package/src/data/table/column-header/useTableColumnResize.ts +7 -4
  69. package/src/data/table/helpers/selection/selection.types.ts +0 -1
  70. package/src/data/table/helpers/table-focus.ts +1 -1
  71. package/src/data/table/hooks/useColumnOptions.ts +46 -11
  72. package/src/data/table/root/DataGridTableRoot.tsx +2 -1
  73. package/src/data/table/tr/DataTableTr.tsx +1 -5
  74. package/src/data-grid/root/DataGrid.types.ts +10 -0
  75. package/src/data-grid/root/DataGridRoot.tsx +2 -0
  76. package/src/primitives/bleed/Bleed.tsx +0 -2
  77. package/src/utils/components/dismissablelayer/DismissableLayer.tsx +1 -0
  78. package/src/utils/components/dismissablelayer/util/useFocusOutside.ts +14 -2
  79. package/src/utils/hooks/useControllableState.ts +10 -8
@@ -137,7 +137,7 @@ function getStickyOffsets(element: HTMLElement): {
137
137
  }
138
138
 
139
139
  const stickyHeader = table.querySelector<HTMLElement>(
140
- `.aksel-data-table__tr[data-sticky="true"]`,
140
+ `.aksel-data-table__thead[data-sticky="true"]`,
141
141
  );
142
142
 
143
143
  const stickyNodesStart = table.querySelectorAll<HTMLElement>(
@@ -1,4 +1,5 @@
1
1
  import { useMemo } from "react";
2
+ import { consoleWarning } from "../../../utils/helpers/consoleWarning";
2
3
  import type {
3
4
  ColumnDefinition,
4
5
  ColumnDefinitions,
@@ -13,6 +14,10 @@ type UseColumnOptions = {
13
14
  hasSelection: boolean;
14
15
  hasDetailsPanel: boolean;
15
16
  layout: "fixed" | "auto";
17
+ columnDisplay?: {
18
+ id: string;
19
+ visible: boolean;
20
+ }[];
16
21
  };
17
22
 
18
23
  type StickyStartState = {
@@ -37,7 +42,13 @@ function useColumnOptions<T>(
37
42
  columnDefinitions: ColumnDefinitions<T>,
38
43
  options: UseColumnOptions,
39
44
  ): UseColumnOptionsResult<T> {
40
- const { stickyColumns, hasSelection, hasDetailsPanel, layout } = options;
45
+ const {
46
+ stickyColumns,
47
+ hasSelection,
48
+ hasDetailsPanel,
49
+ layout,
50
+ columnDisplay,
51
+ } = options;
41
52
 
42
53
  const hasStickyStart = stickyColumns?.start === 1;
43
54
 
@@ -49,11 +60,15 @@ function useColumnOptions<T>(
49
60
  (stickyExpansion ? ACTION_CELL_WIDTH : 0) +
50
61
  (stickySelection ? ACTION_CELL_WIDTH : 0);
51
62
 
63
+ const visibleColumns = useMemo(() => {
64
+ return orderColumnsAndFilterByVisibility(columnDefinitions, columnDisplay);
65
+ }, [columnDefinitions, columnDisplay]);
66
+
52
67
  const columns = useMemo(() => {
53
- return columnDefinitions.map((colDef, index) => {
68
+ return visibleColumns.map((colDef, index) => {
54
69
  const isFirstSticky = hasStickyStart && index === 0;
55
70
  const isLastSticky =
56
- stickyColumns?.end === 1 && index === columnDefinitions.length - 1;
71
+ stickyColumns?.end === 1 && index === visibleColumns.length - 1;
57
72
 
58
73
  return {
59
74
  isSticky: isFirstSticky
@@ -66,12 +81,7 @@ function useColumnOptions<T>(
66
81
  colDef,
67
82
  };
68
83
  });
69
- }, [
70
- columnDefinitions,
71
- hasStickyStart,
72
- stickyColumns,
73
- stickyFirstColumnOffset,
74
- ]);
84
+ }, [visibleColumns, hasStickyStart, stickyColumns, stickyFirstColumnOffset]);
75
85
 
76
86
  const totalColSpan =
77
87
  columns.length +
@@ -91,6 +101,31 @@ function useColumnOptions<T>(
91
101
  };
92
102
  }
93
103
 
104
+ function orderColumnsAndFilterByVisibility<T>(
105
+ columns: ColumnDefinition<T>[],
106
+ columnDisplay?: { id: string; visible: boolean }[],
107
+ ): ColumnDefinition<T>[] {
108
+ if (!columnDisplay) {
109
+ return columns;
110
+ }
111
+
112
+ const columnMap = new Map(columns.map((col) => [col.id, col]));
113
+
114
+ return columnDisplay.reduce<ColumnDefinition<T>[]>((acc, { id, visible }) => {
115
+ const col = columnMap.get(id);
116
+
117
+ if (!col) {
118
+ consoleWarning(
119
+ `DataGrid: Column with id "${id}" not found in column definitions. Please check your columnDisplay configuration.`,
120
+ );
121
+ }
122
+
123
+ if (col && visible) {
124
+ acc.push(col);
125
+ }
126
+ return acc;
127
+ }, []);
128
+ }
129
+
94
130
  export { useColumnOptions };
95
- export type { StickyStartState };
96
- export type { UseColumnOptionsResult };
131
+ export type { StickyStartState, UseColumnOptionsResult };
@@ -195,6 +195,7 @@ const DataGridTableInternal = forwardRef<
195
195
  hasSelection: tableSelectionState.selection.mode !== "none",
196
196
  hasDetailsPanel: !!detailsPanel?.getContent,
197
197
  layout,
198
+ columnDisplay: tableSettings?.columnDisplay,
198
199
  },
199
200
  );
200
201
 
@@ -239,7 +240,7 @@ const DataGridTableInternal = forwardRef<
239
240
  aria-busy={isLoading || undefined}
240
241
  >
241
242
  <DataTableDetailsPanelProvider detailsPanel={detailsPanel}>
242
- <DataTableThead>
243
+ <DataTableThead data-sticky={stickyHeader || undefined}>
243
244
  <DataTableTr>
244
245
  {columns.map(
245
246
  ({ isSticky, isStickyLast, stickyLeftOffset, colDef }) => {
@@ -51,18 +51,15 @@ const DataTableTr = forwardRef<HTMLTableRowElement, DataTableTrProps>(
51
51
  },
52
52
  forwardedRef,
53
53
  ) => {
54
- const { layout, stickyHeader, selectionState, onRowAction } =
54
+ const { layout, selectionState, onRowAction, tableItems } =
55
55
  useDataTableContext();
56
56
  const { location } = useDataTableLocation();
57
- const { tableItems } = useDataTableContext();
58
57
 
59
58
  const renderFillerCell = layout === "fixed" && children;
60
59
 
61
60
  const selected =
62
61
  selectionState.selection.isRowSelected(rowId ?? "") ?? selectedProp;
63
62
 
64
- const isSticky = location === "thead" && stickyHeader;
65
-
66
63
  const handleClick =
67
64
  location === "tbody" &&
68
65
  rowId !== undefined &&
@@ -131,7 +128,6 @@ const DataTableTr = forwardRef<HTMLTableRowElement, DataTableTrProps>(
131
128
  ref={forwardedRef}
132
129
  className={cl("aksel-data-table__tr", className)}
133
130
  data-selected={selected}
134
- data-sticky={isSticky || undefined}
135
131
  >
136
132
  <RowExpansionCell rowId={rowId} />
137
133
  <RowSelectionCell rowId={rowId} />
@@ -31,6 +31,16 @@ type DataGridSettings = {
31
31
  * @default "medium"
32
32
  */
33
33
  textSize?: "small" | "medium";
34
+ /**
35
+ * Optional configuration for column order and visibility.
36
+ *
37
+ * If provided, only columns listed here are rendered, and the array order
38
+ * determines the rendered column order.
39
+ *
40
+ * Set `visible: false` to hide a column without removing it from the list.
41
+ * Each `id` must be unique and match a column `id` in the `columns` prop.
42
+ */
43
+ columnDisplay?: { id: string; visible: boolean }[];
34
44
  };
35
45
 
36
46
  export type { DataGridSettings };
@@ -97,6 +97,7 @@ const DataGridInternal = forwardRef<HTMLDivElement, DataGridProps<any>>(
97
97
  truncateContent: settings?.truncateContent,
98
98
  stickyColumns: settings?.stickyColumns ?? {},
99
99
  textSize: settings?.textSize ?? "medium",
100
+ columnDisplay: settings?.columnDisplay,
100
101
  }),
101
102
  [
102
103
  settings?.rowDensity,
@@ -104,6 +105,7 @@ const DataGridInternal = forwardRef<HTMLDivElement, DataGridProps<any>>(
104
105
  settings?.truncateContent,
105
106
  settings?.stickyColumns,
106
107
  settings?.textSize,
108
+ settings?.columnDisplay,
107
109
  ],
108
110
  );
109
111
 
@@ -13,7 +13,6 @@ export interface BleedProps extends React.HTMLAttributes<HTMLDivElement> {
13
13
  * Accepts a [spacing token](https://aksel.nav.no/grunnleggende/styling/design-tokens#space)
14
14
  * or an object of spacing tokens for different breakpoints.
15
15
  *
16
- * The `px` value is useful to nudge by just 1px.
17
16
  * The `full` value is used to extend the margin to the full width of the parent.
18
17
  *
19
18
  * @example
@@ -29,7 +28,6 @@ export interface BleedProps extends React.HTMLAttributes<HTMLDivElement> {
29
28
  * Accepts a [spacing token](https://aksel.nav.no/grunnleggende/styling/design-tokens#space)
30
29
  * or an object of spacing tokens for different breakpoints.
31
30
  *
32
- * The `px` value is useful to nudge by just 1px.
33
31
  * This prop does **not** accept the `full` value.
34
32
  *
35
33
  * @example
@@ -460,6 +460,7 @@ const DismissableLayer = forwardRef<HTMLDivElement, DismissableLayerProps>(
460
460
  () => {
461
461
  pointerDownOutside.onPointerDownCapture();
462
462
  pointerUpOutside.onPointerDownCapture();
463
+ focusOutside.onPointerDownCapture();
463
464
  },
464
465
  )}
465
466
  onPointerUpCapture={composeEventHandlers(
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useRef } from "react";
2
- import { useEventCallback } from "../../../hooks";
2
+ import { useEventCallback, useTimeout } from "../../../hooks";
3
3
  import {
4
4
  CUSTOM_EVENTS,
5
5
  CustomFocusEvent,
@@ -16,6 +16,8 @@ export function useFocusOutside(
16
16
  ) {
17
17
  const handleFocusOutside = useEventCallback(callback) as EventListener;
18
18
  const isFocusInsideReactTreeRef = useRef(false);
19
+ const skipFocusCheckRef = useRef(false);
20
+ const timeout = useTimeout();
19
21
 
20
22
  useEffect(() => {
21
23
  if (!enabled) {
@@ -23,7 +25,11 @@ export function useFocusOutside(
23
25
  }
24
26
 
25
27
  const handleFocus = (event: FocusEvent) => {
26
- if (event.target && !isFocusInsideReactTreeRef.current) {
28
+ if (
29
+ event.target &&
30
+ !isFocusInsideReactTreeRef.current &&
31
+ !skipFocusCheckRef.current
32
+ ) {
27
33
  const eventDetail = { originalEvent: event };
28
34
 
29
35
  /**
@@ -59,5 +65,11 @@ export function useFocusOutside(
59
65
  onBlurCapture: () => {
60
66
  isFocusInsideReactTreeRef.current = false;
61
67
  },
68
+ onPointerDownCapture: () => {
69
+ skipFocusCheckRef.current = true;
70
+ timeout.start(0, () => {
71
+ skipFocusCheckRef.current = false;
72
+ });
73
+ },
62
74
  };
63
75
  }
@@ -25,16 +25,18 @@ export function useControllableState<StateT, ChangeT extends StateT = StateT>({
25
25
  const controlled = valueProp !== undefined;
26
26
  const value = controlled ? valueProp : uncontrolledState;
27
27
 
28
- const setValue = useEventCallback((next: React.SetStateAction<ChangeT>) => {
29
- const setter = next as (prevState?: StateT) => ChangeT;
30
- const nextValue = typeof next === "function" ? setter(value) : next;
28
+ const setValue = useEventCallback(
29
+ (next: ChangeT | ((prevState: StateT) => ChangeT)) => {
30
+ const setter = next as (prevState?: StateT) => ChangeT;
31
+ const nextValue = typeof next === "function" ? setter(value) : next;
31
32
 
32
- if (!controlled) {
33
- setUncontrolledState(nextValue);
34
- }
33
+ if (!controlled) {
34
+ setUncontrolledState(nextValue);
35
+ }
35
36
 
36
- onChangeProp(nextValue);
37
- });
37
+ onChangeProp(nextValue);
38
+ },
39
+ );
38
40
 
39
41
  return [value, setValue] as const;
40
42
  }