@olenbetong/appframe-ds 1.0.1 → 1.0.2
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.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @olenbetong/appframe-ds
|
|
2
2
|
|
|
3
|
+
## 1.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e7e2be3: Migrate the lookup combobox and grid to react-window 2 (`List` with `rowComponent`/`rowProps`, dynamic row heights via `useDynamicRowHeight`), update @atlaskit/pragmatic-drag-and-drop to 3.x and its non-deprecated entry points, and drop the unused react-virtualized-auto-sizer and @types/react-window dependencies.
|
|
8
|
+
- Updated dependencies [c48fb2f]
|
|
9
|
+
- @olenbetong/appframe-react@2.2.0
|
|
10
|
+
|
|
3
11
|
## 1.0.1
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olenbetong/appframe-ds",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Components that bind Designsystemet components to Appframe data objects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./es/index.d.ts",
|
|
@@ -56,23 +56,22 @@
|
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@atlaskit/pragmatic-drag-and-drop": "
|
|
59
|
+
"@atlaskit/pragmatic-drag-and-drop": "3.1.0",
|
|
60
60
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "2.2.0",
|
|
61
61
|
"@base-ui/react": "1.8.0",
|
|
62
62
|
"clsx": "^2.1.1",
|
|
63
|
-
"dockview-react": "8.3.
|
|
63
|
+
"dockview-react": "8.3.1",
|
|
64
64
|
"localforage": "^1.10.0",
|
|
65
65
|
"mitt": "^3.0.1",
|
|
66
66
|
"react": "19.3.0",
|
|
67
67
|
"react-dom": "19.3.0",
|
|
68
|
-
"react-
|
|
69
|
-
"react-window": "^1.8.11",
|
|
70
|
-
"@olenbetong/appframe-data": "1.7.1",
|
|
68
|
+
"react-window": "2.3.1",
|
|
71
69
|
"@olenbetong/appframe-core": "2.13.1",
|
|
72
|
-
"@olenbetong/appframe-
|
|
70
|
+
"@olenbetong/appframe-data": "1.7.1",
|
|
71
|
+
"@olenbetong/appframe-react": "2.2.0"
|
|
73
72
|
},
|
|
74
73
|
"devDependencies": {
|
|
75
|
-
"@digdir/designsystemet-react": "1.21.
|
|
74
|
+
"@digdir/designsystemet-react": "1.21.1",
|
|
76
75
|
"@emotion/react": "^11.14.0",
|
|
77
76
|
"@emotion/styled": "^11.14.1",
|
|
78
77
|
"@mui/material": "9.4.0",
|
|
@@ -85,8 +84,7 @@
|
|
|
85
84
|
"@testing-library/user-event": "^14.6.7",
|
|
86
85
|
"@types/react": "19.3.0",
|
|
87
86
|
"@types/react-dom": "19.3.0",
|
|
88
|
-
"
|
|
89
|
-
"happy-dom": "^20.14.3",
|
|
87
|
+
"happy-dom": "20.14.5",
|
|
90
88
|
"react-error-boundary": "6.1.5",
|
|
91
89
|
"react-router": "8.3.1",
|
|
92
90
|
"typescript": "7.0.2"
|
|
@@ -6,8 +6,8 @@ import type { DataObject } from "@olenbetong/appframe-data";
|
|
|
6
6
|
import { type LookupColumn, useComboboxData } from "@olenbetong/appframe-react";
|
|
7
7
|
|
|
8
8
|
import clsx from "clsx";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
10
|
+
import { List, type RowComponentProps, useDynamicRowHeight, useListRef } from "react-window";
|
|
11
11
|
|
|
12
12
|
import { LookupGrid } from "./LookupGrid.js";
|
|
13
13
|
|
|
@@ -43,6 +43,46 @@ export type LookupComboboxProps<T extends Record<string, unknown>> =
|
|
|
43
43
|
| LookupComboboxListProps<T>
|
|
44
44
|
| LookupComboboxGridProps<T>;
|
|
45
45
|
|
|
46
|
+
type LookupComboboxRowProps<T extends Record<string, unknown>> = {
|
|
47
|
+
data: T[];
|
|
48
|
+
currentIndex: number;
|
|
49
|
+
isDesktop: boolean;
|
|
50
|
+
getItemId: (item: T | undefined) => string | undefined;
|
|
51
|
+
isItemSelected?: (item: T) => boolean;
|
|
52
|
+
onSelectItem: (item: T) => void;
|
|
53
|
+
renderItem: LookupComboboxListProps<T>["renderItem"];
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function LookupComboboxRow<T extends Record<string, unknown>>({
|
|
57
|
+
index,
|
|
58
|
+
style,
|
|
59
|
+
data,
|
|
60
|
+
currentIndex,
|
|
61
|
+
isDesktop,
|
|
62
|
+
getItemId,
|
|
63
|
+
isItemSelected,
|
|
64
|
+
onSelectItem,
|
|
65
|
+
renderItem: ItemComponent,
|
|
66
|
+
}: RowComponentProps<LookupComboboxRowProps<T>>) {
|
|
67
|
+
let item = data[index];
|
|
68
|
+
let itemId = getItemId(item);
|
|
69
|
+
let active = index === currentIndex;
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<ItemComponent
|
|
73
|
+
className={clsx("ObCombobox-listItem", active && "active", isItemSelected?.(item) && "selected")}
|
|
74
|
+
aria-selected={active}
|
|
75
|
+
onClick={() => onSelectItem(item)}
|
|
76
|
+
item={item}
|
|
77
|
+
style={{
|
|
78
|
+
...style,
|
|
79
|
+
padding: isDesktop ? "0 8px" : "8px",
|
|
80
|
+
}}
|
|
81
|
+
id={itemId}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
46
86
|
export function LookupCombobox<T extends Record<string, unknown>>(props: LookupComboboxProps<T>) {
|
|
47
87
|
let {
|
|
48
88
|
nullable,
|
|
@@ -55,7 +95,7 @@ export function LookupCombobox<T extends Record<string, unknown>>(props: LookupC
|
|
|
55
95
|
} = props;
|
|
56
96
|
let isGrid = props.variant === "grid";
|
|
57
97
|
let columns = isGrid ? (props as LookupComboboxGridProps<T>).columns : undefined;
|
|
58
|
-
let listRef =
|
|
98
|
+
let listRef = useListRef(null);
|
|
59
99
|
|
|
60
100
|
let [isDesktop, setIsDesktop] = useState(
|
|
61
101
|
() => typeof window !== "undefined" && window.matchMedia("(width >= 768px) and (pointer: fine)").matches,
|
|
@@ -102,47 +142,15 @@ export function LookupCombobox<T extends Record<string, unknown>>(props: LookupC
|
|
|
102
142
|
return () => resizeObserver.disconnect();
|
|
103
143
|
}, []);
|
|
104
144
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
let
|
|
108
|
-
if (sizeMap.current.get(index) !== size) {
|
|
109
|
-
sizeMap.current.set(index, size);
|
|
110
|
-
// listRef will not be set on the initial render, so delay resetting
|
|
111
|
-
setTimeout(() => listRef.current?.resetAfterIndex(index, true));
|
|
112
|
-
}
|
|
113
|
-
}, []);
|
|
114
|
-
|
|
115
|
-
let Row = ({ index, style: { height, ...style } }: { index: number; style: React.CSSProperties }) => {
|
|
116
|
-
let rowRef = useRef<HTMLLIElement>(null);
|
|
117
|
-
|
|
118
|
-
useLayoutEffect(() => {
|
|
119
|
-
if (!rowRef.current) return;
|
|
120
|
-
setSize(index, rowRef.current.offsetHeight);
|
|
121
|
-
}, [index]);
|
|
122
|
-
|
|
123
|
-
let item = data[index];
|
|
124
|
-
let itemId = getItemId(item);
|
|
125
|
-
let active = index === currentIndex;
|
|
126
|
-
let ItemComponent = (props as LookupComboboxListProps<T>).renderItem;
|
|
127
|
-
|
|
128
|
-
return (
|
|
129
|
-
<ItemComponent
|
|
130
|
-
className={clsx("ObCombobox-listItem", active && "active", isItemSelected?.(item) && "selected")}
|
|
131
|
-
aria-selected={active}
|
|
132
|
-
onClick={() => handleSelectItem(item)}
|
|
133
|
-
item={item}
|
|
134
|
-
style={{
|
|
135
|
-
...style,
|
|
136
|
-
padding: isDesktop ? "0 8px" : "8px",
|
|
137
|
-
}}
|
|
138
|
-
ref={rowRef}
|
|
139
|
-
key={itemId}
|
|
140
|
-
id={itemId}
|
|
141
|
-
/>
|
|
142
|
-
);
|
|
143
|
-
};
|
|
145
|
+
// Rows are rendered by the consumer and have no fixed height, so let the
|
|
146
|
+
// list measure them as they mount.
|
|
147
|
+
let rowHeight = useDynamicRowHeight({ defaultRowHeight: 56 });
|
|
144
148
|
|
|
145
|
-
useEffect(() =>
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
if (currentIndex >= 0 && currentIndex < data.length) {
|
|
151
|
+
listRef.current?.scrollToRow({ index: currentIndex, align: "smart" });
|
|
152
|
+
}
|
|
153
|
+
}, [currentIndex, data.length, listRef]);
|
|
146
154
|
|
|
147
155
|
useEffect(() => {
|
|
148
156
|
if (initialSearchValue !== undefined) {
|
|
@@ -203,16 +211,24 @@ export function LookupCombobox<T extends Record<string, unknown>>(props: LookupC
|
|
|
203
211
|
/>
|
|
204
212
|
) : (
|
|
205
213
|
<div className="ObCombobox-list" role="listbox">
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
214
|
+
<List<LookupComboboxRowProps<T>>
|
|
215
|
+
role="presentation"
|
|
216
|
+
listRef={listRef}
|
|
217
|
+
style={{ height }}
|
|
218
|
+
rowCount={data.length}
|
|
219
|
+
rowHeight={rowHeight}
|
|
212
220
|
overscanCount={5}
|
|
213
|
-
|
|
214
|
-
{
|
|
215
|
-
|
|
221
|
+
rowComponent={LookupComboboxRow}
|
|
222
|
+
rowProps={{
|
|
223
|
+
data,
|
|
224
|
+
currentIndex,
|
|
225
|
+
isDesktop,
|
|
226
|
+
getItemId,
|
|
227
|
+
isItemSelected,
|
|
228
|
+
onSelectItem: handleSelectItem,
|
|
229
|
+
renderItem: (props as LookupComboboxListProps<T>).renderItem,
|
|
230
|
+
}}
|
|
231
|
+
/>
|
|
216
232
|
</div>
|
|
217
233
|
))}
|
|
218
234
|
</div>
|
|
@@ -4,8 +4,8 @@ import { getLocalizedString } from "@olenbetong/appframe-core";
|
|
|
4
4
|
import type { LookupColumn } from "@olenbetong/appframe-react";
|
|
5
5
|
|
|
6
6
|
import clsx from "clsx";
|
|
7
|
-
import { useEffect, useMemo
|
|
8
|
-
import {
|
|
7
|
+
import { useEffect, useMemo } from "react";
|
|
8
|
+
import { List, type RowComponentProps, useListRef } from "react-window";
|
|
9
9
|
|
|
10
10
|
const ROW_HEIGHT = 40;
|
|
11
11
|
const HEADER_HEIGHT = 40;
|
|
@@ -29,6 +29,49 @@ function getColumnWidth<T extends Record<string, unknown>>(column: LookupColumn<
|
|
|
29
29
|
return column.width ?? "minmax(80px, 1fr)";
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
type LookupGridRowProps<T extends Record<string, unknown>> = Omit<LookupGridProps<T>, "height"> & {
|
|
33
|
+
gridTemplateColumns: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function LookupGridRow<T extends Record<string, unknown>>({
|
|
37
|
+
index,
|
|
38
|
+
style,
|
|
39
|
+
columns,
|
|
40
|
+
data,
|
|
41
|
+
currentIndex,
|
|
42
|
+
currentColumn,
|
|
43
|
+
gridTemplateColumns,
|
|
44
|
+
getItemId,
|
|
45
|
+
isItemSelected,
|
|
46
|
+
onSelectItem,
|
|
47
|
+
}: RowComponentProps<LookupGridRowProps<T>>) {
|
|
48
|
+
let item = data[index];
|
|
49
|
+
let itemId = getItemId(item);
|
|
50
|
+
let active = index === currentIndex;
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
role="row"
|
|
55
|
+
aria-selected={active}
|
|
56
|
+
className={clsx("ObLookupGrid-row", active && "active", isItemSelected?.(item) && "selected")}
|
|
57
|
+
style={{ ...style, gridTemplateColumns }}
|
|
58
|
+
onClick={() => onSelectItem(item)}
|
|
59
|
+
id={itemId}
|
|
60
|
+
>
|
|
61
|
+
{columns.map((column, columnIndex) => (
|
|
62
|
+
<div
|
|
63
|
+
role="gridcell"
|
|
64
|
+
className={clsx("ObLookupGrid-cell", active && columnIndex === currentColumn && "active")}
|
|
65
|
+
key={column.field}
|
|
66
|
+
title={String(item[column.field] ?? "")}
|
|
67
|
+
>
|
|
68
|
+
{column.renderCell ? column.renderCell(item[column.field], item) : String(item[column.field] ?? "")}
|
|
69
|
+
</div>
|
|
70
|
+
))}
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
32
75
|
export function LookupGrid<T extends Record<string, unknown>>({
|
|
33
76
|
columns,
|
|
34
77
|
data,
|
|
@@ -39,40 +82,14 @@ export function LookupGrid<T extends Record<string, unknown>>({
|
|
|
39
82
|
isItemSelected,
|
|
40
83
|
onSelectItem,
|
|
41
84
|
}: LookupGridProps<T>) {
|
|
42
|
-
let listRef =
|
|
85
|
+
let listRef = useListRef(null);
|
|
43
86
|
let gridTemplateColumns = useMemo(() => columns.map(getColumnWidth).join(" "), [columns]);
|
|
44
87
|
|
|
45
|
-
useEffect(() =>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
let itemId = getItemId(item);
|
|
51
|
-
let active = index === currentIndex;
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<div
|
|
55
|
-
role="row"
|
|
56
|
-
aria-selected={active}
|
|
57
|
-
className={clsx("ObLookupGrid-row", active && "active", isItemSelected?.(item) && "selected")}
|
|
58
|
-
style={{ ...style, gridTemplateColumns }}
|
|
59
|
-
onClick={() => onSelectItem(item)}
|
|
60
|
-
key={itemId}
|
|
61
|
-
id={itemId}
|
|
62
|
-
>
|
|
63
|
-
{columns.map((column, columnIndex) => (
|
|
64
|
-
<div
|
|
65
|
-
role="gridcell"
|
|
66
|
-
className={clsx("ObLookupGrid-cell", active && columnIndex === currentColumn && "active")}
|
|
67
|
-
key={column.field}
|
|
68
|
-
title={String(item[column.field] ?? "")}
|
|
69
|
-
>
|
|
70
|
-
{column.renderCell ? column.renderCell(item[column.field], item) : String(item[column.field] ?? "")}
|
|
71
|
-
</div>
|
|
72
|
-
))}
|
|
73
|
-
</div>
|
|
74
|
-
);
|
|
75
|
-
};
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (currentIndex >= 0 && currentIndex < data.length) {
|
|
90
|
+
listRef.current?.scrollToRow({ index: currentIndex, align: "smart" });
|
|
91
|
+
}
|
|
92
|
+
}, [currentIndex, data.length, listRef]);
|
|
76
93
|
|
|
77
94
|
return (
|
|
78
95
|
<div role="grid" aria-label={getLocalizedString("Search results")} className="ObLookupGrid-root">
|
|
@@ -83,16 +100,25 @@ export function LookupGrid<T extends Record<string, unknown>>({
|
|
|
83
100
|
</div>
|
|
84
101
|
))}
|
|
85
102
|
</div>
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
<List<LookupGridRowProps<T>>
|
|
104
|
+
role="rowgroup"
|
|
105
|
+
listRef={listRef}
|
|
106
|
+
style={{ height: Math.max(0, height - HEADER_HEIGHT) }}
|
|
107
|
+
rowCount={data.length}
|
|
108
|
+
rowHeight={ROW_HEIGHT}
|
|
92
109
|
overscanCount={5}
|
|
93
|
-
|
|
94
|
-
{
|
|
95
|
-
|
|
110
|
+
rowComponent={LookupGridRow}
|
|
111
|
+
rowProps={{
|
|
112
|
+
columns,
|
|
113
|
+
data,
|
|
114
|
+
currentIndex,
|
|
115
|
+
currentColumn,
|
|
116
|
+
gridTemplateColumns,
|
|
117
|
+
getItemId,
|
|
118
|
+
isItemSelected,
|
|
119
|
+
onSelectItem,
|
|
120
|
+
}}
|
|
121
|
+
/>
|
|
96
122
|
</div>
|
|
97
123
|
);
|
|
98
124
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { monitorForElements } from "@atlaskit/pragmatic-drag-and-drop/element
|
|
1
|
+
import { monitorForElements } from "@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter";
|
|
2
2
|
import { type Edge, extractClosestEdge } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";
|
|
3
3
|
import { useEffect, useRef } from "react";
|
|
4
4
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
|
|
2
|
-
import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element
|
|
1
|
+
import { combine } from "@atlaskit/pragmatic-drag-and-drop/utils/combine";
|
|
2
|
+
import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter";
|
|
3
3
|
import {
|
|
4
4
|
attachClosestEdge,
|
|
5
5
|
type Edge,
|