@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.
- package/assets/icons/arrow-down-1-9.svg +1 -0
- package/assets/icons/arrow-down-big-small.svg +1 -0
- package/assets/icons/arrow-up-9-1.svg +1 -0
- package/assets/icons/arrow-up-big-small.svg +1 -0
- package/assets/icons/chevron-down.svg +1 -0
- package/assets/icons/ellipsis.svg +1 -0
- package/assets/icons/sigma.svg +1 -0
- package/assets/icons/table-footer-slash.svg +5 -0
- package/assets/icons/table-footer.svg +4 -0
- package/assets/icons/table.svg +1 -0
- package/assets/icons/tally.svg +1 -0
- package/assets/icons/x-bar.svg +4 -0
- package/dist/Icons.d.ts +13 -1
- package/dist/Icons.js +25 -1
- package/dist/components/data/AdvancedRequestDataGrid/index.js +3 -3
- package/dist/components/data/DataGrid/DataGridColumnsModal/hooks.js +2 -1
- package/dist/components/data/DataGrid/DataGridFilterMenu/index.js +85 -7
- package/dist/components/data/DataGrid/DataGridFilterMenu/styles.d.ts +3 -9
- package/dist/components/data/DataGrid/DataGridFilterMenu/styles.js +10 -37
- package/dist/components/data/DataGrid/DataGridFooter.d.ts +1 -1
- package/dist/components/data/DataGrid/DataGridFooter.js +35 -12
- package/dist/components/data/DataGrid/DataGridHeader.js +1 -1
- package/dist/components/data/DataGrid/DataGridHeaderCell.js +6 -22
- package/dist/components/data/DataGrid/helpers/columns.d.ts +1 -1
- package/dist/components/data/DataGrid/helpers/columns.js +71 -16
- package/dist/components/data/DataGrid/hooks/useDataGrid.d.ts +1 -1
- package/dist/components/data/DataGrid/hooks/useDataGrid.js +60 -30
- package/dist/components/data/DataGrid/hooks/useDataGridCopy.d.ts +2 -2
- package/dist/components/data/DataGrid/hooks/useDataGridCopy.js +41 -39
- package/dist/components/data/DataGrid/index.d.ts +4 -2
- package/dist/components/data/DataGrid/index.js +22 -12
- package/dist/components/data/DataGrid/styles.d.ts +8 -4
- package/dist/components/data/DataGrid/styles.js +27 -17
- package/dist/components/data/DataGrid/types.d.ts +8 -1
- package/dist/components/data/SqlRequestDataGrid/helpers/columns.d.ts +1 -1
- package/dist/components/data/SqlRequestDataGrid/helpers/columns.js +24 -11
- package/dist/components/data/SqlRequestDataGrid/index.js +105 -33
- package/dist/components/ui/ContextMenu/index.d.ts +11 -0
- package/dist/components/ui/ContextMenu/index.js +58 -0
- package/dist/components/ui/ContextMenu/styles.d.ts +18 -0
- package/dist/components/ui/ContextMenu/styles.js +56 -0
- package/dist/services/advancedRequests.d.ts +1 -1
- package/dist/services/sqlRequests.d.ts +9 -4
- package/dist/services/sqlRequests.js +1 -0
- package/package.json +1 -1
- package/src/Icons.tsx +24 -0
- package/src/components/data/AdvancedRequestDataGrid/index.tsx +3 -5
- package/src/components/data/DataGrid/DataGridColumnsModal/hooks.tsx +2 -1
- package/src/components/data/DataGrid/DataGridFilterMenu/index.tsx +173 -9
- package/src/components/data/DataGrid/DataGridFilterMenu/styles.ts +13 -64
- package/src/components/data/DataGrid/DataGridFooter.tsx +20 -22
- package/src/components/data/DataGrid/DataGridHeader.tsx +5 -5
- package/src/components/data/DataGrid/DataGridHeaderCell.tsx +3 -38
- package/src/components/data/DataGrid/helpers/columns.tsx +98 -7
- package/src/components/data/DataGrid/hooks/useDataGrid.tsx +58 -17
- package/src/components/data/DataGrid/hooks/useDataGridCopy.ts +35 -30
- package/src/components/data/DataGrid/index.tsx +21 -14
- package/src/components/data/DataGrid/styles.ts +28 -7
- package/src/components/data/DataGrid/types.ts +20 -1
- package/src/components/data/SqlRequestDataGrid/helpers/columns.tsx +38 -3
- package/src/components/data/SqlRequestDataGrid/index.tsx +116 -21
- package/src/components/ui/ContextMenu/index.tsx +73 -0
- package/src/components/ui/ContextMenu/styles.ts +115 -0
- package/src/services/advancedRequests.ts +1 -1
- package/src/services/sqlRequests.ts +16 -5
- 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?:
|
|
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: {
|
|
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: {
|
|
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: {
|
|
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 {
|
|
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 [
|
|
24
|
-
const [sqlRequest, sqlIdRequest] = useSqlRequestHandler<R>(
|
|
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
|
-
|
|
71
|
+
setCount(-1);
|
|
56
72
|
}, []);
|
|
57
73
|
|
|
58
74
|
const onFiltersChanged = useCallback((filters: DataGridFilters) => {
|
|
59
75
|
const newConditions = convertSqlFiltersToConditions(filters);
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-
|
|
118
|
+
getCount,
|
|
100
119
|
}).then((response) => {
|
|
101
|
-
if (
|
|
102
|
-
currentRows.current = Array(response.
|
|
103
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
238
|
+
length: count,
|
|
215
239
|
}).then((response) => response.data.map((row) => row['Id'])),
|
|
216
|
-
[columnsKeys, conditions, orderBy, props.conditions, sqlIdRequest,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
): [
|
|
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
|
];
|