@addev-be/ui 0.2.16 → 0.2.18

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 (88) hide show
  1. package/eslint.config.js +28 -0
  2. package/package.json +13 -20
  3. package/src/Icons.tsx +108 -0
  4. package/src/components/data/AdvancedRequestDataGrid/helpers/advancedRequests.ts +93 -0
  5. package/src/components/data/AdvancedRequestDataGrid/helpers/columns.tsx +262 -0
  6. package/src/components/data/AdvancedRequestDataGrid/helpers/index.ts +2 -0
  7. package/src/components/data/AdvancedRequestDataGrid/index.tsx +267 -0
  8. package/src/components/data/AdvancedRequestDataGrid/types.ts +47 -0
  9. package/src/components/data/DataGrid/DataGridCell.tsx +73 -0
  10. package/src/components/data/DataGrid/DataGridColumnsModal/helpers.ts +14 -0
  11. package/src/components/data/DataGrid/DataGridColumnsModal/hooks.tsx +59 -0
  12. package/src/components/data/DataGrid/DataGridColumnsModal/index.tsx +181 -0
  13. package/src/components/data/DataGrid/DataGridColumnsModal/styles.ts +104 -0
  14. package/src/components/data/DataGrid/DataGridEditableCell.tsx +43 -0
  15. package/src/components/data/DataGrid/DataGridFilterMenu/FilterValuesScroller.tsx +120 -0
  16. package/src/components/data/DataGrid/DataGridFilterMenu/hooks.tsx +75 -0
  17. package/src/components/data/DataGrid/DataGridFilterMenu/index.tsx +360 -0
  18. package/src/components/data/DataGrid/DataGridFilterMenu/styles.ts +96 -0
  19. package/src/components/data/DataGrid/DataGridFooter.tsx +42 -0
  20. package/src/components/data/DataGrid/DataGridHeader.tsx +126 -0
  21. package/src/components/data/DataGrid/DataGridHeaderCell.tsx +132 -0
  22. package/src/components/data/DataGrid/FilterModalContent/index.tsx +136 -0
  23. package/src/components/data/DataGrid/FilterModalContent/styles.ts +22 -0
  24. package/src/components/data/DataGrid/VirtualScroller.tsx +46 -0
  25. package/src/components/data/DataGrid/helpers/columns.tsx +295 -0
  26. package/src/components/data/DataGrid/helpers/filters.ts +287 -0
  27. package/src/components/data/DataGrid/helpers/index.ts +2 -0
  28. package/src/components/data/DataGrid/hooks/index.ts +30 -0
  29. package/src/components/data/DataGrid/hooks/useDataGrid.tsx +306 -0
  30. package/src/components/data/DataGrid/hooks/useDataGridCopy.ts +175 -0
  31. package/src/components/data/DataGrid/hooks/useDataGridSettings.ts +48 -0
  32. package/src/components/data/DataGrid/index.tsx +140 -0
  33. package/src/components/data/DataGrid/styles.ts +323 -0
  34. package/src/components/data/DataGrid/types.ts +267 -0
  35. package/src/components/data/SqlRequestDataGrid/helpers/columns.tsx +277 -0
  36. package/src/components/data/SqlRequestDataGrid/helpers/index.ts +2 -0
  37. package/src/components/data/SqlRequestDataGrid/helpers/sqlRequests.ts +16 -0
  38. package/src/components/data/SqlRequestDataGrid/index.tsx +347 -0
  39. package/src/components/data/SqlRequestDataGrid/types.ts +47 -0
  40. package/src/components/data/index.ts +8 -0
  41. package/src/components/forms/Button.tsx +99 -0
  42. package/src/components/forms/IconButton.tsx +56 -0
  43. package/src/components/forms/IndeterminateCheckbox.tsx +46 -0
  44. package/src/components/forms/Select.tsx +40 -0
  45. package/src/components/forms/index.ts +5 -0
  46. package/src/components/forms/styles.ts +20 -0
  47. package/src/components/index.ts +3 -0
  48. package/src/components/layout/Dropdown/index.tsx +79 -0
  49. package/src/components/layout/Dropdown/styles.ts +44 -0
  50. package/src/components/layout/Loading/index.tsx +29 -0
  51. package/src/components/layout/Loading/styles.ts +29 -0
  52. package/src/components/layout/Modal/index.tsx +51 -0
  53. package/src/components/layout/Modal/styles.ts +110 -0
  54. package/src/components/layout/index.ts +3 -0
  55. package/src/components/ui/ContextMenu/index.tsx +79 -0
  56. package/src/components/ui/ContextMenu/styles.ts +119 -0
  57. package/src/config/index.ts +14 -0
  58. package/src/helpers/dates.ts +9 -0
  59. package/src/helpers/getScrollbarSize.ts +14 -0
  60. package/src/helpers/numbers.ts +26 -0
  61. package/src/hooks/index.ts +2 -0
  62. package/src/hooks/useElementSize.ts +24 -0
  63. package/src/hooks/useWindowSize.ts +20 -0
  64. package/src/index.ts +7 -0
  65. package/src/providers/PortalsProvider/index.tsx +54 -0
  66. package/src/providers/PortalsProvider/styles.ts +27 -0
  67. package/src/providers/SettingsProvider/index.tsx +70 -0
  68. package/src/providers/ThemeProvider/ThemeProvider.ts +55 -0
  69. package/src/providers/ThemeProvider/defaultTheme.ts +444 -0
  70. package/src/providers/ThemeProvider/index.ts +3 -0
  71. package/src/providers/ThemeProvider/types.ts +123 -0
  72. package/src/providers/UiProviders/index.tsx +65 -0
  73. package/src/providers/UiProviders/styles.ts +10 -0
  74. package/src/providers/hooks.ts +8 -0
  75. package/src/providers/index.ts +5 -0
  76. package/src/services/HttpService.ts +80 -0
  77. package/src/services/WebSocketService.ts +147 -0
  78. package/src/services/advancedRequests.ts +101 -0
  79. package/src/services/base.ts +31 -0
  80. package/src/services/globalSearch.ts +27 -0
  81. package/src/services/hooks.ts +23 -0
  82. package/src/services/index.ts +2 -0
  83. package/src/services/sqlRequests.ts +110 -0
  84. package/src/styles/animations.scss +30 -0
  85. package/src/styles/index.scss +42 -0
  86. package/src/typings.d.ts +6 -0
  87. package/tsconfig.json +18 -0
  88. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,306 @@
1
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+
4
+ import { DEFAULT_ROW_HEIGHT, VIRTUAL_SCROLL_TOLERANCE } from '../styles';
5
+ import {
6
+ DataGridColumns,
7
+ DataGridContext,
8
+ DataGridContextProps,
9
+ DataGridFilters,
10
+ DataGridFooterFunction,
11
+ DataGridProps,
12
+ DataGridSort,
13
+ } from '../types';
14
+ import {
15
+ createContext,
16
+ useCallback,
17
+ useEffect,
18
+ useMemo,
19
+ useRef,
20
+ useState,
21
+ } from 'react';
22
+ import { useDataGridCopy, useSortedColumns } from '.';
23
+
24
+ import { applyFilter } from '../helpers';
25
+ import { merge } from 'lodash';
26
+ import { useDataGridSettings } from './useDataGridSettings';
27
+ import { useElementSize } from '../../../../hooks';
28
+
29
+ export const useDataGrid = <R,>(
30
+ props: DataGridProps<R>,
31
+ override?: Partial<DataGridContextProps<R>>
32
+ ): [DataGridContextProps<R>, DataGridContext<R>] => {
33
+ const {
34
+ rows,
35
+ onFiltersChanged,
36
+ onSortsChanged,
37
+ loadCopyRows,
38
+ rowHeight = DEFAULT_ROW_HEIGHT,
39
+ onVisibleRowsChange,
40
+ onSelectionChange,
41
+ selectable,
42
+ initialSorts,
43
+ } = props;
44
+ const [columns, setColumns] = useState<DataGridColumns<R>>(props.columns);
45
+
46
+ const visibleColumns = useSortedColumns(columns);
47
+
48
+ /** SETTINGS */
49
+
50
+ const { settings, setSettings, saveSettings } = useDataGridSettings(
51
+ props.name
52
+ );
53
+
54
+ const setColumnWidth = useCallback(
55
+ (key: string, width: number) => {
56
+ setSettings((prev) => ({ ...prev, [key]: { ...prev[key], width } }));
57
+ },
58
+ [setSettings]
59
+ );
60
+
61
+ const gridTemplateColumns = useMemo(
62
+ () =>
63
+ [
64
+ ...(selectable ? ['var(--space-10)'] : []),
65
+ ...visibleColumns.map(([, col]) => `${col.width ?? 150}px`),
66
+ ].join(' '),
67
+ [selectable, visibleColumns]
68
+ );
69
+
70
+ // Update columns when settings change
71
+ useEffect(() => {
72
+ setColumns((prev) => merge({}, prev, settings));
73
+ }, [settings]);
74
+
75
+ /** ROWS SELECTION **/
76
+
77
+ const rowKeyGetter = useMemo(
78
+ () =>
79
+ typeof props.rowKey === 'function'
80
+ ? props.rowKey
81
+ : (row: R) => String(row?.[props.rowKey as keyof R]),
82
+ [props.rowKey]
83
+ );
84
+ const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
85
+
86
+ const selectedRows = useMemo(
87
+ () => props.rows.filter((row) => selectedKeys.includes(rowKeyGetter(row))),
88
+ [props.rows, rowKeyGetter, selectedKeys]
89
+ );
90
+
91
+ useEffect(() => {
92
+ onSelectionChange?.(selectedKeys);
93
+ }, [onSelectionChange, selectedKeys]);
94
+
95
+ /** ROWS FILTERING **/
96
+
97
+ const [filters, setFilters] = useState<DataGridFilters>({});
98
+ const [filteredRows, setFilteredRows] = useState<R[]>(rows ?? []);
99
+
100
+ useEffect(() => {
101
+ onFiltersChanged?.(filters);
102
+ }, [filters, onFiltersChanged]);
103
+
104
+ useEffect(() => {
105
+ const filteredRows =
106
+ props.filter === false
107
+ ? rows
108
+ : Object.entries(filters).reduce(
109
+ (tempRows, [, filter]) =>
110
+ applyFilter(tempRows, filter as any /* TODO : fix typings */),
111
+ rows
112
+ );
113
+ setFilteredRows(filteredRows ?? []);
114
+ }, [filters, props.filter, rows]);
115
+
116
+ /** CELL EDITING */
117
+
118
+ const [editingCell, setEditingCell] = useState<[number, number]>([-1, -1]);
119
+
120
+ /** ROWS SORTING **/
121
+
122
+ const [sorts, setSorts] = useState<Record<string, DataGridSort>>(
123
+ initialSorts ?? {}
124
+ );
125
+ const [sortedRows, setSortedRows] = useState<R[]>(rows);
126
+
127
+ useEffect(() => {
128
+ const sortedRows = [...filteredRows];
129
+ if (props.sort !== false) {
130
+ Object.entries(sorts).forEach(([sortKey, sort]) => {
131
+ // Get the sort column and make sure it exists and is sortable
132
+ const sortColumn = columns[sortKey];
133
+ if (!sortColumn || typeof sortColumn.sortGetter !== 'function') return;
134
+
135
+ // Sort the rows using the sortGetter function of the column
136
+ const sortDirection = sort === 'asc' ? 1 : -1;
137
+ sortedRows.sort((a: R, b: R) => {
138
+ const aValue = sortColumn.sortGetter!(a);
139
+ const bValue = sortColumn.sortGetter!(b);
140
+ if (aValue === bValue) return 0;
141
+ return aValue > bValue ? sortDirection : -sortDirection;
142
+ });
143
+ });
144
+ }
145
+ setSortedRows(sortedRows);
146
+ }, [columns, filteredRows, sorts, onSortsChanged, props.sort]);
147
+
148
+ useEffect(() => {
149
+ onSortsChanged?.(sorts);
150
+ }, [sorts, onSortsChanged]);
151
+
152
+ /** VIRTUAL SCROLLING */
153
+
154
+ const scrollableRef = useRef<HTMLTableSectionElement | null>(null);
155
+ const { height } = useElementSize(scrollableRef.current);
156
+
157
+ const [scrollTop, setScrollTop] = useState(0);
158
+ const [index, length] =
159
+ // props.showAllRows ? [0, sortedRows.length] :
160
+ [Math.floor(scrollTop / rowHeight), Math.ceil(height / rowHeight)];
161
+
162
+ const onScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
163
+ const target = e.target as HTMLDivElement;
164
+ setScrollTop(target.scrollTop);
165
+ }, []);
166
+
167
+ const indexWithTolerance = Math.max(0, index - VIRTUAL_SCROLL_TOLERANCE);
168
+ const lengthWithTolerance = length + 2 * VIRTUAL_SCROLL_TOLERANCE;
169
+ const visibleRows = sortedRows.slice(
170
+ indexWithTolerance,
171
+ indexWithTolerance + lengthWithTolerance
172
+ );
173
+
174
+ useEffect(() => {
175
+ onVisibleRowsChange?.(indexWithTolerance, lengthWithTolerance);
176
+ }, [indexWithTolerance, lengthWithTolerance, onVisibleRowsChange]);
177
+
178
+ /** FOOTERS */
179
+ const [footers, setFooters] = useState<Record<string, string>>(
180
+ props.initialFooters ?? {}
181
+ );
182
+
183
+ const footerFunctions = useMemo(
184
+ () =>
185
+ Object.entries(footers).reduce((acc, [columnKey, footerKey]) => {
186
+ const column = columns[columnKey];
187
+ if (!column) {
188
+ return acc;
189
+ }
190
+ if (typeof column.footer === 'function') {
191
+ acc[columnKey] = column.footer;
192
+ } else if (
193
+ typeof column.footer === 'object' &&
194
+ typeof column.footer[footerKey] === 'function' &&
195
+ column.footer[footerKey] !== null
196
+ ) {
197
+ acc[columnKey] = column.footer[footerKey] as any;
198
+ }
199
+ return acc;
200
+ }, {} as Record<string, DataGridFooterFunction<R>>),
201
+ [columns, footers]
202
+ );
203
+
204
+ /** COPYING */
205
+
206
+ const { copyTable } = useDataGridCopy({
207
+ rows,
208
+ visibleColumns,
209
+ footerFunctions,
210
+ loadCopyRows,
211
+ });
212
+
213
+ const contextValue = useMemo<DataGridContextProps<R>>(
214
+ () => ({
215
+ ...props,
216
+ columns,
217
+ visibleColumns,
218
+ sortedRows,
219
+ selectedRows,
220
+ selectedKeys,
221
+ setSelectedKeys,
222
+ sorts,
223
+ setSorts,
224
+ filters,
225
+ setFilters,
226
+ editingCell,
227
+ setEditingCell,
228
+ copyTable,
229
+ setColumnWidth,
230
+ settings,
231
+ setSettings,
232
+ saveSettings,
233
+ visibleRows,
234
+ scrollableRef,
235
+ onScroll,
236
+ index,
237
+ length,
238
+ rowKeyGetter,
239
+ gridTemplateColumns,
240
+ footers,
241
+ setFooters,
242
+ footerFunctions,
243
+ ...override,
244
+ }),
245
+ [
246
+ props,
247
+ columns,
248
+ visibleColumns,
249
+ sortedRows,
250
+ selectedRows,
251
+ selectedKeys,
252
+ sorts,
253
+ filters,
254
+ editingCell,
255
+ copyTable,
256
+ setColumnWidth,
257
+ settings,
258
+ setSettings,
259
+ saveSettings,
260
+ visibleRows,
261
+ onScroll,
262
+ index,
263
+ length,
264
+ rowKeyGetter,
265
+ gridTemplateColumns,
266
+ footers,
267
+ footerFunctions,
268
+ override,
269
+ ]
270
+ );
271
+
272
+ const context = useMemo(
273
+ () =>
274
+ createContext<DataGridContextProps<R>>({
275
+ rows: [],
276
+ columns: {},
277
+ visibleColumns: [],
278
+ rowKey: '' as keyof R,
279
+ rowKeyGetter: () => '',
280
+ sortedRows: [],
281
+ selectedRows: [],
282
+ selectedKeys: [],
283
+ setSelectedKeys: () => {},
284
+ setSorts: () => {},
285
+ setFilters: () => {},
286
+ editingCell: [-1, -1],
287
+ setEditingCell: () => {},
288
+ copyTable: async () => {},
289
+ setColumnWidth: () => {},
290
+ saveSettings: () => {},
291
+ settings: {},
292
+ setSettings: () => {},
293
+ visibleRows: [],
294
+ scrollableRef: { current: null },
295
+ onScroll: () => {},
296
+ index: 0,
297
+ length: 0,
298
+ gridTemplateColumns: '',
299
+ footers: {},
300
+ setFooters: () => {},
301
+ }),
302
+ []
303
+ );
304
+
305
+ return useMemo(() => [contextValue, context], [context, contextValue]);
306
+ };
@@ -0,0 +1,175 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ import { DataGridColumn, DataGridContextProps } from '../types';
4
+
5
+ import { useCallback } from 'react';
6
+
7
+ export const useDataGridCopy = <R>({
8
+ rows,
9
+ visibleColumns = [],
10
+ loadCopyRows,
11
+ footerFunctions,
12
+ }: Pick<
13
+ DataGridContextProps<R>,
14
+ 'rows' | 'visibleColumns' | 'loadCopyRows' | 'footerFunctions'
15
+ >) => {
16
+ const generateHeadersHtml = useCallback(() => {
17
+ return `<tr>
18
+ ${visibleColumns
19
+ .map(
20
+ ([, col]) =>
21
+ `<th style="
22
+ text-align: left;
23
+ font-weight: bold;
24
+ white-space: nowrap;
25
+ ">
26
+ ${col.name}
27
+ </th>`
28
+ )
29
+ .join('\n')}
30
+ </tr>`;
31
+ }, [visibleColumns]);
32
+
33
+ const generateFootersHtml = useCallback(
34
+ (rowsToCopy: R[]) => {
35
+ console.log('generate footers html');
36
+ return `<tr>
37
+ ${visibleColumns
38
+ .map(
39
+ ([colKey]) =>
40
+ `<td style="
41
+ font-weight: bold;
42
+ white-space: nowrap;
43
+ ">
44
+ ${
45
+ footerFunctions?.[colKey]?.(
46
+ rowsToCopy,
47
+ rowsToCopy,
48
+ []
49
+ ) ?? ''
50
+ }
51
+ </td>`
52
+ )
53
+ .join('\n')}
54
+ </tr>`;
55
+ },
56
+ [footerFunctions, visibleColumns]
57
+ );
58
+
59
+ const generateCellText = useCallback((col: DataGridColumn<R>, value: any) => {
60
+ return col.excelValue ? col.excelValue(value) : value?.toString() ?? '';
61
+ }, []);
62
+
63
+ const generateCellHtml = useCallback(
64
+ (col: DataGridColumn<R>, value: any) => `<td style="
65
+ ${
66
+ col.excelFormatter
67
+ ? "mso-number-format: '" + col.excelFormatter(col) + "';"
68
+ : ''
69
+ }
70
+ ${
71
+ col.excelBackgroundColor
72
+ ? "background-color: '" + col.excelBackgroundColor(value) + "';"
73
+ : ''
74
+ }
75
+ white-space: nowrap;
76
+ ">
77
+ ${generateCellText(col, value)}
78
+ </td>`,
79
+ [generateCellText]
80
+ );
81
+
82
+ const generateCopyHtml = useCallback(
83
+ (rowsToCopy: R[], includeHeaders: boolean, includeFooters: boolean) => {
84
+ return `
85
+ <table>
86
+ ${includeHeaders ? generateHeadersHtml() : ''}
87
+ ${(rowsToCopy || [])
88
+ .map(
89
+ (row) =>
90
+ `<tr>
91
+ ${visibleColumns
92
+ .map(([, col]) => {
93
+ const value: any =
94
+ col.getter && row ? col.getter(row) : '';
95
+ return generateCellHtml(col, value);
96
+ })
97
+ .join('\n')}
98
+ </tr>`
99
+ )
100
+ .join('\n')}
101
+ ${includeFooters ? generateFootersHtml(rowsToCopy) : ''}
102
+ </table>
103
+ `;
104
+ },
105
+ [visibleColumns, generateCellHtml, generateFootersHtml, generateHeadersHtml]
106
+ );
107
+
108
+ const generateHeadersText = useCallback(() => {
109
+ return visibleColumns.map(([, col]) => col.name).join('\t');
110
+ }, [visibleColumns]);
111
+
112
+ const generateFootersText = useCallback(
113
+ (rowsToCopy: R[]) => {
114
+ return visibleColumns
115
+ .map(
116
+ ([colKey]) => footerFunctions?.[colKey]?.(rowsToCopy, [], []) ?? ''
117
+ )
118
+ .join('\t');
119
+ },
120
+ [footerFunctions, visibleColumns]
121
+ );
122
+
123
+ const generateCopyText = useCallback(
124
+ (rowsToCopy: R[], includeHeaders: boolean, includeFooters: boolean) => {
125
+ return [
126
+ includeHeaders ? generateHeadersText() : '',
127
+ ...(rowsToCopy || []).map((row) =>
128
+ visibleColumns
129
+ .map(([, col]) => {
130
+ const value: any = col.getter && row ? col.getter(row) : '';
131
+ return col.excelValue
132
+ ? col.excelValue(value)
133
+ : value != null && value !== undefined
134
+ ? value.toString()
135
+ : '';
136
+ })
137
+ .join('\t')
138
+ ),
139
+ includeFooters ? generateFootersText(rowsToCopy) : '',
140
+ ]
141
+ .filter((s) => s != null)
142
+ .join('\n');
143
+ },
144
+ [visibleColumns, generateFootersText, generateHeadersText]
145
+ );
146
+
147
+ const copyTable = useCallback(
148
+ async (includeHeaders = true, includeFooters = true) => {
149
+ const rowsToCopy = await (loadCopyRows ?? (async () => rows))();
150
+
151
+ const copyHtml = generateCopyHtml(
152
+ rowsToCopy,
153
+ includeHeaders,
154
+ includeFooters
155
+ );
156
+ const copyText = generateCopyText(
157
+ rowsToCopy,
158
+ includeHeaders,
159
+ includeFooters
160
+ );
161
+
162
+ await navigator['clipboard'].write([
163
+ new ClipboardItem({
164
+ 'text/plain': new Blob([copyText], { type: 'text/plain' }),
165
+ 'text/html': new Blob([copyHtml], { type: 'text/html' }),
166
+ }),
167
+ ]);
168
+ },
169
+ [generateCopyHtml, generateCopyText, loadCopyRows, rows]
170
+ );
171
+
172
+ return {
173
+ copyTable,
174
+ };
175
+ };
@@ -0,0 +1,48 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+
3
+ import { DataGridSettings } from '../types';
4
+ import { useSettings } from '../../../../providers';
5
+
6
+ export const useDataGridSettings = (name?: string) => {
7
+ // TODO: Implement settings context
8
+ const { settings, updateSettings } = useSettings();
9
+ const [dataGridSettings, setDataGridSettings] = useState<DataGridSettings>(
10
+ {}
11
+ );
12
+
13
+ useEffect(() => {
14
+ if (name) {
15
+ const settingName = `user.datagrid.settings.${name}`;
16
+ const gridSettingsJson = settings?.[settingName] || '{}';
17
+ let gridSettings: DataGridSettings = {};
18
+ try {
19
+ gridSettings = JSON.parse(gridSettingsJson);
20
+ } catch (error) {
21
+ console.error('[GRID] cannot decode datagrid settings for', {
22
+ name,
23
+ gridSettingsJson,
24
+ error,
25
+ });
26
+ }
27
+ setDataGridSettings(gridSettings);
28
+ }
29
+ }, [name, settings]);
30
+
31
+ const saveSettings = useCallback(
32
+ (newValue?: DataGridSettings) => {
33
+ if (name) {
34
+ const settingName = `user.datagrid.settings.${name}`;
35
+ const newSettings = newValue ?? dataGridSettings;
36
+ const newSettingsJson = JSON.stringify(newSettings);
37
+ updateSettings({ [settingName]: newSettingsJson });
38
+ }
39
+ },
40
+ [dataGridSettings, name, updateSettings]
41
+ );
42
+
43
+ return {
44
+ settings: dataGridSettings,
45
+ setSettings: setDataGridSettings,
46
+ saveSettings,
47
+ };
48
+ };
@@ -0,0 +1,140 @@
1
+ import * as styles from './styles';
2
+
3
+ import { DataGridContextProps, DataGridProps } from './types';
4
+
5
+ import { DataGridCell } from './DataGridCell';
6
+ import { DataGridFooter } from './DataGridFooter';
7
+ import { DataGridHeader } from './DataGridHeader';
8
+ import { VirtualScroller } from './VirtualScroller';
9
+ import { useCallback } from 'react';
10
+ import { useDataGrid } from './hooks';
11
+
12
+ /* eslint-disable @typescript-eslint/no-explicit-any */
13
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
14
+
15
+ export const DataGrid = <R,>({
16
+ contextOverride,
17
+ ...props
18
+ }: DataGridProps<R> & {
19
+ contextOverride?: Partial<DataGridContextProps<R>>;
20
+ }) => {
21
+ const {
22
+ className,
23
+ // onRowDoubleClick,
24
+ onVisibleRowsChange,
25
+ } = props;
26
+ const [contextProps, DataGridContext] = useDataGrid(props, contextOverride);
27
+ const {
28
+ selectedKeys,
29
+ setSelectedKeys,
30
+ columns,
31
+ visibleColumns,
32
+ rowHeight = 32,
33
+ headerRowHeight = 40,
34
+ scrollableRef,
35
+ onScroll,
36
+ rowKeyGetter,
37
+ } = contextProps;
38
+
39
+ const hasFooter = Object.values(columns).some((col) => col.footer);
40
+
41
+ const toggleSelection = useCallback(
42
+ (key: string) => {
43
+ if (selectedKeys.includes(key)) {
44
+ setSelectedKeys(selectedKeys.filter((p) => p !== key));
45
+ } else {
46
+ setSelectedKeys([...selectedKeys, key]);
47
+ }
48
+ },
49
+ [selectedKeys, setSelectedKeys]
50
+ );
51
+
52
+ const rowTemplate = useCallback(
53
+ (row: R, rowIndex: number) => {
54
+ if (!row) {
55
+ return (
56
+ <styles.DataGridRow key={`loading-row-${rowIndex}`}>
57
+ {!!props.selectable && (
58
+ <styles.LoadingCell className="animate-pulse">
59
+ <div />
60
+ </styles.LoadingCell>
61
+ )}
62
+ {visibleColumns.map((_, index) => (
63
+ <styles.LoadingCell
64
+ className="animate-pulse"
65
+ key={`loading-${rowIndex}-${index}`}
66
+ >
67
+ <div />
68
+ </styles.LoadingCell>
69
+ ))}
70
+ </styles.DataGridRow>
71
+ );
72
+ }
73
+ const key = rowKeyGetter(row);
74
+ const { className, style } = props.rowClassNameGetter?.(row) ?? {
75
+ className: '',
76
+ style: undefined,
77
+ };
78
+ return (
79
+ <styles.DataGridRow key={key}>
80
+ {!!props.selectable && (
81
+ <styles.SelectionCell
82
+ key="__select_checkbox__"
83
+ onClick={() => toggleSelection(key)}
84
+ >
85
+ <input
86
+ type="checkbox"
87
+ value={key as string}
88
+ checked={selectedKeys.includes(key)}
89
+ readOnly
90
+ />
91
+ </styles.SelectionCell>
92
+ )}
93
+ {visibleColumns.map(([key, col], index) => (
94
+ <DataGridCell
95
+ key={`loading-${rowIndex}-${index}`}
96
+ {...(index === 0 ? { className, style } : {})}
97
+ row={row}
98
+ rowIndex={rowIndex}
99
+ column={col}
100
+ columnIndex={index}
101
+ context={DataGridContext}
102
+ columnKey={key}
103
+ />
104
+ ))}
105
+ </styles.DataGridRow>
106
+ );
107
+ },
108
+ [
109
+ DataGridContext,
110
+ props,
111
+ rowKeyGetter,
112
+ selectedKeys,
113
+ toggleSelection,
114
+ visibleColumns,
115
+ ]
116
+ );
117
+
118
+ return (
119
+ <DataGridContext.Provider value={contextProps}>
120
+ <styles.DataGridContainer
121
+ ref={scrollableRef}
122
+ onScroll={onScroll}
123
+ $headerRowHeight={headerRowHeight}
124
+ $rowHeight={rowHeight}
125
+ $rowsCount={contextProps.sortedRows.length}
126
+ $withFooter={hasFooter}
127
+ className={className}
128
+ >
129
+ <DataGridHeader context={DataGridContext} />
130
+ <VirtualScroller
131
+ onRangeChange={onVisibleRowsChange}
132
+ hasFooter={hasFooter}
133
+ rowTemplate={rowTemplate}
134
+ context={DataGridContext}
135
+ />
136
+ {hasFooter && <DataGridFooter context={DataGridContext} />}
137
+ </styles.DataGridContainer>
138
+ </DataGridContext.Provider>
139
+ );
140
+ };