@rnaga/wp-next-ui 1.1.3 → 1.1.5
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 +108 -46
- 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,4CAyUA,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,19 +88,50 @@ 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
|
-
const handleMouseDown = (
|
|
127
|
+
const handleMouseDown = (_e) => {
|
|
82
128
|
const dragged = draggedItemRef.current;
|
|
83
129
|
if (!dragged)
|
|
84
130
|
return;
|
|
85
|
-
const { element
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
refPos.current.x = 0;
|
|
90
|
-
element.style.zIndex = "100000";
|
|
91
|
-
element.style.transform = `translate(${refPos.current.x}px, ${refPos.current.y}px)`;
|
|
92
|
-
}
|
|
131
|
+
const { element } = dragged;
|
|
132
|
+
refPos.current.y = 0;
|
|
133
|
+
refPos.current.x = 0;
|
|
134
|
+
element.style.zIndex = "100000";
|
|
93
135
|
};
|
|
94
136
|
const handleMouseUp = (e) => {
|
|
95
137
|
const dragged = draggedItemRef.current;
|
|
@@ -103,16 +145,44 @@ export const SortableList = (props) => {
|
|
|
103
145
|
if (targetRef.current) {
|
|
104
146
|
const toItemIndex = targetRef.current.getAttribute("data-sortable-item-index");
|
|
105
147
|
swapItems(e, index, toItemIndex ? parseInt(toItemIndex) : -1);
|
|
106
|
-
targetRef.current
|
|
148
|
+
targetRef.current.style.removeProperty("border");
|
|
107
149
|
targetRef.current = null;
|
|
108
150
|
}
|
|
151
|
+
else if (dropTargetRef.current) {
|
|
152
|
+
// Dropped onto an external drop target or foreign SortableList drop zone
|
|
153
|
+
const targetId = dropTargetRef.current.getAttribute("data-drop-target-id") ??
|
|
154
|
+
dropTargetRef.current.getAttribute("data-sortable-drop-zone-id");
|
|
155
|
+
const droppedItem = itemsRef.current.find((i) => i.index === index);
|
|
156
|
+
if (droppedItem && targetId) {
|
|
157
|
+
onDropToTarget?.(droppedItem, targetId);
|
|
158
|
+
}
|
|
159
|
+
onDropTargetLeave?.(targetId ?? "");
|
|
160
|
+
dropTargetRef.current = null;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// Check if the drop point is still within this list's own drop zone.
|
|
164
|
+
// If so, the item was dropped on empty space inside the same list — no-op.
|
|
165
|
+
const elementsAtPoint = document.elementsFromPoint(e.clientX, e.clientY);
|
|
166
|
+
const isInsideOwnDropZone = elementsAtPoint.some((el) => {
|
|
167
|
+
const dropZone = el.closest("[data-sortable-drop-zone-id]");
|
|
168
|
+
return (dropZone &&
|
|
169
|
+
dropZone.getAttribute("data-sortable-drop-zone-id") === listId);
|
|
170
|
+
});
|
|
171
|
+
if (!isInsideOwnDropZone) {
|
|
172
|
+
// Dropped outside any list or target — fire onDrop
|
|
173
|
+
const droppedItem = itemsRef.current.find((i) => i.index === index);
|
|
174
|
+
if (droppedItem) {
|
|
175
|
+
onDrop?.(droppedItem);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
109
179
|
draggedItemRef.current = null;
|
|
110
180
|
};
|
|
111
181
|
const { initMouseMove } = useMouseMove({
|
|
112
182
|
onDeltaChange: handleDeltaChange,
|
|
113
183
|
onMouseUp: handleMouseUp,
|
|
114
184
|
onMouseDown: handleMouseDown,
|
|
115
|
-
cursor: "grabbing",
|
|
185
|
+
cursor: cursor?.dragging ?? "grabbing",
|
|
116
186
|
threshold: 1,
|
|
117
187
|
});
|
|
118
188
|
useEffect(() => {
|
|
@@ -124,26 +194,20 @@ export const SortableList = (props) => {
|
|
|
124
194
|
}));
|
|
125
195
|
setItems(initialItems);
|
|
126
196
|
}, [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
|
-
}
|
|
197
|
+
const sx = useMemo(() => {
|
|
198
|
+
if (props.displayType === "horizontal" ||
|
|
199
|
+
props.displayType === "horizontal-fit") {
|
|
200
|
+
return { display: "flex", flexDirection: "row" };
|
|
201
|
+
}
|
|
202
|
+
if (props.displayType === "grid") {
|
|
203
|
+
return {
|
|
204
|
+
display: "grid",
|
|
205
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(100px, 1fr))",
|
|
206
|
+
gap: 1,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return {};
|
|
210
|
+
}, [props.displayType]);
|
|
147
211
|
const handleItemMouseDown = (item, e) => {
|
|
148
212
|
// Find the MuiListItem parent element
|
|
149
213
|
const listItemElement = e.target.closest("[data-sortable-item-index]");
|
|
@@ -153,9 +217,6 @@ export const SortableList = (props) => {
|
|
|
153
217
|
initMouseMove(listItemElement)(e);
|
|
154
218
|
}
|
|
155
219
|
};
|
|
156
|
-
const renderSortableItem = (item) => {
|
|
157
|
-
return renderItem ? renderItem(item) : item.label;
|
|
158
|
-
};
|
|
159
220
|
return (_jsx(SortableContext.Provider, { value: {
|
|
160
221
|
items,
|
|
161
222
|
findItem: findItem,
|
|
@@ -163,11 +224,12 @@ export const SortableList = (props) => {
|
|
|
163
224
|
refPos,
|
|
164
225
|
targetRef,
|
|
165
226
|
displayType,
|
|
166
|
-
}, children: _jsx(ListBase, { items: items, size: size, sx: { ...sx, ...props.sx }, displayType: displayType, renderItem:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
227
|
+
}, 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) => ({
|
|
228
|
+
"data-sortable-item-index": item.index,
|
|
229
|
+
"data-sortable-list-id": listId,
|
|
230
|
+
}), slotSxProps: {
|
|
231
|
+
listItem: {
|
|
232
|
+
cursor: cursor?.idle ?? "move",
|
|
233
|
+
},
|
|
234
|
+
} }) }) }));
|
|
173
235
|
};
|