@replohq/sandbox-runtime 1.7.9 → 1.7.11
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/_vendor/replo-utils/react/zustand.d.ts +13 -0
- package/_vendor/replo-utils/react/zustand.mjs +24 -0
- package/_vendor/schemas/generated/selectModeIframeRpc.d.ts +12 -2
- package/_vendor/schemas/selectModeIframeRpc.d.ts +66 -4
- package/_vendor/schemas/selectModeIframeRpc.mjs +16 -3
- package/buildMetadata.js +3 -3
- package/package.json +6 -5
- package/select-mode/EditorSelection.d.ts +1 -22
- package/select-mode/EditorSelection.js +61 -100
- package/select-mode/SelectionFrames.d.ts +1 -0
- package/select-mode/SelectionFrames.js +58 -0
- package/select-mode/annotationFrames.d.ts +13 -0
- package/select-mode/annotationFrames.js +31 -1
- package/select-mode/descriptor/elementDescriptor.js +1 -1
- package/select-mode/displayModeStore.d.ts +0 -2
- package/select-mode/displayModeStore.js +11 -30
- package/select-mode/hooks/useParentMessaging.js +3 -0
- package/select-mode/hooks/useSelectionHover.d.ts +1 -19
- package/select-mode/hooks/useSelectionHover.js +37 -49
- package/select-mode/hooks/useSelectionInteractions.d.ts +1 -12
- package/select-mode/hooks/useSelectionInteractions.js +57 -39
- package/select-mode/hooks/useSelectionScrollSync.d.ts +1 -25
- package/select-mode/hooks/useSelectionScrollSync.js +48 -40
- package/select-mode/iframeRpcChannel.d.ts +24 -1
- package/select-mode/inlineEditFeedback.d.ts +6 -0
- package/select-mode/inlineEditFeedback.js +47 -0
- package/select-mode/isTextEditableElement.d.ts +1 -0
- package/select-mode/isTextEditableElement.js +15 -0
- package/select-mode/selectModeStore.d.ts +160 -0
- package/select-mode/selectModeStore.js +585 -0
- package/select-mode/selectionFrame.d.ts +10 -59
- package/select-mode/selectionFrame.js +26 -150
- package/select-mode/symbolicate.js +4 -0
- package/select-mode/tiptapInlineEditor.js +16 -1
- package/select-mode/hooks/useCrosshairCursor.d.ts +0 -10
- package/select-mode/hooks/useCrosshairCursor.js +0 -23
- package/select-mode/hooks/useEditModeStateMachine.d.ts +0 -16
- package/select-mode/hooks/useEditModeStateMachine.js +0 -228
- package/select-mode/hooks/useSelectModeController.d.ts +0 -11
- package/select-mode/hooks/useSelectModeController.js +0 -72
|
@@ -1,45 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
4
|
-
let displayMode = "browse";
|
|
5
|
-
const SERVER_SNAPSHOT = "browse";
|
|
6
|
-
function notify() {
|
|
7
|
-
for (const listener of listeners) {
|
|
8
|
-
listener();
|
|
9
|
-
}
|
|
10
|
-
}
|
|
2
|
+
import { selectModeStore } from "./selectModeStore";
|
|
11
3
|
const displayModeStore = {
|
|
12
4
|
getSnapshot() {
|
|
13
|
-
return displayMode;
|
|
5
|
+
return selectModeStore.getState().displayMode;
|
|
14
6
|
},
|
|
15
7
|
getServerSnapshot() {
|
|
16
|
-
return
|
|
8
|
+
return "browse";
|
|
17
9
|
},
|
|
18
10
|
subscribe(listener) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
return selectModeStore.subscribe((state, previous) => {
|
|
12
|
+
if (state.displayMode !== previous.displayMode) {
|
|
13
|
+
listener();
|
|
14
|
+
}
|
|
15
|
+
});
|
|
23
16
|
},
|
|
24
17
|
setDisplayMode(nextDisplayMode) {
|
|
25
|
-
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
displayMode = nextDisplayMode;
|
|
29
|
-
notify();
|
|
18
|
+
selectModeStore.getState().actions.setDisplayMode(nextDisplayMode);
|
|
30
19
|
},
|
|
31
20
|
reset() {
|
|
32
|
-
|
|
21
|
+
selectModeStore.getState().actions.reset();
|
|
33
22
|
}
|
|
34
23
|
};
|
|
35
|
-
function useDisplayMode() {
|
|
36
|
-
return useSyncExternalStore(
|
|
37
|
-
displayModeStore.subscribe,
|
|
38
|
-
displayModeStore.getSnapshot,
|
|
39
|
-
displayModeStore.getServerSnapshot
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
24
|
export {
|
|
43
|
-
displayModeStore
|
|
44
|
-
useDisplayMode
|
|
25
|
+
displayModeStore
|
|
45
26
|
};
|
|
@@ -1,19 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { DisplayMode } from "../../_vendor/schemas/generated/selectMode";
|
|
3
|
-
import type { SelectModeInteractionSession } from "../selectModeSession";
|
|
4
|
-
export declare function resetHoverRectDedup(): void;
|
|
5
|
-
/**
|
|
6
|
-
* Tracks whichever element is under the user's pointer while in `selection`
|
|
7
|
-
* mode and reports its bounding rect to the parent so the parent can render
|
|
8
|
-
* the hover overlay.
|
|
9
|
-
*
|
|
10
|
-
* Two behaviors worth knowing about:
|
|
11
|
-
* - During an active drag we suppress hover updates; the drag overlay is
|
|
12
|
-
* the only highlight we want visible.
|
|
13
|
-
* - Unchanged rectangles do not cross the iframe bridge. Viewport updates
|
|
14
|
-
* are coalesced because scroll and wheel can fire in the same frame.
|
|
15
|
-
*/
|
|
16
|
-
export declare function useSelectionHover({ displayMode, interactionSessionRef, }: {
|
|
17
|
-
displayMode: DisplayMode;
|
|
18
|
-
interactionSessionRef: MutableRefObject<SelectModeInteractionSession>;
|
|
19
|
-
}): void;
|
|
1
|
+
export declare function useSelectionHover(): void;
|
|
@@ -2,34 +2,25 @@ import { useEffect } from "react";
|
|
|
2
2
|
import { getNearestSelectableElement } from "../descriptor/elementDescriptor";
|
|
3
3
|
import { getWebsiteBuilderRpc } from "../iframeRpcChannel";
|
|
4
4
|
import { getElementBoundingRect } from "../interactions/selectionGeometry";
|
|
5
|
-
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
function useSelectionHover({
|
|
10
|
-
displayMode,
|
|
11
|
-
interactionSessionRef
|
|
12
|
-
}) {
|
|
5
|
+
import { useDisplayMode, useSelectModeStore } from "../selectModeStore";
|
|
6
|
+
function useSelectionHover() {
|
|
7
|
+
const store = useSelectModeStore();
|
|
8
|
+
const displayMode = useDisplayMode();
|
|
13
9
|
useEffect(() => {
|
|
14
10
|
if (displayMode !== "selection") {
|
|
15
11
|
return;
|
|
16
12
|
}
|
|
17
|
-
let hoverSyncAnimationFrameId = null;
|
|
18
|
-
let lastPointerClientPosition = null;
|
|
19
|
-
let lastPostedRect;
|
|
20
|
-
activeHoverDedupReset = () => {
|
|
21
|
-
lastPostedRect = void 0;
|
|
22
|
-
};
|
|
23
13
|
const postHoveredElement = (element) => {
|
|
14
|
+
const { lastPostedHoverRect, actions } = store.getState();
|
|
24
15
|
const rect = element ? getElementBoundingRect(element) : null;
|
|
25
|
-
const isUnchanged =
|
|
16
|
+
const isUnchanged = lastPostedHoverRect !== void 0 && (rect === lastPostedHoverRect || rect !== null && lastPostedHoverRect !== null && rect.x === lastPostedHoverRect.x && rect.y === lastPostedHoverRect.y && rect.width === lastPostedHoverRect.width && rect.height === lastPostedHoverRect.height);
|
|
26
17
|
if (isUnchanged) {
|
|
27
18
|
return;
|
|
28
19
|
}
|
|
29
|
-
|
|
20
|
+
actions.setLastPostedHoverRect(rect);
|
|
30
21
|
getWebsiteBuilderRpc()?.hoverElement({ rect });
|
|
31
22
|
};
|
|
32
|
-
const syncCurrentHover = (pointerClientPosition = lastPointerClientPosition) => {
|
|
23
|
+
const syncCurrentHover = (pointerClientPosition = store.getState().lastPointerClientPosition) => {
|
|
33
24
|
let hoveredElement = null;
|
|
34
25
|
if (pointerClientPosition) {
|
|
35
26
|
const isPointerWithinViewport = pointerClientPosition.x >= 0 && pointerClientPosition.y >= 0 && pointerClientPosition.x <= window.innerWidth && pointerClientPosition.y <= window.innerHeight;
|
|
@@ -49,79 +40,76 @@ function useSelectionHover({
|
|
|
49
40
|
);
|
|
50
41
|
};
|
|
51
42
|
const scheduleHoverSync = () => {
|
|
52
|
-
|
|
43
|
+
const { interactionSession, hoverSyncAnimationFrameId, actions } = store.getState();
|
|
44
|
+
if (interactionSession.type === "drag" || hoverSyncAnimationFrameId !== null) {
|
|
53
45
|
return;
|
|
54
46
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
actions.setHoverSyncAnimationFrameId(
|
|
48
|
+
requestAnimationFrame(() => {
|
|
49
|
+
store.getState().actions.setHoverSyncAnimationFrameId(null);
|
|
50
|
+
syncCurrentHover();
|
|
51
|
+
})
|
|
52
|
+
);
|
|
59
53
|
};
|
|
60
54
|
const handlePointerMove = (event) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
};
|
|
65
|
-
if (interactionSessionRef.current.type === "drag") {
|
|
55
|
+
const position = { x: event.clientX, y: event.clientY };
|
|
56
|
+
store.getState().actions.setLastPointerClientPosition(position);
|
|
57
|
+
if (store.getState().interactionSession.type === "drag") {
|
|
66
58
|
return;
|
|
67
59
|
}
|
|
68
|
-
syncCurrentHover(
|
|
60
|
+
syncCurrentHover(position);
|
|
69
61
|
};
|
|
70
62
|
const handlePointerOver = (event) => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
75
|
-
if (interactionSessionRef.current.type === "drag") {
|
|
63
|
+
const position = { x: event.clientX, y: event.clientY };
|
|
64
|
+
store.getState().actions.setLastPointerClientPosition(position);
|
|
65
|
+
if (store.getState().interactionSession.type === "drag") {
|
|
76
66
|
return;
|
|
77
67
|
}
|
|
78
|
-
syncCurrentHover(
|
|
68
|
+
syncCurrentHover(position);
|
|
79
69
|
};
|
|
80
70
|
const handlePointerOut = (event) => {
|
|
81
|
-
if (
|
|
71
|
+
if (store.getState().interactionSession.type === "drag") {
|
|
82
72
|
return;
|
|
83
73
|
}
|
|
84
74
|
const nextRelatedTarget = event.relatedTarget;
|
|
85
75
|
if (nextRelatedTarget instanceof Node && document.contains(nextRelatedTarget)) {
|
|
86
76
|
return;
|
|
87
77
|
}
|
|
88
|
-
|
|
78
|
+
const { hoverSyncAnimationFrameId, actions } = store.getState();
|
|
79
|
+
actions.setLastPointerClientPosition(null);
|
|
89
80
|
if (hoverSyncAnimationFrameId !== null) {
|
|
90
81
|
cancelAnimationFrame(hoverSyncAnimationFrameId);
|
|
91
|
-
|
|
82
|
+
actions.setHoverSyncAnimationFrameId(null);
|
|
92
83
|
}
|
|
93
84
|
postHoveredElement(null);
|
|
94
85
|
};
|
|
95
|
-
const handleViewportChange = () => {
|
|
96
|
-
scheduleHoverSync();
|
|
97
|
-
};
|
|
98
86
|
syncCurrentHover();
|
|
99
87
|
document.addEventListener("pointermove", handlePointerMove, true);
|
|
100
88
|
document.addEventListener("pointerover", handlePointerOver, true);
|
|
101
89
|
document.addEventListener("pointerout", handlePointerOut, true);
|
|
102
|
-
document.addEventListener("scroll",
|
|
103
|
-
document.addEventListener("wheel",
|
|
90
|
+
document.addEventListener("scroll", scheduleHoverSync, true);
|
|
91
|
+
document.addEventListener("wheel", scheduleHoverSync, {
|
|
104
92
|
capture: true,
|
|
105
93
|
passive: true
|
|
106
94
|
});
|
|
107
|
-
window.addEventListener("resize",
|
|
95
|
+
window.addEventListener("resize", scheduleHoverSync);
|
|
108
96
|
return () => {
|
|
109
|
-
|
|
97
|
+
const { hoverSyncAnimationFrameId } = store.getState();
|
|
110
98
|
if (hoverSyncAnimationFrameId !== null) {
|
|
111
99
|
cancelAnimationFrame(hoverSyncAnimationFrameId);
|
|
100
|
+
store.getState().actions.setHoverSyncAnimationFrameId(null);
|
|
112
101
|
}
|
|
113
102
|
document.removeEventListener("pointermove", handlePointerMove, true);
|
|
114
103
|
document.removeEventListener("pointerover", handlePointerOver, true);
|
|
115
104
|
document.removeEventListener("pointerout", handlePointerOut, true);
|
|
116
|
-
document.removeEventListener("scroll",
|
|
117
|
-
document.removeEventListener("wheel",
|
|
105
|
+
document.removeEventListener("scroll", scheduleHoverSync, true);
|
|
106
|
+
document.removeEventListener("wheel", scheduleHoverSync, {
|
|
118
107
|
capture: true
|
|
119
108
|
});
|
|
120
|
-
window.removeEventListener("resize",
|
|
109
|
+
window.removeEventListener("resize", scheduleHoverSync);
|
|
121
110
|
};
|
|
122
|
-
}, [displayMode,
|
|
111
|
+
}, [displayMode, store]);
|
|
123
112
|
}
|
|
124
113
|
export {
|
|
125
|
-
resetHoverRectDedup,
|
|
126
114
|
useSelectionHover
|
|
127
115
|
};
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import type { MutableRefObject } from "react";
|
|
2
|
-
import type { DisplayMode, SelectableData } from "../../_vendor/schemas/generated/selectMode";
|
|
3
|
-
import type { SelectModeInteractionSession } from "../selectModeSession";
|
|
4
1
|
/**
|
|
5
2
|
* Owns the iframe-side pointer interactions that drive selection:
|
|
6
3
|
*
|
|
@@ -24,12 +21,4 @@ import type { SelectModeInteractionSession } from "../selectModeSession";
|
|
|
24
21
|
* underlying page can't navigate or trigger handlers while the user is
|
|
25
22
|
* working in selection mode.
|
|
26
23
|
*/
|
|
27
|
-
export declare function useSelectionInteractions(
|
|
28
|
-
displayMode: DisplayMode;
|
|
29
|
-
editingElementRef?: MutableRefObject<HTMLElement | null>;
|
|
30
|
-
interactionSessionRef: MutableRefObject<SelectModeInteractionSession>;
|
|
31
|
-
onBeginEditing: (args: {
|
|
32
|
-
element: Element;
|
|
33
|
-
descriptorPromise: Promise<SelectableData>;
|
|
34
|
-
}) => void;
|
|
35
|
-
}): void;
|
|
24
|
+
export declare function useSelectionInteractions(): void;
|
|
@@ -19,18 +19,14 @@ import {
|
|
|
19
19
|
isAdditiveSelectionClick,
|
|
20
20
|
unionElementPageRects
|
|
21
21
|
} from "../interactions/selectionGeometry";
|
|
22
|
-
import {
|
|
23
|
-
hideSelectionFrame,
|
|
24
|
-
showAreaSelectionFrames,
|
|
25
|
-
showElementSelectionFrames
|
|
26
|
-
} from "../selectionFrame";
|
|
22
|
+
import { isTextEditableElement } from "../isTextEditableElement";
|
|
27
23
|
import { isSelectionStillCurrent } from "../selectionPublish";
|
|
28
24
|
import {
|
|
29
25
|
getConnectedSelectedElement,
|
|
30
26
|
IDLE_SESSION
|
|
31
27
|
} from "../selectModeSession";
|
|
28
|
+
import { useDisplayMode, useSelectModeStore } from "../selectModeStore";
|
|
32
29
|
import { symbolicateSelectedElement } from "../symbolicate";
|
|
33
|
-
import { isTextEditableElement } from "./useEditModeStateMachine";
|
|
34
30
|
import { getRepetitionSiblingElements } from "./useSiblingResolution";
|
|
35
31
|
const DOUBLE_CLICK_THRESHOLD_MS = 500;
|
|
36
32
|
function getSelectedElementsFromSession(session) {
|
|
@@ -49,21 +45,35 @@ function getSelectedElementsFromSession(session) {
|
|
|
49
45
|
async function maybeSymbolicate(data) {
|
|
50
46
|
return data.kind === "react-element" ? symbolicateSelectedElement(data) : data;
|
|
51
47
|
}
|
|
52
|
-
function useSelectionInteractions({
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
interactionSessionRef,
|
|
56
|
-
onBeginEditing
|
|
57
|
-
}) {
|
|
48
|
+
function useSelectionInteractions() {
|
|
49
|
+
const selectModeStore = useSelectModeStore();
|
|
50
|
+
const displayMode = useDisplayMode();
|
|
58
51
|
useEffect(() => {
|
|
59
52
|
if (displayMode !== "selection") {
|
|
60
53
|
return;
|
|
61
54
|
}
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
55
|
+
const store = () => selectModeStore.getState();
|
|
56
|
+
const interactionSessionRef = {
|
|
57
|
+
get current() {
|
|
58
|
+
return store().interactionSession;
|
|
59
|
+
},
|
|
60
|
+
set current(next) {
|
|
61
|
+
store().actions.setInteractionSession(next);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const editingElementRef = {
|
|
65
|
+
get current() {
|
|
66
|
+
return store().editingElement;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const actions = () => store().actions;
|
|
70
|
+
const onBeginEditing = actions().beginEditing;
|
|
71
|
+
const hideSelectionFrame = () => {
|
|
72
|
+
actions().hideSelectionFrames();
|
|
73
|
+
};
|
|
74
|
+
const showAreaSelectionFrames = actions().showAreaSelectionFrames;
|
|
75
|
+
const showElementSelectionFrames = actions().showElementSelectionFrames;
|
|
76
|
+
const editingPointerIds = store().editingPointerIds;
|
|
67
77
|
const clearCommittedSelection = ({
|
|
68
78
|
rect
|
|
69
79
|
}) => {
|
|
@@ -72,11 +82,12 @@ function useSelectionInteractions({
|
|
|
72
82
|
getWebsiteBuilderRpc()?.areaSelected({ rect, items: [] });
|
|
73
83
|
};
|
|
74
84
|
const stopDragAnimation = () => {
|
|
85
|
+
const dragAnimationFrameId = store().dragAnimationFrameId;
|
|
75
86
|
if (dragAnimationFrameId === null) {
|
|
76
87
|
return;
|
|
77
88
|
}
|
|
78
89
|
cancelAnimationFrame(dragAnimationFrameId);
|
|
79
|
-
|
|
90
|
+
actions().setDragAnimationFrameId(null);
|
|
80
91
|
};
|
|
81
92
|
const resetDragState = ({
|
|
82
93
|
notifyParentDragCleared = true
|
|
@@ -84,7 +95,7 @@ function useSelectionInteractions({
|
|
|
84
95
|
const currentSession = interactionSessionRef.current;
|
|
85
96
|
stopDragAnimation();
|
|
86
97
|
if (currentSession.type !== "drag") {
|
|
87
|
-
|
|
98
|
+
actions().setDragCandidates([]);
|
|
88
99
|
return;
|
|
89
100
|
}
|
|
90
101
|
if (notifyParentDragCleared && currentSession.hasExceededThreshold) {
|
|
@@ -93,7 +104,7 @@ function useSelectionInteractions({
|
|
|
93
104
|
selectionRects: []
|
|
94
105
|
});
|
|
95
106
|
}
|
|
96
|
-
|
|
107
|
+
actions().setDragCandidates([]);
|
|
97
108
|
interactionSessionRef.current = IDLE_SESSION;
|
|
98
109
|
};
|
|
99
110
|
const postCurrentDragRect = () => {
|
|
@@ -120,7 +131,7 @@ function useSelectionInteractions({
|
|
|
120
131
|
scrollOffset: session.scrollOffset
|
|
121
132
|
});
|
|
122
133
|
const selectionRects = getCandidatesInSelection({
|
|
123
|
-
candidates: dragCandidates,
|
|
134
|
+
candidates: store().dragCandidates,
|
|
124
135
|
selectionPageRect
|
|
125
136
|
}).map((candidate) => ({
|
|
126
137
|
id: candidate.id,
|
|
@@ -137,7 +148,7 @@ function useSelectionInteractions({
|
|
|
137
148
|
const syncDragRectToViewport = () => {
|
|
138
149
|
const session = interactionSessionRef.current;
|
|
139
150
|
if (session.type !== "drag") {
|
|
140
|
-
|
|
151
|
+
actions().setDragAnimationFrameId(null);
|
|
141
152
|
return;
|
|
142
153
|
}
|
|
143
154
|
const nextScrollOffset = getDocumentScrollOffset();
|
|
@@ -159,7 +170,9 @@ function useSelectionInteractions({
|
|
|
159
170
|
scrollOffset: nextScrollOffset
|
|
160
171
|
};
|
|
161
172
|
}
|
|
162
|
-
|
|
173
|
+
actions().setDragAnimationFrameId(
|
|
174
|
+
requestAnimationFrame(syncDragRectToViewport)
|
|
175
|
+
);
|
|
163
176
|
};
|
|
164
177
|
const handlePointerDown = (event) => {
|
|
165
178
|
if (event.button !== 0) {
|
|
@@ -173,8 +186,8 @@ function useSelectionInteractions({
|
|
|
173
186
|
event.preventDefault();
|
|
174
187
|
event.stopPropagation();
|
|
175
188
|
const scrollOffset = getDocumentScrollOffset();
|
|
176
|
-
|
|
177
|
-
interactionSessionRef.current
|
|
189
|
+
actions().setCommittedSelectionAtPointerDown(
|
|
190
|
+
getSelectedElementsFromSession(interactionSessionRef.current)
|
|
178
191
|
);
|
|
179
192
|
const dragStart = getDragPositionFromClient({
|
|
180
193
|
clientX: event.clientX,
|
|
@@ -188,9 +201,11 @@ function useSelectionInteractions({
|
|
|
188
201
|
scrollOffset,
|
|
189
202
|
hasExceededThreshold: false
|
|
190
203
|
};
|
|
191
|
-
|
|
192
|
-
if (dragAnimationFrameId === null) {
|
|
193
|
-
|
|
204
|
+
actions().setDragCandidates(collectDragCandidates(scrollOffset));
|
|
205
|
+
if (store().dragAnimationFrameId === null) {
|
|
206
|
+
actions().setDragAnimationFrameId(
|
|
207
|
+
requestAnimationFrame(syncDragRectToViewport)
|
|
208
|
+
);
|
|
194
209
|
}
|
|
195
210
|
};
|
|
196
211
|
const handlePointerMove = (event) => {
|
|
@@ -210,10 +225,12 @@ function useSelectionInteractions({
|
|
|
210
225
|
};
|
|
211
226
|
const handleViewportChangeDuringDrag = () => {
|
|
212
227
|
const session = interactionSessionRef.current;
|
|
213
|
-
if (session.type !== "drag" || dragAnimationFrameId !== null) {
|
|
228
|
+
if (session.type !== "drag" || store().dragAnimationFrameId !== null) {
|
|
214
229
|
return;
|
|
215
230
|
}
|
|
216
|
-
|
|
231
|
+
actions().setDragAnimationFrameId(
|
|
232
|
+
requestAnimationFrame(syncDragRectToViewport)
|
|
233
|
+
);
|
|
217
234
|
};
|
|
218
235
|
const commitAreaSelection = ({
|
|
219
236
|
selectedElements,
|
|
@@ -275,9 +292,9 @@ function useSelectionInteractions({
|
|
|
275
292
|
scrollOffset
|
|
276
293
|
});
|
|
277
294
|
const now = pointerEvent.timeStamp;
|
|
278
|
-
const previousClick = lastClick;
|
|
295
|
+
const previousClick = store().lastClick;
|
|
279
296
|
if (previousClick && previousClick.target === selectionElement && now - previousClick.time <= DOUBLE_CLICK_THRESHOLD_MS && isTextEditableElement(selectionElement)) {
|
|
280
|
-
|
|
297
|
+
actions().setLastClick(null);
|
|
281
298
|
onBeginEditing({
|
|
282
299
|
element: selectionElement,
|
|
283
300
|
descriptorPromise: previousClick.descriptorPromise
|
|
@@ -289,11 +306,11 @@ function useSelectionInteractions({
|
|
|
289
306
|
clickPosition,
|
|
290
307
|
siblingSelectors: siblingElements.map(buildSelector)
|
|
291
308
|
});
|
|
292
|
-
|
|
309
|
+
actions().setLastClick({
|
|
293
310
|
descriptorPromise: Promise.resolve(data),
|
|
294
311
|
target: selectionElement,
|
|
295
312
|
time: now
|
|
296
|
-
};
|
|
313
|
+
});
|
|
297
314
|
if (!selectionElement.isConnected) {
|
|
298
315
|
return;
|
|
299
316
|
}
|
|
@@ -309,6 +326,7 @@ function useSelectionInteractions({
|
|
|
309
326
|
};
|
|
310
327
|
void maybeSymbolicate(data).then((symbolicated) => {
|
|
311
328
|
const session = interactionSessionRef.current;
|
|
329
|
+
const committedSelectionAtPointerDown = store().committedSelectionAtPointerDown;
|
|
312
330
|
const isPendingClickOnSelection = session.type === "drag" && !session.hasExceededThreshold && committedSelectionAtPointerDown.length === 1 && committedSelectionAtPointerDown[0] === selectionElement;
|
|
313
331
|
if (!isPendingClickOnSelection && !isSelectionStillCurrent({
|
|
314
332
|
session: interactionSessionRef.current,
|
|
@@ -329,14 +347,14 @@ function useSelectionInteractions({
|
|
|
329
347
|
commitElementSelection({ selectionElement, pointerEvent });
|
|
330
348
|
return;
|
|
331
349
|
}
|
|
332
|
-
|
|
350
|
+
actions().setLastClick(null);
|
|
333
351
|
const pointerRect = getPointRect({
|
|
334
352
|
x: pointerEvent.clientX,
|
|
335
353
|
y: pointerEvent.clientY
|
|
336
354
|
});
|
|
337
355
|
const nextElements = getSelectionAfterAdditiveClick({
|
|
338
356
|
candidates,
|
|
339
|
-
currentSelectedElements: committedSelectionAtPointerDown,
|
|
357
|
+
currentSelectedElements: store().committedSelectionAtPointerDown,
|
|
340
358
|
targetElement: selectionElement
|
|
341
359
|
});
|
|
342
360
|
const [firstElement] = nextElements;
|
|
@@ -349,7 +367,7 @@ function useSelectionInteractions({
|
|
|
349
367
|
selectionElement: firstElement,
|
|
350
368
|
pointerEvent
|
|
351
369
|
});
|
|
352
|
-
|
|
370
|
+
actions().setLastClick(null);
|
|
353
371
|
return;
|
|
354
372
|
}
|
|
355
373
|
const scrollOffset = getDocumentScrollOffset();
|
|
@@ -382,7 +400,7 @@ function useSelectionInteractions({
|
|
|
382
400
|
const dragStart = snapshot.type === "drag" ? snapshot.dragStart : null;
|
|
383
401
|
const dragCurrent = snapshot.type === "drag" ? snapshot.dragCurrent : null;
|
|
384
402
|
const scrollOffset = snapshot.type === "drag" ? snapshot.scrollOffset : getDocumentScrollOffset();
|
|
385
|
-
const candidates = dragCandidates;
|
|
403
|
+
const candidates = store().dragCandidates;
|
|
386
404
|
resetDragState({ notifyParentDragCleared: false });
|
|
387
405
|
if (!dragStart || snapshot.type !== "drag") {
|
|
388
406
|
const targetElement = event.target;
|
|
@@ -469,7 +487,7 @@ function useSelectionInteractions({
|
|
|
469
487
|
window.removeEventListener("resize", handleViewportChangeDuringDrag);
|
|
470
488
|
resetDragState();
|
|
471
489
|
};
|
|
472
|
-
}, [displayMode,
|
|
490
|
+
}, [displayMode, selectModeStore]);
|
|
473
491
|
}
|
|
474
492
|
export {
|
|
475
493
|
useSelectionInteractions
|
|
@@ -1,25 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { DisplayMode } from "../../_vendor/schemas/generated/selectMode";
|
|
3
|
-
import type { SelectModeInteractionSession } from "../selectModeSession";
|
|
4
|
-
/**
|
|
5
|
-
* After a selection (element or area) has been resolved, the parent's
|
|
6
|
-
* highlight overlay is positioned in viewport coordinates. When the user
|
|
7
|
-
* scrolls the iframe document the highlight would otherwise lag behind the
|
|
8
|
-
* underlying element. This hook re-sends an updated rect on every scroll
|
|
9
|
-
* (rAF-coalesced) so the parent overlay tracks the element/area through
|
|
10
|
-
* scrolls without us having to mirror scroll state into the parent.
|
|
11
|
-
*
|
|
12
|
-
* - For an `element` session we send only `{ boundingRect, clickPosition }`
|
|
13
|
-
* — the rest of the descriptor (selector, outerHTML, computed styles,
|
|
14
|
-
* text metrics, source location) was captured at selection-commit time
|
|
15
|
-
* and doesn't change while the user is just scrolling. Rebuilding the
|
|
16
|
-
* full descriptor here would call `serializeStyledElement` recursively.
|
|
17
|
-
* The in-iframe frames only remeasure the selected repetition group.
|
|
18
|
-
* - For an `area` session we keep the original *page* rect (which doesn't
|
|
19
|
-
* move) and convert it to viewport coordinates using the latest scroll
|
|
20
|
-
* offset before sending.
|
|
21
|
-
*/
|
|
22
|
-
export declare function useSelectionScrollSync({ displayMode, interactionSessionRef, }: {
|
|
23
|
-
displayMode: DisplayMode;
|
|
24
|
-
interactionSessionRef: MutableRefObject<SelectModeInteractionSession>;
|
|
25
|
-
}): void;
|
|
1
|
+
export declare function useSelectionScrollSync(): void;
|
|
@@ -5,59 +5,67 @@ import {
|
|
|
5
5
|
getElementBoundingRect,
|
|
6
6
|
getViewportRectFromPageRect
|
|
7
7
|
} from "../interactions/selectionGeometry";
|
|
8
|
-
import {
|
|
9
|
-
function useSelectionScrollSync({
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}) {
|
|
8
|
+
import { useDisplayMode, useSelectModeStore } from "../selectModeStore";
|
|
9
|
+
function useSelectionScrollSync() {
|
|
10
|
+
const store = useSelectModeStore();
|
|
11
|
+
const displayMode = useDisplayMode();
|
|
13
12
|
useEffect(() => {
|
|
14
13
|
if (displayMode !== "selection") {
|
|
15
14
|
return;
|
|
16
15
|
}
|
|
17
|
-
let animationFrameId = null;
|
|
18
16
|
const handleScroll = () => {
|
|
19
|
-
|
|
17
|
+
const { scrollSyncAnimationFrameId, actions } = store.getState();
|
|
18
|
+
if (scrollSyncAnimationFrameId !== null) {
|
|
20
19
|
return;
|
|
21
20
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
scrollOffset
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
21
|
+
actions.setScrollSyncAnimationFrameId(
|
|
22
|
+
requestAnimationFrame(() => {
|
|
23
|
+
const { interactionSession, actions: storeActions } = store.getState();
|
|
24
|
+
storeActions.setScrollSyncAnimationFrameId(null);
|
|
25
|
+
if (interactionSession.type === "element") {
|
|
26
|
+
const boundingRect = getElementBoundingRect(
|
|
27
|
+
interactionSession.element
|
|
28
|
+
);
|
|
29
|
+
const sessionClickPosition = interactionSession.clickPosition;
|
|
30
|
+
const clickPosition = {
|
|
31
|
+
x: sessionClickPosition !== null ? boundingRect.left + sessionClickPosition.x : boundingRect.right,
|
|
32
|
+
y: sessionClickPosition !== null ? boundingRect.top + sessionClickPosition.y : boundingRect.bottom
|
|
33
|
+
};
|
|
34
|
+
const scrollOffset = getDocumentScrollOffset();
|
|
35
|
+
storeActions.showElementSelectionFrames({
|
|
36
|
+
elements: [
|
|
37
|
+
interactionSession.element,
|
|
38
|
+
...interactionSession.siblingElements
|
|
39
|
+
],
|
|
40
|
+
scrollOffset
|
|
41
|
+
});
|
|
42
|
+
getWebsiteBuilderRpc()?.selectedElementRectUpdate({
|
|
43
|
+
rect: boundingRect,
|
|
44
|
+
clickPosition
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (interactionSession.type === "area") {
|
|
49
|
+
getWebsiteBuilderRpc()?.selectedAreaRectUpdate({
|
|
50
|
+
rect: getViewportRectFromPageRect({
|
|
51
|
+
rect: interactionSession.pageRect,
|
|
52
|
+
scrollOffset: getDocumentScrollOffset()
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
);
|
|
52
58
|
};
|
|
53
59
|
document.addEventListener("scroll", handleScroll, true);
|
|
54
60
|
return () => {
|
|
55
61
|
document.removeEventListener("scroll", handleScroll, true);
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
const { scrollSyncAnimationFrameId, actions } = store.getState();
|
|
63
|
+
if (scrollSyncAnimationFrameId !== null) {
|
|
64
|
+
cancelAnimationFrame(scrollSyncAnimationFrameId);
|
|
65
|
+
actions.setScrollSyncAnimationFrameId(null);
|
|
58
66
|
}
|
|
59
67
|
};
|
|
60
|
-
}, [displayMode,
|
|
68
|
+
}, [displayMode, store]);
|
|
61
69
|
}
|
|
62
70
|
export {
|
|
63
71
|
useSelectionScrollSync
|