@addev-be/ui 0.2.6 → 0.2.7

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 (66) hide show
  1. package/assets/icons/arrow-down-1-9.svg +1 -0
  2. package/assets/icons/arrow-down-big-small.svg +1 -0
  3. package/assets/icons/arrow-up-9-1.svg +1 -0
  4. package/assets/icons/arrow-up-big-small.svg +1 -0
  5. package/assets/icons/chevron-down.svg +1 -0
  6. package/assets/icons/ellipsis.svg +1 -0
  7. package/assets/icons/sigma.svg +1 -0
  8. package/assets/icons/table-footer-slash.svg +5 -0
  9. package/assets/icons/table-footer.svg +4 -0
  10. package/assets/icons/table.svg +1 -0
  11. package/assets/icons/tally.svg +1 -0
  12. package/assets/icons/x-bar.svg +4 -0
  13. package/dist/Icons.d.ts +13 -1
  14. package/dist/Icons.js +25 -1
  15. package/dist/components/data/AdvancedRequestDataGrid/index.js +3 -3
  16. package/dist/components/data/DataGrid/DataGridColumnsModal/hooks.js +2 -1
  17. package/dist/components/data/DataGrid/DataGridFilterMenu/index.js +85 -7
  18. package/dist/components/data/DataGrid/DataGridFilterMenu/styles.d.ts +3 -9
  19. package/dist/components/data/DataGrid/DataGridFilterMenu/styles.js +10 -37
  20. package/dist/components/data/DataGrid/DataGridFooter.d.ts +1 -1
  21. package/dist/components/data/DataGrid/DataGridFooter.js +35 -12
  22. package/dist/components/data/DataGrid/DataGridHeader.js +1 -1
  23. package/dist/components/data/DataGrid/DataGridHeaderCell.js +6 -22
  24. package/dist/components/data/DataGrid/helpers/columns.d.ts +1 -1
  25. package/dist/components/data/DataGrid/helpers/columns.js +71 -16
  26. package/dist/components/data/DataGrid/hooks/useDataGrid.d.ts +1 -1
  27. package/dist/components/data/DataGrid/hooks/useDataGrid.js +60 -30
  28. package/dist/components/data/DataGrid/hooks/useDataGridCopy.d.ts +2 -2
  29. package/dist/components/data/DataGrid/hooks/useDataGridCopy.js +41 -39
  30. package/dist/components/data/DataGrid/index.d.ts +4 -2
  31. package/dist/components/data/DataGrid/index.js +22 -12
  32. package/dist/components/data/DataGrid/styles.d.ts +8 -4
  33. package/dist/components/data/DataGrid/styles.js +27 -17
  34. package/dist/components/data/DataGrid/types.d.ts +8 -1
  35. package/dist/components/data/SqlRequestDataGrid/helpers/columns.d.ts +1 -1
  36. package/dist/components/data/SqlRequestDataGrid/helpers/columns.js +24 -11
  37. package/dist/components/data/SqlRequestDataGrid/index.js +105 -33
  38. package/dist/components/ui/ContextMenu/index.d.ts +11 -0
  39. package/dist/components/ui/ContextMenu/index.js +58 -0
  40. package/dist/components/ui/ContextMenu/styles.d.ts +18 -0
  41. package/dist/components/ui/ContextMenu/styles.js +56 -0
  42. package/dist/services/advancedRequests.d.ts +1 -1
  43. package/dist/services/sqlRequests.d.ts +9 -4
  44. package/dist/services/sqlRequests.js +1 -0
  45. package/package.json +1 -1
  46. package/src/Icons.tsx +24 -0
  47. package/src/components/data/AdvancedRequestDataGrid/index.tsx +3 -5
  48. package/src/components/data/DataGrid/DataGridColumnsModal/hooks.tsx +2 -1
  49. package/src/components/data/DataGrid/DataGridFilterMenu/index.tsx +173 -9
  50. package/src/components/data/DataGrid/DataGridFilterMenu/styles.ts +13 -64
  51. package/src/components/data/DataGrid/DataGridFooter.tsx +20 -22
  52. package/src/components/data/DataGrid/DataGridHeader.tsx +5 -5
  53. package/src/components/data/DataGrid/DataGridHeaderCell.tsx +3 -38
  54. package/src/components/data/DataGrid/helpers/columns.tsx +98 -7
  55. package/src/components/data/DataGrid/hooks/useDataGrid.tsx +58 -17
  56. package/src/components/data/DataGrid/hooks/useDataGridCopy.ts +35 -30
  57. package/src/components/data/DataGrid/index.tsx +21 -14
  58. package/src/components/data/DataGrid/styles.ts +28 -7
  59. package/src/components/data/DataGrid/types.ts +20 -1
  60. package/src/components/data/SqlRequestDataGrid/helpers/columns.tsx +38 -3
  61. package/src/components/data/SqlRequestDataGrid/index.tsx +116 -21
  62. package/src/components/ui/ContextMenu/index.tsx +73 -0
  63. package/src/components/ui/ContextMenu/styles.ts +115 -0
  64. package/src/services/advancedRequests.ts +1 -1
  65. package/src/services/sqlRequests.ts +16 -5
  66. package/tsconfig.tsbuildinfo +1 -1
@@ -3,15 +3,9 @@
3
3
 
4
4
  import * as styles from './styles';
5
5
 
6
- import {
7
- ArrowDownAZIcon,
8
- ArrowUpZAIcon,
9
- ArrowsUpDownIcon,
10
- FilterFullIcon,
11
- FilterIcon,
12
- } from '../../../Icons';
13
6
  import { MouseEvent, useCallback, useMemo, useRef, useState } from 'react';
14
7
 
8
+ import { ChevronDownIcon } from '../../../Icons';
15
9
  import { DataGridFilterMenu } from './DataGridFilterMenu';
16
10
  import { DataGridHeaderCellProps } from './types';
17
11
  import { Dropdown } from '../../layout';
@@ -29,33 +23,12 @@ export const DataGridHeaderCell = <R,>({
29
23
  const contextValue = useDataGridContext(context);
30
24
  const {
31
25
  filters = {},
32
- sorts = {},
33
- setSorts,
34
26
  setColumnWidth,
35
27
  saveSettings,
36
28
  headerColor,
37
29
  } = contextValue;
38
30
  const filterButtonRef = useRef<HTMLButtonElement | null>(null);
39
31
 
40
- /** SORTING */
41
- const SortIcon = sorts[columnKey]
42
- ? sorts[columnKey] === 'desc'
43
- ? ArrowUpZAIcon
44
- : ArrowDownAZIcon
45
- : ArrowsUpDownIcon;
46
-
47
- const onSortButtonClicked = useCallback(
48
- (columnKey: string) => {
49
- const sort = sorts[columnKey];
50
- if (!sort || sort === 'desc') {
51
- setSorts({ [columnKey]: 'asc' });
52
- } else {
53
- setSorts({ [columnKey]: 'desc' });
54
- }
55
- },
56
- [setSorts, sorts]
57
- );
58
-
59
32
  /** RESIZING */
60
33
 
61
34
  const [isResizing, setIsResizing] = useState(false);
@@ -97,7 +70,7 @@ export const DataGridHeaderCell = <R,>({
97
70
  $sourceRect={filterButtonRect}
98
71
  onClose={() => setIsFilterDropdownVisible(false)}
99
72
  $width={320}
100
- $height={[200, 320]}
73
+ $height={[250, 400]}
101
74
  $autoPositionX
102
75
  >
103
76
  <DataGridFilterMenu
@@ -129,20 +102,12 @@ export const DataGridHeaderCell = <R,>({
129
102
  >
130
103
  {filterDropdown}
131
104
  <span>{column.name}</span>
132
- {!!column.sortGetter && (
133
- <IconButton
134
- color={headerColor}
135
- size="small"
136
- icon={SortIcon}
137
- onClick={() => onSortButtonClicked?.(columnKey)}
138
- />
139
- )}
140
105
  {!!column.filter && (
141
106
  <IconButton
142
107
  size="small"
143
108
  className={hasFilters ? 'danger' : ''}
144
109
  ref={filterButtonRef}
145
- icon={hasFilters ? FilterFullIcon : FilterIcon}
110
+ icon={ChevronDownIcon}
146
111
  color={hasFilters ? 'danger' : headerColor}
147
112
  onClick={onFilterButtonClicked}
148
113
  />
@@ -9,11 +9,15 @@ import {
9
9
  import { numberFilter, textFilter } from './filters';
10
10
 
11
11
  import moment from 'moment';
12
+ import { repeat } from 'lodash';
12
13
 
13
14
  export const isColumnVisible = <R,>(
14
15
  obj: DataGridColumn<R> | DataGridSettings
15
16
  ): boolean => obj?.order !== -1;
16
17
 
18
+ const buildExcelFormat = (decimals = 2, suffix = '') =>
19
+ `#0${decimals > 0 ? `.${repeat('0', decimals)}` : ''}${suffix}`;
20
+
17
21
  export const textColumn = <R extends Record<string, any>>(
18
22
  key: string,
19
23
  title: string,
@@ -24,6 +28,7 @@ export const textColumn = <R extends Record<string, any>>(
24
28
  render: (row) => row[key] ?? '',
25
29
  getter: (row) => row[key] ?? '',
26
30
  sortGetter: (row) => row[key] ?? '',
31
+ footer: (_, filteredRows) => `${filteredRows.length} éléments`,
27
32
  filter: textFilter(key),
28
33
  ...options,
29
34
  },
@@ -46,6 +51,7 @@ export const mailColumn = <R extends Record<string, any>>(
46
51
  ),
47
52
  getter: (row) => row[key] ?? '',
48
53
  sortGetter: (row) => row[key] ?? '',
54
+ footer: (_, filteredRows) => `${filteredRows.length} éléments`,
49
55
  filter: textFilter(key),
50
56
  ...options,
51
57
  },
@@ -68,6 +74,7 @@ export const phoneColumn = <R extends Record<string, any>>(
68
74
  ),
69
75
  getter: (row) => row[key] ?? '',
70
76
  sortGetter: (row) => row[key] ?? '',
77
+ footer: (_, filteredRows) => `${filteredRows.length} éléments`,
71
78
  filter: textFilter(key),
72
79
  ...options,
73
80
  },
@@ -83,6 +90,7 @@ export const dateColumn = <R extends Record<string, any>>(
83
90
  render: (row) => moment(row[key]).format('DD/MM/YYYY') ?? '',
84
91
  getter: (row) => row[key] ?? '',
85
92
  sortGetter: (row) => row[key] ?? '',
93
+ footer: (_, filteredRows) => `${filteredRows.length} éléments`,
86
94
  filter: textFilter(key),
87
95
  ...options,
88
96
  },
@@ -98,6 +106,7 @@ export const monthColumn = <R extends Record<string, any>>(
98
106
  render: (row) => (row[key] ? `${row[key]} mois ` : ''),
99
107
  getter: (row) => row[key] ?? '',
100
108
  sortGetter: (row) => row[key] ?? '',
109
+ footer: (_, filteredRows) => `${filteredRows.length} éléments`,
101
110
  filter: textFilter(key),
102
111
  ...options,
103
112
  },
@@ -115,11 +124,33 @@ export const numberColumn = <R extends Record<string, any>>(
115
124
  excelFormatter: () => '#',
116
125
  getter: (row) => row[key] ?? '',
117
126
  sortGetter: (row) => row[key] ?? '',
127
+ footer: {
128
+ sum: (_, filteredRows) =>
129
+ formatNumber(
130
+ filteredRows.reduce((acc, row) => acc + (row[key] ?? 0), 0),
131
+ decimals
132
+ ),
133
+ average: (_, filteredRows) =>
134
+ formatNumber(
135
+ filteredRows.reduce((acc, row) => acc + (row[key] ?? 0), 0) /
136
+ (filteredRows.length || 1),
137
+ decimals
138
+ ),
139
+ count: (_, filteredRows) => `${filteredRows.length} éléments`,
140
+ max: (_, filteredRows) =>
141
+ formatNumber(
142
+ Math.max(...filteredRows.map((row) => row[key] ?? 0)),
143
+ decimals
144
+ ),
145
+ min: (_, filteredRows) =>
146
+ formatNumber(
147
+ Math.min(...filteredRows.map((row) => row[key] ?? 0)),
148
+ decimals
149
+ ),
150
+ },
118
151
  filter: {
119
- type: 'number',
120
- operator: 'equals',
121
- values: [0],
122
- getter: (value) => value ?? 0,
152
+ ...numberFilter(key),
153
+ renderer: (value) => formatNumber(value, decimals) ?? '',
123
154
  },
124
155
  ...options,
125
156
  },
@@ -134,10 +165,37 @@ export const moneyColumn = <R extends Record<string, any>>(
134
165
  [key]: {
135
166
  name: title,
136
167
  render: (row) => formatMoney(row[key], decimals) ?? '',
137
- excelFormatter: () => '#0.00',
168
+ excelFormatter: () => buildExcelFormat(decimals, ''),
138
169
  getter: (row) => row[key] ?? '',
139
170
  sortGetter: (row) => row[key] ?? '',
140
- filter: numberFilter(key),
171
+ filter: {
172
+ ...numberFilter(key),
173
+ renderer: (value) => formatMoney(value, decimals) ?? '',
174
+ },
175
+ footer: {
176
+ sum: (_, filteredRows) =>
177
+ formatMoney(
178
+ filteredRows.reduce((acc, row) => acc + (row[key] ?? 0), 0),
179
+ decimals
180
+ ),
181
+ average: (_, filteredRows) =>
182
+ formatMoney(
183
+ filteredRows.reduce((acc, row) => acc + (row[key] ?? 0), 0) /
184
+ (filteredRows.length || 1),
185
+ decimals
186
+ ),
187
+ count: (_, filteredRows) => `${filteredRows.length} éléments`,
188
+ max: (_, filteredRows) =>
189
+ formatMoney(
190
+ Math.max(...filteredRows.map((row) => row[key] ?? 0)),
191
+ decimals
192
+ ),
193
+ min: (_, filteredRows) =>
194
+ formatMoney(
195
+ Math.min(...filteredRows.map((row) => row[key] ?? 0)),
196
+ decimals
197
+ ),
198
+ },
141
199
  ...options,
142
200
  },
143
201
  });
@@ -145,15 +203,40 @@ export const moneyColumn = <R extends Record<string, any>>(
145
203
  export const percentageColumn = <R extends Record<string, any>>(
146
204
  key: string,
147
205
  title: string,
206
+ decimals = 2,
148
207
  options?: Partial<DataGridColumn<R>>
149
208
  ): DataGridColumns<R> => ({
150
209
  [key]: {
151
210
  name: title,
152
211
  render: (row) => formatPercentage(row[key]) ?? '',
153
- excelFormatter: () => '#0.00',
212
+ excelFormatter: () => buildExcelFormat(decimals),
154
213
  getter: (row) => row[key] ?? '',
155
214
  sortGetter: (row) => row[key] ?? '',
156
215
  filter: numberFilter(key),
216
+ footer: {
217
+ average: (_, filteredRows) =>
218
+ formatNumber(
219
+ filteredRows.reduce((acc, row) => acc + (row[key] ?? 0), 0) /
220
+ (filteredRows.length || 1),
221
+ decimals
222
+ ),
223
+ count: (_, filteredRows) => `${filteredRows.length} éléments`,
224
+ max: (_, filteredRows) =>
225
+ formatNumber(
226
+ Math.max(...filteredRows.map((row) => row[key] ?? 0)),
227
+ decimals
228
+ ),
229
+ min: (_, filteredRows) =>
230
+ formatNumber(
231
+ Math.min(...filteredRows.map((row) => row[key] ?? 0)),
232
+ decimals
233
+ ),
234
+ sum: (_, filteredRows) =>
235
+ formatNumber(
236
+ filteredRows.reduce((acc, row) => acc + (row[key] ?? 0), 0),
237
+ decimals
238
+ ),
239
+ },
157
240
  ...options,
158
241
  },
159
242
  });
@@ -174,6 +257,13 @@ export const checkboxColumn = <R extends Record<string, any>>(
174
257
  getter: (row) => row[key] ?? '',
175
258
  sortGetter: (row) => row[key] ?? '',
176
259
  filter: numberFilter(key),
260
+ footer: {
261
+ count: (_, filteredRows) => `${filteredRows.length} éléments`,
262
+ checked: (_, filteredRows) =>
263
+ `${filteredRows.filter((row) => !!row[key]).length} cochés`,
264
+ unchecked: (_, filteredRows) =>
265
+ `${filteredRows.filter((row) => !row[key]).length} décochés`,
266
+ },
177
267
  ...options,
178
268
  },
179
269
  });
@@ -191,6 +281,7 @@ export const colorColumn = <R extends Record<string, any>>(
191
281
  getter: (row) => row[key] ?? '',
192
282
  sortGetter: (row) => row[key] ?? '',
193
283
  filter: textFilter(key),
284
+ footer: (rows) => `${rows.length} éléments`,
194
285
  ...options,
195
286
  },
196
287
  });
@@ -7,6 +7,7 @@ import {
7
7
  DataGridContext,
8
8
  DataGridContextProps,
9
9
  DataGridFilters,
10
+ DataGridFooterFunction,
10
11
  DataGridProps,
11
12
  DataGridSort,
12
13
  } from '../types';
@@ -26,7 +27,8 @@ import { useDataGridSettings } from './useDataGridSettings';
26
27
  import { useElementSize } from '../../../../hooks';
27
28
 
28
29
  export const useDataGrid = <R,>(
29
- props: DataGridProps<R>
30
+ props: DataGridProps<R>,
31
+ override?: Partial<DataGridContextProps<R>>
30
32
  ): [DataGridContextProps<R>, DataGridContext<R>] => {
31
33
  const {
32
34
  rows,
@@ -59,7 +61,7 @@ export const useDataGrid = <R,>(
59
61
  const gridTemplateColumns = useMemo(
60
62
  () =>
61
63
  [
62
- ...(selectable ? ['var(--space-8)'] : []),
64
+ ...(selectable ? ['var(--space-10)'] : []),
63
65
  ...visibleColumns.map(([, col]) => `${col.width ?? 150}px`),
64
66
  ].join(' '),
65
67
  [selectable, visibleColumns]
@@ -173,9 +175,39 @@ export const useDataGrid = <R,>(
173
175
  onVisibleRowsChange?.(indexWithTolerance, lengthWithTolerance);
174
176
  }, [indexWithTolerance, lengthWithTolerance, onVisibleRowsChange]);
175
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
+ ) {
196
+ acc[columnKey] = column.footer[footerKey];
197
+ }
198
+ return acc;
199
+ }, {} as Record<string, DataGridFooterFunction<R>>),
200
+ [columns, footers]
201
+ );
202
+
176
203
  /** COPYING */
177
204
 
178
- const { copyTable } = useDataGridCopy(sortedRows, columns, loadCopyRows);
205
+ const { copyTable } = useDataGridCopy({
206
+ rows,
207
+ visibleColumns,
208
+ footerFunctions,
209
+ loadCopyRows,
210
+ });
179
211
 
180
212
  const contextValue = useMemo<DataGridContextProps<R>>(
181
213
  () => ({
@@ -204,28 +236,35 @@ export const useDataGrid = <R,>(
204
236
  length,
205
237
  rowKeyGetter,
206
238
  gridTemplateColumns,
239
+ footers,
240
+ setFooters,
241
+ footerFunctions,
242
+ ...override,
207
243
  }),
208
244
  [
209
- columns,
210
- copyTable,
211
- editingCell,
212
- filters,
213
- index,
214
- length,
215
- onScroll,
216
245
  props,
217
- rowKeyGetter,
218
- saveSettings,
219
- selectedKeys,
246
+ columns,
247
+ visibleColumns,
248
+ sortedRows,
220
249
  selectedRows,
250
+ selectedKeys,
251
+ sorts,
252
+ filters,
253
+ editingCell,
254
+ copyTable,
221
255
  setColumnWidth,
222
- setSettings,
223
256
  settings,
224
- sortedRows,
225
- sorts,
226
- visibleColumns,
257
+ setSettings,
258
+ saveSettings,
227
259
  visibleRows,
260
+ onScroll,
261
+ index,
262
+ length,
263
+ rowKeyGetter,
228
264
  gridTemplateColumns,
265
+ footers,
266
+ footerFunctions,
267
+ override,
229
268
  ]
230
269
  );
231
270
 
@@ -256,6 +295,8 @@ export const useDataGrid = <R,>(
256
295
  index: 0,
257
296
  length: 0,
258
297
  gridTemplateColumns: '',
298
+ footers: {},
299
+ setFooters: () => {},
259
300
  }),
260
301
  []
261
302
  );
@@ -1,19 +1,23 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
 
3
- import { DataGridColumn, DataGridColumns } from '../types';
3
+ import { DataGridColumn, DataGridContextProps } from '../types';
4
4
 
5
5
  import { useCallback } from 'react';
6
6
 
7
- export const useDataGridCopy = <R>(
8
- rows: R[],
9
- columns: DataGridColumns<R>,
10
- loadCopyRows?: () => Promise<R[]>
11
- ) => {
7
+ export const useDataGridCopy = <R>({
8
+ rows,
9
+ visibleColumns = [],
10
+ loadCopyRows,
11
+ footerFunctions,
12
+ }: Pick<
13
+ DataGridContextProps<R>,
14
+ 'rows' | 'visibleColumns' | 'loadCopyRows' | 'footerFunctions'
15
+ >) => {
12
16
  const generateHeadersHtml = useCallback(() => {
13
17
  return `<tr>
14
- ${Object.values(columns ?? {})
18
+ ${visibleColumns
15
19
  .map(
16
- (col) =>
20
+ ([, col]) =>
17
21
  `<th style="
18
22
  text-align: left;
19
23
  font-weight: bold;
@@ -24,29 +28,32 @@ export const useDataGridCopy = <R>(
24
28
  )
25
29
  .join('\n')}
26
30
  </tr>`;
27
- }, [columns]);
31
+ }, [visibleColumns]);
28
32
 
29
33
  const generateFootersHtml = useCallback(
30
34
  (rowsToCopy: R[]) => {
35
+ console.log('generate footers html');
31
36
  return `<tr>
32
- ${Object.values(columns ?? {})
37
+ ${visibleColumns
33
38
  .map(
34
- (col) =>
39
+ ([colKey]) =>
35
40
  `<td style="
36
41
  font-weight: bold;
37
42
  white-space: nowrap;
38
43
  ">
39
44
  ${
40
- col.footer
41
- ? col.footer(rowsToCopy, rowsToCopy, [])
42
- : ''
45
+ footerFunctions?.[colKey]?.(
46
+ rowsToCopy,
47
+ rowsToCopy,
48
+ []
49
+ ) ?? ''
43
50
  }
44
51
  </td>`
45
52
  )
46
53
  .join('\n')}
47
54
  </tr>`;
48
55
  },
49
- [columns]
56
+ [footerFunctions, visibleColumns]
50
57
  );
51
58
 
52
59
  const generateCellText = useCallback((col: DataGridColumn<R>, value: any) => {
@@ -76,8 +83,8 @@ export const useDataGridCopy = <R>(
76
83
  .map(
77
84
  (row) =>
78
85
  `<tr>
79
- ${Object.values(columns ?? {})
80
- .map((col) => {
86
+ ${visibleColumns
87
+ .map(([, col]) => {
81
88
  const value: any =
82
89
  col.getter && row ? col.getter(row) : '';
83
90
  return generateCellHtml(col, value);
@@ -90,24 +97,22 @@ export const useDataGridCopy = <R>(
90
97
  </table>
91
98
  `;
92
99
  },
93
- [columns, generateCellHtml, generateFootersHtml, generateHeadersHtml]
100
+ [visibleColumns, generateCellHtml, generateFootersHtml, generateHeadersHtml]
94
101
  );
95
102
 
96
103
  const generateHeadersText = useCallback(() => {
97
- return Object.values(columns ?? {})
98
- .map((col) => col.name)
99
- .join('\t');
100
- }, [columns]);
104
+ return visibleColumns.map(([, col]) => col.name).join('\t');
105
+ }, [visibleColumns]);
101
106
 
102
107
  const generateFootersText = useCallback(
103
108
  (rowsToCopy: R[]) => {
104
- return Object.values(columns ?? {})
105
- .map((col) =>
106
- col.footer ? col.footer(rowsToCopy, rowsToCopy, []) : ''
109
+ return visibleColumns
110
+ .map(
111
+ ([colKey]) => footerFunctions?.[colKey]?.(rowsToCopy, [], []) ?? ''
107
112
  )
108
113
  .join('\t');
109
114
  },
110
- [columns]
115
+ [footerFunctions, visibleColumns]
111
116
  );
112
117
 
113
118
  const generateCopyText = useCallback(
@@ -115,8 +120,8 @@ export const useDataGridCopy = <R>(
115
120
  return [
116
121
  includeHeaders ? generateHeadersText() : '',
117
122
  ...(rowsToCopy || []).map((row) =>
118
- Object.values(columns ?? {})
119
- .map((col) => {
123
+ visibleColumns
124
+ .map(([, col]) => {
120
125
  const value: any = col.getter && row ? col.getter(row) : '';
121
126
  return col.excelValue
122
127
  ? col.excelValue(value)
@@ -131,12 +136,12 @@ export const useDataGridCopy = <R>(
131
136
  .filter((s) => s != null)
132
137
  .join('\n');
133
138
  },
134
- [columns, generateFootersText, generateHeadersText]
139
+ [visibleColumns, generateFootersText, generateHeadersText]
135
140
  );
136
141
 
137
142
  const copyTable = useCallback(
138
143
  async (includeHeaders = true, includeFooters = true) => {
139
- const rowsToCopy = loadCopyRows ? await loadCopyRows() : rows;
144
+ const rowsToCopy = await (loadCopyRows ?? (async () => rows))();
140
145
 
141
146
  const copyHtml = generateCopyHtml(
142
147
  rowsToCopy,
@@ -1,9 +1,10 @@
1
1
  import * as styles from './styles';
2
2
 
3
+ import { DataGridContextProps, DataGridProps } from './types';
4
+
3
5
  import { DataGridCell } from './DataGridCell';
4
6
  import { DataGridFooter } from './DataGridFooter';
5
7
  import { DataGridHeader } from './DataGridHeader';
6
- import { DataGridProps } from './types';
7
8
  import { VirtualScroller } from './VirtualScroller';
8
9
  import { useCallback } from 'react';
9
10
  import { useDataGrid } from './hooks';
@@ -11,13 +12,18 @@ import { useDataGrid } from './hooks';
11
12
  /* eslint-disable @typescript-eslint/no-explicit-any */
12
13
  /* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
13
14
 
14
- export const DataGrid = <R,>(props: DataGridProps<R>) => {
15
+ export const DataGrid = <R,>({
16
+ contextOverride,
17
+ ...props
18
+ }: DataGridProps<R> & {
19
+ contextOverride?: Partial<DataGridContextProps<R>>;
20
+ }) => {
15
21
  const {
16
22
  className,
17
23
  // onRowDoubleClick,
18
24
  onVisibleRowsChange,
19
25
  } = props;
20
- const [contextProps, DataGridContext] = useDataGrid(props);
26
+ const [contextProps, DataGridContext] = useDataGrid(props, contextOverride);
21
27
  const {
22
28
  selectedKeys,
23
29
  setSelectedKeys,
@@ -32,17 +38,15 @@ export const DataGrid = <R,>(props: DataGridProps<R>) => {
32
38
 
33
39
  const hasFooter = Object.values(columns).some((col) => col.footer);
34
40
 
35
- const setRowSelection = useCallback(
36
- (row: R, selected: boolean) => {
37
- const key = rowKeyGetter(row);
38
- if (selected) {
39
- if (!selectedKeys.includes(key))
40
- setSelectedKeys([...selectedKeys, key]);
41
- } else {
41
+ const toggleSelection = useCallback(
42
+ (key: string) => {
43
+ if (selectedKeys.includes(key)) {
42
44
  setSelectedKeys(selectedKeys.filter((p) => p !== key));
45
+ } else {
46
+ setSelectedKeys([...selectedKeys, key]);
43
47
  }
44
48
  },
45
- [rowKeyGetter, selectedKeys, setSelectedKeys]
49
+ [selectedKeys, setSelectedKeys]
46
50
  );
47
51
 
48
52
  const rowTemplate = useCallback(
@@ -74,12 +78,15 @@ export const DataGrid = <R,>(props: DataGridProps<R>) => {
74
78
  return (
75
79
  <styles.DataGridRow key={key}>
76
80
  {!!props.selectable && (
77
- <styles.SelectionCell key="__select_checkbox__">
81
+ <styles.SelectionCell
82
+ key="__select_checkbox__"
83
+ onClick={() => toggleSelection(key)}
84
+ >
78
85
  <input
79
86
  type="checkbox"
80
87
  value={key as string}
81
88
  checked={selectedKeys.includes(key)}
82
- onChange={(e) => setRowSelection(row, e.target.checked)}
89
+ readOnly
83
90
  />
84
91
  </styles.SelectionCell>
85
92
  )}
@@ -103,7 +110,7 @@ export const DataGrid = <R,>(props: DataGridProps<R>) => {
103
110
  props,
104
111
  rowKeyGetter,
105
112
  selectedKeys,
106
- setRowSelection,
113
+ toggleSelection,
107
114
  visibleColumns,
108
115
  ]
109
116
  );
@@ -208,21 +208,18 @@ export const DataGridContainer = styled.div<{
208
208
  `;
209
209
  DataGridContainer.displayName = 'DataGridContainer';
210
210
 
211
- export const DataGridHeaderRow = styled.div.attrs<{
211
+ type DataGridHeaderOrFooterRowProps = {
212
212
  $headerRowHeight?: number;
213
213
  $headerColor?: ThemeColor;
214
214
  $gridTemplateColumns: string;
215
- }>(({ $gridTemplateColumns }) => ({
216
- style: {
217
- gridTemplateColumns: $gridTemplateColumns,
218
- },
219
- }))`
215
+ };
216
+
217
+ const dataGridHeaderOrFooterRowCss = css<DataGridHeaderOrFooterRowProps>`
220
218
  display: grid;
221
219
  grid-template-rows: ${({ $headerRowHeight = DEFAULT_HEADER_ROW_HEIGHT }) =>
222
220
  `${$headerRowHeight}px`};
223
221
  z-index: 10;
224
222
  position: sticky;
225
- top: ${TOOLBAR_HEIGHT}px;
226
223
  align-items: center;
227
224
 
228
225
  ${({ $headerColor }) =>
@@ -236,8 +233,31 @@ export const DataGridHeaderRow = styled.div.attrs<{
236
233
  color: var(--color-neutral-900);
237
234
  `}
238
235
  `;
236
+
237
+ export const DataGridHeaderRow = styled.div.attrs<DataGridHeaderOrFooterRowProps>(
238
+ ({ $gridTemplateColumns }) => ({
239
+ style: {
240
+ gridTemplateColumns: $gridTemplateColumns,
241
+ },
242
+ })
243
+ )`
244
+ ${dataGridHeaderOrFooterRowCss}
245
+ top: ${TOOLBAR_HEIGHT}px;
246
+ `;
239
247
  DataGridHeaderRow.displayName = 'DataGridHeaderRow';
240
248
 
249
+ export const DataGridFooterRow = styled.div.attrs<DataGridHeaderOrFooterRowProps>(
250
+ ({ $gridTemplateColumns }) => ({
251
+ style: {
252
+ gridTemplateColumns: $gridTemplateColumns,
253
+ },
254
+ })
255
+ )`
256
+ ${dataGridHeaderOrFooterRowCss}
257
+ bottom: 0;
258
+ `;
259
+ DataGridFooterRow.displayName = 'DataGridFooterRow';
260
+
241
261
  export const DataGridRow = styled.div`
242
262
  display: contents;
243
263
  `;
@@ -262,6 +282,7 @@ const selectionCellStyle = css`
262
282
  justify-content: center;
263
283
  width: var(--space-8);
264
284
  padding: 0 var(--space-1);
285
+ cursor: pointer;
265
286
 
266
287
  & > input[type='checkbox'] {
267
288
  height: var(--space-4);