@rnaga/wp-next-ui 1.1.2 → 1.1.4
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/SortableList.d.ts +16 -1
- package/SortableList.d.ts.map +1 -1
- package/SortableList.js +94 -40
- package/package.json +1 -1
package/SortableList.d.ts
CHANGED
|
@@ -15,10 +15,25 @@ export declare const SortableList: <T extends any = string>(props: {
|
|
|
15
15
|
label?: SxProps;
|
|
16
16
|
};
|
|
17
17
|
displayType: DisplayType;
|
|
18
|
+
/**
|
|
19
|
+
* Unique ID for this list. Used as a `data-drop-target-id` on the wrapper Box
|
|
20
|
+
* so that items dragged from another SortableList can be dropped here via
|
|
21
|
+
* the dragging list's `onDropToTarget` callback.
|
|
22
|
+
* Also used to prevent cross-list item swapping.
|
|
23
|
+
*/
|
|
24
|
+
dropZoneId?: string;
|
|
18
25
|
renderItem?: (item: SortableListItemType<T>) => JSX.Element;
|
|
19
26
|
onDelete?: (index: number) => void;
|
|
20
27
|
onEdit?: (index: number) => void;
|
|
21
|
-
onChange?: (items: SortableListItemType<T>[]) => void;
|
|
28
|
+
onChange?: (items: SortableListItemType<T>[], fromIndex: number, toIndex: number) => void;
|
|
29
|
+
onDrop?: (item: SortableListItemType<T>) => void;
|
|
30
|
+
onDropToTarget?: (item: SortableListItemType<T>, targetId: string) => void;
|
|
31
|
+
onDropTargetEnter?: (targetId: string) => void;
|
|
32
|
+
onDropTargetLeave?: (targetId: string) => void;
|
|
33
|
+
cursor?: {
|
|
34
|
+
idle?: string;
|
|
35
|
+
dragging?: string;
|
|
36
|
+
};
|
|
22
37
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
23
38
|
export {};
|
|
24
39
|
//# sourceMappingURL=SortableList.d.ts.map
|
package/SortableList.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SortableList.d.ts","sourceRoot":"","sources":["../src/SortableList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,EAEL,GAAG,
|
|
1
|
+
{"version":3,"file":"SortableList.d.ts","sourceRoot":"","sources":["../src/SortableList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,EAEL,GAAG,EAOJ,MAAM,OAAO,CAAC;AAIf,MAAM,MAAM,oBAAoB,CAAC,CAAC,GAAG,MAAM,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;AAE/D,KAAK,WAAW,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,GAAG,gBAAgB,CAAC;AAczE,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;AAEnE,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,GAAG,GAAG,MAAM,EAAE,OAAO;IAC1D,IAAI,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACpC,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC1B,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,WAAW,CAAC,EAAE;QACZ,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IACF,WAAW,EAAE,WAAW,CAAC;IACzB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC;IAC5D,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,oBAAoB,CAAC,CAAC,CAAC,EAAE,EAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,KACZ,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IACjD,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3E,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C,4CA8TA,CAAC"}
|
package/SortableList.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useMouseMove } from "./hooks/use-mouse-move";
|
|
3
|
-
import { createContext, useEffect, useRef, useState, } from "react";
|
|
3
|
+
import { createContext, useEffect, useId, useMemo, useRef, useState, } from "react";
|
|
4
|
+
import { Box } from "@mui/material";
|
|
4
5
|
import { ListBase } from "./ListBase";
|
|
5
6
|
const SortableContext = createContext({});
|
|
6
7
|
export const SortableList = (props) => {
|
|
7
|
-
const { size = "small", displayType = "vertical", renderItem, onDelete, onChange, onEdit, } = props;
|
|
8
|
+
const { size = "small", displayType = "vertical", renderItem, onDelete, onChange, onEdit, onDrop, onDropToTarget, onDropTargetEnter, onDropTargetLeave, cursor, dropZoneId, } = props;
|
|
9
|
+
// Stable unique ID for this list instance (used to tag items)
|
|
10
|
+
const generatedId = useId();
|
|
11
|
+
const listId = dropZoneId ?? generatedId;
|
|
8
12
|
const [items, setItems] = useState([]);
|
|
9
13
|
const targetRef = useRef(null);
|
|
14
|
+
const dropTargetRef = useRef(null);
|
|
10
15
|
const refPos = useRef({ x: 0, y: 0 });
|
|
11
16
|
const itemRefs = useRef(new Map());
|
|
12
17
|
const draggedItemRef = useRef(null);
|
|
@@ -16,7 +21,6 @@ export const SortableList = (props) => {
|
|
|
16
21
|
return items.find((item) => item.index === index);
|
|
17
22
|
};
|
|
18
23
|
const swapItems = (_e, fromIndex, toIndex) => {
|
|
19
|
-
console.log("Swapping items", fromIndex, toIndex);
|
|
20
24
|
const currentItems = itemsRef.current;
|
|
21
25
|
if (fromIndex < 0 ||
|
|
22
26
|
toIndex < 0 ||
|
|
@@ -31,7 +35,7 @@ export const SortableList = (props) => {
|
|
|
31
35
|
newItems[fromIndex].index = fromIndex;
|
|
32
36
|
newItems[toIndex].index = toIndex;
|
|
33
37
|
setItems(newItems);
|
|
34
|
-
onChange?.(newItems);
|
|
38
|
+
onChange?.(newItems, fromIndex, toIndex);
|
|
35
39
|
};
|
|
36
40
|
const handleDeltaChange = (e, delta) => {
|
|
37
41
|
const dragged = draggedItemRef.current;
|
|
@@ -62,12 +66,18 @@ export const SortableList = (props) => {
|
|
|
62
66
|
const sortableItem = el.closest("[data-sortable-item-index]");
|
|
63
67
|
if (!sortableItem)
|
|
64
68
|
continue;
|
|
69
|
+
// Only accept items that belong to THIS list (same listId)
|
|
70
|
+
if (sortableItem.getAttribute("data-sortable-list-id") !== listId) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
65
73
|
const itemIndex = sortableItem.getAttribute("data-sortable-item-index");
|
|
66
|
-
if (!itemIndex || itemIndex === String(index))
|
|
74
|
+
if (!itemIndex || itemIndex === String(index)) {
|
|
67
75
|
continue;
|
|
76
|
+
}
|
|
68
77
|
const itemIndexNumber = parseInt(itemIndex);
|
|
69
|
-
if (itemIndexNumber < 0 || itemIndexNumber >= itemsRef.current.length)
|
|
78
|
+
if (itemIndexNumber < 0 || itemIndexNumber >= itemsRef.current.length) {
|
|
70
79
|
continue;
|
|
80
|
+
}
|
|
71
81
|
foundTarget = sortableItem;
|
|
72
82
|
break;
|
|
73
83
|
}
|
|
@@ -78,13 +88,47 @@ export const SortableList = (props) => {
|
|
|
78
88
|
foundTarget.style.border = "1px solid red";
|
|
79
89
|
}
|
|
80
90
|
targetRef.current = foundTarget;
|
|
91
|
+
// Check for external drop targets: both explicit [data-drop-target-id] elements
|
|
92
|
+
// and foreign SortableList drop zones ([data-sortable-drop-zone-id] that differ from this list)
|
|
93
|
+
{
|
|
94
|
+
let foundDropTarget = null;
|
|
95
|
+
if (!foundTarget) {
|
|
96
|
+
for (const el of elementsAtPoint) {
|
|
97
|
+
// Look for explicit external drop targets (e.g. collection headers)
|
|
98
|
+
const explicitTarget = el.closest("[data-drop-target-id]");
|
|
99
|
+
if (explicitTarget) {
|
|
100
|
+
foundDropTarget = explicitTarget;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
// Look for foreign SortableList drop zones (different list ID)
|
|
104
|
+
const dropZone = el.closest("[data-sortable-drop-zone-id]");
|
|
105
|
+
if (dropZone &&
|
|
106
|
+
dropZone.getAttribute("data-sortable-drop-zone-id") !== listId) {
|
|
107
|
+
foundDropTarget = dropZone;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (dropTargetRef.current && dropTargetRef.current !== foundDropTarget) {
|
|
113
|
+
const leavingId = dropTargetRef.current.getAttribute("data-drop-target-id") ??
|
|
114
|
+
dropTargetRef.current.getAttribute("data-sortable-drop-zone-id");
|
|
115
|
+
if (leavingId)
|
|
116
|
+
onDropTargetLeave?.(leavingId);
|
|
117
|
+
}
|
|
118
|
+
if (foundDropTarget && foundDropTarget !== dropTargetRef.current) {
|
|
119
|
+
const enteringId = foundDropTarget.getAttribute("data-drop-target-id") ??
|
|
120
|
+
foundDropTarget.getAttribute("data-sortable-drop-zone-id");
|
|
121
|
+
if (enteringId)
|
|
122
|
+
onDropTargetEnter?.(enteringId);
|
|
123
|
+
}
|
|
124
|
+
dropTargetRef.current = foundDropTarget;
|
|
125
|
+
}
|
|
81
126
|
};
|
|
82
127
|
const handleMouseDown = (e) => {
|
|
83
128
|
const dragged = draggedItemRef.current;
|
|
84
129
|
if (!dragged)
|
|
85
130
|
return;
|
|
86
131
|
const { element, index } = dragged;
|
|
87
|
-
console.log("Mouse down on item", index);
|
|
88
132
|
const rect = element.getBoundingClientRect();
|
|
89
133
|
if (rect) {
|
|
90
134
|
refPos.current.y = e.clientY - rect.top - rect.height / 2;
|
|
@@ -105,16 +149,34 @@ export const SortableList = (props) => {
|
|
|
105
149
|
if (targetRef.current) {
|
|
106
150
|
const toItemIndex = targetRef.current.getAttribute("data-sortable-item-index");
|
|
107
151
|
swapItems(e, index, toItemIndex ? parseInt(toItemIndex) : -1);
|
|
108
|
-
targetRef.current
|
|
152
|
+
targetRef.current.style.removeProperty("border");
|
|
109
153
|
targetRef.current = null;
|
|
110
154
|
}
|
|
155
|
+
else if (dropTargetRef.current) {
|
|
156
|
+
// Dropped onto an external drop target or foreign SortableList drop zone
|
|
157
|
+
const targetId = dropTargetRef.current.getAttribute("data-drop-target-id") ??
|
|
158
|
+
dropTargetRef.current.getAttribute("data-sortable-drop-zone-id");
|
|
159
|
+
const droppedItem = itemsRef.current.find((i) => i.index === index);
|
|
160
|
+
if (droppedItem && targetId) {
|
|
161
|
+
onDropToTarget?.(droppedItem, targetId);
|
|
162
|
+
}
|
|
163
|
+
onDropTargetLeave?.(targetId ?? "");
|
|
164
|
+
dropTargetRef.current = null;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
// Dropped outside any list or target — fire onDrop
|
|
168
|
+
const droppedItem = itemsRef.current.find((i) => i.index === index);
|
|
169
|
+
if (droppedItem) {
|
|
170
|
+
onDrop?.(droppedItem);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
111
173
|
draggedItemRef.current = null;
|
|
112
174
|
};
|
|
113
175
|
const { initMouseMove } = useMouseMove({
|
|
114
176
|
onDeltaChange: handleDeltaChange,
|
|
115
177
|
onMouseUp: handleMouseUp,
|
|
116
178
|
onMouseDown: handleMouseDown,
|
|
117
|
-
cursor: "grabbing",
|
|
179
|
+
cursor: cursor?.dragging ?? "grabbing",
|
|
118
180
|
threshold: 1,
|
|
119
181
|
});
|
|
120
182
|
useEffect(() => {
|
|
@@ -126,26 +188,20 @@ export const SortableList = (props) => {
|
|
|
126
188
|
}));
|
|
127
189
|
setItems(initialItems);
|
|
128
190
|
}, [props.enum]);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
display: "flex",
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
sx = {
|
|
144
|
-
display: "grid",
|
|
145
|
-
gridTemplateColumns: "repeat(auto-fill, minmax(100px, 1fr))",
|
|
146
|
-
gap: 1,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
191
|
+
const sx = useMemo(() => {
|
|
192
|
+
if (props.displayType === "horizontal" ||
|
|
193
|
+
props.displayType === "horizontal-fit") {
|
|
194
|
+
return { display: "flex", flexDirection: "row" };
|
|
195
|
+
}
|
|
196
|
+
if (props.displayType === "grid") {
|
|
197
|
+
return {
|
|
198
|
+
display: "grid",
|
|
199
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(100px, 1fr))",
|
|
200
|
+
gap: 1,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return {};
|
|
204
|
+
}, [props.displayType]);
|
|
149
205
|
const handleItemMouseDown = (item, e) => {
|
|
150
206
|
// Find the MuiListItem parent element
|
|
151
207
|
const listItemElement = e.target.closest("[data-sortable-item-index]");
|
|
@@ -155,9 +211,6 @@ export const SortableList = (props) => {
|
|
|
155
211
|
initMouseMove(listItemElement)(e);
|
|
156
212
|
}
|
|
157
213
|
};
|
|
158
|
-
const renderSortableItem = (item) => {
|
|
159
|
-
return renderItem ? renderItem(item) : item.label;
|
|
160
|
-
};
|
|
161
214
|
return (_jsx(SortableContext.Provider, { value: {
|
|
162
215
|
items,
|
|
163
216
|
findItem: findItem,
|
|
@@ -165,11 +218,12 @@ export const SortableList = (props) => {
|
|
|
165
218
|
refPos,
|
|
166
219
|
targetRef,
|
|
167
220
|
displayType,
|
|
168
|
-
}, children: _jsx(ListBase, { items: items, size: size, sx: { ...sx, ...props.sx }, displayType: displayType, renderItem:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
221
|
+
}, children: _jsx(Box, { "data-sortable-drop-zone-id": listId, children: _jsx(ListBase, { items: items, size: size, sx: { ...sx, ...props.sx }, displayType: displayType, renderItem: (item) => renderItem ? renderItem(item) : _jsx(_Fragment, { children: item.label }), onDelete: onDelete, onEdit: onEdit, onMouseDown: handleItemMouseDown, getItemDataAttributes: (item) => ({
|
|
222
|
+
"data-sortable-item-index": item.index,
|
|
223
|
+
"data-sortable-list-id": listId,
|
|
224
|
+
}), slotSxProps: {
|
|
225
|
+
listItem: {
|
|
226
|
+
cursor: cursor?.idle ?? "move",
|
|
227
|
+
},
|
|
228
|
+
} }) }) }));
|
|
175
229
|
};
|