@rnaga/wp-next-ui 1.1.3 → 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 +15 -0
- package/SortableList.d.ts.map +1 -1
- package/SortableList.js +93 -37
- 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
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);
|
|
@@ -61,12 +66,18 @@ export const SortableList = (props) => {
|
|
|
61
66
|
const sortableItem = el.closest("[data-sortable-item-index]");
|
|
62
67
|
if (!sortableItem)
|
|
63
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
|
+
}
|
|
64
73
|
const itemIndex = sortableItem.getAttribute("data-sortable-item-index");
|
|
65
|
-
if (!itemIndex || itemIndex === String(index))
|
|
74
|
+
if (!itemIndex || itemIndex === String(index)) {
|
|
66
75
|
continue;
|
|
76
|
+
}
|
|
67
77
|
const itemIndexNumber = parseInt(itemIndex);
|
|
68
|
-
if (itemIndexNumber < 0 || itemIndexNumber >= itemsRef.current.length)
|
|
78
|
+
if (itemIndexNumber < 0 || itemIndexNumber >= itemsRef.current.length) {
|
|
69
79
|
continue;
|
|
80
|
+
}
|
|
70
81
|
foundTarget = sortableItem;
|
|
71
82
|
break;
|
|
72
83
|
}
|
|
@@ -77,6 +88,41 @@ export const SortableList = (props) => {
|
|
|
77
88
|
foundTarget.style.border = "1px solid red";
|
|
78
89
|
}
|
|
79
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
|
+
}
|
|
80
126
|
};
|
|
81
127
|
const handleMouseDown = (e) => {
|
|
82
128
|
const dragged = draggedItemRef.current;
|
|
@@ -103,16 +149,34 @@ export const SortableList = (props) => {
|
|
|
103
149
|
if (targetRef.current) {
|
|
104
150
|
const toItemIndex = targetRef.current.getAttribute("data-sortable-item-index");
|
|
105
151
|
swapItems(e, index, toItemIndex ? parseInt(toItemIndex) : -1);
|
|
106
|
-
targetRef.current
|
|
152
|
+
targetRef.current.style.removeProperty("border");
|
|
107
153
|
targetRef.current = null;
|
|
108
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
|
+
}
|
|
109
173
|
draggedItemRef.current = null;
|
|
110
174
|
};
|
|
111
175
|
const { initMouseMove } = useMouseMove({
|
|
112
176
|
onDeltaChange: handleDeltaChange,
|
|
113
177
|
onMouseUp: handleMouseUp,
|
|
114
178
|
onMouseDown: handleMouseDown,
|
|
115
|
-
cursor: "grabbing",
|
|
179
|
+
cursor: cursor?.dragging ?? "grabbing",
|
|
116
180
|
threshold: 1,
|
|
117
181
|
});
|
|
118
182
|
useEffect(() => {
|
|
@@ -124,26 +188,20 @@ export const SortableList = (props) => {
|
|
|
124
188
|
}));
|
|
125
189
|
setItems(initialItems);
|
|
126
190
|
}, [props.enum]);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
display: "flex",
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
sx = {
|
|
142
|
-
display: "grid",
|
|
143
|
-
gridTemplateColumns: "repeat(auto-fill, minmax(100px, 1fr))",
|
|
144
|
-
gap: 1,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
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]);
|
|
147
205
|
const handleItemMouseDown = (item, e) => {
|
|
148
206
|
// Find the MuiListItem parent element
|
|
149
207
|
const listItemElement = e.target.closest("[data-sortable-item-index]");
|
|
@@ -153,9 +211,6 @@ export const SortableList = (props) => {
|
|
|
153
211
|
initMouseMove(listItemElement)(e);
|
|
154
212
|
}
|
|
155
213
|
};
|
|
156
|
-
const renderSortableItem = (item) => {
|
|
157
|
-
return renderItem ? renderItem(item) : item.label;
|
|
158
|
-
};
|
|
159
214
|
return (_jsx(SortableContext.Provider, { value: {
|
|
160
215
|
items,
|
|
161
216
|
findItem: findItem,
|
|
@@ -163,11 +218,12 @@ export const SortableList = (props) => {
|
|
|
163
218
|
refPos,
|
|
164
219
|
targetRef,
|
|
165
220
|
displayType,
|
|
166
|
-
}, children: _jsx(ListBase, { items: items, size: size, sx: { ...sx, ...props.sx }, displayType: displayType, renderItem:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
+
} }) }) }));
|
|
173
229
|
};
|