@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 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
@@ -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,EAKJ,MAAM,OAAO,CAAC;AAGf,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,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;CACX,4CA8OA,CAAC"}
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 = (e) => {
127
+ const handleMouseDown = (_e) => {
82
128
  const dragged = draggedItemRef.current;
83
129
  if (!dragged)
84
130
  return;
85
- const { element, index } = dragged;
86
- const rect = element.getBoundingClientRect();
87
- if (rect) {
88
- refPos.current.y = e.clientY - rect.top - rect.height / 2;
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?.style.removeProperty("border");
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
- let sx = {};
128
- if (props.displayType === "horizontal") {
129
- sx = {
130
- display: "flex",
131
- flexDirection: "row",
132
- };
133
- }
134
- else if (props.displayType === "horizontal-fit") {
135
- sx = {
136
- display: "flex",
137
- flexDirection: "row",
138
- };
139
- }
140
- else if (props.displayType === "grid") {
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: renderSortableItem, onDelete: onDelete, onEdit: onEdit, onMouseDown: handleItemMouseDown, getItemDataAttributes: (item) => ({
167
- "data-sortable-item-index": item.index,
168
- }), slotSxProps: {
169
- listItem: {
170
- cursor: "move",
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rnaga/wp-next-ui",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "scripts": {
5
5
  "build": "node ../../scripts/build-ui.mjs",
6
6
  "release-old": "node ../../scripts/release.mjs",