@addev-be/ui 0.14.5 → 0.15.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.
- package/package.json +1 -1
- package/src/components/data/DataGrid/DataGridEditableCell/index.tsx +54 -9
- package/src/components/data/DataGrid/DataGridEditableCell/types.ts +5 -4
- package/src/components/data/DataGrid/helpers/columns.tsx +3 -2
- package/src/components/data/DataGrid/hooks/useDataGrid.tsx +0 -33
- package/src/components/data/DataGrid/types.ts +19 -7
- package/src/components/data/SqlRequestDataGrid/SqlRequestForeignListEditableCell.tsx +16 -0
- package/src/components/data/SqlRequestDataGrid/helpers/columns.tsx +41 -0
- package/src/components/data/SqlRequestDataGrid/types.ts +9 -1
- package/src/components/data/SqlRequestForeignList/index.tsx +118 -74
- package/src/components/data/SqlRequestForeignList/types.ts +31 -0
- package/src/components/forms/Form/Select.tsx +4 -2
- package/src/components/layout/Dropdown/index.tsx +10 -9
package/package.json
CHANGED
|
@@ -1,38 +1,83 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
|
|
3
3
|
|
|
4
|
+
import { EditableDataGridCellProps, EditableDataGridColumn } from '../types';
|
|
4
5
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
6
|
|
|
6
|
-
import { DataGridCellProps } from '../types';
|
|
7
7
|
import { DataGridEditableCellFC } from './types';
|
|
8
8
|
import { EditableCellContainer } from '../styles';
|
|
9
9
|
import { useDataGridContext } from '../hooks';
|
|
10
10
|
|
|
11
|
-
export const DataGridEditableCell = <R
|
|
11
|
+
export const DataGridEditableCell = <R, T>({
|
|
12
12
|
row,
|
|
13
13
|
columnKey,
|
|
14
14
|
column,
|
|
15
15
|
context,
|
|
16
|
-
}:
|
|
17
|
-
const {
|
|
16
|
+
}: EditableDataGridCellProps<R, T>) => {
|
|
17
|
+
const { setEditingCell, setRows, editRow, rows, rowKeyGetter } =
|
|
18
|
+
useDataGridContext(context);
|
|
18
19
|
const initialValue = useMemo(() => column.getter(row) ?? '', [column, row]);
|
|
19
20
|
const [value, setValue] = useState(initialValue);
|
|
20
21
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
22
|
+
const rowKey = rowKeyGetter(row);
|
|
23
|
+
|
|
24
|
+
const defaultPartialRowGetter = useCallback(
|
|
25
|
+
(row: R, newValue: T): Partial<R> =>
|
|
26
|
+
({ [columnKey]: newValue } as Partial<R>),
|
|
27
|
+
[columnKey]
|
|
28
|
+
);
|
|
29
|
+
const partialRowGetter: EditableDataGridColumn<R, T>['partialRowGetter'] =
|
|
30
|
+
column.partialRowGetter ?? defaultPartialRowGetter;
|
|
31
|
+
|
|
32
|
+
const save = useCallback(() => {
|
|
33
|
+
const rowIndex = rows.findIndex(
|
|
34
|
+
(r) => rowKeyGetter(r) === rowKeyGetter(row)
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// If row is found
|
|
38
|
+
if (rowIndex >= 0) {
|
|
39
|
+
// Update the row with the new value
|
|
40
|
+
const partialRow = partialRowGetter(row, value);
|
|
41
|
+
const newRow = {
|
|
42
|
+
...row,
|
|
43
|
+
...partialRow,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Update the edited rows state
|
|
47
|
+
editRow(rowKey, partialRow);
|
|
48
|
+
|
|
49
|
+
// Update the rows
|
|
50
|
+
setRows?.((oldRows) => {
|
|
51
|
+
const newRows = [...oldRows];
|
|
52
|
+
newRows[rowIndex] = newRow;
|
|
53
|
+
return newRows;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}, [
|
|
57
|
+
editRow,
|
|
58
|
+
partialRowGetter,
|
|
59
|
+
row,
|
|
60
|
+
rowKey,
|
|
61
|
+
rowKeyGetter,
|
|
62
|
+
rows,
|
|
63
|
+
setRows,
|
|
64
|
+
value,
|
|
65
|
+
]);
|
|
21
66
|
|
|
22
67
|
const onClose = useCallback(
|
|
23
|
-
(
|
|
24
|
-
if (
|
|
25
|
-
|
|
68
|
+
(shouldSave: boolean, nextEditingCell: [number, number] = [-1, -1]) => {
|
|
69
|
+
if (shouldSave && value !== initialValue) {
|
|
70
|
+
save();
|
|
26
71
|
}
|
|
27
72
|
setEditingCell(nextEditingCell);
|
|
28
73
|
},
|
|
29
|
-
[
|
|
74
|
+
[initialValue, save, setEditingCell, value]
|
|
30
75
|
);
|
|
31
76
|
|
|
32
77
|
useEffect(() => {
|
|
33
78
|
if (inputRef.current) {
|
|
34
79
|
inputRef.current.focus();
|
|
35
|
-
inputRef.current.select();
|
|
80
|
+
inputRef.current.select?.();
|
|
36
81
|
}
|
|
37
82
|
}, [inputRef]);
|
|
38
83
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ForwardedRef } from 'react';
|
|
2
2
|
|
|
3
3
|
export type DataGridEditableCellProps<R, T> = {
|
|
4
4
|
ref?: ForwardedRef<HTMLInputElement>;
|
|
@@ -9,6 +9,7 @@ export type DataGridEditableCellProps<R, T> = {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
-
export type DataGridEditableCellFC<R, T = any> =
|
|
13
|
-
DataGridEditableCellProps<R, T
|
|
14
|
-
|
|
12
|
+
export type DataGridEditableCellFC<R, T = any> = (
|
|
13
|
+
props: DataGridEditableCellProps<R, T>,
|
|
14
|
+
ref: ForwardedRef<HTMLInputElement>
|
|
15
|
+
) => JSX.Element | null;
|
|
@@ -351,8 +351,9 @@ export const selectColumn = <R extends Record<string, any>, T>(
|
|
|
351
351
|
items,
|
|
352
352
|
itemKey,
|
|
353
353
|
itemLabel,
|
|
354
|
-
editComponent: ({ value, onChange, onClose }) => (
|
|
354
|
+
editComponent: forwardRef(({ value, onChange, onClose }, ref) => (
|
|
355
355
|
<Select
|
|
356
|
+
ref={ref}
|
|
356
357
|
items={items}
|
|
357
358
|
itemKey={itemKey}
|
|
358
359
|
itemLabel={itemLabel}
|
|
@@ -360,7 +361,7 @@ export const selectColumn = <R extends Record<string, any>, T>(
|
|
|
360
361
|
onChange={onChange}
|
|
361
362
|
onBlur={() => onClose(true)}
|
|
362
363
|
/>
|
|
363
|
-
),
|
|
364
|
+
)),
|
|
364
365
|
...otherOptions,
|
|
365
366
|
});
|
|
366
367
|
|
|
@@ -151,37 +151,6 @@ export const useDataGrid = <R,>(
|
|
|
151
151
|
clearChangedRows,
|
|
152
152
|
} = useDataGridChangedRows<R>(onRowAdded);
|
|
153
153
|
|
|
154
|
-
const onCellEdited = useCallback(
|
|
155
|
-
(row: R, columnKey: string, value: any) => {
|
|
156
|
-
setEditingCell([-1, -1]);
|
|
157
|
-
const column = columns.find((col) => col.key === columnKey);
|
|
158
|
-
const rowIndex = rows.findIndex(
|
|
159
|
-
(r) => rowKeyGetter(r) === rowKeyGetter(row)
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
// If row and column are valid
|
|
163
|
-
if (rowIndex >= 0 && column) {
|
|
164
|
-
// Update the row with the new value
|
|
165
|
-
const partialRow: Partial<R> = {};
|
|
166
|
-
partialRow[columnKey as keyof R] = value;
|
|
167
|
-
const newRow = { ...row, ...partialRow };
|
|
168
|
-
const rowKey = rowKeyGetter(newRow);
|
|
169
|
-
|
|
170
|
-
// Update the edited rows state
|
|
171
|
-
editRow(rowKey, partialRow);
|
|
172
|
-
|
|
173
|
-
// Update the rows
|
|
174
|
-
const newRows = [...rows];
|
|
175
|
-
newRows[rowIndex] = newRow;
|
|
176
|
-
setRows?.(newRows);
|
|
177
|
-
|
|
178
|
-
// Call the onRowEdited callback if provided
|
|
179
|
-
// onRowEdited?.(newRow, columnKey, value);
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
[columns, editRow, rowKeyGetter, rows, setRows]
|
|
183
|
-
);
|
|
184
|
-
|
|
185
154
|
/** ROWS SORTING **/
|
|
186
155
|
|
|
187
156
|
const [sorts, setSorts] = useState<Record<string, DataGridSort>>(
|
|
@@ -301,7 +270,6 @@ export const useDataGrid = <R,>(
|
|
|
301
270
|
setFilters,
|
|
302
271
|
editingCell,
|
|
303
272
|
setEditingCell,
|
|
304
|
-
onCellEdited,
|
|
305
273
|
copyTable,
|
|
306
274
|
setColumnWidth,
|
|
307
275
|
settings,
|
|
@@ -340,7 +308,6 @@ export const useDataGrid = <R,>(
|
|
|
340
308
|
sorts,
|
|
341
309
|
filters,
|
|
342
310
|
editingCell,
|
|
343
|
-
onCellEdited,
|
|
344
311
|
copyTable,
|
|
345
312
|
setColumnWidth,
|
|
346
313
|
settings,
|
|
@@ -64,30 +64,31 @@ type CommonGridColumnProps<R, T> = {
|
|
|
64
64
|
width?: number;
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
type
|
|
67
|
+
export type EditableDataGridColumn<R, T> = CommonGridColumnProps<R, T> & {
|
|
68
68
|
editable?: boolean;
|
|
69
69
|
editComponent?: DataGridEditableCellFC<R, T>;
|
|
70
|
+
partialRowGetter?: (row: R, value: T) => Partial<R>;
|
|
70
71
|
};
|
|
71
72
|
|
|
72
|
-
export type DataGridTextColumn<R> =
|
|
73
|
+
export type DataGridTextColumn<R> = EditableDataGridColumn<R, string> & {
|
|
73
74
|
type: 'text';
|
|
74
75
|
};
|
|
75
76
|
|
|
76
|
-
export type DataGridNumberColumn<R> =
|
|
77
|
+
export type DataGridNumberColumn<R> = EditableDataGridColumn<R, number> & {
|
|
77
78
|
type: 'number';
|
|
78
79
|
decimals?: number;
|
|
79
80
|
currency?: string | ((row: R) => string);
|
|
80
81
|
};
|
|
81
82
|
|
|
82
|
-
export type DataGridDateColumn<R> =
|
|
83
|
+
export type DataGridDateColumn<R> = EditableDataGridColumn<R, Date> & {
|
|
83
84
|
type: 'date';
|
|
84
85
|
};
|
|
85
86
|
|
|
86
|
-
export type DataGridCheckboxColumn<R> =
|
|
87
|
+
export type DataGridCheckboxColumn<R> = EditableDataGridColumn<R, boolean> & {
|
|
87
88
|
type: 'checkbox';
|
|
88
89
|
};
|
|
89
90
|
|
|
90
|
-
export type DataGridSelectColumn<R, T = any> =
|
|
91
|
+
export type DataGridSelectColumn<R, T = any> = EditableDataGridColumn<
|
|
91
92
|
R,
|
|
92
93
|
T | null
|
|
93
94
|
> & {
|
|
@@ -97,7 +98,14 @@ export type DataGridSelectColumn<R, T = any> = EditableColumnProps<
|
|
|
97
98
|
itemLabel: (item: T) => string;
|
|
98
99
|
};
|
|
99
100
|
|
|
100
|
-
export type
|
|
101
|
+
export type DataGridForeignListColumn<R> = EditableDataGridColumn<
|
|
102
|
+
R,
|
|
103
|
+
string | null
|
|
104
|
+
> & {
|
|
105
|
+
type: 'foreignList';
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export type DataGridColorColumn<R> = EditableDataGridColumn<R, string> & {
|
|
101
109
|
type: 'color';
|
|
102
110
|
};
|
|
103
111
|
|
|
@@ -107,6 +115,7 @@ export type DataGridColumn<R> =
|
|
|
107
115
|
| DataGridDateColumn<R>
|
|
108
116
|
| DataGridCheckboxColumn<R>
|
|
109
117
|
| DataGridSelectColumn<R>
|
|
118
|
+
| DataGridForeignListColumn<R>
|
|
110
119
|
| DataGridColorColumn<R>;
|
|
111
120
|
|
|
112
121
|
export type DataGridColumnWithFilter<R> = DataGridColumn<R> & {
|
|
@@ -246,6 +255,9 @@ export type DataGridCellProps<R> = {
|
|
|
246
255
|
userSelect?: boolean;
|
|
247
256
|
color?: ThemeColor;
|
|
248
257
|
};
|
|
258
|
+
export type EditableDataGridCellProps<R, T> = DataGridCellProps<R> & {
|
|
259
|
+
column: EditableDataGridColumn<R, T>;
|
|
260
|
+
};
|
|
249
261
|
|
|
250
262
|
export type DataGridHeaderCellProps<R> = {
|
|
251
263
|
columnKey: string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ForwardedRef, forwardRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import { SqlRequestForeignList } from '../SqlRequestForeignList';
|
|
4
|
+
import { SqlRequestForeignListProps } from '../SqlRequestForeignList/types';
|
|
5
|
+
|
|
6
|
+
const SqlRequestForeignListEditableCellInner = <
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
R extends Record<string, any>
|
|
9
|
+
>(
|
|
10
|
+
props: SqlRequestForeignListProps<R>,
|
|
11
|
+
ref: ForwardedRef<HTMLDivElement>
|
|
12
|
+
) => <SqlRequestForeignList<R> {...props} ref={ref} />;
|
|
13
|
+
|
|
14
|
+
export const SqlRequestForeignListEditableCell = forwardRef(
|
|
15
|
+
SqlRequestForeignListEditableCellInner
|
|
16
|
+
);
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
import { SqlImage, SqlImageWrapper } from '../styles';
|
|
4
4
|
import {
|
|
5
5
|
SqlRequestDataGridCheckboxColumn,
|
|
6
|
+
SqlRequestDataGridColumn,
|
|
6
7
|
SqlRequestDataGridDateColumn,
|
|
8
|
+
SqlRequestDataGridForeignListColumn,
|
|
7
9
|
SqlRequestDataGridNumberColumn,
|
|
8
10
|
SqlRequestDataGridTextColumn,
|
|
9
11
|
} from '../types';
|
|
@@ -22,11 +24,18 @@ import {
|
|
|
22
24
|
} from '../../../../helpers/numbers';
|
|
23
25
|
|
|
24
26
|
import { CheckboxEditableCell } from '../../DataGrid/DataGridEditableCell/CheckboxEditableCell';
|
|
27
|
+
import { DataGridSettings } from '../../DataGrid/types';
|
|
25
28
|
import { DateEditableCell } from '../../DataGrid/DataGridEditableCell/DateEditableCell';
|
|
26
29
|
import { NumberEditableCell } from '../../DataGrid/DataGridEditableCell/NumberEditableCell';
|
|
30
|
+
import { SqlRequestForeignList } from '../../SqlRequestForeignList';
|
|
31
|
+
import { SqlRequestForeignListProps } from '../../SqlRequestForeignList/types';
|
|
27
32
|
import { TextEditableCell } from '../../DataGrid/DataGridEditableCell/TextEditableCell';
|
|
28
33
|
import { forwardRef } from 'react';
|
|
29
34
|
|
|
35
|
+
export const isColumnVisible = <R,>(
|
|
36
|
+
obj: SqlRequestDataGridColumn<R> | DataGridSettings
|
|
37
|
+
): boolean => obj?.order !== -1;
|
|
38
|
+
|
|
30
39
|
export const sqlTextColumn = <R extends Record<string, any>>(
|
|
31
40
|
key: string,
|
|
32
41
|
title: React.ReactNode,
|
|
@@ -364,3 +373,35 @@ export const sqlColorColumn = <R extends Record<string, any>>(
|
|
|
364
373
|
},
|
|
365
374
|
...options,
|
|
366
375
|
});
|
|
376
|
+
|
|
377
|
+
export const sqlForeignListColumn = <
|
|
378
|
+
R extends Record<string, any>,
|
|
379
|
+
T extends Record<string, any>
|
|
380
|
+
>(
|
|
381
|
+
key: string,
|
|
382
|
+
title: string,
|
|
383
|
+
foreignListOptions: SqlRequestForeignListProps<T>,
|
|
384
|
+
options?: Partial<SqlRequestDataGridForeignListColumn<R, T | null>>
|
|
385
|
+
): SqlRequestDataGridForeignListColumn<R, T | null> => ({
|
|
386
|
+
key,
|
|
387
|
+
name: title,
|
|
388
|
+
type: 'foreignList',
|
|
389
|
+
getter: (row) => row[key] ?? '',
|
|
390
|
+
sortGetter: (row) => row[key] ?? '',
|
|
391
|
+
filter: {
|
|
392
|
+
...textFilter(key),
|
|
393
|
+
getter: (value) => value[key] ?? '',
|
|
394
|
+
renderer: (value) => value,
|
|
395
|
+
},
|
|
396
|
+
footer: (rows) => `${rows[0][key]} éléments`,
|
|
397
|
+
editComponent: forwardRef(({ row, onChange, onClose }, ref) => (
|
|
398
|
+
<SqlRequestForeignList<T>
|
|
399
|
+
initialId={row[key] ?? null}
|
|
400
|
+
onItemChanged={onChange}
|
|
401
|
+
onBlur={() => onClose(true)}
|
|
402
|
+
{...foreignListOptions}
|
|
403
|
+
ref={ref}
|
|
404
|
+
/>
|
|
405
|
+
)),
|
|
406
|
+
...options,
|
|
407
|
+
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
ConditionDTO,
|
|
3
5
|
FieldDTO,
|
|
@@ -12,6 +14,7 @@ import {
|
|
|
12
14
|
DataGridNumberColumn,
|
|
13
15
|
DataGridProps,
|
|
14
16
|
DataGridTextColumn,
|
|
17
|
+
EditableDataGridColumn,
|
|
15
18
|
} from '../DataGrid/types';
|
|
16
19
|
|
|
17
20
|
export type SqlRequestDataGridFilter<
|
|
@@ -41,12 +44,17 @@ export type SqlRequestDataGridCheckboxColumn<R> = DataGridCheckboxColumn<R> &
|
|
|
41
44
|
SqlRequestDataGridCommonProps;
|
|
42
45
|
export type SqlRequestDataGridDateColumn<R> = DataGridDateColumn<R> &
|
|
43
46
|
SqlRequestDataGridCommonProps;
|
|
47
|
+
export type SqlRequestDataGridForeignListColumn<R, T> = {
|
|
48
|
+
type: 'foreignList';
|
|
49
|
+
} & EditableDataGridColumn<R, T> &
|
|
50
|
+
SqlRequestDataGridCommonProps;
|
|
44
51
|
|
|
45
52
|
export type SqlRequestDataGridColumn<R> =
|
|
46
53
|
| SqlRequestDataGridTextColumn<R>
|
|
47
54
|
| SqlRequestDataGridNumberColumn<R>
|
|
48
55
|
| SqlRequestDataGridCheckboxColumn<R>
|
|
49
|
-
| SqlRequestDataGridDateColumn<R
|
|
56
|
+
| SqlRequestDataGridDateColumn<R>
|
|
57
|
+
| SqlRequestDataGridForeignListColumn<R, any>;
|
|
50
58
|
|
|
51
59
|
export type SqlRequestDataGridColumns<R> = SqlRequestDataGridColumn<R>[];
|
|
52
60
|
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
ForwardedRef,
|
|
5
|
+
MouseEvent,
|
|
6
|
+
forwardRef,
|
|
7
|
+
useCallback,
|
|
8
|
+
useEffect,
|
|
9
|
+
useImperativeHandle,
|
|
10
|
+
useMemo,
|
|
11
|
+
useRef,
|
|
12
|
+
useState,
|
|
13
|
+
} from 'react';
|
|
3
14
|
import {
|
|
4
15
|
SqlRequestForeignListInput,
|
|
5
16
|
SqlRequestForeignListItem,
|
|
6
17
|
SqlRequestForeignListValue,
|
|
7
18
|
} from './styles';
|
|
8
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
SqlRequestForeignListItemProps,
|
|
21
|
+
SqlRequestForeignListProps,
|
|
22
|
+
} from './types';
|
|
9
23
|
|
|
10
24
|
import { ChevronDownIcon } from '../../../Icons';
|
|
11
25
|
import { Dropdown } from '../../layout';
|
|
@@ -14,93 +28,105 @@ import { useSqlRequestHandler } from '../../../services';
|
|
|
14
28
|
|
|
15
29
|
const DEFAULT_ROW_HEIGHT = 24;
|
|
16
30
|
|
|
17
|
-
type SqlRequestForeignListItemProps<R extends Record<string, any>> = {
|
|
18
|
-
onClick?: (item: R | null) => void;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
31
|
const createValueTemplate =
|
|
22
32
|
<R extends Record<string, any>>(
|
|
23
|
-
|
|
33
|
+
ValueTemplate: VirtualScrollerTemplateFC<
|
|
34
|
+
R,
|
|
35
|
+
SqlRequestForeignListItemProps<R>
|
|
36
|
+
>,
|
|
24
37
|
rowHeight = DEFAULT_ROW_HEIGHT
|
|
25
38
|
): VirtualScrollerTemplateFC<R, SqlRequestForeignListItemProps<R>> =>
|
|
26
39
|
({ item }) =>
|
|
27
40
|
(
|
|
28
41
|
<SqlRequestForeignListValue $rowHeight={rowHeight}>
|
|
29
|
-
{item
|
|
42
|
+
<ValueTemplate item={item} index={0} />
|
|
30
43
|
</SqlRequestForeignListValue>
|
|
31
44
|
);
|
|
32
45
|
|
|
33
46
|
const createItemTemplate =
|
|
34
47
|
<R extends Record<string, any>>(
|
|
35
|
-
|
|
48
|
+
ItemTemplate: VirtualScrollerTemplateFC<
|
|
49
|
+
R,
|
|
50
|
+
SqlRequestForeignListItemProps<R>
|
|
51
|
+
>,
|
|
36
52
|
rowHeight = DEFAULT_ROW_HEIGHT
|
|
37
53
|
): VirtualScrollerTemplateFC<R, SqlRequestForeignListItemProps<R>> =>
|
|
38
|
-
({ item, onClick }) =>
|
|
39
|
-
(
|
|
54
|
+
({ item, index, onClick: onItemClick }) => {
|
|
55
|
+
const onClick = useCallback(
|
|
56
|
+
(
|
|
57
|
+
e: MouseEvent<HTMLElement>,
|
|
58
|
+
item: R | null,
|
|
59
|
+
onItemClick?: (item: R | null) => void
|
|
60
|
+
) => {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
e.stopPropagation();
|
|
63
|
+
onItemClick?.(item);
|
|
64
|
+
},
|
|
65
|
+
[]
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return (
|
|
40
69
|
<SqlRequestForeignListItem
|
|
41
70
|
$rowHeight={rowHeight}
|
|
42
|
-
onClick={() => onClick
|
|
71
|
+
onClick={(e) => onClick(e, item, onItemClick)}
|
|
43
72
|
>
|
|
44
|
-
{item
|
|
73
|
+
<ItemTemplate item={item} index={index} />
|
|
45
74
|
</SqlRequestForeignListItem>
|
|
46
75
|
);
|
|
76
|
+
};
|
|
47
77
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
export const SqlRequestForeignList = <R extends Record<string, any>>({
|
|
67
|
-
requestName,
|
|
68
|
-
idColumn,
|
|
69
|
-
columns = [],
|
|
70
|
-
value,
|
|
71
|
-
onChange,
|
|
72
|
-
itemTemplate,
|
|
73
|
-
valueTemplate,
|
|
74
|
-
rowHeight = DEFAULT_ROW_HEIGHT,
|
|
75
|
-
maxRows = 10,
|
|
76
|
-
}: SqlRequestForeignListProps<R>) => {
|
|
77
|
-
const inputRef = useRef<HTMLDivElement>(null);
|
|
78
|
+
export const SqlRequestForeignListInner = <T extends Record<string, any>>(
|
|
79
|
+
{
|
|
80
|
+
requestName,
|
|
81
|
+
idColumn,
|
|
82
|
+
columns = [],
|
|
83
|
+
initialId,
|
|
84
|
+
onItemChanged,
|
|
85
|
+
onIdChanged,
|
|
86
|
+
onClosed,
|
|
87
|
+
onBlur,
|
|
88
|
+
itemTemplate,
|
|
89
|
+
valueTemplate,
|
|
90
|
+
rowHeight = DEFAULT_ROW_HEIGHT,
|
|
91
|
+
maxRows = 10,
|
|
92
|
+
}: SqlRequestForeignListProps<T>,
|
|
93
|
+
ref: ForwardedRef<HTMLDivElement>
|
|
94
|
+
) => {
|
|
78
95
|
const [opened, setOpened] = useState(false);
|
|
79
96
|
const [rect, setRect] = useState<DOMRect | null>(null);
|
|
80
|
-
const [rows, setRows] = useState<
|
|
97
|
+
const [rows, setRows] = useState<T[]>([]);
|
|
81
98
|
const [loaded, setLoaded] = useState(false);
|
|
99
|
+
const [selectedItem, setSelectedItem] = useState<T | null>(null);
|
|
100
|
+
|
|
101
|
+
// Create an internal ref for the component to use
|
|
102
|
+
const internalRef = useRef<HTMLDivElement>(null);
|
|
103
|
+
|
|
104
|
+
// Forward the ref while linking it to our internal ref
|
|
105
|
+
useImperativeHandle(ref, () => internalRef.current!, []);
|
|
82
106
|
|
|
83
|
-
const [sqlRequest] = useSqlRequestHandler<
|
|
84
|
-
const [selectedItem, setSelectedItem] = useState<R | null>(null);
|
|
107
|
+
const [sqlRequest] = useSqlRequestHandler<T>(requestName);
|
|
85
108
|
|
|
86
109
|
const open = useCallback(() => {
|
|
87
|
-
if (
|
|
88
|
-
|
|
110
|
+
if (internalRef.current) {
|
|
111
|
+
const parentRect = internalRef.current.getBoundingClientRect();
|
|
112
|
+
setRect(new DOMRect(0, 0, parentRect.width, parentRect.height));
|
|
89
113
|
setOpened(true);
|
|
90
114
|
}
|
|
91
115
|
}, []);
|
|
92
116
|
|
|
93
117
|
const close = useCallback(() => {
|
|
94
118
|
setOpened(false);
|
|
95
|
-
|
|
119
|
+
onClosed?.();
|
|
120
|
+
}, [onClosed]);
|
|
96
121
|
|
|
97
122
|
const onItemClick = useCallback(
|
|
98
|
-
(item:
|
|
123
|
+
(item: T | null) => {
|
|
99
124
|
setSelectedItem(item);
|
|
100
|
-
|
|
125
|
+
onItemChanged?.(item);
|
|
126
|
+
onIdChanged?.(item?.[idColumn] ?? null);
|
|
101
127
|
close();
|
|
102
128
|
},
|
|
103
|
-
[close, idColumn,
|
|
129
|
+
[close, idColumn, onIdChanged, onItemChanged]
|
|
104
130
|
);
|
|
105
131
|
|
|
106
132
|
useEffect(() => {
|
|
@@ -110,48 +136,66 @@ export const SqlRequestForeignList = <R extends Record<string, any>>({
|
|
|
110
136
|
// conditions,
|
|
111
137
|
// orderBy,
|
|
112
138
|
}).then((response) => {
|
|
113
|
-
const data = (response.data ?? []) as
|
|
139
|
+
const data = (response.data ?? []) as T[];
|
|
114
140
|
setRows(data);
|
|
115
141
|
setSelectedItem(
|
|
116
|
-
data.find((item) => String(item[idColumn]) ===
|
|
142
|
+
data.find((item) => String(item[idColumn]) === initialId) ?? null
|
|
117
143
|
);
|
|
118
144
|
setLoaded(true);
|
|
119
145
|
});
|
|
120
146
|
}
|
|
121
|
-
}, [columns, idColumn, loaded, sqlRequest
|
|
147
|
+
}, [columns, idColumn, initialId, loaded, sqlRequest]);
|
|
122
148
|
|
|
123
149
|
const ItemTemplate = useMemo(
|
|
124
|
-
() =>
|
|
150
|
+
() =>
|
|
151
|
+
createItemTemplate<T>(
|
|
152
|
+
itemTemplate ??
|
|
153
|
+
(({ item }) => <span>{item?.[columns[0]] ?? '(Aucun)'}</span>),
|
|
154
|
+
rowHeight
|
|
155
|
+
),
|
|
125
156
|
[columns, itemTemplate, rowHeight]
|
|
126
157
|
);
|
|
127
158
|
const ValueTemplate = useMemo(
|
|
128
|
-
() =>
|
|
159
|
+
() =>
|
|
160
|
+
createValueTemplate<T>(
|
|
161
|
+
valueTemplate ??
|
|
162
|
+
(({ item }) => <span>{item?.[columns[0]] ?? '(Aucun)'}</span>),
|
|
163
|
+
rowHeight
|
|
164
|
+
),
|
|
129
165
|
[columns, rowHeight, valueTemplate]
|
|
130
166
|
);
|
|
131
167
|
|
|
132
168
|
return (
|
|
133
169
|
<>
|
|
134
|
-
<SqlRequestForeignListInput
|
|
170
|
+
<SqlRequestForeignListInput
|
|
171
|
+
tabIndex={0}
|
|
172
|
+
ref={internalRef}
|
|
173
|
+
onClick={open}
|
|
174
|
+
onBlur={onBlur}
|
|
175
|
+
>
|
|
135
176
|
<ValueTemplate item={selectedItem} index={0} />
|
|
136
177
|
<ChevronDownIcon />
|
|
178
|
+
{opened && rect && (
|
|
179
|
+
<Dropdown
|
|
180
|
+
$height={[Math.min(maxRows, rows.length) * rowHeight, 600]}
|
|
181
|
+
$width={rect.width}
|
|
182
|
+
$sourceRect={rect}
|
|
183
|
+
onClose={close}
|
|
184
|
+
usePortal={false}
|
|
185
|
+
>
|
|
186
|
+
{rows.map((row, index) => (
|
|
187
|
+
<ItemTemplate
|
|
188
|
+
item={row}
|
|
189
|
+
onClick={() => onItemClick(row)}
|
|
190
|
+
key={index}
|
|
191
|
+
index={index}
|
|
192
|
+
/>
|
|
193
|
+
))}
|
|
194
|
+
</Dropdown>
|
|
195
|
+
)}
|
|
137
196
|
</SqlRequestForeignListInput>
|
|
138
|
-
{opened && rect && (
|
|
139
|
-
<Dropdown
|
|
140
|
-
$height={[Math.min(maxRows, rows.length) * rowHeight, 600]}
|
|
141
|
-
$width={rect.width}
|
|
142
|
-
$sourceRect={rect}
|
|
143
|
-
onClose={close}
|
|
144
|
-
>
|
|
145
|
-
{rows.map((row, index) => (
|
|
146
|
-
<ItemTemplate
|
|
147
|
-
item={row}
|
|
148
|
-
onClick={onItemClick}
|
|
149
|
-
key={index}
|
|
150
|
-
index={index}
|
|
151
|
-
/>
|
|
152
|
-
))}
|
|
153
|
-
</Dropdown>
|
|
154
|
-
)}
|
|
155
197
|
</>
|
|
156
198
|
);
|
|
157
199
|
};
|
|
200
|
+
|
|
201
|
+
export const SqlRequestForeignList = forwardRef(SqlRequestForeignListInner);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
|
|
3
|
+
import { VirtualScrollerTemplateFC } from '../VirtualScroller/types';
|
|
4
|
+
|
|
5
|
+
export type SqlRequestForeignListProps<T extends Record<string, any>> = {
|
|
6
|
+
requestName: string;
|
|
7
|
+
idColumn: string;
|
|
8
|
+
columns: string[];
|
|
9
|
+
initialId?: string | null;
|
|
10
|
+
onIdChanged?: (value: string | null) => void;
|
|
11
|
+
onItemChanged?: (value: T | null) => void;
|
|
12
|
+
onClosed?: () => void;
|
|
13
|
+
onBlur?: () => void;
|
|
14
|
+
valueTemplate?: VirtualScrollerTemplateFC<
|
|
15
|
+
T,
|
|
16
|
+
SqlRequestForeignListItemProps<T>
|
|
17
|
+
>;
|
|
18
|
+
itemTemplate?: VirtualScrollerTemplateFC<
|
|
19
|
+
T,
|
|
20
|
+
SqlRequestForeignListItemProps<T>
|
|
21
|
+
>;
|
|
22
|
+
rowHeight?: number;
|
|
23
|
+
maxRows?: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type SqlRequestForeignListItemProps<T extends Record<string, any>> = {
|
|
27
|
+
onClick?: (item: T | null) => void;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type SqlRequestForeignListItemFC<T extends Record<string, any>> =
|
|
31
|
+
VirtualScrollerTemplateFC<T, SqlRequestForeignListItemProps<T>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SelectHTMLAttributes, useCallback } from 'react';
|
|
1
|
+
import { SelectHTMLAttributes, forwardRef, useCallback } from 'react';
|
|
2
2
|
|
|
3
3
|
import { InputContainerProps } from './types';
|
|
4
4
|
import { InputWithLabel } from './InputWithLabel';
|
|
@@ -14,7 +14,7 @@ type SelectProps<T> = {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
-
|
|
17
|
+
const SelectInner = <T,>({
|
|
18
18
|
icon,
|
|
19
19
|
label,
|
|
20
20
|
items,
|
|
@@ -63,3 +63,5 @@ export const Select = <T,>({
|
|
|
63
63
|
</InputWithLabel>
|
|
64
64
|
);
|
|
65
65
|
};
|
|
66
|
+
|
|
67
|
+
export const Select = forwardRef(SelectInner);
|
|
@@ -18,6 +18,7 @@ type DropdownProps = PropsWithChildren<
|
|
|
18
18
|
$autoPositionX?: boolean;
|
|
19
19
|
$autoPositionY?: boolean;
|
|
20
20
|
onClose?: () => void;
|
|
21
|
+
usePortal?: boolean;
|
|
21
22
|
style?: CSSProperties;
|
|
22
23
|
}
|
|
23
24
|
>;
|
|
@@ -65,6 +66,7 @@ const getDropdownStyle = (dropdown: DropdownProps): CSSProperties => {
|
|
|
65
66
|
export const Dropdown: FC<DropdownProps> = ({
|
|
66
67
|
children,
|
|
67
68
|
onClose,
|
|
69
|
+
usePortal = true,
|
|
68
70
|
...props
|
|
69
71
|
}) => {
|
|
70
72
|
const { createPortal } = usePortals();
|
|
@@ -98,15 +100,14 @@ export const Dropdown: FC<DropdownProps> = ({
|
|
|
98
100
|
}
|
|
99
101
|
}, [onDocumentClick]);
|
|
100
102
|
|
|
101
|
-
const modalPortal = useMemo(
|
|
102
|
-
(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
);
|
|
103
|
+
const modalPortal = useMemo(() => {
|
|
104
|
+
const content = (
|
|
105
|
+
<styles.DropdownContainer ref={containerRef} {...props} style={style}>
|
|
106
|
+
{children}
|
|
107
|
+
</styles.DropdownContainer>
|
|
108
|
+
);
|
|
109
|
+
return usePortal ? createPortal(content) : content;
|
|
110
|
+
}, [children, createPortal, props, style, usePortal]);
|
|
110
111
|
|
|
111
112
|
return <>{modalPortal}</>;
|
|
112
113
|
};
|