@applica-software-guru/react-admin 1.5.361 → 1.5.362
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/dist/components/ra-lists/Datagrid/Datagrid.d.ts.map +1 -1
- package/dist/components/ra-lists/Datagrid/DatagridHeader.d.ts +14 -0
- package/dist/components/ra-lists/Datagrid/DatagridHeader.d.ts.map +1 -0
- package/dist/react-admin.cjs.js +55 -55
- package/dist/react-admin.cjs.js.gz +0 -0
- package/dist/react-admin.cjs.js.map +1 -1
- package/dist/react-admin.es.js +7774 -7670
- package/dist/react-admin.es.js.gz +0 -0
- package/dist/react-admin.es.js.map +1 -1
- package/dist/react-admin.umd.js +55 -55
- package/dist/react-admin.umd.js.gz +0 -0
- package/dist/react-admin.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ra-lists/Datagrid/Datagrid.tsx +36 -3
- package/src/components/ra-lists/Datagrid/DatagridHeader.tsx +179 -0
package/package.json
CHANGED
|
@@ -5,19 +5,29 @@ import clsx from 'clsx';
|
|
|
5
5
|
import difference from 'lodash/difference';
|
|
6
6
|
import union from 'lodash/union';
|
|
7
7
|
import { Identifier, sanitizeListRestProps, useListContext, useTranslate } from 'ra-core';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
FC,
|
|
10
|
+
cloneElement,
|
|
11
|
+
createElement,
|
|
12
|
+
isValidElement,
|
|
13
|
+
useCallback,
|
|
14
|
+
useEffect,
|
|
15
|
+
useMemo,
|
|
16
|
+
useRef,
|
|
17
|
+
useState
|
|
18
|
+
} from 'react';
|
|
9
19
|
import * as React from 'react';
|
|
10
20
|
import {
|
|
11
21
|
BulkDeleteButton,
|
|
12
22
|
DatagridBody,
|
|
13
23
|
DatagridClasses,
|
|
14
|
-
DatagridHeader,
|
|
15
24
|
DatagridLoading,
|
|
16
25
|
DatagridRoot,
|
|
17
26
|
PureDatagridBody,
|
|
18
27
|
DatagridProps as RaDatagridProps
|
|
19
28
|
} from 'react-admin';
|
|
20
29
|
import { Empty } from '@/components/ra-lists/Empty';
|
|
30
|
+
import { DatagridHeader, SortItem } from './DatagridHeader';
|
|
21
31
|
|
|
22
32
|
const defaultBulkActionButtons = <BulkDeleteButton />;
|
|
23
33
|
|
|
@@ -125,6 +135,29 @@ const Datagrid = React.forwardRef((props, ref) => {
|
|
|
125
135
|
|
|
126
136
|
const translate = useTranslate();
|
|
127
137
|
const { sort, data, isLoading, onSelect, onToggleItem, selectedIds, setSort, total } = useListContext(props);
|
|
138
|
+
const [multiSort, setMultiSort] = useState<SortItem[]>([]);
|
|
139
|
+
|
|
140
|
+
const prevMultiSortKeyRef = useRef<string>('');
|
|
141
|
+
const multiSortKey = useMemo(() => multiSort.map((s) => `${s.field}:${s.order}`).join('|'), [multiSort]);
|
|
142
|
+
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
const prevKey = prevMultiSortKeyRef.current;
|
|
145
|
+
if (prevKey === multiSortKey) return;
|
|
146
|
+
|
|
147
|
+
if (multiSort.length > 0) {
|
|
148
|
+
setSort({
|
|
149
|
+
field: multiSort.map((s) => s.field).join(','),
|
|
150
|
+
order: multiSort.map((s) => s.order).join(',') as 'ASC' | 'DESC'
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
prevMultiSortKeyRef.current = multiSortKey;
|
|
155
|
+
}, [multiSortKey, multiSort, setSort]);
|
|
156
|
+
|
|
157
|
+
const onMultiSortChange = useCallback((newMultiSort: SortItem[]) => {
|
|
158
|
+
setMultiSort(newMultiSort);
|
|
159
|
+
}, []);
|
|
160
|
+
|
|
128
161
|
const hasBulkActions = !!bulkActionButtons !== false;
|
|
129
162
|
const contextValue = useMemo(() => ({ isRowExpandable, expandSingle }), [isRowExpandable, expandSingle]);
|
|
130
163
|
const lastSelected = useRef(null);
|
|
@@ -216,7 +249,7 @@ const Datagrid = React.forwardRef((props, ref) => {
|
|
|
216
249
|
<div className={DatagridClasses.tableWrapper}>
|
|
217
250
|
<Table ref={ref} className={DatagridClasses.table} size={size} {...sanitizeRestProps(rest)}>
|
|
218
251
|
{createOrCloneElement(
|
|
219
|
-
|
|
252
|
+
<DatagridHeader multiSort={multiSort} setMultiSort={onMultiSortChange} />,
|
|
220
253
|
{
|
|
221
254
|
children,
|
|
222
255
|
sort,
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { Checkbox, TableCell, TableHead, TableRow, TableSortLabel, Typography } from '@mui/material';
|
|
2
|
+
import { RaRecord, useListContext, useTranslateLabel } from 'ra-core';
|
|
3
|
+
import React, { ReactElement, ReactNode, useCallback, useMemo } from 'react';
|
|
4
|
+
import { DatagridHeaderProps } from 'react-admin';
|
|
5
|
+
|
|
6
|
+
type SortItem = {
|
|
7
|
+
field: string;
|
|
8
|
+
order: 'ASC' | 'DESC';
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
interface MultiSortDatagridHeaderProps extends Omit<DatagridHeaderProps, 'setSort'> {
|
|
12
|
+
setMultiSort?: (sort: SortItem[]) => void;
|
|
13
|
+
multiSort?: SortItem[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function DatagridHeader(props: MultiSortDatagridHeaderProps): ReactElement {
|
|
17
|
+
const {
|
|
18
|
+
children,
|
|
19
|
+
hasExpand,
|
|
20
|
+
hasBulkActions,
|
|
21
|
+
isRowSelectable,
|
|
22
|
+
onSelect,
|
|
23
|
+
selectedIds = [],
|
|
24
|
+
data = [],
|
|
25
|
+
multiSort = [],
|
|
26
|
+
setMultiSort,
|
|
27
|
+
resource
|
|
28
|
+
} = props;
|
|
29
|
+
const { setSort } = useListContext();
|
|
30
|
+
const translateLabel = useTranslateLabel();
|
|
31
|
+
|
|
32
|
+
const updateSort = useCallback(
|
|
33
|
+
(field: string, isMultiSort: boolean = false) => {
|
|
34
|
+
if (!setMultiSort) {
|
|
35
|
+
const currentSortForField = multiSort.find((s) => s.field === field);
|
|
36
|
+
const newOrder = currentSortForField?.order === 'ASC' ? 'DESC' : 'ASC';
|
|
37
|
+
setSort({ field, order: newOrder });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const existingIndex = multiSort.findIndex((s) => s.field === field);
|
|
42
|
+
|
|
43
|
+
if (!isMultiSort) {
|
|
44
|
+
if (existingIndex >= 0) {
|
|
45
|
+
const currentOrder = multiSort[existingIndex].order;
|
|
46
|
+
if (currentOrder === 'ASC') {
|
|
47
|
+
setMultiSort([{ field, order: 'DESC' }]);
|
|
48
|
+
} else {
|
|
49
|
+
setMultiSort([]);
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
setMultiSort([{ field, order: 'ASC' }]);
|
|
53
|
+
}
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const newMultiSort = [...multiSort];
|
|
58
|
+
|
|
59
|
+
if (existingIndex >= 0) {
|
|
60
|
+
const currentOrder = newMultiSort[existingIndex].order;
|
|
61
|
+
if (currentOrder === 'ASC') {
|
|
62
|
+
newMultiSort[existingIndex] = { field, order: 'DESC' };
|
|
63
|
+
} else {
|
|
64
|
+
newMultiSort.splice(existingIndex, 1);
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
newMultiSort.push({ field, order: 'ASC' });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
setMultiSort(newMultiSort);
|
|
71
|
+
},
|
|
72
|
+
[multiSort, setMultiSort, setSort]
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const handleSelectAll = useCallback(
|
|
76
|
+
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
77
|
+
if (!onSelect) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (event.target.checked) {
|
|
81
|
+
const allIds = data
|
|
82
|
+
.filter((record: RaRecord) => (isRowSelectable ? isRowSelectable(record) : true))
|
|
83
|
+
.map((record: RaRecord) => record.id);
|
|
84
|
+
onSelect(allIds);
|
|
85
|
+
} else {
|
|
86
|
+
onSelect([]);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
[data, isRowSelectable, onSelect]
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const getSortForField = useCallback(
|
|
93
|
+
(field: string): { order: 'ASC' | 'DESC'; index: number } | null => {
|
|
94
|
+
const index = multiSort.findIndex((s) => s.field === field);
|
|
95
|
+
if (index >= 0) {
|
|
96
|
+
return { order: multiSort[index].order, index };
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
},
|
|
100
|
+
[multiSort]
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const childrenArray = useMemo(() => React.Children.toArray(children), [children]);
|
|
104
|
+
|
|
105
|
+
const selectableCount = useMemo(
|
|
106
|
+
() => data.filter((record: RaRecord) => (isRowSelectable ? isRowSelectable(record) : true)).length,
|
|
107
|
+
[data, isRowSelectable]
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const handleSortClick = useCallback(
|
|
111
|
+
(source: string) => (event: React.MouseEvent<unknown>) => {
|
|
112
|
+
updateSort(source, event.ctrlKey || event.metaKey);
|
|
113
|
+
},
|
|
114
|
+
[updateSort]
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<TableHead>
|
|
119
|
+
<TableRow>
|
|
120
|
+
{!!hasExpand && <TableCell />}
|
|
121
|
+
{!!hasBulkActions && (
|
|
122
|
+
<TableCell padding="checkbox">
|
|
123
|
+
<Checkbox
|
|
124
|
+
color="primary"
|
|
125
|
+
checked={selectedIds.length > 0 && selectedIds.length === selectableCount}
|
|
126
|
+
onChange={handleSelectAll}
|
|
127
|
+
/>
|
|
128
|
+
</TableCell>
|
|
129
|
+
)}
|
|
130
|
+
{childrenArray.length > 0 ? (
|
|
131
|
+
childrenArray.map((child: ReactNode, index) => {
|
|
132
|
+
if (!child || typeof child !== 'object' || !('props' in child)) {
|
|
133
|
+
return <TableCell key={index} />;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const source =
|
|
137
|
+
child.props.source ?? (typeof child.props.label === 'string' ? child.props.label : undefined);
|
|
138
|
+
const sortable = child.props.sortable !== false && !!source;
|
|
139
|
+
const sortInfo = sortable ? getSortForField(source) : null;
|
|
140
|
+
|
|
141
|
+
const translated = translateLabel({
|
|
142
|
+
...child.props,
|
|
143
|
+
source: child.props.source ?? source,
|
|
144
|
+
resource
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const displayLabel: ReactNode = translated ?? child.props.label ?? null;
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<TableCell
|
|
151
|
+
key={child.props.source ?? index}
|
|
152
|
+
align={child.props.textAlign || 'left'}
|
|
153
|
+
sx={{ cursor: sortable ? 'pointer' : 'default' }}
|
|
154
|
+
>
|
|
155
|
+
{sortable ? (
|
|
156
|
+
<TableSortLabel
|
|
157
|
+
active={!!sortInfo}
|
|
158
|
+
direction={(sortInfo?.order.toLowerCase() as 'asc' | 'desc') || 'asc'}
|
|
159
|
+
onClick={handleSortClick(source)}
|
|
160
|
+
>
|
|
161
|
+
<Typography>{displayLabel}</Typography>
|
|
162
|
+
</TableSortLabel>
|
|
163
|
+
) : (
|
|
164
|
+
displayLabel
|
|
165
|
+
)}
|
|
166
|
+
</TableCell>
|
|
167
|
+
);
|
|
168
|
+
})
|
|
169
|
+
) : (
|
|
170
|
+
<TableCell />
|
|
171
|
+
)}
|
|
172
|
+
</TableRow>
|
|
173
|
+
</TableHead>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export { DatagridHeader };
|
|
178
|
+
|
|
179
|
+
export type { MultiSortDatagridHeaderProps, SortItem };
|