@etsoo/materialui 1.5.60 → 1.5.62
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/lib/cjs/ButtonPopupCheckbox.js +13 -13
- package/lib/cjs/DnDList.d.ts +11 -2
- package/lib/cjs/DnDList.js +26 -26
- package/lib/mjs/ButtonPopupCheckbox.js +13 -13
- package/lib/mjs/DnDList.d.ts +11 -2
- package/lib/mjs/DnDList.js +26 -26
- package/package.json +7 -7
- package/src/ButtonPopupCheckbox.tsx +50 -50
- package/src/DnDList.tsx +64 -43
|
@@ -16,10 +16,10 @@ const IconButton_1 = __importDefault(require("@mui/material/IconButton"));
|
|
|
16
16
|
const FormControlLabel_1 = __importDefault(require("@mui/material/FormControlLabel"));
|
|
17
17
|
const Checkbox_1 = __importDefault(require("@mui/material/Checkbox"));
|
|
18
18
|
const TextField_1 = __importDefault(require("@mui/material/TextField"));
|
|
19
|
-
const DnDList_1 = require("./DnDList");
|
|
20
19
|
const FlexBox_1 = require("./FlexBox");
|
|
21
20
|
const ReactApp_1 = require("./app/ReactApp");
|
|
22
21
|
const FormLabel_1 = __importDefault(require("@mui/material/FormLabel"));
|
|
22
|
+
const DnDList_1 = require("./DnDList");
|
|
23
23
|
function ButtonPopupList(props) {
|
|
24
24
|
// Destruct
|
|
25
25
|
const { addSplitter = /\s*[,;]\s*/, value = [], items, labelField, labelFormatter, labels, onAdd, onValueChange } = props;
|
|
@@ -35,18 +35,18 @@ function ButtonPopupList(props) {
|
|
|
35
35
|
// Set selected ids
|
|
36
36
|
setSelectedIds([...value]);
|
|
37
37
|
}, [value]);
|
|
38
|
-
return ((0, jsx_runtime_1.jsxs)(FlexBox_1.VBox, { gap: 2, children: [(0, jsx_runtime_1.jsx)(Grid_1.default, { container: true, spacing: 0
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
38
|
+
return ((0, jsx_runtime_1.jsxs)(FlexBox_1.VBox, { gap: 2, children: [(0, jsx_runtime_1.jsx)(DnDList_1.DnDList, { component: Grid_1.default, componentProps: { container: true, spacing: 0 }, items: items, labelField: labelField, onFormChange: (items) => {
|
|
39
|
+
const ids = items
|
|
40
|
+
.filter((item) => selectedIds.includes(item.id))
|
|
41
|
+
.map((item) => item.id);
|
|
42
|
+
onValueChange(ids);
|
|
43
|
+
}, itemRenderer: (item, index, nodeRef, actionNodeRef) => ((0, jsx_runtime_1.jsxs)(Grid_1.default, { size: { xs: 12, md: 6, lg: 4 }, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, ...nodeRef, children: [(0, jsx_runtime_1.jsx)(IconButton_1.default, { style: { cursor: "move" }, size: "small", title: labels?.dragIndicator, ...actionNodeRef, children: (0, jsx_runtime_1.jsx)(DragIndicator_1.default, {}) }), (0, jsx_runtime_1.jsx)(FormControlLabel_1.default, { control: (0, jsx_runtime_1.jsx)(Checkbox_1.default, { name: "item", value: item.id, checked: selectedIds.includes(item.id), onChange: (e) => {
|
|
44
|
+
const checked = e.target.checked;
|
|
45
|
+
const newIds = [
|
|
46
|
+
...selectedIds.toggleItem(item.id, checked)
|
|
47
|
+
];
|
|
48
|
+
setSelectedIds(newIds);
|
|
49
|
+
} }), label: `${index + 1}. ${labelFormatter(item)}` })] })), height: 200, mRef: dndRef }), onAdd && ((0, jsx_runtime_1.jsxs)(FlexBox_1.HBox, { gap: 1, children: [(0, jsx_runtime_1.jsx)(TextField_1.default, { variant: "outlined", label: labels?.more, fullWidth: true, inputRef: inputRef }), (0, jsx_runtime_1.jsx)(Button_1.default, { sx: { width: "120px" }, variant: "contained", startIcon: (0, jsx_runtime_1.jsx)(Add_1.default, {}), size: "small", onClick: async () => {
|
|
50
50
|
if (inputRef.current == null)
|
|
51
51
|
return;
|
|
52
52
|
const input = inputRef.current.value.trim();
|
package/lib/cjs/DnDList.d.ts
CHANGED
|
@@ -50,7 +50,16 @@ export interface DnDListRef<D extends object> {
|
|
|
50
50
|
*/
|
|
51
51
|
export interface DnDListPros<D extends {
|
|
52
52
|
id: UniqueIdentifier;
|
|
53
|
-
}> {
|
|
53
|
+
}, E extends React.ElementType = React.ElementType> {
|
|
54
|
+
/**
|
|
55
|
+
* Component type to render the list into
|
|
56
|
+
* Default is React.Fragment
|
|
57
|
+
*/
|
|
58
|
+
component?: E;
|
|
59
|
+
/**
|
|
60
|
+
* Component props
|
|
61
|
+
*/
|
|
62
|
+
componentProps?: React.ComponentProps<E>;
|
|
54
63
|
/**
|
|
55
64
|
* Get list item style callback
|
|
56
65
|
*/
|
|
@@ -99,4 +108,4 @@ export interface DnDListPros<D extends {
|
|
|
99
108
|
*/
|
|
100
109
|
export declare function DnDList<D extends {
|
|
101
110
|
id: UniqueIdentifier;
|
|
102
|
-
}>(props: DnDListPros<D>): import("react/jsx-runtime").JSX.Element;
|
|
111
|
+
}, E extends React.ElementType = React.ElementType>(props: DnDListPros<D, E>): import("react/jsx-runtime").JSX.Element;
|
package/lib/cjs/DnDList.js
CHANGED
|
@@ -54,7 +54,8 @@ exports.DnDItemStyle = DnDItemStyle;
|
|
|
54
54
|
*/
|
|
55
55
|
function DnDList(props) {
|
|
56
56
|
// Destruct
|
|
57
|
-
const { height = 360, itemRenderer, labelField, mRef, sortingStrategy, onChange, onFormChange, onDragEnd } = props;
|
|
57
|
+
const { componentProps, height = 360, itemRenderer, labelField, mRef, sortingStrategy, onChange, onFormChange, onDragEnd } = props;
|
|
58
|
+
const Component = props.component || react_1.default.Fragment;
|
|
58
59
|
// Theme
|
|
59
60
|
const theme = (0, styles_1.useTheme)();
|
|
60
61
|
// States
|
|
@@ -64,8 +65,10 @@ function DnDList(props) {
|
|
|
64
65
|
setItems(props.items);
|
|
65
66
|
}, [props.items]);
|
|
66
67
|
const doFormChange = react_1.default.useCallback((newItems) => {
|
|
67
|
-
if (onFormChange)
|
|
68
|
-
|
|
68
|
+
if (onFormChange) {
|
|
69
|
+
const locals = Array.isArray(newItems) ? newItems : items;
|
|
70
|
+
onFormChange(locals);
|
|
71
|
+
}
|
|
69
72
|
}, [items, onFormChange]);
|
|
70
73
|
const changeItems = react_1.default.useCallback((newItems) => {
|
|
71
74
|
// Possible to alter items with the handler
|
|
@@ -152,38 +155,27 @@ function DnDList(props) {
|
|
|
152
155
|
]);
|
|
153
156
|
});
|
|
154
157
|
}, []);
|
|
155
|
-
const doChange = react_1.default.useCallback(() => doFormChange(), []);
|
|
156
158
|
const setupDiv = (div, clearup = false) => {
|
|
157
159
|
// Inputs
|
|
158
160
|
div
|
|
159
161
|
.querySelectorAll("input")
|
|
160
162
|
.forEach((input) => clearup
|
|
161
|
-
? input.removeEventListener("change",
|
|
162
|
-
: input.addEventListener("change",
|
|
163
|
+
? input.removeEventListener("change", doFormChange)
|
|
164
|
+
: input.addEventListener("change", doFormChange));
|
|
163
165
|
// Textareas
|
|
164
166
|
div
|
|
165
167
|
.querySelectorAll("textarea")
|
|
166
168
|
.forEach((input) => clearup
|
|
167
|
-
? input.removeEventListener("change",
|
|
168
|
-
: input.addEventListener("change",
|
|
169
|
+
? input.removeEventListener("change", doFormChange)
|
|
170
|
+
: input.addEventListener("change", doFormChange));
|
|
169
171
|
// Select
|
|
170
172
|
div
|
|
171
173
|
.querySelectorAll("select")
|
|
172
174
|
.forEach((input) => clearup
|
|
173
|
-
? input.removeEventListener("change",
|
|
174
|
-
: input.addEventListener("change",
|
|
175
|
+
? input.removeEventListener("change", doFormChange)
|
|
176
|
+
: input.addEventListener("change", doFormChange));
|
|
175
177
|
};
|
|
176
|
-
const divRef = react_1.default.useRef(
|
|
177
|
-
react_1.default.useEffect(() => {
|
|
178
|
-
if (divRef.current) {
|
|
179
|
-
setupDiv(divRef.current);
|
|
180
|
-
}
|
|
181
|
-
return () => {
|
|
182
|
-
if (divRef.current) {
|
|
183
|
-
setupDiv(divRef.current, true);
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
}, []);
|
|
178
|
+
const divRef = react_1.default.useRef();
|
|
187
179
|
if (dnd == null) {
|
|
188
180
|
return (0, jsx_runtime_1.jsx)(Skeleton_1.default, { variant: "rectangular", width: "100%", height: height });
|
|
189
181
|
}
|
|
@@ -227,12 +219,20 @@ function DnDList(props) {
|
|
|
227
219
|
}
|
|
228
220
|
setActiveId(undefined);
|
|
229
221
|
}
|
|
230
|
-
const children = ((0, jsx_runtime_1.jsx)(DndContextType, { onDragStart: handleDragStart, onDragEnd: handleDragEnd, children: (0, jsx_runtime_1.jsx)(SortableContextType, { items: items, strategy: strategy, children: items.map((item, index) => {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
222
|
+
const children = ((0, jsx_runtime_1.jsx)(DndContextType, { onDragStart: handleDragStart, onDragEnd: handleDragEnd, children: (0, jsx_runtime_1.jsx)(SortableContextType, { items: items, strategy: strategy, children: (0, jsx_runtime_1.jsx)(Component, { ...componentProps, children: items.map((item, index) => {
|
|
223
|
+
const id = item.id;
|
|
224
|
+
return ((0, jsx_runtime_1.jsx)(SortableItem, { id: id, useSortableType: useSortableType, CSSType: CSSType, style: getItemStyle(index, id === activeId), itemRenderer: (nodeRef, actionNodeRef) => itemRenderer(item, index, nodeRef, actionNodeRef) }, id));
|
|
225
|
+
}) }) }) }));
|
|
234
226
|
if (onFormChange) {
|
|
235
|
-
return ((0, jsx_runtime_1.jsx)("div", { style: { width: "100%" }, ref:
|
|
227
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: { width: "100%" }, ref: (div) => {
|
|
228
|
+
if (div && divRef.current != div) {
|
|
229
|
+
if (divRef.current) {
|
|
230
|
+
setupDiv(divRef.current, true);
|
|
231
|
+
}
|
|
232
|
+
divRef.current = div;
|
|
233
|
+
setupDiv(div);
|
|
234
|
+
}
|
|
235
|
+
}, children: children }));
|
|
236
236
|
}
|
|
237
237
|
return children;
|
|
238
238
|
}
|
|
@@ -10,10 +10,10 @@ import IconButton from "@mui/material/IconButton";
|
|
|
10
10
|
import FormControlLabel from "@mui/material/FormControlLabel";
|
|
11
11
|
import Checkbox from "@mui/material/Checkbox";
|
|
12
12
|
import TextField from "@mui/material/TextField";
|
|
13
|
-
import { DnDList } from "./DnDList";
|
|
14
13
|
import { HBox, VBox } from "./FlexBox";
|
|
15
14
|
import { useRequiredAppContext } from "./app/ReactApp";
|
|
16
15
|
import FormLabel from "@mui/material/FormLabel";
|
|
16
|
+
import { DnDList } from "./DnDList";
|
|
17
17
|
function ButtonPopupList(props) {
|
|
18
18
|
// Destruct
|
|
19
19
|
const { addSplitter = /\s*[,;]\s*/, value = [], items, labelField, labelFormatter, labels, onAdd, onValueChange } = props;
|
|
@@ -29,18 +29,18 @@ function ButtonPopupList(props) {
|
|
|
29
29
|
// Set selected ids
|
|
30
30
|
setSelectedIds([...value]);
|
|
31
31
|
}, [value]);
|
|
32
|
-
return (_jsxs(VBox, { gap: 2, children: [_jsx(Grid, { container: true, spacing: 0
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
return (_jsxs(VBox, { gap: 2, children: [_jsx(DnDList, { component: Grid, componentProps: { container: true, spacing: 0 }, items: items, labelField: labelField, onFormChange: (items) => {
|
|
33
|
+
const ids = items
|
|
34
|
+
.filter((item) => selectedIds.includes(item.id))
|
|
35
|
+
.map((item) => item.id);
|
|
36
|
+
onValueChange(ids);
|
|
37
|
+
}, itemRenderer: (item, index, nodeRef, actionNodeRef) => (_jsxs(Grid, { size: { xs: 12, md: 6, lg: 4 }, display: "flex", justifyContent: "flex-start", alignItems: "center", gap: 1, ...nodeRef, children: [_jsx(IconButton, { style: { cursor: "move" }, size: "small", title: labels?.dragIndicator, ...actionNodeRef, children: _jsx(DragIndicatorIcon, {}) }), _jsx(FormControlLabel, { control: _jsx(Checkbox, { name: "item", value: item.id, checked: selectedIds.includes(item.id), onChange: (e) => {
|
|
38
|
+
const checked = e.target.checked;
|
|
39
|
+
const newIds = [
|
|
40
|
+
...selectedIds.toggleItem(item.id, checked)
|
|
41
|
+
];
|
|
42
|
+
setSelectedIds(newIds);
|
|
43
|
+
} }), label: `${index + 1}. ${labelFormatter(item)}` })] })), height: 200, mRef: dndRef }), onAdd && (_jsxs(HBox, { gap: 1, children: [_jsx(TextField, { variant: "outlined", label: labels?.more, fullWidth: true, inputRef: inputRef }), _jsx(Button, { sx: { width: "120px" }, variant: "contained", startIcon: _jsx(AddIcon, {}), size: "small", onClick: async () => {
|
|
44
44
|
if (inputRef.current == null)
|
|
45
45
|
return;
|
|
46
46
|
const input = inputRef.current.value.trim();
|
package/lib/mjs/DnDList.d.ts
CHANGED
|
@@ -50,7 +50,16 @@ export interface DnDListRef<D extends object> {
|
|
|
50
50
|
*/
|
|
51
51
|
export interface DnDListPros<D extends {
|
|
52
52
|
id: UniqueIdentifier;
|
|
53
|
-
}> {
|
|
53
|
+
}, E extends React.ElementType = React.ElementType> {
|
|
54
|
+
/**
|
|
55
|
+
* Component type to render the list into
|
|
56
|
+
* Default is React.Fragment
|
|
57
|
+
*/
|
|
58
|
+
component?: E;
|
|
59
|
+
/**
|
|
60
|
+
* Component props
|
|
61
|
+
*/
|
|
62
|
+
componentProps?: React.ComponentProps<E>;
|
|
54
63
|
/**
|
|
55
64
|
* Get list item style callback
|
|
56
65
|
*/
|
|
@@ -99,4 +108,4 @@ export interface DnDListPros<D extends {
|
|
|
99
108
|
*/
|
|
100
109
|
export declare function DnDList<D extends {
|
|
101
110
|
id: UniqueIdentifier;
|
|
102
|
-
}>(props: DnDListPros<D>): import("react/jsx-runtime").JSX.Element;
|
|
111
|
+
}, E extends React.ElementType = React.ElementType>(props: DnDListPros<D, E>): import("react/jsx-runtime").JSX.Element;
|
package/lib/mjs/DnDList.js
CHANGED
|
@@ -46,7 +46,8 @@ export const DnDItemStyle = (index, isDragging, theme) => ({
|
|
|
46
46
|
*/
|
|
47
47
|
export function DnDList(props) {
|
|
48
48
|
// Destruct
|
|
49
|
-
const { height = 360, itemRenderer, labelField, mRef, sortingStrategy, onChange, onFormChange, onDragEnd } = props;
|
|
49
|
+
const { componentProps, height = 360, itemRenderer, labelField, mRef, sortingStrategy, onChange, onFormChange, onDragEnd } = props;
|
|
50
|
+
const Component = props.component || React.Fragment;
|
|
50
51
|
// Theme
|
|
51
52
|
const theme = useTheme();
|
|
52
53
|
// States
|
|
@@ -56,8 +57,10 @@ export function DnDList(props) {
|
|
|
56
57
|
setItems(props.items);
|
|
57
58
|
}, [props.items]);
|
|
58
59
|
const doFormChange = React.useCallback((newItems) => {
|
|
59
|
-
if (onFormChange)
|
|
60
|
-
|
|
60
|
+
if (onFormChange) {
|
|
61
|
+
const locals = Array.isArray(newItems) ? newItems : items;
|
|
62
|
+
onFormChange(locals);
|
|
63
|
+
}
|
|
61
64
|
}, [items, onFormChange]);
|
|
62
65
|
const changeItems = React.useCallback((newItems) => {
|
|
63
66
|
// Possible to alter items with the handler
|
|
@@ -144,38 +147,27 @@ export function DnDList(props) {
|
|
|
144
147
|
]);
|
|
145
148
|
});
|
|
146
149
|
}, []);
|
|
147
|
-
const doChange = React.useCallback(() => doFormChange(), []);
|
|
148
150
|
const setupDiv = (div, clearup = false) => {
|
|
149
151
|
// Inputs
|
|
150
152
|
div
|
|
151
153
|
.querySelectorAll("input")
|
|
152
154
|
.forEach((input) => clearup
|
|
153
|
-
? input.removeEventListener("change",
|
|
154
|
-
: input.addEventListener("change",
|
|
155
|
+
? input.removeEventListener("change", doFormChange)
|
|
156
|
+
: input.addEventListener("change", doFormChange));
|
|
155
157
|
// Textareas
|
|
156
158
|
div
|
|
157
159
|
.querySelectorAll("textarea")
|
|
158
160
|
.forEach((input) => clearup
|
|
159
|
-
? input.removeEventListener("change",
|
|
160
|
-
: input.addEventListener("change",
|
|
161
|
+
? input.removeEventListener("change", doFormChange)
|
|
162
|
+
: input.addEventListener("change", doFormChange));
|
|
161
163
|
// Select
|
|
162
164
|
div
|
|
163
165
|
.querySelectorAll("select")
|
|
164
166
|
.forEach((input) => clearup
|
|
165
|
-
? input.removeEventListener("change",
|
|
166
|
-
: input.addEventListener("change",
|
|
167
|
+
? input.removeEventListener("change", doFormChange)
|
|
168
|
+
: input.addEventListener("change", doFormChange));
|
|
167
169
|
};
|
|
168
|
-
const divRef = React.useRef(
|
|
169
|
-
React.useEffect(() => {
|
|
170
|
-
if (divRef.current) {
|
|
171
|
-
setupDiv(divRef.current);
|
|
172
|
-
}
|
|
173
|
-
return () => {
|
|
174
|
-
if (divRef.current) {
|
|
175
|
-
setupDiv(divRef.current, true);
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
}, []);
|
|
170
|
+
const divRef = React.useRef();
|
|
179
171
|
if (dnd == null) {
|
|
180
172
|
return _jsx(Skeleton, { variant: "rectangular", width: "100%", height: height });
|
|
181
173
|
}
|
|
@@ -219,12 +211,20 @@ export function DnDList(props) {
|
|
|
219
211
|
}
|
|
220
212
|
setActiveId(undefined);
|
|
221
213
|
}
|
|
222
|
-
const children = (_jsx(DndContextType, { onDragStart: handleDragStart, onDragEnd: handleDragEnd, children: _jsx(SortableContextType, { items: items, strategy: strategy, children: items.map((item, index) => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
214
|
+
const children = (_jsx(DndContextType, { onDragStart: handleDragStart, onDragEnd: handleDragEnd, children: _jsx(SortableContextType, { items: items, strategy: strategy, children: _jsx(Component, { ...componentProps, children: items.map((item, index) => {
|
|
215
|
+
const id = item.id;
|
|
216
|
+
return (_jsx(SortableItem, { id: id, useSortableType: useSortableType, CSSType: CSSType, style: getItemStyle(index, id === activeId), itemRenderer: (nodeRef, actionNodeRef) => itemRenderer(item, index, nodeRef, actionNodeRef) }, id));
|
|
217
|
+
}) }) }) }));
|
|
226
218
|
if (onFormChange) {
|
|
227
|
-
return (_jsx("div", { style: { width: "100%" }, ref:
|
|
219
|
+
return (_jsx("div", { style: { width: "100%" }, ref: (div) => {
|
|
220
|
+
if (div && divRef.current != div) {
|
|
221
|
+
if (divRef.current) {
|
|
222
|
+
setupDiv(divRef.current, true);
|
|
223
|
+
}
|
|
224
|
+
divRef.current = div;
|
|
225
|
+
setupDiv(div);
|
|
226
|
+
}
|
|
227
|
+
}, children: children }));
|
|
228
228
|
}
|
|
229
229
|
return children;
|
|
230
230
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/materialui",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.62",
|
|
4
4
|
"description": "TypeScript Material-UI Implementation",
|
|
5
5
|
"main": "lib/cjs/index.js",
|
|
6
6
|
"module": "lib/mjs/index.js",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"@dnd-kit/sortable": "^10.0.0",
|
|
41
41
|
"@emotion/react": "^11.14.0",
|
|
42
42
|
"@emotion/styled": "^11.14.0",
|
|
43
|
-
"@etsoo/appscript": "^1.6.
|
|
44
|
-
"@etsoo/notificationbase": "^1.1.
|
|
45
|
-
"@etsoo/react": "^1.8.
|
|
46
|
-
"@etsoo/shared": "^1.2.
|
|
43
|
+
"@etsoo/appscript": "^1.6.39",
|
|
44
|
+
"@etsoo/notificationbase": "^1.1.63",
|
|
45
|
+
"@etsoo/react": "^1.8.49",
|
|
46
|
+
"@etsoo/shared": "^1.2.75",
|
|
47
47
|
"@mui/icons-material": "^7.1.1",
|
|
48
48
|
"@mui/material": "^7.1.1",
|
|
49
49
|
"@mui/x-data-grid": "^8.5.1",
|
|
@@ -81,9 +81,9 @@
|
|
|
81
81
|
"@types/react-dom": "^18.3.7",
|
|
82
82
|
"@types/react-input-mask": "^3.0.6",
|
|
83
83
|
"@types/react-window": "^1.8.8",
|
|
84
|
-
"@vitejs/plugin-react": "^4.5.
|
|
84
|
+
"@vitejs/plugin-react": "^4.5.2",
|
|
85
85
|
"jsdom": "^26.1.0",
|
|
86
86
|
"typescript": "^5.8.3",
|
|
87
|
-
"vitest": "^3.2.
|
|
87
|
+
"vitest": "^3.2.3"
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -10,10 +10,10 @@ import IconButton from "@mui/material/IconButton";
|
|
|
10
10
|
import FormControlLabel from "@mui/material/FormControlLabel";
|
|
11
11
|
import Checkbox from "@mui/material/Checkbox";
|
|
12
12
|
import TextField from "@mui/material/TextField";
|
|
13
|
-
import { DnDList, DnDListRef } from "./DnDList";
|
|
14
13
|
import { HBox, VBox } from "./FlexBox";
|
|
15
14
|
import { useRequiredAppContext } from "./app/ReactApp";
|
|
16
15
|
import FormLabel from "@mui/material/FormLabel";
|
|
16
|
+
import { DnDList, DnDListRef } from "./DnDList";
|
|
17
17
|
|
|
18
18
|
type DnDItemType = {
|
|
19
19
|
id: IdType;
|
|
@@ -152,56 +152,56 @@ function ButtonPopupList<D extends DnDItemType>(
|
|
|
152
152
|
|
|
153
153
|
return (
|
|
154
154
|
<VBox gap={2}>
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
155
|
+
<DnDList<D>
|
|
156
|
+
component={Grid}
|
|
157
|
+
componentProps={{ container: true, spacing: 0 }}
|
|
158
|
+
items={items}
|
|
159
|
+
labelField={labelField}
|
|
160
|
+
onFormChange={(items) => {
|
|
161
|
+
const ids = items
|
|
162
|
+
.filter((item) => selectedIds.includes(item.id))
|
|
163
|
+
.map((item) => item.id);
|
|
164
|
+
onValueChange(ids);
|
|
165
|
+
}}
|
|
166
|
+
itemRenderer={(item, index, nodeRef, actionNodeRef) => (
|
|
167
|
+
<Grid
|
|
168
|
+
size={{ xs: 12, md: 6, lg: 4 }}
|
|
169
|
+
display="flex"
|
|
170
|
+
justifyContent="flex-start"
|
|
171
|
+
alignItems="center"
|
|
172
|
+
gap={1}
|
|
173
|
+
{...nodeRef}
|
|
174
|
+
>
|
|
175
|
+
<IconButton
|
|
176
|
+
style={{ cursor: "move" }}
|
|
177
|
+
size="small"
|
|
178
|
+
title={labels?.dragIndicator}
|
|
179
|
+
{...actionNodeRef}
|
|
173
180
|
>
|
|
174
|
-
<
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
/>
|
|
199
|
-
</Grid>
|
|
200
|
-
)}
|
|
201
|
-
height={200}
|
|
202
|
-
mRef={dndRef}
|
|
203
|
-
></DnDList>
|
|
204
|
-
</Grid>
|
|
181
|
+
<DragIndicatorIcon />
|
|
182
|
+
</IconButton>
|
|
183
|
+
<FormControlLabel
|
|
184
|
+
control={
|
|
185
|
+
<Checkbox
|
|
186
|
+
name="item"
|
|
187
|
+
value={item.id}
|
|
188
|
+
checked={selectedIds.includes(item.id)}
|
|
189
|
+
onChange={(e) => {
|
|
190
|
+
const checked = e.target.checked;
|
|
191
|
+
const newIds = [
|
|
192
|
+
...selectedIds.toggleItem(item.id, checked)
|
|
193
|
+
];
|
|
194
|
+
setSelectedIds(newIds);
|
|
195
|
+
}}
|
|
196
|
+
/>
|
|
197
|
+
}
|
|
198
|
+
label={`${index + 1}. ${labelFormatter(item)}`}
|
|
199
|
+
/>
|
|
200
|
+
</Grid>
|
|
201
|
+
)}
|
|
202
|
+
height={200}
|
|
203
|
+
mRef={dndRef}
|
|
204
|
+
></DnDList>
|
|
205
205
|
{onAdd && (
|
|
206
206
|
<HBox gap={1}>
|
|
207
207
|
<TextField
|
package/src/DnDList.tsx
CHANGED
|
@@ -119,7 +119,21 @@ export interface DnDListRef<D extends object> {
|
|
|
119
119
|
/**
|
|
120
120
|
* DnD sortable list properties
|
|
121
121
|
*/
|
|
122
|
-
export interface DnDListPros<
|
|
122
|
+
export interface DnDListPros<
|
|
123
|
+
D extends { id: UniqueIdentifier },
|
|
124
|
+
E extends React.ElementType = React.ElementType
|
|
125
|
+
> {
|
|
126
|
+
/**
|
|
127
|
+
* Component type to render the list into
|
|
128
|
+
* Default is React.Fragment
|
|
129
|
+
*/
|
|
130
|
+
component?: E;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Component props
|
|
134
|
+
*/
|
|
135
|
+
componentProps?: React.ComponentProps<E>;
|
|
136
|
+
|
|
123
137
|
/**
|
|
124
138
|
* Get list item style callback
|
|
125
139
|
*/
|
|
@@ -186,11 +200,13 @@ export interface DnDListPros<D extends { id: UniqueIdentifier }> {
|
|
|
186
200
|
* @param props Props
|
|
187
201
|
* @returns Component
|
|
188
202
|
*/
|
|
189
|
-
export function DnDList<
|
|
190
|
-
|
|
191
|
-
|
|
203
|
+
export function DnDList<
|
|
204
|
+
D extends { id: UniqueIdentifier },
|
|
205
|
+
E extends React.ElementType = React.ElementType
|
|
206
|
+
>(props: DnDListPros<D, E>) {
|
|
192
207
|
// Destruct
|
|
193
208
|
const {
|
|
209
|
+
componentProps,
|
|
194
210
|
height = 360,
|
|
195
211
|
itemRenderer,
|
|
196
212
|
labelField,
|
|
@@ -201,6 +217,8 @@ export function DnDList<D extends { id: UniqueIdentifier }>(
|
|
|
201
217
|
onDragEnd
|
|
202
218
|
} = props;
|
|
203
219
|
|
|
220
|
+
const Component = props.component || React.Fragment;
|
|
221
|
+
|
|
204
222
|
// Theme
|
|
205
223
|
const theme = useTheme();
|
|
206
224
|
|
|
@@ -213,8 +231,11 @@ export function DnDList<D extends { id: UniqueIdentifier }>(
|
|
|
213
231
|
}, [props.items]);
|
|
214
232
|
|
|
215
233
|
const doFormChange = React.useCallback(
|
|
216
|
-
(newItems?: D[]) => {
|
|
217
|
-
if (onFormChange)
|
|
234
|
+
(newItems?: D[] | Event) => {
|
|
235
|
+
if (onFormChange) {
|
|
236
|
+
const locals = Array.isArray(newItems) ? newItems : items;
|
|
237
|
+
onFormChange(locals);
|
|
238
|
+
}
|
|
218
239
|
},
|
|
219
240
|
[items, onFormChange]
|
|
220
241
|
);
|
|
@@ -362,16 +383,14 @@ export function DnDList<D extends { id: UniqueIdentifier }>(
|
|
|
362
383
|
);
|
|
363
384
|
}, []);
|
|
364
385
|
|
|
365
|
-
const doChange = React.useCallback(() => doFormChange(), []);
|
|
366
|
-
|
|
367
386
|
const setupDiv = (div: HTMLDivElement, clearup: boolean = false) => {
|
|
368
387
|
// Inputs
|
|
369
388
|
div
|
|
370
389
|
.querySelectorAll("input")
|
|
371
390
|
.forEach((input) =>
|
|
372
391
|
clearup
|
|
373
|
-
? input.removeEventListener("change",
|
|
374
|
-
: input.addEventListener("change",
|
|
392
|
+
? input.removeEventListener("change", doFormChange)
|
|
393
|
+
: input.addEventListener("change", doFormChange)
|
|
375
394
|
);
|
|
376
395
|
|
|
377
396
|
// Textareas
|
|
@@ -379,8 +398,8 @@ export function DnDList<D extends { id: UniqueIdentifier }>(
|
|
|
379
398
|
.querySelectorAll("textarea")
|
|
380
399
|
.forEach((input) =>
|
|
381
400
|
clearup
|
|
382
|
-
? input.removeEventListener("change",
|
|
383
|
-
: input.addEventListener("change",
|
|
401
|
+
? input.removeEventListener("change", doFormChange)
|
|
402
|
+
: input.addEventListener("change", doFormChange)
|
|
384
403
|
);
|
|
385
404
|
|
|
386
405
|
// Select
|
|
@@ -388,24 +407,12 @@ export function DnDList<D extends { id: UniqueIdentifier }>(
|
|
|
388
407
|
.querySelectorAll("select")
|
|
389
408
|
.forEach((input) =>
|
|
390
409
|
clearup
|
|
391
|
-
? input.removeEventListener("change",
|
|
392
|
-
: input.addEventListener("change",
|
|
410
|
+
? input.removeEventListener("change", doFormChange)
|
|
411
|
+
: input.addEventListener("change", doFormChange)
|
|
393
412
|
);
|
|
394
413
|
};
|
|
395
414
|
|
|
396
|
-
const divRef = React.useRef<HTMLDivElement>(
|
|
397
|
-
|
|
398
|
-
React.useEffect(() => {
|
|
399
|
-
if (divRef.current) {
|
|
400
|
-
setupDiv(divRef.current);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return () => {
|
|
404
|
-
if (divRef.current) {
|
|
405
|
-
setupDiv(divRef.current, true);
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
}, []);
|
|
415
|
+
const divRef = React.useRef<HTMLDivElement>();
|
|
409
416
|
|
|
410
417
|
if (dnd == null) {
|
|
411
418
|
return <Skeleton variant="rectangular" width="100%" height={height} />;
|
|
@@ -476,28 +483,42 @@ export function DnDList<D extends { id: UniqueIdentifier }>(
|
|
|
476
483
|
const children = (
|
|
477
484
|
<DndContextType onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
|
478
485
|
<SortableContextType items={items} strategy={strategy}>
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
itemRenderer(
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
486
|
+
<Component {...componentProps}>
|
|
487
|
+
{items.map((item, index) => {
|
|
488
|
+
const id = item.id;
|
|
489
|
+
return (
|
|
490
|
+
<SortableItem
|
|
491
|
+
id={id}
|
|
492
|
+
useSortableType={useSortableType}
|
|
493
|
+
CSSType={CSSType}
|
|
494
|
+
key={id}
|
|
495
|
+
style={getItemStyle!(index, id === activeId)}
|
|
496
|
+
itemRenderer={(nodeRef, actionNodeRef) =>
|
|
497
|
+
itemRenderer(item, index, nodeRef, actionNodeRef)
|
|
498
|
+
}
|
|
499
|
+
/>
|
|
500
|
+
);
|
|
501
|
+
})}
|
|
502
|
+
</Component>
|
|
494
503
|
</SortableContextType>
|
|
495
504
|
</DndContextType>
|
|
496
505
|
);
|
|
497
506
|
|
|
498
507
|
if (onFormChange) {
|
|
499
508
|
return (
|
|
500
|
-
<div
|
|
509
|
+
<div
|
|
510
|
+
style={{ width: "100%" }}
|
|
511
|
+
ref={(div) => {
|
|
512
|
+
if (div && divRef.current != div) {
|
|
513
|
+
if (divRef.current) {
|
|
514
|
+
setupDiv(divRef.current, true);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
divRef.current = div;
|
|
518
|
+
setupDiv(div);
|
|
519
|
+
}
|
|
520
|
+
}}
|
|
521
|
+
>
|
|
501
522
|
{children}
|
|
502
523
|
</div>
|
|
503
524
|
);
|