@rnaga/wp-next-ui 1.0.5 → 1.0.7
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/DraggableBox.d.ts +6 -1
- package/DraggableBox.d.ts.map +1 -1
- package/DraggableBox.js +191 -31
- package/package.json +1 -1
package/DraggableBox.d.ts
CHANGED
|
@@ -10,13 +10,17 @@
|
|
|
10
10
|
* - size: Sets the Typography size ("small" | "medium").
|
|
11
11
|
* - targetRef: Reference to an element whose relative position determines the box's initial placement.
|
|
12
12
|
* - sx: Style overrides for the outer Box wrapping the content.
|
|
13
|
-
* - slotSxProps: Style overrides for the header and
|
|
13
|
+
* - slotSxProps: Style overrides for the header, title, and border (width, color).
|
|
14
14
|
*/
|
|
15
15
|
import React, { RefObject } from "react";
|
|
16
16
|
import { SxProps } from "@mui/material";
|
|
17
17
|
type SlotSxProps = {
|
|
18
18
|
header?: SxProps;
|
|
19
19
|
title?: SxProps;
|
|
20
|
+
border?: {
|
|
21
|
+
width?: number;
|
|
22
|
+
color?: string;
|
|
23
|
+
};
|
|
20
24
|
};
|
|
21
25
|
export declare const DraggableBox: (props: {
|
|
22
26
|
children: React.ReactNode;
|
|
@@ -34,6 +38,7 @@ export declare const DraggableBox: (props: {
|
|
|
34
38
|
slotSxProps?: SlotSxProps;
|
|
35
39
|
zIndexLayer?: number;
|
|
36
40
|
placement?: "target" | "left";
|
|
41
|
+
resizable?: boolean;
|
|
37
42
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
38
43
|
export {};
|
|
39
44
|
//# sourceMappingURL=DraggableBox.d.ts.map
|
package/DraggableBox.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DraggableBox.d.ts","sourceRoot":"","sources":["../src/DraggableBox.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,EACZ,SAAS,EAKV,MAAM,OAAO,CAAC;AACf,OAAO,
|
|
1
|
+
{"version":3,"file":"DraggableBox.d.ts","sourceRoot":"","sources":["../src/DraggableBox.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,EACZ,SAAS,EAKV,MAAM,OAAO,CAAC;AACf,OAAO,EAAmB,OAAO,EAAE,MAAM,eAAe,CAAC;AAOzD,KAAK,WAAW,GAAG;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,OAAO;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,CAAC,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC1B,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC1C,GAAG,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACpC,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,4CA0bA,CAAC"}
|
package/DraggableBox.js
CHANGED
|
@@ -11,16 +11,17 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
11
11
|
* - size: Sets the Typography size ("small" | "medium").
|
|
12
12
|
* - targetRef: Reference to an element whose relative position determines the box's initial placement.
|
|
13
13
|
* - sx: Style overrides for the outer Box wrapping the content.
|
|
14
|
-
* - slotSxProps: Style overrides for the header and
|
|
14
|
+
* - slotSxProps: Style overrides for the header, title, and border (width, color).
|
|
15
15
|
*/
|
|
16
16
|
import { useCallback, useEffect, useRef, useState, } from "react";
|
|
17
|
-
import { Box } from "@mui/material";
|
|
17
|
+
import { Box, IconButton } from "@mui/material";
|
|
18
|
+
import CloseIcon from "@mui/icons-material/Close";
|
|
18
19
|
import { Typography } from "./Typography";
|
|
19
20
|
import { Background } from "./Background";
|
|
20
21
|
import { Portal } from "./portal";
|
|
21
22
|
export const DraggableBox = (props) => {
|
|
22
23
|
// Destructure props
|
|
23
|
-
const { children, targetRef, ref: selfRef, title, size, onClose, open, sx, slotSxProps, offset: initialOffset = {}, zIndexLayer = 0, placement = "left", } = props;
|
|
24
|
+
const { children, targetRef, ref: selfRef, title, size, onClose, open, sx, slotSxProps, offset: initialOffset = {}, zIndexLayer = 0, placement = "left", resizable = false, } = props;
|
|
24
25
|
// Provide default offset values for the box
|
|
25
26
|
const { left = 0, top = 0 } = initialOffset;
|
|
26
27
|
// Reference to the draggable box DOM element
|
|
@@ -31,6 +32,17 @@ export const DraggableBox = (props) => {
|
|
|
31
32
|
const [isDragging, setIsDragging] = useState(false);
|
|
32
33
|
// Stores the mouse down offset within the box
|
|
33
34
|
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
|
35
|
+
// State to store the current size of the draggable box
|
|
36
|
+
const [boxSize, setBoxSize] = useState({ width: 0, height: 0 });
|
|
37
|
+
// Determines if the box is currently being resized and from which direction
|
|
38
|
+
const [isResizing, setIsResizing] = useState(null);
|
|
39
|
+
// Stores the initial mouse position when resizing starts
|
|
40
|
+
const [resizeStart, setResizeStart] = useState({
|
|
41
|
+
x: 0,
|
|
42
|
+
y: 0,
|
|
43
|
+
width: 0,
|
|
44
|
+
height: 0,
|
|
45
|
+
});
|
|
34
46
|
/**
|
|
35
47
|
* Ensures the new position is within the browser window bounds.
|
|
36
48
|
* Returns null if the position would place the box partially off-screen.
|
|
@@ -46,35 +58,51 @@ export const DraggableBox = (props) => {
|
|
|
46
58
|
};
|
|
47
59
|
/**
|
|
48
60
|
* Callback ref to get the DOM element for the draggable box.
|
|
49
|
-
*
|
|
61
|
+
* Positions the box relative to a target element if provided, or centers it on screen.
|
|
50
62
|
*/
|
|
51
63
|
const callbackRef = useCallback((ref) => {
|
|
52
|
-
if (!ref
|
|
64
|
+
if (!ref)
|
|
53
65
|
return;
|
|
54
66
|
boxRef.current = ref;
|
|
55
67
|
if (selfRef) {
|
|
56
68
|
// If a selfRef is provided, assign the boxRef to it
|
|
57
69
|
selfRef.current = ref;
|
|
58
70
|
}
|
|
59
|
-
// Calculate the referenced element’s position
|
|
60
|
-
const rect = targetRef.current.getBoundingClientRect();
|
|
61
71
|
const boxWidth = boxRef.current.offsetWidth || 0;
|
|
62
72
|
const boxHeight = boxRef.current.offsetHeight || 0;
|
|
63
|
-
|
|
64
|
-
let
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
let newX;
|
|
74
|
+
let newY;
|
|
75
|
+
if (targetRef?.current) {
|
|
76
|
+
// Calculate the referenced element's position
|
|
77
|
+
const rect = targetRef.current.getBoundingClientRect();
|
|
78
|
+
// Compute initial position based on target element
|
|
79
|
+
newX = rect.left + left - boxWidth;
|
|
80
|
+
newY = rect.bottom - boxHeight / 2 + top;
|
|
81
|
+
// if placement is "target", x and y are relative to the target element
|
|
82
|
+
if (placement === "target") {
|
|
83
|
+
newX = rect.left;
|
|
84
|
+
newY = rect.top;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// Center the box on screen if no targetRef is provided
|
|
89
|
+
newX = (window.innerWidth - boxWidth) / 2 + left;
|
|
90
|
+
newY = (window.innerHeight - boxHeight) / 2 + top;
|
|
70
91
|
}
|
|
71
92
|
// If the box goes off-screen at the bottom, adjust upward
|
|
72
93
|
if (newY + boxHeight > window.innerHeight) {
|
|
73
94
|
newY = window.innerHeight - boxHeight;
|
|
74
95
|
}
|
|
96
|
+
// If the box goes off-screen at the right, adjust leftward
|
|
97
|
+
if (newX + boxWidth > window.innerWidth) {
|
|
98
|
+
newX = window.innerWidth - boxWidth;
|
|
99
|
+
}
|
|
100
|
+
// Ensure minimum position is not negative
|
|
101
|
+
newX = Math.max(0, newX);
|
|
102
|
+
newY = Math.max(0, newY);
|
|
75
103
|
// Store the position state
|
|
76
104
|
setPosition({ x: newX, y: newY });
|
|
77
|
-
}, [targetRef, left, top]);
|
|
105
|
+
}, [targetRef, left, top, placement, selfRef]);
|
|
78
106
|
/**
|
|
79
107
|
* Called when the user presses the mouse button within the box header.
|
|
80
108
|
* Captures the offset from the mouse position to the box's top-left corner.
|
|
@@ -91,21 +119,79 @@ export const DraggableBox = (props) => {
|
|
|
91
119
|
* Updates the box position while respecting window bounds.
|
|
92
120
|
*/
|
|
93
121
|
const handleMouseMove = (e) => {
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
122
|
+
if (isDragging && boxRef.current) {
|
|
123
|
+
const { offsetWidth, offsetHeight } = boxRef.current;
|
|
124
|
+
const newPos = clampPosition(e.clientX - offset.x, e.clientY - offset.y, offsetWidth, offsetHeight);
|
|
125
|
+
if (newPos)
|
|
126
|
+
setPosition(newPos);
|
|
127
|
+
}
|
|
128
|
+
if (isResizing && boxRef.current) {
|
|
129
|
+
const deltaX = e.clientX - resizeStart.x;
|
|
130
|
+
const deltaY = e.clientY - resizeStart.y;
|
|
131
|
+
let newWidth = resizeStart.width;
|
|
132
|
+
let newHeight = resizeStart.height;
|
|
133
|
+
if (isResizing.includes("right")) {
|
|
134
|
+
newWidth = Math.max(300, resizeStart.width + deltaX);
|
|
135
|
+
}
|
|
136
|
+
if (isResizing.includes("left")) {
|
|
137
|
+
newWidth = Math.max(300, resizeStart.width - deltaX);
|
|
138
|
+
}
|
|
139
|
+
if (isResizing.includes("bottom")) {
|
|
140
|
+
newHeight = Math.max(200, resizeStart.height + deltaY);
|
|
141
|
+
}
|
|
142
|
+
if (isResizing.includes("top")) {
|
|
143
|
+
newHeight = Math.max(200, resizeStart.height - deltaY);
|
|
144
|
+
}
|
|
145
|
+
setBoxSize({ width: newWidth, height: newHeight });
|
|
146
|
+
}
|
|
100
147
|
};
|
|
101
148
|
// Called when the mouse is released. Ends the dragging session.
|
|
102
|
-
const handleMouseUp = () =>
|
|
149
|
+
const handleMouseUp = () => {
|
|
150
|
+
setIsDragging(false);
|
|
151
|
+
setIsResizing(null);
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Called when the user presses the mouse button on a resize handle.
|
|
155
|
+
* Captures the initial state for resizing.
|
|
156
|
+
*/
|
|
157
|
+
const handleResizeMouseDown = useCallback((e, direction) => {
|
|
158
|
+
e.stopPropagation();
|
|
159
|
+
setIsResizing(direction);
|
|
160
|
+
const rect = boxRef.current?.getBoundingClientRect();
|
|
161
|
+
if (rect) {
|
|
162
|
+
setResizeStart({
|
|
163
|
+
x: e.clientX,
|
|
164
|
+
y: e.clientY,
|
|
165
|
+
width: rect.width,
|
|
166
|
+
height: rect.height,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}, []);
|
|
170
|
+
/**
|
|
171
|
+
* Handles mouse wheel events on resize handles to adjust size.
|
|
172
|
+
*/
|
|
173
|
+
const handleResizeWheel = useCallback((e, direction) => {
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
if (!boxRef.current)
|
|
176
|
+
return;
|
|
177
|
+
const rect = boxRef.current.getBoundingClientRect();
|
|
178
|
+
const delta = e.deltaY > 0 ? -10 : 10;
|
|
179
|
+
let newWidth = boxSize.width || rect.width;
|
|
180
|
+
let newHeight = boxSize.height || rect.height;
|
|
181
|
+
if (direction.includes("right") || direction.includes("left")) {
|
|
182
|
+
newWidth = Math.max(300, newWidth + delta);
|
|
183
|
+
}
|
|
184
|
+
if (direction.includes("bottom") || direction.includes("top")) {
|
|
185
|
+
newHeight = Math.max(200, newHeight + delta);
|
|
186
|
+
}
|
|
187
|
+
setBoxSize({ width: newWidth, height: newHeight });
|
|
188
|
+
}, [boxSize.width, boxSize.height]);
|
|
103
189
|
/**
|
|
104
|
-
* Subscribes to mousemove and mouseup while dragging; unsubscribes otherwise.
|
|
190
|
+
* Subscribes to mousemove and mouseup while dragging or resizing; unsubscribes otherwise.
|
|
105
191
|
* This ensures we only respond to these events when necessary.
|
|
106
192
|
*/
|
|
107
193
|
useEffect(() => {
|
|
108
|
-
if (isDragging) {
|
|
194
|
+
if (isDragging || isResizing) {
|
|
109
195
|
window.addEventListener("mousemove", handleMouseMove);
|
|
110
196
|
window.addEventListener("mouseup", handleMouseUp);
|
|
111
197
|
}
|
|
@@ -117,7 +203,7 @@ export const DraggableBox = (props) => {
|
|
|
117
203
|
window.removeEventListener("mousemove", handleMouseMove);
|
|
118
204
|
window.removeEventListener("mouseup", handleMouseUp);
|
|
119
205
|
};
|
|
120
|
-
}, [isDragging, offset]);
|
|
206
|
+
}, [isDragging, isResizing, offset, resizeStart]);
|
|
121
207
|
/**
|
|
122
208
|
* Observes changes to the box size and repositions if part of the box
|
|
123
209
|
* moves off-screen. This keeps the box fully visible upon resizing.
|
|
@@ -155,17 +241,91 @@ export const DraggableBox = (props) => {
|
|
|
155
241
|
top: position.y,
|
|
156
242
|
userSelect: "none",
|
|
157
243
|
minWidth: 300,
|
|
244
|
+
width: boxSize.width || "auto",
|
|
245
|
+
height: boxSize.height || "auto",
|
|
158
246
|
zIndex: 1000 + zIndexLayer,
|
|
159
247
|
bgcolor: "background.paper",
|
|
160
|
-
borderRadius:
|
|
248
|
+
borderRadius: 1,
|
|
161
249
|
p: 1,
|
|
250
|
+
border: `${slotSxProps?.border?.width ?? 1}px solid`,
|
|
251
|
+
borderColor: slotSxProps?.border?.color ?? "grey.500",
|
|
252
|
+
transform: "translateZ(0)",
|
|
162
253
|
...sx,
|
|
163
|
-
}, children: [
|
|
254
|
+
}, children: [_jsxs(Box, { sx: {
|
|
164
255
|
height: 30,
|
|
165
256
|
cursor: "move",
|
|
257
|
+
display: "flex",
|
|
258
|
+
alignItems: "center",
|
|
259
|
+
justifyContent: "space-between",
|
|
166
260
|
...slotSxProps?.header,
|
|
167
|
-
}, onMouseDown: handleMouseDown, children: _jsx(Typography, { size: size, sx: {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
261
|
+
}, onMouseDown: handleMouseDown, children: [_jsx(Typography, { size: size, sx: {
|
|
262
|
+
p: 0.5,
|
|
263
|
+
flexGrow: 1,
|
|
264
|
+
...slotSxProps?.title,
|
|
265
|
+
}, bold: true, children: title }), _jsx(IconButton, { size: "small", onClick: (e) => {
|
|
266
|
+
e.stopPropagation();
|
|
267
|
+
onClose();
|
|
268
|
+
}, onMouseDown: (e) => {
|
|
269
|
+
e.stopPropagation();
|
|
270
|
+
}, sx: {
|
|
271
|
+
padding: 0.5,
|
|
272
|
+
"&:hover": {
|
|
273
|
+
bgcolor: "action.hover",
|
|
274
|
+
},
|
|
275
|
+
}, children: _jsx(CloseIcon, { fontSize: "small" }) })] }), _jsx(Box, { children: children }), resizable && (_jsxs(_Fragment, { children: [_jsx(Box, { sx: {
|
|
276
|
+
position: "absolute",
|
|
277
|
+
right: 0,
|
|
278
|
+
top: 0,
|
|
279
|
+
bottom: 0,
|
|
280
|
+
width: 8,
|
|
281
|
+
cursor: "ew-resize",
|
|
282
|
+
"&:hover": {
|
|
283
|
+
bgcolor: "primary.main",
|
|
284
|
+
opacity: 0.3,
|
|
285
|
+
},
|
|
286
|
+
}, onMouseDown: (e) => handleResizeMouseDown(e, "right"), onWheel: (e) => handleResizeWheel(e, "right") }), _jsx(Box, { sx: {
|
|
287
|
+
position: "absolute",
|
|
288
|
+
bottom: 0,
|
|
289
|
+
left: 0,
|
|
290
|
+
right: 0,
|
|
291
|
+
height: 8,
|
|
292
|
+
cursor: "ns-resize",
|
|
293
|
+
"&:hover": {
|
|
294
|
+
bgcolor: "primary.main",
|
|
295
|
+
opacity: 0.3,
|
|
296
|
+
},
|
|
297
|
+
}, onMouseDown: (e) => handleResizeMouseDown(e, "bottom"), onWheel: (e) => handleResizeWheel(e, "bottom") }), _jsx(Box, { sx: {
|
|
298
|
+
position: "absolute",
|
|
299
|
+
bottom: 0,
|
|
300
|
+
right: 0,
|
|
301
|
+
width: 16,
|
|
302
|
+
height: 16,
|
|
303
|
+
cursor: "nwse-resize",
|
|
304
|
+
"&:hover": {
|
|
305
|
+
bgcolor: "primary.main",
|
|
306
|
+
opacity: 0.3,
|
|
307
|
+
},
|
|
308
|
+
}, onMouseDown: (e) => handleResizeMouseDown(e, "bottom-right"), onWheel: (e) => handleResizeWheel(e, "bottom-right") }), _jsx(Box, { sx: {
|
|
309
|
+
position: "absolute",
|
|
310
|
+
left: 0,
|
|
311
|
+
top: 0,
|
|
312
|
+
bottom: 0,
|
|
313
|
+
width: 8,
|
|
314
|
+
cursor: "ew-resize",
|
|
315
|
+
"&:hover": {
|
|
316
|
+
bgcolor: "primary.main",
|
|
317
|
+
opacity: 0.3,
|
|
318
|
+
},
|
|
319
|
+
}, onMouseDown: (e) => handleResizeMouseDown(e, "left"), onWheel: (e) => handleResizeWheel(e, "left") }), _jsx(Box, { sx: {
|
|
320
|
+
position: "absolute",
|
|
321
|
+
top: 0,
|
|
322
|
+
left: 0,
|
|
323
|
+
right: 0,
|
|
324
|
+
height: 8,
|
|
325
|
+
cursor: "ns-resize",
|
|
326
|
+
"&:hover": {
|
|
327
|
+
bgcolor: "primary.main",
|
|
328
|
+
opacity: 0.3,
|
|
329
|
+
},
|
|
330
|
+
}, onMouseDown: (e) => handleResizeMouseDown(e, "top"), onWheel: (e) => handleResizeWheel(e, "top") })] }))] }) })] }));
|
|
171
331
|
};
|