react-resizable-panels 2.0.3 → 2.0.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/CHANGELOG.md +8 -0
- package/dist/declarations/src/PanelResizeHandle.d.ts +1 -0
- package/dist/declarations/src/PanelResizeHandleRegistry.d.ts +1 -2
- package/dist/declarations/src/index.d.ts +3 -1
- package/dist/declarations/src/utils/rects/getIntersectingRectangle.d.ts +2 -0
- package/dist/declarations/src/utils/rects/intersects.d.ts +2 -0
- package/dist/declarations/src/utils/rects/types.d.ts +6 -0
- package/dist/react-resizable-panels.browser.cjs.js +138 -56
- package/dist/react-resizable-panels.browser.cjs.mjs +3 -1
- package/dist/react-resizable-panels.browser.development.cjs.js +138 -56
- package/dist/react-resizable-panels.browser.development.cjs.mjs +3 -1
- package/dist/react-resizable-panels.browser.development.esm.js +137 -57
- package/dist/react-resizable-panels.browser.esm.js +137 -57
- package/dist/react-resizable-panels.cjs.js +138 -56
- package/dist/react-resizable-panels.cjs.mjs +3 -1
- package/dist/react-resizable-panels.development.cjs.js +138 -56
- package/dist/react-resizable-panels.development.cjs.mjs +3 -1
- package/dist/react-resizable-panels.development.esm.js +137 -57
- package/dist/react-resizable-panels.development.node.cjs.js +138 -56
- package/dist/react-resizable-panels.development.node.cjs.mjs +3 -1
- package/dist/react-resizable-panels.development.node.esm.js +137 -57
- package/dist/react-resizable-panels.esm.js +137 -57
- package/dist/react-resizable-panels.node.cjs.js +138 -56
- package/dist/react-resizable-panels.node.cjs.mjs +3 -1
- package/dist/react-resizable-panels.node.esm.js +137 -57
- package/package.json +4 -1
- package/src/Panel.test.tsx +63 -0
- package/src/PanelGroup.test.tsx +21 -1
- package/src/PanelResizeHandle.test.tsx +181 -22
- package/src/PanelResizeHandle.ts +44 -24
- package/src/PanelResizeHandleRegistry.ts +87 -30
- package/src/index.ts +4 -0
- package/src/utils/rects/getIntersectingRectangle.test.ts +198 -0
- package/src/utils/rects/getIntersectingRectangle.ts +28 -0
- package/src/utils/rects/intersects.test.ts +197 -0
- package/src/utils/rects/intersects.ts +23 -0
- package/src/utils/rects/types.ts +6 -0
- package/src/utils/test-utils.ts +39 -0
package/src/PanelResizeHandle.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
ReactElement,
|
|
8
8
|
useContext,
|
|
9
9
|
useEffect,
|
|
10
|
+
useLayoutEffect,
|
|
10
11
|
useRef,
|
|
11
12
|
useState,
|
|
12
13
|
} from "./vendor/react";
|
|
@@ -21,11 +22,11 @@ import {
|
|
|
21
22
|
PointerHitAreaMargins,
|
|
22
23
|
registerResizeHandle,
|
|
23
24
|
ResizeHandlerAction,
|
|
24
|
-
ResizeHandlerState,
|
|
25
25
|
} from "./PanelResizeHandleRegistry";
|
|
26
26
|
import { assert } from "./utils/assert";
|
|
27
27
|
|
|
28
28
|
export type PanelResizeHandleOnDragging = (isDragging: boolean) => void;
|
|
29
|
+
export type ResizeHandlerState = "drag" | "hover" | "inactive";
|
|
29
30
|
|
|
30
31
|
export type PanelResizeHandleProps = Omit<
|
|
31
32
|
HTMLAttributes<keyof HTMLElementTagNameMap>,
|
|
@@ -90,6 +91,16 @@ export function PanelResizeHandle({
|
|
|
90
91
|
null
|
|
91
92
|
);
|
|
92
93
|
|
|
94
|
+
const committedValuesRef = useRef<{
|
|
95
|
+
state: ResizeHandlerState;
|
|
96
|
+
}>({
|
|
97
|
+
state,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
useLayoutEffect(() => {
|
|
101
|
+
committedValuesRef.current.state = state;
|
|
102
|
+
});
|
|
103
|
+
|
|
93
104
|
useEffect(() => {
|
|
94
105
|
if (disabled) {
|
|
95
106
|
setResizeHandler(null);
|
|
@@ -109,37 +120,46 @@ export function PanelResizeHandle({
|
|
|
109
120
|
|
|
110
121
|
const setResizeHandlerState = (
|
|
111
122
|
action: ResizeHandlerAction,
|
|
112
|
-
|
|
123
|
+
isActive: boolean,
|
|
113
124
|
event: ResizeEvent
|
|
114
125
|
) => {
|
|
115
|
-
|
|
126
|
+
if (isActive) {
|
|
127
|
+
switch (action) {
|
|
128
|
+
case "down": {
|
|
129
|
+
setState("drag");
|
|
130
|
+
|
|
131
|
+
startDragging(resizeHandleId, event);
|
|
132
|
+
|
|
133
|
+
const { onDragging } = callbacksRef.current;
|
|
134
|
+
if (onDragging) {
|
|
135
|
+
onDragging(true);
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case "move": {
|
|
140
|
+
const { state } = committedValuesRef.current;
|
|
116
141
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
142
|
+
if (state !== "drag") {
|
|
143
|
+
setState("hover");
|
|
144
|
+
}
|
|
120
145
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
onDragging(true);
|
|
146
|
+
resizeHandler(event);
|
|
147
|
+
break;
|
|
124
148
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
case "up": {
|
|
128
|
-
stopDragging();
|
|
149
|
+
case "up": {
|
|
150
|
+
setState("hover");
|
|
129
151
|
|
|
130
|
-
|
|
131
|
-
if (onDragging) {
|
|
132
|
-
onDragging(false);
|
|
133
|
-
}
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
152
|
+
stopDragging();
|
|
137
153
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
154
|
+
const { onDragging } = callbacksRef.current;
|
|
155
|
+
if (onDragging) {
|
|
156
|
+
onDragging(false);
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
142
160
|
}
|
|
161
|
+
} else {
|
|
162
|
+
setState("inactive");
|
|
143
163
|
}
|
|
144
164
|
};
|
|
145
165
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import { compare } from "stacking-order";
|
|
1
2
|
import { Direction, ResizeEvent } from "./types";
|
|
2
3
|
import { resetGlobalCursorStyle, setGlobalCursorStyle } from "./utils/cursor";
|
|
3
4
|
import { getResizeEventCoordinates } from "./utils/events/getResizeEventCoordinates";
|
|
4
5
|
import { getInputType } from "./utils/getInputType";
|
|
6
|
+
import { intersects } from "./utils/rects/intersects";
|
|
5
7
|
|
|
6
8
|
export type ResizeHandlerAction = "down" | "move" | "up";
|
|
7
|
-
export type ResizeHandlerState = "drag" | "hover" | "inactive";
|
|
8
9
|
export type SetResizeHandlerState = (
|
|
9
10
|
action: ResizeHandlerAction,
|
|
10
|
-
|
|
11
|
+
isActive: boolean,
|
|
11
12
|
event: ResizeEvent
|
|
12
13
|
) => void;
|
|
13
14
|
|
|
@@ -76,11 +77,12 @@ export function registerResizeHandle(
|
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
function handlePointerDown(event: ResizeEvent) {
|
|
80
|
+
const { target } = event;
|
|
79
81
|
const { x, y } = getResizeEventCoordinates(event);
|
|
80
82
|
|
|
81
83
|
isPointerDown = true;
|
|
82
84
|
|
|
83
|
-
recalculateIntersectingHandles({ x, y });
|
|
85
|
+
recalculateIntersectingHandles({ target, x, y });
|
|
84
86
|
updateListeners();
|
|
85
87
|
|
|
86
88
|
if (intersectingHandles.length > 0) {
|
|
@@ -93,27 +95,27 @@ function handlePointerDown(event: ResizeEvent) {
|
|
|
93
95
|
function handlePointerMove(event: ResizeEvent) {
|
|
94
96
|
const { x, y } = getResizeEventCoordinates(event);
|
|
95
97
|
|
|
96
|
-
if (isPointerDown) {
|
|
97
|
-
|
|
98
|
-
const { setResizeHandlerState } = data;
|
|
98
|
+
if (!isPointerDown) {
|
|
99
|
+
const { target } = event;
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
updateCursor();
|
|
105
|
-
} else {
|
|
106
|
-
recalculateIntersectingHandles({ x, y });
|
|
107
|
-
updateResizeHandlerStates("move", event);
|
|
108
|
-
updateCursor();
|
|
101
|
+
// Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
|
|
102
|
+
// at that point, the handles may not move with the pointer (depending on constraints)
|
|
103
|
+
// but the same set of active handles should be locked until the pointer is released
|
|
104
|
+
recalculateIntersectingHandles({ target, x, y });
|
|
109
105
|
}
|
|
110
106
|
|
|
107
|
+
updateResizeHandlerStates("move", event);
|
|
108
|
+
|
|
109
|
+
// Update cursor based on return value(s) from active handles
|
|
110
|
+
updateCursor();
|
|
111
|
+
|
|
111
112
|
if (intersectingHandles.length > 0) {
|
|
112
113
|
event.preventDefault();
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
function handlePointerUp(event: ResizeEvent) {
|
|
118
|
+
const { target } = event;
|
|
117
119
|
const { x, y } = getResizeEventCoordinates(event);
|
|
118
120
|
|
|
119
121
|
panelConstraintFlags.clear();
|
|
@@ -123,31 +125,92 @@ function handlePointerUp(event: ResizeEvent) {
|
|
|
123
125
|
event.preventDefault();
|
|
124
126
|
}
|
|
125
127
|
|
|
126
|
-
recalculateIntersectingHandles({ x, y });
|
|
127
128
|
updateResizeHandlerStates("up", event);
|
|
129
|
+
recalculateIntersectingHandles({ target, x, y });
|
|
128
130
|
updateCursor();
|
|
129
131
|
|
|
130
132
|
updateListeners();
|
|
131
133
|
}
|
|
132
134
|
|
|
133
|
-
function recalculateIntersectingHandles({
|
|
135
|
+
function recalculateIntersectingHandles({
|
|
136
|
+
target,
|
|
137
|
+
x,
|
|
138
|
+
y,
|
|
139
|
+
}: {
|
|
140
|
+
target: EventTarget | null;
|
|
141
|
+
x: number;
|
|
142
|
+
y: number;
|
|
143
|
+
}) {
|
|
134
144
|
intersectingHandles.splice(0);
|
|
135
145
|
|
|
146
|
+
let targetElement: HTMLElement | null = null;
|
|
147
|
+
if (target instanceof HTMLElement) {
|
|
148
|
+
targetElement = target;
|
|
149
|
+
}
|
|
150
|
+
|
|
136
151
|
registeredResizeHandlers.forEach((data) => {
|
|
137
|
-
const { element, hitAreaMargins } = data;
|
|
138
|
-
|
|
152
|
+
const { element: dragHandleElement, hitAreaMargins } = data;
|
|
153
|
+
|
|
154
|
+
const dragHandleRect = dragHandleElement.getBoundingClientRect();
|
|
155
|
+
const { bottom, left, right, top } = dragHandleRect;
|
|
139
156
|
|
|
140
157
|
const margin = isCoarsePointer
|
|
141
158
|
? hitAreaMargins.coarse
|
|
142
159
|
: hitAreaMargins.fine;
|
|
143
160
|
|
|
144
|
-
const
|
|
161
|
+
const eventIntersects =
|
|
145
162
|
x >= left - margin &&
|
|
146
163
|
x <= right + margin &&
|
|
147
164
|
y >= top - margin &&
|
|
148
165
|
y <= bottom + margin;
|
|
149
166
|
|
|
150
|
-
if (
|
|
167
|
+
if (eventIntersects) {
|
|
168
|
+
// TRICKY
|
|
169
|
+
// We listen for pointers events at the root in order to support hit area margins
|
|
170
|
+
// (determining when the pointer is close enough to an element to be considered a "hit")
|
|
171
|
+
// Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
|
|
172
|
+
// so at this point we need to compare stacking order of a potentially intersecting drag handle,
|
|
173
|
+
// and the element that was actually clicked/touched
|
|
174
|
+
if (
|
|
175
|
+
targetElement !== null &&
|
|
176
|
+
dragHandleElement !== targetElement &&
|
|
177
|
+
!dragHandleElement.contains(targetElement) &&
|
|
178
|
+
!targetElement.contains(dragHandleElement) &&
|
|
179
|
+
// Calculating stacking order has a cost, so we should avoid it if possible
|
|
180
|
+
// That is why we only check potentially intersecting handles,
|
|
181
|
+
// and why we skip if the event target is within the handle's DOM
|
|
182
|
+
compare(targetElement, dragHandleElement) > 0
|
|
183
|
+
) {
|
|
184
|
+
// If the target is above the drag handle, then we also need to confirm they overlap
|
|
185
|
+
// If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
|
|
186
|
+
//
|
|
187
|
+
// It's not enough to compare only the target
|
|
188
|
+
// The target might be a small element inside of a larger container
|
|
189
|
+
// (For example, a SPAN or a DIV inside of a larger modal dialog)
|
|
190
|
+
let currentElement: HTMLElement | null = targetElement;
|
|
191
|
+
let didIntersect = false;
|
|
192
|
+
while (currentElement) {
|
|
193
|
+
if (currentElement.contains(dragHandleElement)) {
|
|
194
|
+
break;
|
|
195
|
+
} else if (
|
|
196
|
+
intersects(
|
|
197
|
+
currentElement.getBoundingClientRect(),
|
|
198
|
+
dragHandleRect,
|
|
199
|
+
true
|
|
200
|
+
)
|
|
201
|
+
) {
|
|
202
|
+
didIntersect = true;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
currentElement = currentElement.parentElement;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (didIntersect) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
151
214
|
intersectingHandles.push(data);
|
|
152
215
|
}
|
|
153
216
|
});
|
|
@@ -250,14 +313,8 @@ function updateResizeHandlerStates(
|
|
|
250
313
|
registeredResizeHandlers.forEach((data) => {
|
|
251
314
|
const { setResizeHandlerState } = data;
|
|
252
315
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
} else {
|
|
257
|
-
setResizeHandlerState(action, "hover", event);
|
|
258
|
-
}
|
|
259
|
-
} else {
|
|
260
|
-
setResizeHandlerState(action, "inactive", event);
|
|
261
|
-
}
|
|
316
|
+
const isActive = intersectingHandles.includes(data);
|
|
317
|
+
|
|
318
|
+
setResizeHandlerState(action, isActive, event);
|
|
262
319
|
});
|
|
263
320
|
}
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,8 @@ import { getResizeHandleElement } from "./utils/dom/getResizeHandleElement";
|
|
|
9
9
|
import { getResizeHandleElementIndex } from "./utils/dom/getResizeHandleElementIndex";
|
|
10
10
|
import { getResizeHandleElementsForGroup } from "./utils/dom/getResizeHandleElementsForGroup";
|
|
11
11
|
import { getResizeHandlePanelIds } from "./utils/dom/getResizeHandlePanelIds";
|
|
12
|
+
import { getIntersectingRectangle } from "./utils/rects/getIntersectingRectangle";
|
|
13
|
+
import { intersects } from "./utils/rects/intersects";
|
|
12
14
|
|
|
13
15
|
import type {
|
|
14
16
|
ImperativePanelHandle,
|
|
@@ -49,6 +51,8 @@ export {
|
|
|
49
51
|
|
|
50
52
|
// Utility methods
|
|
51
53
|
assert,
|
|
54
|
+
getIntersectingRectangle,
|
|
55
|
+
intersects,
|
|
52
56
|
|
|
53
57
|
// DOM helpers
|
|
54
58
|
getPanelElement,
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { getIntersectingRectangle } from "./getIntersectingRectangle";
|
|
2
|
+
import { Rectangle } from "./types";
|
|
3
|
+
|
|
4
|
+
const emptyRect = { x: 0, y: 0, width: 0, height: 0 };
|
|
5
|
+
const rect = { x: 25, y: 25, width: 50, height: 50 };
|
|
6
|
+
|
|
7
|
+
function forkRect(partial: Partial<Rectangle>, baseRect: Rectangle = rect) {
|
|
8
|
+
return { ...rect, ...partial };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe("getIntersectingRectangle", () => {
|
|
12
|
+
let strict: boolean = false;
|
|
13
|
+
|
|
14
|
+
function verify(rectOne: Rectangle, rectTwo: Rectangle, expected: Rectangle) {
|
|
15
|
+
const actual = getIntersectingRectangle(rectOne, rectTwo, strict);
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
expect(actual).toEqual(expected);
|
|
19
|
+
} catch (thrown) {
|
|
20
|
+
console.log(
|
|
21
|
+
"Expect",
|
|
22
|
+
strict ? "strict mode" : "loose mode",
|
|
23
|
+
"\n",
|
|
24
|
+
rectOne,
|
|
25
|
+
"\n",
|
|
26
|
+
rectTwo,
|
|
27
|
+
"\n\nto intersect as:\n",
|
|
28
|
+
expected,
|
|
29
|
+
"\n\nbut got:\n",
|
|
30
|
+
actual
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
throw thrown;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("loose", () => {
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
strict = false;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should support empty rects", () => {
|
|
43
|
+
verify(emptyRect, emptyRect, emptyRect);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should support fully overlapping rects", () => {
|
|
47
|
+
verify(rect, forkRect({ x: 35, width: 30 }), {
|
|
48
|
+
x: 35,
|
|
49
|
+
y: 25,
|
|
50
|
+
width: 30,
|
|
51
|
+
height: 50,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
verify(rect, forkRect({ y: 35, height: 30 }), {
|
|
55
|
+
x: 25,
|
|
56
|
+
y: 35,
|
|
57
|
+
width: 50,
|
|
58
|
+
height: 30,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
verify(
|
|
62
|
+
rect,
|
|
63
|
+
forkRect({
|
|
64
|
+
x: 35,
|
|
65
|
+
y: 35,
|
|
66
|
+
width: 30,
|
|
67
|
+
height: 30,
|
|
68
|
+
}),
|
|
69
|
+
|
|
70
|
+
{
|
|
71
|
+
x: 35,
|
|
72
|
+
y: 35,
|
|
73
|
+
width: 30,
|
|
74
|
+
height: 30,
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should support partially overlapping rects", () => {
|
|
80
|
+
verify(rect, forkRect({ x: 10, y: 10 }), {
|
|
81
|
+
x: 25,
|
|
82
|
+
y: 25,
|
|
83
|
+
width: 35,
|
|
84
|
+
height: 35,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
verify(rect, forkRect({ x: 45, y: 30 }), {
|
|
88
|
+
x: 45,
|
|
89
|
+
y: 30,
|
|
90
|
+
width: 30,
|
|
91
|
+
height: 45,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should support non-overlapping rects", () => {
|
|
96
|
+
verify(rect, forkRect({ x: 100, y: 100 }), emptyRect);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should support all negative coordinates", () => {
|
|
100
|
+
verify(
|
|
101
|
+
{
|
|
102
|
+
x: -100,
|
|
103
|
+
y: -100,
|
|
104
|
+
width: 50,
|
|
105
|
+
height: 50,
|
|
106
|
+
},
|
|
107
|
+
{ x: -80, y: -80, width: 50, height: 50 },
|
|
108
|
+
{
|
|
109
|
+
x: -80,
|
|
110
|
+
y: -80,
|
|
111
|
+
width: 30,
|
|
112
|
+
height: 30,
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("strict", () => {
|
|
119
|
+
beforeEach(() => {
|
|
120
|
+
strict = true;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should support empty rects", () => {
|
|
124
|
+
verify(emptyRect, emptyRect, emptyRect);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should support fully overlapping rects", () => {
|
|
128
|
+
verify(rect, forkRect({ x: 35, width: 30 }), {
|
|
129
|
+
x: 35,
|
|
130
|
+
y: 25,
|
|
131
|
+
width: 30,
|
|
132
|
+
height: 50,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
verify(rect, forkRect({ y: 35, height: 30 }), {
|
|
136
|
+
x: 25,
|
|
137
|
+
y: 35,
|
|
138
|
+
width: 50,
|
|
139
|
+
height: 30,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
verify(
|
|
143
|
+
rect,
|
|
144
|
+
forkRect({
|
|
145
|
+
x: 35,
|
|
146
|
+
y: 35,
|
|
147
|
+
width: 30,
|
|
148
|
+
height: 30,
|
|
149
|
+
}),
|
|
150
|
+
|
|
151
|
+
{
|
|
152
|
+
x: 35,
|
|
153
|
+
y: 35,
|
|
154
|
+
width: 30,
|
|
155
|
+
height: 30,
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should support partially overlapping rects", () => {
|
|
161
|
+
verify(rect, forkRect({ x: 10, y: 10 }), {
|
|
162
|
+
x: 25,
|
|
163
|
+
y: 25,
|
|
164
|
+
width: 35,
|
|
165
|
+
height: 35,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
verify(rect, forkRect({ x: 45, y: 30 }), {
|
|
169
|
+
x: 45,
|
|
170
|
+
y: 30,
|
|
171
|
+
width: 30,
|
|
172
|
+
height: 45,
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("should support non-overlapping rects", () => {
|
|
177
|
+
verify(rect, forkRect({ x: 100, y: 100 }), emptyRect);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("should support all negative coordinates", () => {
|
|
181
|
+
verify(
|
|
182
|
+
{
|
|
183
|
+
x: -100,
|
|
184
|
+
y: -100,
|
|
185
|
+
width: 50,
|
|
186
|
+
height: 50,
|
|
187
|
+
},
|
|
188
|
+
{ x: -80, y: -80, width: 50, height: 50 },
|
|
189
|
+
{
|
|
190
|
+
x: -80,
|
|
191
|
+
y: -80,
|
|
192
|
+
width: 30,
|
|
193
|
+
height: 30,
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { intersects } from "./intersects";
|
|
2
|
+
import { Rectangle } from "./types";
|
|
3
|
+
|
|
4
|
+
export function getIntersectingRectangle(
|
|
5
|
+
rectOne: Rectangle,
|
|
6
|
+
rectTwo: Rectangle,
|
|
7
|
+
strict: boolean
|
|
8
|
+
): Rectangle {
|
|
9
|
+
if (!intersects(rectOne, rectTwo, strict)) {
|
|
10
|
+
return {
|
|
11
|
+
x: 0,
|
|
12
|
+
y: 0,
|
|
13
|
+
width: 0,
|
|
14
|
+
height: 0,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
x: Math.max(rectOne.x, rectTwo.x),
|
|
20
|
+
y: Math.max(rectOne.y, rectTwo.y),
|
|
21
|
+
width:
|
|
22
|
+
Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) -
|
|
23
|
+
Math.max(rectOne.x, rectTwo.x),
|
|
24
|
+
height:
|
|
25
|
+
Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) -
|
|
26
|
+
Math.max(rectOne.y, rectTwo.y),
|
|
27
|
+
};
|
|
28
|
+
}
|