@hrnec06/react_utils 1.2.7 → 1.3.2
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/package.json +3 -2
- package/src/{debugger → components/Debugger}/Debugger.tsx +31 -21
- package/src/{debugger → components/Debugger}/DebuggerScrollBar.tsx +3 -3
- package/src/components/ResizeableBox/DragAreas.tsx +182 -0
- package/src/components/ResizeableBox/ResizeableBox.tsx +156 -0
- package/src/components/ResizeableBox/ResizeableBox.types.ts +23 -0
- package/src/components/ResizeableBox/ResizeableBoxContext.ts +30 -0
- package/src/hooks/useEfficientState.ts +9 -0
- package/src/hooks/useLazySignal.ts +64 -0
- package/src/hooks/useListener.ts +1 -1
- package/src/hooks/useSignal.ts +2 -2
- package/src/hooks/useUUID.ts +6 -0
- package/src/hooks/useWindowSize.ts +9 -12
- package/src/index.tsx +6 -3
- package/src/debugger/DebuggerWindowResize.tsx +0 -136
- package/src/ui/ResizeableBox.tsx +0 -351
- /package/src/{debugger → components/Debugger}/DebuggerContext.ts +0 -0
- /package/src/{debugger → components/Debugger}/DebuggerLogic.ts +0 -0
- /package/src/{debugger → components/Debugger}/DebuggerSymbols.tsx +0 -0
- /package/src/{debugger → components/Debugger}/parser/DebugParser.tsx +0 -0
- /package/src/{debugger → components/Debugger}/parser/DebugParserSimple.tsx +0 -0
- /package/src/{debugger → components/Debugger}/parser/ValueArray.tsx +0 -0
- /package/src/{debugger → components/Debugger}/parser/ValueBoolean.tsx +0 -0
- /package/src/{debugger → components/Debugger}/parser/ValueFunction.tsx +0 -0
- /package/src/{debugger → components/Debugger}/parser/ValueKeyword.tsx +0 -0
- /package/src/{debugger → components/Debugger}/parser/ValueNumber.tsx +0 -0
- /package/src/{debugger → components/Debugger}/parser/ValueObject.tsx +0 -0
- /package/src/{debugger → components/Debugger}/parser/ValueString.tsx +0 -0
- /package/src/{debugger → components/Debugger}/parser/ValueSymbol.tsx +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hrnec06/react_utils",
|
|
3
|
-
"version": "1.2
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "A debugger component for react.",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.tsx"
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"clsx": "^2.1.1",
|
|
31
31
|
"lucide-react": "^0.525.0",
|
|
32
32
|
"react": "^19.1.0",
|
|
33
|
-
"react-dom": "^19.2.3"
|
|
33
|
+
"react-dom": "^19.2.3",
|
|
34
|
+
"uuid": "^13.0.0"
|
|
34
35
|
},
|
|
35
36
|
"scripts": {
|
|
36
37
|
"dev": "tsup src/index.tsx --format cjs,esm --dts --watch"
|
|
@@ -3,12 +3,12 @@ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
import {DebuggerContext} from "./DebuggerContext";
|
|
5
5
|
import clsx from "clsx";
|
|
6
|
-
import ResizeBorder from "./DebuggerWindowResize";
|
|
7
6
|
import ScrollBar from "./DebuggerScrollBar";
|
|
8
|
-
import useKeyListener from "../hooks/useKeyListener";
|
|
9
|
-
import useUpdateEffect from "../hooks/useUpdateEffect";
|
|
10
|
-
import useUpdatedRef from "../hooks/useUpdatedRef";
|
|
11
7
|
import ParseValue from "./parser/DebugParser";
|
|
8
|
+
import useKeyListener from "../../hooks/useKeyListener";
|
|
9
|
+
import useUpdateEffect from "../../hooks/useUpdateEffect";
|
|
10
|
+
import useUpdatedRef from "../../hooks/useUpdatedRef";
|
|
11
|
+
import ResizeableBox from "../ResizeableBox/ResizeableBox";
|
|
12
12
|
|
|
13
13
|
interface DebugProps {
|
|
14
14
|
value: unknown,
|
|
@@ -227,6 +227,11 @@ export default function Debug({
|
|
|
227
227
|
setScrollProgress(p => minmax(p + addProgress, 0, 1));
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
const handleResize = (size: Vector2, offset: Vector2) => {
|
|
231
|
+
setWindowSize(size);
|
|
232
|
+
setPosition(offset);
|
|
233
|
+
}
|
|
234
|
+
|
|
230
235
|
return createPortal(
|
|
231
236
|
(
|
|
232
237
|
<DebuggerContext.Provider
|
|
@@ -273,29 +278,34 @@ export default function Debug({
|
|
|
273
278
|
height: windowSize[1],
|
|
274
279
|
}}
|
|
275
280
|
>
|
|
276
|
-
<
|
|
281
|
+
<ResizeableBox
|
|
282
|
+
$size={windowSize}
|
|
283
|
+
$offset={finalPosition}
|
|
277
284
|
|
|
278
|
-
|
|
279
|
-
onWheel={handleWheel}
|
|
280
|
-
className="bg-[#1f1f1f] shadow-lg rounded-md border border-[#4b4b4b] w-full h-full overflow-hidden flex"
|
|
285
|
+
$onResize={handleResize}
|
|
281
286
|
>
|
|
282
287
|
<div
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
className=" cursor-grab w-full"
|
|
286
|
-
style={{
|
|
287
|
-
transform: `translateY(${-(scrollProgress * scrollHeight)}px)`
|
|
288
|
-
}}
|
|
288
|
+
onWheel={handleWheel}
|
|
289
|
+
className="bg-[#1f1f1f] shadow-lg rounded-md border border-[#4b4b4b] w-full h-full overflow-hidden flex"
|
|
289
290
|
>
|
|
290
|
-
<div
|
|
291
|
-
|
|
291
|
+
<div
|
|
292
|
+
ref={containerRef}
|
|
293
|
+
onMouseDown={handleMouseDown}
|
|
294
|
+
className=" cursor-grab w-full"
|
|
295
|
+
style={{
|
|
296
|
+
transform: `translateY(${-(scrollProgress * scrollHeight)}px)`
|
|
297
|
+
}}
|
|
298
|
+
>
|
|
299
|
+
<div className="p-3 pl-5">
|
|
300
|
+
<ParseValue value={value} />
|
|
301
|
+
</div>
|
|
292
302
|
</div>
|
|
303
|
+
<ScrollBar
|
|
304
|
+
containerHeight={containerHeight}
|
|
305
|
+
scrollHeight={scrollHeight}
|
|
306
|
+
/>
|
|
293
307
|
</div>
|
|
294
|
-
|
|
295
|
-
containerHeight={containerHeight}
|
|
296
|
-
scrollHeight={scrollHeight}
|
|
297
|
-
/>
|
|
298
|
-
</div>
|
|
308
|
+
</ResizeableBox>
|
|
299
309
|
</div>
|
|
300
310
|
</div>
|
|
301
311
|
</DebuggerContext.Provider>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { minmax, Nullable
|
|
2
|
-
import { useEffect,
|
|
1
|
+
import { minmax, Nullable } from "@hrnec06/util";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import useDebugger from "./DebuggerContext";
|
|
4
|
-
import useListener from "
|
|
4
|
+
import useListener from "../../hooks/useListener";
|
|
5
5
|
|
|
6
6
|
interface ScrollBarProps {
|
|
7
7
|
containerHeight: number,
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { matchBool, matchMouseEvent, MouseButton, Vector2 } from "@hrnec06/util";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import useResizeableBox from "./ResizeableBoxContext";
|
|
4
|
+
import RB from "./ResizeableBox.types";
|
|
5
|
+
|
|
6
|
+
export default function DragAreas()
|
|
7
|
+
{
|
|
8
|
+
return (
|
|
9
|
+
<>
|
|
10
|
+
<DragArea
|
|
11
|
+
spanHorizontal
|
|
12
|
+
top
|
|
13
|
+
|
|
14
|
+
applyReposition={'y'}
|
|
15
|
+
onMove={([, y]) => [0, -y]}
|
|
16
|
+
/>
|
|
17
|
+
|
|
18
|
+
<DragArea
|
|
19
|
+
spanVertical
|
|
20
|
+
right
|
|
21
|
+
|
|
22
|
+
onMove={([x]) => [x, 0]}
|
|
23
|
+
/>
|
|
24
|
+
|
|
25
|
+
<DragArea
|
|
26
|
+
spanHorizontal
|
|
27
|
+
bottom
|
|
28
|
+
|
|
29
|
+
onMove={([, y]) => [0, y]}
|
|
30
|
+
/>
|
|
31
|
+
|
|
32
|
+
<DragArea
|
|
33
|
+
spanVertical
|
|
34
|
+
left
|
|
35
|
+
|
|
36
|
+
applyReposition={'x'}
|
|
37
|
+
onMove={([x]) => [-x, 0]}
|
|
38
|
+
/>
|
|
39
|
+
|
|
40
|
+
{/* Corners */}
|
|
41
|
+
|
|
42
|
+
<DragArea
|
|
43
|
+
isCorner
|
|
44
|
+
left
|
|
45
|
+
top
|
|
46
|
+
|
|
47
|
+
applyReposition={true}
|
|
48
|
+
onMove={([x, y]) => [-x, -y]}
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
<DragArea
|
|
52
|
+
isCorner
|
|
53
|
+
right
|
|
54
|
+
top
|
|
55
|
+
|
|
56
|
+
applyReposition={'y'}
|
|
57
|
+
onMove={([x, y]) => [x, -y]}
|
|
58
|
+
/>
|
|
59
|
+
|
|
60
|
+
<DragArea
|
|
61
|
+
isCorner
|
|
62
|
+
left
|
|
63
|
+
bottom
|
|
64
|
+
|
|
65
|
+
applyReposition={'x'}
|
|
66
|
+
onMove={([x, y]) => [-x, y]}
|
|
67
|
+
/>
|
|
68
|
+
|
|
69
|
+
<DragArea
|
|
70
|
+
isCorner
|
|
71
|
+
right
|
|
72
|
+
bottom
|
|
73
|
+
|
|
74
|
+
onMove={([x, y]) => [x, y]}
|
|
75
|
+
/>
|
|
76
|
+
</>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface DragAreaProps {
|
|
81
|
+
isCorner?: boolean,
|
|
82
|
+
|
|
83
|
+
spanVertical?: boolean,
|
|
84
|
+
spanHorizontal?: boolean,
|
|
85
|
+
|
|
86
|
+
onMove: (offset: Vector2) => Vector2,
|
|
87
|
+
applyReposition?: RB.RepositionMode,
|
|
88
|
+
|
|
89
|
+
top?: boolean,
|
|
90
|
+
right?: boolean,
|
|
91
|
+
bottom?: boolean,
|
|
92
|
+
left?: boolean
|
|
93
|
+
}
|
|
94
|
+
function DragArea({
|
|
95
|
+
isCorner,
|
|
96
|
+
|
|
97
|
+
spanVertical,
|
|
98
|
+
spanHorizontal,
|
|
99
|
+
|
|
100
|
+
onMove,
|
|
101
|
+
applyReposition = false,
|
|
102
|
+
|
|
103
|
+
...enabledSides
|
|
104
|
+
}: DragAreaProps)
|
|
105
|
+
{
|
|
106
|
+
const box = useResizeableBox();
|
|
107
|
+
|
|
108
|
+
const isAllowed = (side: RB.DragSide) => box.options.allowedSides.includes(side);
|
|
109
|
+
|
|
110
|
+
if (isCorner && !box.options.allowCorners)
|
|
111
|
+
return undefined;
|
|
112
|
+
|
|
113
|
+
if (
|
|
114
|
+
(enabledSides.top && !isAllowed('top')) ||
|
|
115
|
+
(enabledSides.right && !isAllowed('right')) ||
|
|
116
|
+
(enabledSides.bottom && !isAllowed('bottom')) ||
|
|
117
|
+
(enabledSides.left && !isAllowed('left'))
|
|
118
|
+
)
|
|
119
|
+
return undefined;
|
|
120
|
+
|
|
121
|
+
if (isCorner && Array.isArray(box.options.allowCorners))
|
|
122
|
+
{
|
|
123
|
+
if (!box.options.allowCorners.some((combo) => enabledSides[combo[0]] && enabledSides[combo[1]]))
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
128
|
+
if (box.drag.value !== null || !matchMouseEvent(e.nativeEvent, MouseButton.Left))
|
|
129
|
+
return;
|
|
130
|
+
|
|
131
|
+
e.preventDefault();
|
|
132
|
+
e.stopPropagation();
|
|
133
|
+
|
|
134
|
+
box.drag.value = {
|
|
135
|
+
originalSize: box.size,
|
|
136
|
+
originalOffset: box.offset,
|
|
137
|
+
dragOrigin: [e.clientX, e.clientY],
|
|
138
|
+
mouseMove: onMove,
|
|
139
|
+
applyReposition: applyReposition,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<div
|
|
145
|
+
onMouseDown={handleMouseDown}
|
|
146
|
+
|
|
147
|
+
className={clsx(
|
|
148
|
+
"absolute z-20",
|
|
149
|
+
"select-none",
|
|
150
|
+
|
|
151
|
+
spanVertical ? 'h-full' : clsx(
|
|
152
|
+
box.options.dragAreaSize === 'small' && 'h-2',
|
|
153
|
+
box.options.dragAreaSize === 'medium' && 'h-4',
|
|
154
|
+
box.options.dragAreaSize === 'big' && 'h-6'
|
|
155
|
+
),
|
|
156
|
+
|
|
157
|
+
spanHorizontal ? 'w-full' : clsx(
|
|
158
|
+
box.options.dragAreaSize === 'small' && 'w-2',
|
|
159
|
+
box.options.dragAreaSize === 'medium' && 'w-4',
|
|
160
|
+
box.options.dragAreaSize === 'big' && 'w-6'
|
|
161
|
+
),
|
|
162
|
+
|
|
163
|
+
enabledSides.top && clsx('bottom-full', box.options.insetAreas && 'translate-y-1/2'),
|
|
164
|
+
enabledSides.right && clsx('left-full', box.options.insetAreas && '-translate-x-1/2'),
|
|
165
|
+
enabledSides.bottom && clsx('top-full', box.options.insetAreas && '-translate-y-1/2'),
|
|
166
|
+
enabledSides.left && clsx('right-full', box.options.insetAreas && 'translate-x-1/2'),
|
|
167
|
+
|
|
168
|
+
matchBool({
|
|
169
|
+
"cursor-nw-resize": enabledSides.top && enabledSides.left,
|
|
170
|
+
"cursor-ne-resize": enabledSides.top && enabledSides.right,
|
|
171
|
+
"cursor-se-resize": enabledSides.bottom && enabledSides.right,
|
|
172
|
+
"cursor-sw-resize": enabledSides.bottom && enabledSides.left,
|
|
173
|
+
|
|
174
|
+
"cursor-n-resize": enabledSides.top,
|
|
175
|
+
"cursor-e-resize": enabledSides.right,
|
|
176
|
+
"cursor-s-resize": enabledSides.bottom,
|
|
177
|
+
"cursor-w-resize": enabledSides.left
|
|
178
|
+
}),
|
|
179
|
+
)}
|
|
180
|
+
/>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { addVector2, cloneVector2, matchMouseEvent, MouseButton, Nullable, Optional, Vector2 } from "@hrnec06/util";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
import DragAreas from "./DragAreas";
|
|
5
|
+
import useSignal from "../../hooks/useSignal";
|
|
6
|
+
import useUpdatedRef from "../../hooks/useUpdatedRef";
|
|
7
|
+
import useListener from "../../hooks/useListener";
|
|
8
|
+
import { ResizeableBoxContext } from "./ResizeableBoxContext";
|
|
9
|
+
import RB from "./ResizeableBox.types";
|
|
10
|
+
|
|
11
|
+
// Type definitions
|
|
12
|
+
|
|
13
|
+
// Component
|
|
14
|
+
|
|
15
|
+
interface ResizeableBoxProps extends React.HTMLProps<HTMLDivElement>
|
|
16
|
+
{
|
|
17
|
+
/**
|
|
18
|
+
* Current size of the box. Affects onResize callback.
|
|
19
|
+
*/
|
|
20
|
+
$size?: Vector2,
|
|
21
|
+
/**
|
|
22
|
+
* Current offset of the box. Affects onResize callback.
|
|
23
|
+
*/
|
|
24
|
+
$offset?: Vector2,
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Size of the areas
|
|
28
|
+
*/
|
|
29
|
+
$dragAreaSize?: RB.DragAreaSize,
|
|
30
|
+
/**
|
|
31
|
+
* Allowed sides
|
|
32
|
+
*/
|
|
33
|
+
$allowedSides?: RB.DragSide[],
|
|
34
|
+
/**
|
|
35
|
+
* Allow corner resizers?
|
|
36
|
+
* A corner will be visible only if the 2 neighbouring sides are visible
|
|
37
|
+
*/
|
|
38
|
+
$allowCorners?: boolean | Vector2<RB.DragSide>[],
|
|
39
|
+
/**
|
|
40
|
+
* The drag areas will be inset inside the box by 50%
|
|
41
|
+
*/
|
|
42
|
+
$insetAreas?: boolean,
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* If $size or $offset is specified, onResize provides the new size or offset with changes already applied.
|
|
46
|
+
*/
|
|
47
|
+
$onResize?: RB.ResizeEvent,
|
|
48
|
+
/**
|
|
49
|
+
* Emits when resizing starts
|
|
50
|
+
*/
|
|
51
|
+
$onStart?: RB.StartEvent,
|
|
52
|
+
/**
|
|
53
|
+
* Emits when resizing ends
|
|
54
|
+
*/
|
|
55
|
+
$onEnd?: RB.EndEvent,
|
|
56
|
+
}
|
|
57
|
+
export default function ResizeableBox({
|
|
58
|
+
$size,
|
|
59
|
+
$offset,
|
|
60
|
+
|
|
61
|
+
$dragAreaSize = "medium",
|
|
62
|
+
$allowedSides = ['top', 'right', 'bottom', 'left'],
|
|
63
|
+
$allowCorners = true,
|
|
64
|
+
$insetAreas = true,
|
|
65
|
+
|
|
66
|
+
$onResize,
|
|
67
|
+
$onStart,
|
|
68
|
+
$onEnd,
|
|
69
|
+
|
|
70
|
+
...props
|
|
71
|
+
}: ResizeableBoxProps)
|
|
72
|
+
{
|
|
73
|
+
const drag = useSignal<Nullable<RB.DragState>>(null);
|
|
74
|
+
|
|
75
|
+
const onResizeRef = useUpdatedRef($onResize);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (drag.value === null)
|
|
79
|
+
$onEnd && $onEnd();
|
|
80
|
+
else
|
|
81
|
+
$onStart && $onStart();
|
|
82
|
+
}, [drag.value]);
|
|
83
|
+
|
|
84
|
+
useListener(document, 'mouseup', (e) => {
|
|
85
|
+
if (drag.value === null || !matchMouseEvent(e, MouseButton.Left))
|
|
86
|
+
return;
|
|
87
|
+
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
e.stopPropagation();
|
|
90
|
+
|
|
91
|
+
drag.value = null;
|
|
92
|
+
}, [drag.value === null]);
|
|
93
|
+
|
|
94
|
+
useListener(document, 'mousemove', (e) => {
|
|
95
|
+
if (drag.value === null || !onResizeRef.current)
|
|
96
|
+
return;
|
|
97
|
+
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
e.stopPropagation();
|
|
100
|
+
|
|
101
|
+
const offset: Vector2 = [
|
|
102
|
+
e.clientX - drag.value.dragOrigin[0],
|
|
103
|
+
e.clientY - drag.value.dragOrigin[1],
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const sizeOffset = drag.value.mouseMove(offset);
|
|
107
|
+
const positionOffset: Vector2 = [0, 0];
|
|
108
|
+
|
|
109
|
+
if (drag.value.applyReposition !== false)
|
|
110
|
+
{
|
|
111
|
+
const offset: Vector2 = cloneVector2(sizeOffset);
|
|
112
|
+
|
|
113
|
+
positionOffset[0] = (drag.value.applyReposition === true || drag.value.applyReposition === 'x') ? -offset[0] : 0;
|
|
114
|
+
positionOffset[1] = (drag.value.applyReposition === true || drag.value.applyReposition === 'y') ? -offset[1] : 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (drag.value.originalSize)
|
|
118
|
+
addVector2(sizeOffset, drag.value.originalSize);
|
|
119
|
+
|
|
120
|
+
if (drag.value.originalOffset)
|
|
121
|
+
addVector2(positionOffset, drag.value.originalOffset);
|
|
122
|
+
|
|
123
|
+
onResizeRef.current(sizeOffset, positionOffset);
|
|
124
|
+
}, [drag.value === null]);
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<ResizeableBoxContext.Provider
|
|
128
|
+
value={{
|
|
129
|
+
size: $size,
|
|
130
|
+
offset: $offset,
|
|
131
|
+
|
|
132
|
+
options: {
|
|
133
|
+
dragAreaSize: $dragAreaSize,
|
|
134
|
+
allowedSides: $allowedSides,
|
|
135
|
+
allowCorners: $allowCorners,
|
|
136
|
+
insetAreas: $insetAreas,
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
drag: drag
|
|
140
|
+
}}
|
|
141
|
+
>
|
|
142
|
+
<div
|
|
143
|
+
{...props}
|
|
144
|
+
|
|
145
|
+
className={clsx(
|
|
146
|
+
props.className,
|
|
147
|
+
"relative w-full h-full"
|
|
148
|
+
)}
|
|
149
|
+
>
|
|
150
|
+
<DragAreas />
|
|
151
|
+
|
|
152
|
+
{props.children}
|
|
153
|
+
</div>
|
|
154
|
+
</ResizeableBoxContext.Provider>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Vector2 } from "@hrnec06/util";
|
|
2
|
+
|
|
3
|
+
namespace RB {
|
|
4
|
+
export type DragAreaSize = 'small' | 'medium' | 'big';
|
|
5
|
+
export type DragSide = 'top' | 'right' | 'bottom' | 'left';
|
|
6
|
+
export type ResizeEvent = (size: Vector2, offset: Vector2) => void;
|
|
7
|
+
export type StartEvent = () => void;
|
|
8
|
+
export type EndEvent = () => void;
|
|
9
|
+
|
|
10
|
+
export type RepositionMode = boolean | 'x' | 'y';
|
|
11
|
+
export type MouseMoveHandler = (offset: Vector2) => Vector2;
|
|
12
|
+
|
|
13
|
+
export interface DragState {
|
|
14
|
+
originalSize?: Vector2,
|
|
15
|
+
originalOffset?: Vector2,
|
|
16
|
+
|
|
17
|
+
dragOrigin: Vector2,
|
|
18
|
+
applyReposition: RB.RepositionMode,
|
|
19
|
+
mouseMove: MouseMoveHandler
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default RB;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Nullable, Optional, Vector2 } from "@hrnec06/util";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
import { Signal } from "../../hooks/useSignal";
|
|
4
|
+
import RB from "./ResizeableBox.types";
|
|
5
|
+
|
|
6
|
+
// Context
|
|
7
|
+
interface ResizeableBoxContext {
|
|
8
|
+
size?: Vector2,
|
|
9
|
+
offset?: Vector2,
|
|
10
|
+
|
|
11
|
+
options: {
|
|
12
|
+
insetAreas: boolean,
|
|
13
|
+
dragAreaSize: RB.DragAreaSize,
|
|
14
|
+
allowedSides: RB.DragSide[],
|
|
15
|
+
allowCorners: boolean | Vector2<RB.DragSide>[],
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
drag: Signal<Nullable<RB.DragState>>,
|
|
19
|
+
}
|
|
20
|
+
export const ResizeableBoxContext = createContext<Optional<ResizeableBoxContext>>(undefined);
|
|
21
|
+
|
|
22
|
+
export default function useResizeableBox()
|
|
23
|
+
{
|
|
24
|
+
const ctx = useContext(ResizeableBoxContext);
|
|
25
|
+
|
|
26
|
+
if (!ctx)
|
|
27
|
+
throw new Error("useResizeableBox may be used only within ResizeableBoxContext.Provider!");
|
|
28
|
+
|
|
29
|
+
return ctx;
|
|
30
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ReactUtils } from "@hrnec06/util";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
|
+
|
|
4
|
+
export default function useEfficientState<T>(callback: () => T): [T, ReactUtils.SetState<T>]
|
|
5
|
+
{
|
|
6
|
+
const defaultValue = useMemo(() => callback(), []);
|
|
7
|
+
|
|
8
|
+
return useState<T>(defaultValue);
|
|
9
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ReactUtils } from "@hrnec06/util";
|
|
2
|
+
import { Signal } from "./useSignal";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
|
|
5
|
+
const LazyNoValue = Symbol("LazyNoValue");
|
|
6
|
+
|
|
7
|
+
type LazyValue<T> = T | typeof LazyNoValue;
|
|
8
|
+
|
|
9
|
+
export default function useLazySignal<T>(callback: () => T): LazySignal<T>
|
|
10
|
+
{
|
|
11
|
+
const [_value, _setValue] = useState<LazyValue<T>>(LazyNoValue);
|
|
12
|
+
|
|
13
|
+
const signal = new LazySignal(_value, _setValue, callback);
|
|
14
|
+
|
|
15
|
+
return signal;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class LazySignal<T> extends Signal<LazyValue<T>>
|
|
19
|
+
{
|
|
20
|
+
public static LazyNoValue: typeof LazyNoValue = LazyNoValue;
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
_value: LazyValue<T>,
|
|
24
|
+
setState: ReactUtils.SetState<T | typeof LazyNoValue>,
|
|
25
|
+
private lazyCallback: () => T
|
|
26
|
+
)
|
|
27
|
+
{
|
|
28
|
+
super(_value, setState);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public set value(value: T | ((prev: LazyValue<T>) => T))
|
|
32
|
+
{
|
|
33
|
+
this.setState(value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public get value(): T
|
|
37
|
+
{
|
|
38
|
+
if (this._value === LazyNoValue)
|
|
39
|
+
{
|
|
40
|
+
const result = this.loadValue();
|
|
41
|
+
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return this._value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public loadValue(): T
|
|
49
|
+
{
|
|
50
|
+
if (this.hasValue())
|
|
51
|
+
return this._value as T;
|
|
52
|
+
|
|
53
|
+
const result = this.lazyCallback();
|
|
54
|
+
|
|
55
|
+
this.setState(result);
|
|
56
|
+
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public hasValue(): boolean
|
|
61
|
+
{
|
|
62
|
+
return this._value !== LazyNoValue;
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/hooks/useListener.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DependencyList, useEffect } from "react"
|
|
2
2
|
|
|
3
|
-
export default function useListener<E extends keyof GlobalEventHandlersEventMap>(element:
|
|
3
|
+
export default function useListener<E extends keyof GlobalEventHandlersEventMap>(element: EventTarget, event: E, listener: (event: GlobalEventHandlersEventMap[E]) => void, deps?: DependencyList) {
|
|
4
4
|
useEffect(() => {
|
|
5
5
|
element.addEventListener(event, listener as EventListenerOrEventListenerObject);
|
|
6
6
|
|
package/src/hooks/useSignal.ts
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import { Vector2 } from "@hrnec06/util";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
+
import useListener from "./useListener";
|
|
4
|
+
import useLazySignal, { LazySignal } from "./useLazySignal";
|
|
3
5
|
|
|
4
|
-
export default function useWindowSize(): Vector2
|
|
5
|
-
|
|
6
|
+
export default function useWindowSize(): LazySignal<Vector2>
|
|
7
|
+
{
|
|
8
|
+
const dimensions = useLazySignal<Vector2>(() => [ window.innerWidth, window.innerHeight ]);
|
|
6
9
|
|
|
7
10
|
useEffect(() => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const listener = () => {
|
|
11
|
-
setDimensions([window.innerWidth, window.innerHeight]);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
window.addEventListener('resize', listener);
|
|
11
|
+
dimensions.value = [window.innerWidth, window.innerHeight];
|
|
12
|
+
}, []);
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
14
|
+
useListener(window, 'resize', () => {
|
|
15
|
+
dimensions.value = [window.innerWidth, window.innerHeight];
|
|
19
16
|
}, []);
|
|
20
17
|
|
|
21
18
|
return dimensions;
|
package/src/index.tsx
CHANGED
|
@@ -4,9 +4,10 @@ import useUpdatedRef from "./hooks/useUpdatedRef";
|
|
|
4
4
|
import useUpdateEffect from "./hooks/useUpdateEffect";
|
|
5
5
|
import useWindowSize from "./hooks/useWindowSize";
|
|
6
6
|
import useSignal from "./hooks/useSignal";
|
|
7
|
+
import useLazySignal from "./hooks/useLazySignal";
|
|
7
8
|
|
|
8
|
-
import Debugger from './
|
|
9
|
-
import ResizeableBox from "./
|
|
9
|
+
import Debugger from './components/Debugger/Debugger';
|
|
10
|
+
import ResizeableBox from "./components/ResizeableBox/ResizeableBox";
|
|
10
11
|
|
|
11
12
|
export {
|
|
12
13
|
useKeyListener,
|
|
@@ -15,6 +16,8 @@ export {
|
|
|
15
16
|
useUpdateEffect,
|
|
16
17
|
useWindowSize,
|
|
17
18
|
useSignal,
|
|
19
|
+
useLazySignal,
|
|
20
|
+
|
|
18
21
|
Debugger,
|
|
19
|
-
ResizeableBox
|
|
22
|
+
ResizeableBox,
|
|
20
23
|
};
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { cloneVector2, Nullable, removeVector2, Vector2 } from "@hrnec06/util";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import useDebugger from "./DebuggerContext";
|
|
4
|
-
import clsx from "clsx";
|
|
5
|
-
|
|
6
|
-
export default function ResizeBorder() {
|
|
7
|
-
return (
|
|
8
|
-
<>
|
|
9
|
-
<ResizeBar
|
|
10
|
-
className="-top-1.5 h-3 w-full cursor-ns-resize"
|
|
11
|
-
applyReposition
|
|
12
|
-
onMove={([, y]) => [0, y]}
|
|
13
|
-
/>
|
|
14
|
-
<ResizeBar
|
|
15
|
-
className="-right-1.5 h-full w-3 cursor-ew-resize"
|
|
16
|
-
onMove={([x]) => [-x, 0]}
|
|
17
|
-
/>
|
|
18
|
-
<ResizeBar
|
|
19
|
-
className="-bottom-1.5 h-3 w-full cursor-ns-resize"
|
|
20
|
-
onMove={([, y]) => [0, -y]}
|
|
21
|
-
/>
|
|
22
|
-
<ResizeBar
|
|
23
|
-
className="-left-1.5 h-full w-3 cursor-ew-resize"
|
|
24
|
-
applyReposition
|
|
25
|
-
onMove={([x,]) => [x, 0]}
|
|
26
|
-
/>
|
|
27
|
-
|
|
28
|
-
<ResizeBar
|
|
29
|
-
className="-top-1.5 -left-1.5 size-3 cursor-nwse-resize"
|
|
30
|
-
applyReposition
|
|
31
|
-
onMove={([x, y]) => [x, y]}
|
|
32
|
-
/>
|
|
33
|
-
<ResizeBar
|
|
34
|
-
className="-top-1.5 -right-1.5 size-3 cursor-nesw-resize"
|
|
35
|
-
applyReposition={'y'}
|
|
36
|
-
onMove={([x, y]) => [-x, y]}
|
|
37
|
-
/>
|
|
38
|
-
<ResizeBar
|
|
39
|
-
className="-bottom-1.5 -right-1.5 size-3 cursor-nwse-resize"
|
|
40
|
-
onMove={([x, y]) => [-x, -y]}
|
|
41
|
-
/>
|
|
42
|
-
<ResizeBar
|
|
43
|
-
className="-bottom-1.5 -left-1.5 size-3 cursor-nesw-resize"
|
|
44
|
-
applyReposition={'x'}
|
|
45
|
-
onMove={([x, y]) => [x, -y]}
|
|
46
|
-
/>
|
|
47
|
-
</>
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
interface ResizeBarProps {
|
|
52
|
-
className: string,
|
|
53
|
-
applyReposition?: boolean | 'x' | 'y',
|
|
54
|
-
|
|
55
|
-
onMove: (offset: Vector2, originalSize: Vector2, originalPosition: Vector2) => Vector2
|
|
56
|
-
}
|
|
57
|
-
function ResizeBar({
|
|
58
|
-
className,
|
|
59
|
-
applyReposition = false,
|
|
60
|
-
|
|
61
|
-
onMove
|
|
62
|
-
}: ResizeBarProps) {
|
|
63
|
-
const debug = useDebugger();
|
|
64
|
-
|
|
65
|
-
const [drag, setDrag] = useState<Nullable<{
|
|
66
|
-
dragOrigin: Vector2,
|
|
67
|
-
originalSize: Vector2,
|
|
68
|
-
originalPosition: Vector2
|
|
69
|
-
}>>(null);
|
|
70
|
-
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
const mouseUp = () => {
|
|
73
|
-
if (!drag) return;
|
|
74
|
-
|
|
75
|
-
setDrag(null);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const mouseMove = (e: MouseEvent) => {
|
|
79
|
-
if (!drag) return;
|
|
80
|
-
|
|
81
|
-
const offset: Vector2 = [
|
|
82
|
-
e.clientX - drag.dragOrigin[0],
|
|
83
|
-
e.clientY - drag.dragOrigin[1],
|
|
84
|
-
];
|
|
85
|
-
|
|
86
|
-
const newSize = onMove(
|
|
87
|
-
offset,
|
|
88
|
-
drag.originalSize,
|
|
89
|
-
drag.originalPosition
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
newSize[0] = drag.originalSize[0] - newSize[0];
|
|
93
|
-
newSize[1] = drag.originalSize[1] - newSize[1];
|
|
94
|
-
|
|
95
|
-
if (applyReposition !== false) {
|
|
96
|
-
const repositionOffset = cloneVector2(newSize);
|
|
97
|
-
removeVector2(repositionOffset, drag.originalSize);
|
|
98
|
-
|
|
99
|
-
debug.placement.reposition([
|
|
100
|
-
drag.originalPosition[0] - ((applyReposition === true || applyReposition === 'x') ? repositionOffset[0] : 0),
|
|
101
|
-
drag.originalPosition[1] - ((applyReposition === true || applyReposition === 'y') ? repositionOffset[1] : 0)
|
|
102
|
-
]);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
debug.window.resize(newSize);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
document.addEventListener('mouseup', mouseUp);
|
|
109
|
-
document.addEventListener('mousemove', mouseMove);
|
|
110
|
-
|
|
111
|
-
return () => {
|
|
112
|
-
document.removeEventListener('mouseup', mouseUp);
|
|
113
|
-
document.removeEventListener('mousemove', mouseMove);
|
|
114
|
-
}
|
|
115
|
-
}, [drag]);
|
|
116
|
-
|
|
117
|
-
const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
118
|
-
e.preventDefault();
|
|
119
|
-
|
|
120
|
-
setDrag({
|
|
121
|
-
dragOrigin: [e.clientX, e.clientY],
|
|
122
|
-
originalSize: debug.window.size,
|
|
123
|
-
originalPosition: debug.placement.position
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<div
|
|
129
|
-
onMouseDown={handleMouseDown}
|
|
130
|
-
className={clsx(
|
|
131
|
-
'absolute',
|
|
132
|
-
className,
|
|
133
|
-
)}
|
|
134
|
-
/>
|
|
135
|
-
);
|
|
136
|
-
}
|
package/src/ui/ResizeableBox.tsx
DELETED
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
import { addVector2, cloneVector2, match, matchBool, matchMouseEvent, MouseButton, Nullable, Optional, Vector2 } from "@hrnec06/util";
|
|
2
|
-
import clsx from "clsx";
|
|
3
|
-
import { createContext, useContext, useEffect, useState } from "react";
|
|
4
|
-
import useSignal, { Signal } from "../hooks/useSignal";
|
|
5
|
-
import useListener from "../hooks/useListener";
|
|
6
|
-
import { useUpdatedRef } from "..";
|
|
7
|
-
|
|
8
|
-
// Type definitions
|
|
9
|
-
type DragAreaSize = 'small' | 'medium' | 'big';
|
|
10
|
-
type DragSide = 'top' | 'right' | 'bottom' | 'left';
|
|
11
|
-
type ResizeEvent = (size: Vector2, offset: Vector2) => void;
|
|
12
|
-
type StartEvent = () => void;
|
|
13
|
-
type EndEvent = () => void;
|
|
14
|
-
|
|
15
|
-
type RepositionMode = boolean | 'x' | 'y';
|
|
16
|
-
type MouseMoveHandler = (offset: Vector2) => Vector2
|
|
17
|
-
|
|
18
|
-
interface DragState {
|
|
19
|
-
originalSize?: Vector2,
|
|
20
|
-
originalOffset?: Vector2,
|
|
21
|
-
|
|
22
|
-
dragOrigin: Vector2,
|
|
23
|
-
applyReposition: RepositionMode,
|
|
24
|
-
mouseMove: MouseMoveHandler
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Context
|
|
28
|
-
interface ResizeableBoxContext {
|
|
29
|
-
size?: Vector2,
|
|
30
|
-
offset?: Vector2,
|
|
31
|
-
|
|
32
|
-
options: {
|
|
33
|
-
insetAreas: boolean,
|
|
34
|
-
dragAreaSize: DragAreaSize,
|
|
35
|
-
allowedSides: DragSide[],
|
|
36
|
-
allowCorners: boolean | Vector2<DragSide>[],
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
drag: Signal<Nullable<DragState>>,
|
|
40
|
-
}
|
|
41
|
-
const ResizeableBoxContext = createContext<Optional<ResizeableBoxContext>>(undefined);
|
|
42
|
-
|
|
43
|
-
function useResizeableBox()
|
|
44
|
-
{
|
|
45
|
-
const ctx = useContext(ResizeableBoxContext);
|
|
46
|
-
|
|
47
|
-
if (!ctx)
|
|
48
|
-
throw new Error("useResizeableBox may be used only within ResizeableBoxContext.Provider!");
|
|
49
|
-
|
|
50
|
-
return ctx;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Component
|
|
54
|
-
|
|
55
|
-
interface ResizeableBoxProps extends React.HTMLProps<HTMLDivElement>
|
|
56
|
-
{
|
|
57
|
-
$size?: Vector2,
|
|
58
|
-
$offset?: Vector2,
|
|
59
|
-
|
|
60
|
-
$dragAreaSize?: DragAreaSize,
|
|
61
|
-
$allowedSides?: DragSide[],
|
|
62
|
-
$allowCorners?: boolean | Vector2<DragSide>[],
|
|
63
|
-
$insetAreas?: boolean,
|
|
64
|
-
|
|
65
|
-
$onResize?: ResizeEvent,
|
|
66
|
-
$onStart?: StartEvent,
|
|
67
|
-
$onEnd?: EndEvent,
|
|
68
|
-
}
|
|
69
|
-
export default function ResizeableBox({
|
|
70
|
-
$size,
|
|
71
|
-
$offset,
|
|
72
|
-
|
|
73
|
-
$dragAreaSize = "medium",
|
|
74
|
-
$allowedSides = ['top', 'right', 'bottom', 'left'],
|
|
75
|
-
$allowCorners = true,
|
|
76
|
-
$insetAreas = true,
|
|
77
|
-
|
|
78
|
-
$onResize,
|
|
79
|
-
$onStart,
|
|
80
|
-
$onEnd,
|
|
81
|
-
|
|
82
|
-
...props
|
|
83
|
-
}: ResizeableBoxProps)
|
|
84
|
-
{
|
|
85
|
-
const drag = useSignal<Nullable<DragState>>(null);
|
|
86
|
-
|
|
87
|
-
const onResizeRef = useUpdatedRef($onResize);
|
|
88
|
-
|
|
89
|
-
useEffect(() => {
|
|
90
|
-
if (drag.value === null)
|
|
91
|
-
$onEnd && $onEnd();
|
|
92
|
-
else
|
|
93
|
-
$onStart && $onStart();
|
|
94
|
-
}, [drag.value]);
|
|
95
|
-
|
|
96
|
-
useListener(document, 'mouseup', (e) => {
|
|
97
|
-
if (drag.value === null || !matchMouseEvent(e, MouseButton.Left))
|
|
98
|
-
return;
|
|
99
|
-
|
|
100
|
-
e.preventDefault();
|
|
101
|
-
e.stopPropagation();
|
|
102
|
-
|
|
103
|
-
drag.value = null;
|
|
104
|
-
}, [drag.value === null]);
|
|
105
|
-
|
|
106
|
-
useListener(document, 'mousemove', (e) => {
|
|
107
|
-
if (drag.value === null || !onResizeRef.current)
|
|
108
|
-
return;
|
|
109
|
-
|
|
110
|
-
e.preventDefault();
|
|
111
|
-
e.stopPropagation();
|
|
112
|
-
|
|
113
|
-
const offset: Vector2 = [
|
|
114
|
-
e.clientX - drag.value.dragOrigin[0],
|
|
115
|
-
e.clientY - drag.value.dragOrigin[1],
|
|
116
|
-
];
|
|
117
|
-
|
|
118
|
-
const sizeOffset = drag.value.mouseMove(offset);
|
|
119
|
-
const positionOffset: Vector2 = [0, 0];
|
|
120
|
-
|
|
121
|
-
if (drag.value.applyReposition !== false)
|
|
122
|
-
{
|
|
123
|
-
const offset: Vector2 = cloneVector2(sizeOffset);
|
|
124
|
-
|
|
125
|
-
positionOffset[0] = (drag.value.applyReposition === true || drag.value.applyReposition === 'x') ? -offset[0] : 0;
|
|
126
|
-
positionOffset[1] = (drag.value.applyReposition === true || drag.value.applyReposition === 'y') ? -offset[1] : 0;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (drag.value.originalSize)
|
|
130
|
-
addVector2(sizeOffset, drag.value.originalSize);
|
|
131
|
-
|
|
132
|
-
if (drag.value.originalOffset)
|
|
133
|
-
addVector2(positionOffset, drag.value.originalOffset);
|
|
134
|
-
|
|
135
|
-
onResizeRef.current(sizeOffset, positionOffset);
|
|
136
|
-
}, [drag.value === null]);
|
|
137
|
-
|
|
138
|
-
return (
|
|
139
|
-
<ResizeableBoxContext.Provider
|
|
140
|
-
value={{
|
|
141
|
-
size: $size,
|
|
142
|
-
offset: $offset,
|
|
143
|
-
|
|
144
|
-
options: {
|
|
145
|
-
dragAreaSize: $dragAreaSize,
|
|
146
|
-
allowedSides: $allowedSides,
|
|
147
|
-
allowCorners: $allowCorners,
|
|
148
|
-
insetAreas: $insetAreas,
|
|
149
|
-
},
|
|
150
|
-
|
|
151
|
-
drag: drag
|
|
152
|
-
}}
|
|
153
|
-
>
|
|
154
|
-
<div
|
|
155
|
-
{...props}
|
|
156
|
-
|
|
157
|
-
className={clsx(
|
|
158
|
-
props.className,
|
|
159
|
-
"relative w-full h-full"
|
|
160
|
-
)}
|
|
161
|
-
>
|
|
162
|
-
<DragAreas />
|
|
163
|
-
|
|
164
|
-
{props.children}
|
|
165
|
-
</div>
|
|
166
|
-
</ResizeableBoxContext.Provider>
|
|
167
|
-
)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
interface DragAreasProps {
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
function DragAreas({
|
|
174
|
-
|
|
175
|
-
}: DragAreasProps)
|
|
176
|
-
{
|
|
177
|
-
return (
|
|
178
|
-
<>
|
|
179
|
-
<DragArea
|
|
180
|
-
spanHorizontal
|
|
181
|
-
top
|
|
182
|
-
|
|
183
|
-
applyReposition={'y'}
|
|
184
|
-
onMove={([, y]) => [0, -y]}
|
|
185
|
-
/>
|
|
186
|
-
|
|
187
|
-
<DragArea
|
|
188
|
-
spanVertical
|
|
189
|
-
right
|
|
190
|
-
|
|
191
|
-
onMove={([x]) => [x, 0]}
|
|
192
|
-
/>
|
|
193
|
-
|
|
194
|
-
<DragArea
|
|
195
|
-
spanHorizontal
|
|
196
|
-
bottom
|
|
197
|
-
|
|
198
|
-
onMove={([, y]) => [0, y]}
|
|
199
|
-
/>
|
|
200
|
-
|
|
201
|
-
<DragArea
|
|
202
|
-
spanVertical
|
|
203
|
-
left
|
|
204
|
-
|
|
205
|
-
applyReposition={'x'}
|
|
206
|
-
onMove={([x]) => [-x, 0]}
|
|
207
|
-
/>
|
|
208
|
-
|
|
209
|
-
{/* Corners */}
|
|
210
|
-
|
|
211
|
-
<DragArea
|
|
212
|
-
isCorner
|
|
213
|
-
left
|
|
214
|
-
top
|
|
215
|
-
|
|
216
|
-
applyReposition={true}
|
|
217
|
-
onMove={([x, y]) => [-x, -y]}
|
|
218
|
-
/>
|
|
219
|
-
|
|
220
|
-
<DragArea
|
|
221
|
-
isCorner
|
|
222
|
-
right
|
|
223
|
-
top
|
|
224
|
-
|
|
225
|
-
applyReposition={'y'}
|
|
226
|
-
onMove={([x, y]) => [x, -y]}
|
|
227
|
-
/>
|
|
228
|
-
|
|
229
|
-
<DragArea
|
|
230
|
-
isCorner
|
|
231
|
-
left
|
|
232
|
-
bottom
|
|
233
|
-
|
|
234
|
-
applyReposition={'x'}
|
|
235
|
-
onMove={([x, y]) => [-x, y]}
|
|
236
|
-
/>
|
|
237
|
-
|
|
238
|
-
<DragArea
|
|
239
|
-
isCorner
|
|
240
|
-
right
|
|
241
|
-
bottom
|
|
242
|
-
|
|
243
|
-
onMove={([x, y]) => [x, y]}
|
|
244
|
-
/>
|
|
245
|
-
</>
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
interface DragAreaProps {
|
|
250
|
-
isCorner?: boolean,
|
|
251
|
-
|
|
252
|
-
spanVertical?: boolean,
|
|
253
|
-
spanHorizontal?: boolean,
|
|
254
|
-
|
|
255
|
-
onMove: (offset: Vector2) => Vector2,
|
|
256
|
-
applyReposition?: RepositionMode,
|
|
257
|
-
|
|
258
|
-
top?: boolean,
|
|
259
|
-
right?: boolean,
|
|
260
|
-
bottom?: boolean,
|
|
261
|
-
left?: boolean
|
|
262
|
-
}
|
|
263
|
-
function DragArea({
|
|
264
|
-
isCorner,
|
|
265
|
-
|
|
266
|
-
spanVertical,
|
|
267
|
-
spanHorizontal,
|
|
268
|
-
|
|
269
|
-
onMove,
|
|
270
|
-
applyReposition = false,
|
|
271
|
-
|
|
272
|
-
...enabledSides
|
|
273
|
-
}: DragAreaProps)
|
|
274
|
-
{
|
|
275
|
-
const box = useResizeableBox();
|
|
276
|
-
|
|
277
|
-
const isAllowed = (side: DragSide) => box.options.allowedSides.includes(side);
|
|
278
|
-
|
|
279
|
-
if (isCorner && !box.options.allowCorners)
|
|
280
|
-
return undefined;
|
|
281
|
-
|
|
282
|
-
if (
|
|
283
|
-
(enabledSides.top && !isAllowed('top')) ||
|
|
284
|
-
(enabledSides.right && !isAllowed('right')) ||
|
|
285
|
-
(enabledSides.bottom && !isAllowed('bottom')) ||
|
|
286
|
-
(enabledSides.left && !isAllowed('left'))
|
|
287
|
-
)
|
|
288
|
-
return undefined;
|
|
289
|
-
|
|
290
|
-
if (isCorner && Array.isArray(box.options.allowCorners))
|
|
291
|
-
{
|
|
292
|
-
if (!box.options.allowCorners.some((combo) => enabledSides[combo[0]] && enabledSides[combo[1]]))
|
|
293
|
-
return undefined;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
297
|
-
if (box.drag.value !== null || !matchMouseEvent(e.nativeEvent, MouseButton.Left))
|
|
298
|
-
return;
|
|
299
|
-
|
|
300
|
-
e.preventDefault();
|
|
301
|
-
e.stopPropagation();
|
|
302
|
-
|
|
303
|
-
box.drag.value = {
|
|
304
|
-
originalSize: box.size,
|
|
305
|
-
originalOffset: box.offset,
|
|
306
|
-
dragOrigin: [e.clientX, e.clientY],
|
|
307
|
-
mouseMove: onMove,
|
|
308
|
-
applyReposition: applyReposition,
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
return (
|
|
313
|
-
<div
|
|
314
|
-
onMouseDown={handleMouseDown}
|
|
315
|
-
|
|
316
|
-
className={clsx(
|
|
317
|
-
"absolute",
|
|
318
|
-
"select-none",
|
|
319
|
-
|
|
320
|
-
spanVertical ? 'h-full' : clsx(
|
|
321
|
-
box.options.dragAreaSize === 'small' && 'h-2',
|
|
322
|
-
box.options.dragAreaSize === 'medium' && 'h-4',
|
|
323
|
-
box.options.dragAreaSize === 'big' && 'h-6'
|
|
324
|
-
),
|
|
325
|
-
|
|
326
|
-
spanHorizontal ? 'w-full' : clsx(
|
|
327
|
-
box.options.dragAreaSize === 'small' && 'w-2',
|
|
328
|
-
box.options.dragAreaSize === 'medium' && 'w-4',
|
|
329
|
-
box.options.dragAreaSize === 'big' && 'w-6'
|
|
330
|
-
),
|
|
331
|
-
|
|
332
|
-
enabledSides.top && clsx('bottom-full', box.options.insetAreas && 'translate-y-1/2'),
|
|
333
|
-
enabledSides.right && clsx('left-full', box.options.insetAreas && '-translate-x-1/2'),
|
|
334
|
-
enabledSides.bottom && clsx('top-full', box.options.insetAreas && '-translate-y-1/2'),
|
|
335
|
-
enabledSides.left && clsx('right-full', box.options.insetAreas && 'translate-x-1/2'),
|
|
336
|
-
|
|
337
|
-
matchBool({
|
|
338
|
-
"cursor-nw-resize": enabledSides.top && enabledSides.left,
|
|
339
|
-
"cursor-ne-resize": enabledSides.top && enabledSides.right,
|
|
340
|
-
"cursor-se-resize": enabledSides.bottom && enabledSides.right,
|
|
341
|
-
"cursor-sw-resize": enabledSides.bottom && enabledSides.left,
|
|
342
|
-
|
|
343
|
-
"cursor-n-resize": enabledSides.top,
|
|
344
|
-
"cursor-e-resize": enabledSides.right,
|
|
345
|
-
"cursor-s-resize": enabledSides.bottom,
|
|
346
|
-
"cursor-w-resize": enabledSides.left
|
|
347
|
-
}),
|
|
348
|
-
)}
|
|
349
|
-
/>
|
|
350
|
-
);
|
|
351
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|