@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
@@ -21,13 +21,27 @@ export type DataGridCellFC = FC<{
21
21
  style?: CSSProperties;
22
22
  }>;
23
23
 
24
+ export type DataGridFooterPredefinedFunction =
25
+ | 'average'
26
+ | 'count'
27
+ | 'max'
28
+ | 'min'
29
+ | 'sum';
30
+ export type DataGridFooterFunction<R> = (
31
+ allRows: R[],
32
+ filteredRows: R[],
33
+ selectedRows: R[]
34
+ ) => ReactNode;
35
+
24
36
  export type DataGridColumn<R> = {
25
37
  component?: DataGridCellFC;
26
38
  editable?: boolean;
27
39
  excelFormatter?: (value: any) => string;
28
40
  excelValue?: (value: any) => string;
29
41
  filter?: DataGridFilter;
30
- footer?: (allRows: R[], filteredRows: R[], selectedRows: R[]) => ReactNode;
42
+ footer?:
43
+ | DataGridFooterFunction<R>
44
+ | Record<string, DataGridFooterFunction<R> | null>;
31
45
  getter?: (row: R) => string | number;
32
46
  name: string;
33
47
  order?: number;
@@ -71,6 +85,8 @@ export type DataGridProps<R> = {
71
85
  filter?: boolean;
72
86
  sort?: boolean;
73
87
  initialSorts?: Record<string, DataGridSort>;
88
+ initialFooters?: Record<string, string>;
89
+ onFootersChanged?: (footers: Record<string, string>) => void;
74
90
  filterValuesLoader?: (
75
91
  columnKey: string
76
92
  ) => Promise<(string | number | null)[]>;
@@ -94,6 +110,9 @@ export type DataGridContextProps<R> = DataGridProps<R> & {
94
110
  setSorts: (sorts: Record<string, DataGridSort>) => void;
95
111
  filters?: DataGridFilters;
96
112
  setFilters: Dispatch<SetStateAction<DataGridFilters>>;
113
+ footers?: Record<string, string>;
114
+ setFooters: Dispatch<SetStateAction<Record<string, string>>>;
115
+ footerFunctions?: Record<string, DataGridFooterFunction<R>>;
97
116
  visibleColumns: DataGridColumnKeyValuePair<R>[];
98
117
  copyTable: (
99
118
  includeHeaders?: boolean,
@@ -22,6 +22,7 @@ export const sqlTextColumn = <R extends Record<string, any>>(
22
22
  sortGetter: (row) => row[key] ?? '',
23
23
  filter: { ...textFilter(key), getter: (value) => value[key] ?? '' },
24
24
  ...options,
25
+ footer: (rows) => `${rows[0][key]} éléments`,
25
26
  },
26
27
  });
27
28
 
@@ -54,6 +55,7 @@ export const sqlComposedColumn = <R extends Record<string, any>>(
54
55
  },
55
56
  filterField: fields[0],
56
57
  sortField: fields[0],
58
+ footer: (rows) => `${rows[0][key]} éléments`,
57
59
  ...options,
58
60
  },
59
61
  });
@@ -69,6 +71,7 @@ export const sqlMailColumn = <R extends Record<string, any>>(
69
71
  getter: (row) => row[key] ?? '',
70
72
  sortGetter: (row) => row[key] ?? '',
71
73
  filter: { ...textFilter(key), getter: (value) => value[key] ?? '' },
74
+ footer: (rows) => `${rows[0][key]} éléments`,
72
75
  ...options,
73
76
  },
74
77
  });
@@ -84,6 +87,7 @@ export const sqlPhoneColumn = <R extends Record<string, any>>(
84
87
  getter: (row) => row[key] ?? '',
85
88
  sortGetter: (row) => row[key] ?? '',
86
89
  filter: { ...textFilter(key), getter: (value) => value[key] ?? '' },
90
+ footer: (rows) => `${rows[0][key]} éléments`,
87
91
  ...options,
88
92
  },
89
93
  });
@@ -99,6 +103,7 @@ export const sqlDateColumn = <R extends Record<string, any>>(
99
103
  getter: (row) => row[key] ?? '',
100
104
  sortGetter: (row) => row[key] ?? '',
101
105
  filter: { ...textFilter(key), getter: (value) => value[key] ?? '' },
106
+ footer: (rows) => `${rows[0][key]} éléments`,
102
107
  ...options,
103
108
  },
104
109
  });
@@ -114,6 +119,7 @@ export const sqlMonthColumn = <R extends Record<string, any>>(
114
119
  getter: (row) => row[key] ?? '',
115
120
  sortGetter: (row) => row[key] ?? '',
116
121
  filter: { ...textFilter(key), getter: (value) => value[key] ?? '' },
122
+ footer: (rows) => `${rows[0][key]} éléments`,
117
123
  ...options,
118
124
  },
119
125
  });
@@ -130,7 +136,18 @@ export const sqlNumberColumn = <R extends Record<string, any>>(
130
136
  excelFormatter: () => '#',
131
137
  getter: (row) => row[key] ?? '',
132
138
  sortGetter: (row) => row[key] ?? '',
133
- filter: { ...numberFilter(key), getter: (value) => value[key] ?? 0 },
139
+ filter: {
140
+ ...numberFilter(key),
141
+ getter: (value) => value[key] ?? 0,
142
+ renderer: (value) => formatNumber(value, decimals) ?? '',
143
+ },
144
+ footer: {
145
+ sum: null,
146
+ avg: null,
147
+ count: null,
148
+ max: null,
149
+ min: null,
150
+ },
134
151
  ...options,
135
152
  },
136
153
  });
@@ -143,11 +160,23 @@ export const sqlMoneyColumn = <R extends Record<string, any>>(
143
160
  ): SqlRequestDataGridColumns<R> => ({
144
161
  [key]: {
145
162
  name: title,
163
+ type: 'number',
146
164
  render: (row) => formatMoney(row[key], decimals) ?? '',
147
165
  excelFormatter: () => '#0.00',
148
166
  getter: (row) => row[key] ?? '',
149
167
  sortGetter: (row) => row[key] ?? '',
150
- filter: { ...numberFilter(key), getter: (value) => value[key] ?? 0 },
168
+ filter: {
169
+ ...numberFilter(key),
170
+ getter: (value) => value[key] ?? 0,
171
+ renderer: (value) => formatMoney(value, decimals) ?? '',
172
+ },
173
+ footer: {
174
+ sum: null,
175
+ avg: null,
176
+ count: null,
177
+ max: null,
178
+ min: null,
179
+ },
151
180
  ...options,
152
181
  },
153
182
  });
@@ -155,6 +184,7 @@ export const sqlMoneyColumn = <R extends Record<string, any>>(
155
184
  export const sqlPercentageColumn = <R extends Record<string, any>>(
156
185
  key: string,
157
186
  title: string,
187
+ decimals = 2,
158
188
  options?: Partial<SqlRequestDataGridColumn<R>>
159
189
  ): SqlRequestDataGridColumns<R> => ({
160
190
  [key]: {
@@ -163,7 +193,11 @@ export const sqlPercentageColumn = <R extends Record<string, any>>(
163
193
  excelFormatter: () => '#0.00',
164
194
  getter: (row) => row[key] ?? '',
165
195
  sortGetter: (row) => row[key] ?? '',
166
- filter: { ...numberFilter(key), getter: (value) => value[key] ?? 0 },
196
+ filter: {
197
+ ...numberFilter(key),
198
+ getter: (value) => value[key] ?? 0,
199
+ renderer: (value) => formatPercentage(value, decimals) ?? '',
200
+ },
167
201
  ...options,
168
202
  },
169
203
  });
@@ -184,6 +218,7 @@ export const sqlCheckboxColumn = <R extends Record<string, any>>(
184
218
  getter: (row) => row[key] ?? '',
185
219
  sortGetter: (row) => row[key] ?? '',
186
220
  filter: { ...numberFilter(key), getter: (value) => value[key] ?? 0 },
221
+ footer: (rows) => `${rows[0][key]} éléments`,
187
222
  ...options,
188
223
  },
189
224
  });
@@ -1,9 +1,15 @@
1
1
  import {
2
2
  ConditionDTO,
3
3
  OrderByDTO,
4
+ SqlRequestFooterFunction,
5
+ SqlRequestRow,
4
6
  useSqlRequestHandler,
5
7
  } from '../../../services/sqlRequests';
6
- import { DataGridFilters, DataGridSort } from '../DataGrid/types';
8
+ import {
9
+ DataGridFilters,
10
+ DataGridFooterFunction,
11
+ DataGridSort,
12
+ } from '../DataGrid/types';
7
13
  import _, { debounce } from 'lodash';
8
14
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
9
15
 
@@ -20,8 +26,10 @@ export const SqlRequestDataGrid = <R,>({
20
26
  const [rows, setRows] = useState<R[]>([]);
21
27
  const [start, setStart] = useState(0);
22
28
  const [length, setLength] = useState(50);
23
- const [total, setTotal] = useState(-1);
24
- const [sqlRequest, sqlIdRequest] = useSqlRequestHandler<R>(props.type);
29
+ const [count, setCount] = useState(-1);
30
+ const [sqlRequest, sqlIdRequest, sqlPartialRequest] = useSqlRequestHandler<R>(
31
+ props.type
32
+ );
25
33
 
26
34
  const [conditions, setConditions] = useState<Record<string, ConditionDTO>>(
27
35
  {}
@@ -30,6 +38,7 @@ export const SqlRequestDataGrid = <R,>({
30
38
  Object.entries(props.initialSorts ?? {}).map(
31
39
  ([columnKey, direction]): OrderByDTO => ({
32
40
  field: props.columns[columnKey].field?.fieldAlias ?? columnKey,
41
+ type: props.columns[columnKey].type ?? 'text',
33
42
  direction: direction.toUpperCase() as 'ASC' | 'DESC',
34
43
  })
35
44
  )
@@ -47,17 +56,24 @@ export const SqlRequestDataGrid = <R,>({
47
56
  ],
48
57
  [props.columns, props.hiddenColumns]
49
58
  );
59
+ const columnTypes = useMemo(
60
+ () =>
61
+ visibleColumnsKeys.map((key) =>
62
+ String(props.columns[key].type ?? 'text')
63
+ ),
64
+ [visibleColumnsKeys, props.columns]
65
+ );
50
66
 
51
67
  const refresh = useCallback(() => {
52
68
  setRows([]);
53
69
  setStart(0);
54
70
  setLength(50);
55
- setTotal(-1);
71
+ setCount(-1);
56
72
  }, []);
57
73
 
58
74
  const onFiltersChanged = useCallback((filters: DataGridFilters) => {
59
75
  const newConditions = convertSqlFiltersToConditions(filters);
60
- setTotal(-1);
76
+ setCount(-1);
61
77
  setConditions(newConditions);
62
78
  }, []);
63
79
 
@@ -85,22 +101,25 @@ export const SqlRequestDataGrid = <R,>({
85
101
  orderBy: OrderByDTO[] = [],
86
102
  start = 0,
87
103
  length = 100,
88
- getTotal = false
104
+ getCount = false
89
105
  ) => {
90
106
  sqlRequest({
91
107
  columns: columns.includes('Id') ? columns : [...columns, 'Id'],
92
108
  returnColumns: returnColumns.includes('Id')
93
109
  ? returnColumns
94
110
  : [...returnColumns, 'Id'],
111
+ columnTypes: columnTypes.includes('Id')
112
+ ? columnTypes
113
+ : [...columnTypes, 'Id'],
95
114
  conditions,
96
115
  orderBy,
97
116
  start,
98
117
  length,
99
- getTotal,
118
+ getCount,
100
119
  }).then((response) => {
101
- if (getTotal) {
102
- currentRows.current = Array(response.total).fill(null);
103
- if (getTotal) setTotal(response.total ?? 0);
120
+ if (getCount) {
121
+ currentRows.current = Array(response.count).fill(null);
122
+ if (getCount) setCount(response.count ?? 0);
104
123
  }
105
124
  const parsedRows = props.parser
106
125
  ? response.data.map(props.parser)
@@ -122,11 +141,10 @@ export const SqlRequestDataGrid = <R,>({
122
141
  return sqlRequest({
123
142
  columns: columnsKeys,
124
143
  returnColumns: [columnKey],
144
+ columnTypes: [props.columns[columnKey].type ?? 'text'],
125
145
  conditions: [
126
146
  ...(props.conditions ?? []),
127
- ...Object.values(
128
- _.pickBy(conditions, (condition, key) => key !== columnKey)
129
- ),
147
+ ...Object.values(_.pickBy(conditions, (_, key) => key !== columnKey)),
130
148
  ].filter((condition) => condition.field !== columnKey),
131
149
  orderBy: [
132
150
  {
@@ -135,10 +153,11 @@ export const SqlRequestDataGrid = <R,>({
135
153
  props.columns[columnKey].field?.fieldAlias ??
136
154
  props.columns[columnKey].field?.fieldName ??
137
155
  columnKey,
156
+ type: props.columns[columnKey].type ?? 'text',
138
157
  direction: 'ASC',
139
158
  },
140
159
  ],
141
- getTotal: false,
160
+ getCount: false,
142
161
  unique: true,
143
162
  }).then((response) =>
144
163
  response.data.map(
@@ -158,7 +177,7 @@ export const SqlRequestDataGrid = <R,>({
158
177
  orderBy,
159
178
  start,
160
179
  length,
161
- total < 0
180
+ count < 0
162
181
  ),
163
182
  [
164
183
  props.columns,
@@ -166,7 +185,7 @@ export const SqlRequestDataGrid = <R,>({
166
185
  orderBy,
167
186
  start,
168
187
  length,
169
- total,
188
+ count,
170
189
  props.conditions,
171
190
  columnsKeys,
172
191
  visibleColumnsKeys,
@@ -178,13 +197,14 @@ export const SqlRequestDataGrid = <R,>({
178
197
  sqlRequest({
179
198
  columns: columnsKeys,
180
199
  returnColumns: visibleColumnsKeys,
200
+ columnTypes,
181
201
  conditions: [
182
202
  ...(props.conditions ?? []),
183
203
  ...(Object.values(conditions) ?? []),
184
204
  ],
185
205
  orderBy,
186
206
  start: 0,
187
- length: total,
207
+ length: count,
188
208
  }).then((response) =>
189
209
  props.parser ? response.data.map(props.parser) : (response.data as R[])
190
210
  ),
@@ -192,28 +212,32 @@ export const SqlRequestDataGrid = <R,>({
192
212
  sqlRequest,
193
213
  columnsKeys,
194
214
  visibleColumnsKeys,
215
+ columnTypes,
195
216
  props.conditions,
196
217
  props.parser,
197
218
  conditions,
198
219
  orderBy,
199
- total,
220
+ count,
200
221
  ]
201
222
  );
202
223
 
203
224
  const loadAllIds = useCallback(
204
225
  () =>
205
226
  sqlIdRequest({
206
- columns: columnsKeys,
227
+ columns: columnsKeys.includes('Id')
228
+ ? columnsKeys
229
+ : [...columnsKeys, 'Id'],
207
230
  returnColumns: ['Id'],
231
+ columnTypes: ['text'],
208
232
  conditions: [
209
233
  ...(props.conditions ?? []),
210
234
  ...(Object.values(conditions) ?? []),
211
235
  ],
212
236
  orderBy,
213
237
  start: 0,
214
- length: total,
238
+ length: count,
215
239
  }).then((response) => response.data.map((row) => row['Id'])),
216
- [columnsKeys, conditions, orderBy, props.conditions, sqlIdRequest, total]
240
+ [columnsKeys, conditions, orderBy, props.conditions, sqlIdRequest, count]
217
241
  );
218
242
 
219
243
  const onVisibleRowsChanged = useCallback(
@@ -233,6 +257,76 @@ export const SqlRequestDataGrid = <R,>({
233
257
  [onSelectionChangeFromProps]
234
258
  );
235
259
 
260
+ /** FOOTERS */
261
+ const [footers, setFooters] = useState<Record<string, string>>(
262
+ props.initialFooters ?? {}
263
+ );
264
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
265
+ const [footerData, setFooterData] = useState<SqlRequestRow<any>>({});
266
+
267
+ const loadFooters = useCallback(() => {
268
+ if (Object.keys(footers).length === 0) {
269
+ setFooterData({});
270
+ } else {
271
+ sqlPartialRequest({
272
+ columns: columnsKeys.includes('Id')
273
+ ? columnsKeys
274
+ : [...columnsKeys, 'Id'],
275
+ returnColumns: [],
276
+ columnTypes: [],
277
+ totalColumns: footers as Record<string, SqlRequestFooterFunction>,
278
+ conditions: [
279
+ ...(props.conditions ?? []),
280
+ ...(Object.values(conditions) ?? []),
281
+ ],
282
+ orderBy,
283
+ }).then((response) => setFooterData(response.totals ?? {}));
284
+ }
285
+ }, [
286
+ columnsKeys,
287
+ conditions,
288
+ footers,
289
+ orderBy,
290
+ props.conditions,
291
+ sqlPartialRequest,
292
+ ]);
293
+
294
+ useEffect(() => {
295
+ loadFooters();
296
+ }, [loadFooters]);
297
+
298
+ const footerFunctions = useMemo(
299
+ () =>
300
+ !footerData
301
+ ? {}
302
+ : Object.entries(footers).reduce((acc, [columnKey, footerKey]) => {
303
+ const column = props.columns[columnKey];
304
+ const footerFunc =
305
+ typeof column?.footer === 'function'
306
+ ? column.footer
307
+ : column?.footer?.[footerKey];
308
+ const render = footerFunc
309
+ ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
310
+ (data: any) => footerFunc([data], [], [])
311
+ : column?.render ?? _.identity;
312
+ if (!column) {
313
+ return acc;
314
+ }
315
+ acc[columnKey] = () => render(footerData, column);
316
+ return acc;
317
+ }, {} as Record<string, DataGridFooterFunction<R>>),
318
+ [footerData, footers, props.columns]
319
+ );
320
+
321
+ const contextOverride = useMemo(
322
+ () => ({
323
+ footers,
324
+ setFooters,
325
+ footerFunctions,
326
+ }),
327
+ [footers, setFooters, footerFunctions]
328
+ );
329
+
236
330
  return (
237
331
  <DataGrid
238
332
  onVisibleRowsChange={onVisibleRowsChanged}
@@ -246,6 +340,7 @@ export const SqlRequestDataGrid = <R,>({
246
340
  refresh={refresh}
247
341
  onSelectionChange={onSelectionChange}
248
342
  getAllIds={loadAllIds}
343
+ contextOverride={contextOverride}
249
344
  {...props}
250
345
  />
251
346
  );
@@ -0,0 +1,73 @@
1
+ import { Divider, MenuContainer, MenuItemContainer, SubMenu } from './styles';
2
+ import {
3
+ FC,
4
+ HTMLAttributes,
5
+ PropsWithChildren,
6
+ useCallback,
7
+ useState,
8
+ } from 'react';
9
+
10
+ type ContextMenuFC = FC<PropsWithChildren<HTMLAttributes<HTMLDivElement>>> & {
11
+ Item: typeof MenuItemContainer;
12
+ ParentItem: typeof ParentMenuItem;
13
+ Divider: typeof Divider;
14
+ SubMenu: typeof SubMenu;
15
+ };
16
+
17
+ export const ContextMenu: ContextMenuFC = ({ children }) => {
18
+ return (
19
+ <MenuContainer onClick={(e) => e.stopPropagation()}>
20
+ {children}
21
+ </MenuContainer>
22
+ );
23
+ };
24
+
25
+ export const ParentMenuItem: FC<HTMLAttributes<HTMLDivElement>> = ({
26
+ children,
27
+ ...props
28
+ }) => {
29
+ const [isOpened, setIsOpened] = useState(false);
30
+ const [currentTimeout, setCurrentTimeout] = useState<number | null>(null);
31
+
32
+ const stopTimeout = useCallback(() => {
33
+ if (currentTimeout) {
34
+ clearTimeout(currentTimeout);
35
+ }
36
+ setCurrentTimeout(null);
37
+ }, [currentTimeout]);
38
+
39
+ const startTimeout = useCallback(
40
+ (open: boolean) => {
41
+ stopTimeout();
42
+ setCurrentTimeout(
43
+ window.setTimeout(
44
+ () => {
45
+ setIsOpened(open);
46
+ },
47
+ open ? 100 : 300
48
+ )
49
+ );
50
+ },
51
+ [stopTimeout]
52
+ );
53
+
54
+ const open = useCallback(() => startTimeout(true), [startTimeout]);
55
+ const close = useCallback(() => startTimeout(false), [startTimeout]);
56
+
57
+ return (
58
+ <MenuItemContainer
59
+ {...props}
60
+ className={isOpened ? 'opened' : ''}
61
+ onMouseEnter={open}
62
+ onMouseLeave={close}
63
+ $withArrow
64
+ >
65
+ {children}
66
+ </MenuItemContainer>
67
+ );
68
+ };
69
+
70
+ ContextMenu.Item = MenuItemContainer;
71
+ ContextMenu.ParentItem = ParentMenuItem;
72
+ ContextMenu.Divider = Divider;
73
+ ContextMenu.SubMenu = SubMenu;
@@ -0,0 +1,115 @@
1
+ import styled, { css } from 'styled-components';
2
+
3
+ import { ThemeColor } from '../../../providers/ThemeProvider/types';
4
+
5
+ const menuContainerCss = css`
6
+ position: absolute;
7
+ color: var(--color-neutral-900);
8
+ border-radius: var(--rounded-md);
9
+ padding: var(--space-1) 0;
10
+ box-shadow: var(--shadow-lg);
11
+ background-color: var(--color-neutral-100);
12
+ min-width: 20em;
13
+ outline: 1px solid var(--color-neutral-200);
14
+ display: flex;
15
+ flex-direction: column;
16
+ `;
17
+
18
+ export const MenuContainer = styled.div.attrs({
19
+ className: 'MenuContainer',
20
+ })`
21
+ ${menuContainerCss}
22
+ inset: 0;
23
+ `;
24
+ MenuContainer.displayName = 'MenuContainer';
25
+
26
+ export const SubMenu = styled.div.attrs({
27
+ className: 'SubMenu',
28
+ })`
29
+ ${menuContainerCss}
30
+ top: 0;
31
+ left: 100%;
32
+ margin-top: -var(--space-1);
33
+ `;
34
+ SubMenu.displayName = 'SubMenu';
35
+
36
+ export const MenuItemContainer = styled.div.attrs({
37
+ className: 'MenuItemContainer',
38
+ })<{
39
+ $color?: ThemeColor;
40
+ disabled?: boolean;
41
+ $withArrow?: boolean;
42
+ $opened?: boolean;
43
+ }>`
44
+ position: relative;
45
+ display: flex;
46
+ align-items: center;
47
+ font-family: var(--font-sans);
48
+ font-weight: normal;
49
+ text-align: left;
50
+ padding: var(--space-1) var(--space-2);
51
+ font-size: var(--text-base);
52
+ line-height: var(--leading-6);
53
+ border: none;
54
+ cursor: pointer;
55
+
56
+ ${({ $withArrow }) =>
57
+ $withArrow &&
58
+ css`
59
+ &::after {
60
+ content: '';
61
+ position: absolute;
62
+ right: var(--space-2);
63
+ top: 50%;
64
+ transform: translateY(-50%);
65
+ border: 4px solid transparent;
66
+ border-left-color: var(--color-neutral-600);
67
+ }
68
+ `}
69
+
70
+ ${({ $color, disabled }) =>
71
+ disabled
72
+ ? css`
73
+ color: var(--color-neutral-300);
74
+ background-color: var(--color-neutral-100);
75
+ cursor: default;
76
+ `
77
+ : $color
78
+ ? css`
79
+ color: var(--color-${$color}-600);
80
+ background-color: var(--color-neutral-100);
81
+ &:hover {
82
+ background-color: var(--color-${$color}-200);
83
+ }
84
+ `
85
+ : css`
86
+ color: var(--color-neutral-900);
87
+ background-color: var(--color-neutral-100);
88
+ &:hover {
89
+ background-color: var(--color-neutral-200);
90
+ }
91
+ `}
92
+
93
+ svg {
94
+ fill: currentColor;
95
+ width: var(--space-4);
96
+ height: var(--space-4);
97
+ margin-right: var(--space-2);
98
+ }
99
+
100
+ & > ${SubMenu} {
101
+ display: none;
102
+ margin-top: calc(0px - var(--space-1));
103
+ }
104
+ &.opened > ${SubMenu} {
105
+ display: block;
106
+ }
107
+ `;
108
+ MenuItemContainer.displayName = 'MenuItemContainer';
109
+
110
+ export const Divider = styled.div.attrs({
111
+ className: 'Divider',
112
+ })`
113
+ border-top: 1px solid var(--color-neutral-200);
114
+ margin: var(--space-1) 0;
115
+ `;
@@ -81,7 +81,7 @@ export type AdvancedRequestRow<R> = {
81
81
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
82
  export type AdvancedResponseDTO<T = any> = {
83
83
  data: AdvancedRequestRow<T>[];
84
- total?: number;
84
+ count?: number;
85
85
  };
86
86
 
87
87
  export type AdvancedRequestFieldDTO =
@@ -58,30 +58,36 @@ export type ConditionDTO = {
58
58
 
59
59
  export type OrderByDTO = {
60
60
  field: string;
61
+ type: string;
61
62
  direction?: 'ASC' | 'DESC';
62
63
  };
63
64
 
65
+ export type SqlRequestFooterFunction = 'sum' | 'avg' | 'count' | 'max' | 'min';
66
+
64
67
  export type SqlRequestDTO = {
65
68
  columns?: string[];
66
69
  returnColumns?: string[];
70
+ columnTypes?: string[];
71
+ totalColumns?: Record<string, SqlRequestFooterFunction>;
67
72
  conditions?: ConditionDTO[];
68
73
  orderBy?: OrderByDTO[];
69
74
  start?: number;
70
75
  length?: number;
71
- getTotal?: boolean;
76
+ getCount?: boolean;
72
77
  unique?: boolean;
73
78
  };
74
79
 
75
80
  export type SqlRequestRow<R> = {
76
- [K in keyof R]: R[K] extends string | number | null
81
+ [K in keyof R]: R[K] extends string | number | null | undefined
77
82
  ? R[K]
78
- : string | number | null;
83
+ : string | number | null | undefined;
79
84
  };
80
85
 
81
86
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
87
  export type SqlResponseDTO<T = any> = {
83
88
  data: SqlRequestRow<T>[];
84
- total?: number;
89
+ count?: number;
90
+ totals?: Record<string, string | number | null | undefined>;
85
91
  };
86
92
 
87
93
  type SqlRequestHandler<T> = (
@@ -91,9 +97,14 @@ type SqlRequestHandler<T> = (
91
97
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
98
  export const useSqlRequestHandler = <T = any>(
93
99
  name: string
94
- ): [SqlRequestHandler<T>, SqlRequestHandler<{ Id: string }>] => [
100
+ ): [
101
+ SqlRequestHandler<T>,
102
+ SqlRequestHandler<{ Id: string }>,
103
+ SqlRequestHandler<Partial<T>>
104
+ ] => [
95
105
  useWebSocketRequestHandler<SqlRequestDTO, SqlResponseDTO<T>>(name),
96
106
  useWebSocketRequestHandler<SqlRequestDTO, SqlResponseDTO<{ Id: string }>>(
97
107
  name
98
108
  ),
109
+ useWebSocketRequestHandler<SqlRequestDTO, SqlResponseDTO<Partial<T>>>(name),
99
110
  ];