@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,164 +1,40 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
function applyVariantStyle({
|
|
9
|
-
frame,
|
|
1
|
+
const SELECTION_FRAMES_CONTAINER_ID = "replo-select-mode-selection-frames";
|
|
2
|
+
const SELECTION_FRAME_BORDER_HEX = "#c026d3";
|
|
3
|
+
const SELECTION_FRAME_EDITING_BORDER_HEX = "#e879f9";
|
|
4
|
+
function getFrameSpecs({
|
|
5
|
+
elements,
|
|
6
|
+
scrollOffset,
|
|
10
7
|
variant
|
|
11
8
|
}) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const container = document.getElementById(FRAMES_CONTAINER_ID);
|
|
24
|
-
if (!(container instanceof HTMLDivElement)) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
for (const child of container.children) {
|
|
28
|
-
if (child instanceof HTMLDivElement) {
|
|
29
|
-
child.style.borderColor = getFrameBorderHex();
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function getOrCreateFramesLayerContainer({
|
|
34
|
-
id,
|
|
35
|
-
zIndex
|
|
36
|
-
}) {
|
|
37
|
-
if (typeof document === "undefined") {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
const existing = document.getElementById(id);
|
|
41
|
-
if (existing instanceof HTMLDivElement) {
|
|
42
|
-
return existing;
|
|
43
|
-
}
|
|
44
|
-
if (!document.body) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
const container = document.createElement("div");
|
|
48
|
-
container.id = id;
|
|
49
|
-
container.setAttribute("aria-hidden", "true");
|
|
50
|
-
container.style.cssText = [
|
|
51
|
-
"position: absolute",
|
|
52
|
-
"top: 0",
|
|
53
|
-
"left: 0",
|
|
54
|
-
"width: 0",
|
|
55
|
-
"height: 0",
|
|
56
|
-
"margin: 0",
|
|
57
|
-
"padding: 0",
|
|
58
|
-
"pointer-events: none",
|
|
59
|
-
`z-index: ${zIndex}`
|
|
60
|
-
].join(";");
|
|
61
|
-
document.body.append(container);
|
|
62
|
-
return container;
|
|
63
|
-
}
|
|
64
|
-
function getOrCreateContainer() {
|
|
65
|
-
return getOrCreateFramesLayerContainer({
|
|
66
|
-
id: FRAMES_CONTAINER_ID,
|
|
67
|
-
zIndex: 2147483646
|
|
9
|
+
return elements.map((element) => {
|
|
10
|
+
const rect = element.getBoundingClientRect();
|
|
11
|
+
return {
|
|
12
|
+
variant,
|
|
13
|
+
pageRect: {
|
|
14
|
+
x: rect.left + scrollOffset.left,
|
|
15
|
+
y: rect.top + scrollOffset.top,
|
|
16
|
+
width: rect.width,
|
|
17
|
+
height: rect.height
|
|
18
|
+
}
|
|
19
|
+
};
|
|
68
20
|
});
|
|
69
21
|
}
|
|
70
|
-
function
|
|
71
|
-
const frame = document.createElement("div");
|
|
72
|
-
frame.setAttribute("aria-hidden", "true");
|
|
73
|
-
frame.style.cssText = [
|
|
74
|
-
"position: absolute",
|
|
75
|
-
"top: 0",
|
|
76
|
-
"left: 0",
|
|
77
|
-
"box-sizing: border-box",
|
|
78
|
-
"border-radius: 2px",
|
|
79
|
-
"pointer-events: none",
|
|
80
|
-
"margin: 0",
|
|
81
|
-
"padding: 0"
|
|
82
|
-
].join(";");
|
|
83
|
-
return frame;
|
|
84
|
-
}
|
|
85
|
-
function showSelectionFrames({
|
|
86
|
-
frames
|
|
87
|
-
}) {
|
|
88
|
-
const container = getOrCreateContainer();
|
|
89
|
-
if (!container) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
while (container.childElementCount > frames.length) {
|
|
93
|
-
container.lastElementChild?.remove();
|
|
94
|
-
}
|
|
95
|
-
while (container.childElementCount < frames.length) {
|
|
96
|
-
container.append(createFrameNode());
|
|
97
|
-
}
|
|
98
|
-
frames.forEach((spec, index) => {
|
|
99
|
-
const frame = container.children[index];
|
|
100
|
-
if (!(frame instanceof HTMLDivElement)) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
applyVariantStyle({ frame, variant: spec.variant });
|
|
104
|
-
frame.style.left = `${spec.pageRect.x}px`;
|
|
105
|
-
frame.style.top = `${spec.pageRect.y}px`;
|
|
106
|
-
frame.style.width = `${spec.pageRect.width}px`;
|
|
107
|
-
frame.style.height = `${spec.pageRect.height}px`;
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
function showElementSelectionFrames({
|
|
22
|
+
function getElementSelectionFrameSpecs({
|
|
111
23
|
elements,
|
|
112
24
|
scrollOffset
|
|
113
25
|
}) {
|
|
114
|
-
|
|
115
|
-
frames: elements.map((element) => {
|
|
116
|
-
const rect = element.getBoundingClientRect();
|
|
117
|
-
return {
|
|
118
|
-
variant: "element",
|
|
119
|
-
pageRect: {
|
|
120
|
-
x: rect.left + scrollOffset.left,
|
|
121
|
-
y: rect.top + scrollOffset.top,
|
|
122
|
-
width: rect.width,
|
|
123
|
-
height: rect.height
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
})
|
|
127
|
-
});
|
|
26
|
+
return getFrameSpecs({ elements, scrollOffset, variant: "element" });
|
|
128
27
|
}
|
|
129
|
-
function
|
|
28
|
+
function getAreaSelectionFrameSpecs({
|
|
130
29
|
elements,
|
|
131
30
|
scrollOffset
|
|
132
31
|
}) {
|
|
133
|
-
|
|
134
|
-
frames: elements.map((element) => {
|
|
135
|
-
const rect = element.getBoundingClientRect();
|
|
136
|
-
return {
|
|
137
|
-
variant: "area",
|
|
138
|
-
pageRect: {
|
|
139
|
-
x: rect.left + scrollOffset.left,
|
|
140
|
-
y: rect.top + scrollOffset.top,
|
|
141
|
-
width: rect.width,
|
|
142
|
-
height: rect.height
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
})
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
function hideSelectionFrame() {
|
|
149
|
-
if (typeof document === "undefined") {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const container = document.getElementById(FRAMES_CONTAINER_ID);
|
|
153
|
-
if (container instanceof HTMLDivElement) {
|
|
154
|
-
container.replaceChildren();
|
|
155
|
-
}
|
|
32
|
+
return getFrameSpecs({ elements, scrollOffset, variant: "area" });
|
|
156
33
|
}
|
|
157
34
|
export {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
showSelectionFrames
|
|
35
|
+
SELECTION_FRAMES_CONTAINER_ID,
|
|
36
|
+
SELECTION_FRAME_BORDER_HEX,
|
|
37
|
+
SELECTION_FRAME_EDITING_BORDER_HEX,
|
|
38
|
+
getAreaSelectionFrameSpecs,
|
|
39
|
+
getElementSelectionFrameSpecs
|
|
164
40
|
};
|
|
@@ -47,6 +47,10 @@ function normalizeFilePathForSymbolicator(filePath) {
|
|
|
47
47
|
let normalized = filePath;
|
|
48
48
|
if (normalized.startsWith("about://React/Server/")) {
|
|
49
49
|
normalized = normalized.slice("about://React/Server/".length);
|
|
50
|
+
try {
|
|
51
|
+
normalized = decodeURI(normalized);
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
50
54
|
}
|
|
51
55
|
normalized = normalized.replace(/\?\d+$/, "");
|
|
52
56
|
return rewriteBrowserChunkUrlToDistPath(normalized);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Editor, Mark } from "@tiptap/core";
|
|
1
|
+
import { Editor, Mark, Node } from "@tiptap/core";
|
|
2
2
|
import Bold from "@tiptap/extension-bold";
|
|
3
3
|
import Document from "@tiptap/extension-document";
|
|
4
4
|
import Italic from "@tiptap/extension-italic";
|
|
@@ -8,6 +8,20 @@ import Underline from "@tiptap/extension-underline";
|
|
|
8
8
|
const InlineDocument = Document.extend({
|
|
9
9
|
content: "inline*"
|
|
10
10
|
});
|
|
11
|
+
const InlineBreak = Node.create({
|
|
12
|
+
name: "hardBreak",
|
|
13
|
+
inline: true,
|
|
14
|
+
group: "inline",
|
|
15
|
+
selectable: false,
|
|
16
|
+
parseHTML: () => [{ tag: "br" }],
|
|
17
|
+
renderHTML: () => ["br"],
|
|
18
|
+
renderText: () => "\n",
|
|
19
|
+
addKeyboardShortcuts() {
|
|
20
|
+
return {
|
|
21
|
+
Enter: () => this.editor.commands.insertContent({ type: this.name })
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
});
|
|
11
25
|
const InlineLink = Mark.create({
|
|
12
26
|
name: "link",
|
|
13
27
|
inclusive: false,
|
|
@@ -51,6 +65,7 @@ function createInlineTiptapEditor({
|
|
|
51
65
|
extensions: [
|
|
52
66
|
InlineDocument,
|
|
53
67
|
Text,
|
|
68
|
+
InlineBreak,
|
|
54
69
|
Bold,
|
|
55
70
|
Italic,
|
|
56
71
|
Underline,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { DisplayMode } from "../../_vendor/schemas/generated/selectMode";
|
|
2
|
-
/**
|
|
3
|
-
* Forces a crosshair cursor + disables text selection / drag while the iframe
|
|
4
|
-
* is in `selection` mode. We inject a `<style>` tag into the iframe document
|
|
5
|
-
* (rather than toggling a class on `<html>`) so that the `!important` rule
|
|
6
|
-
* wins against any per-element cursor styling on the page being edited.
|
|
7
|
-
*/
|
|
8
|
-
export declare function useCrosshairCursor({ displayMode, }: {
|
|
9
|
-
displayMode: DisplayMode;
|
|
10
|
-
}): void;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
2
|
-
function useCrosshairCursor({
|
|
3
|
-
displayMode
|
|
4
|
-
}) {
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
if (displayMode !== "selection") {
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
const styleElement = document.createElement("style");
|
|
10
|
-
styleElement.textContent = `
|
|
11
|
-
* { cursor: crosshair !important; user-select: none !important; -webkit-user-drag: none !important; }
|
|
12
|
-
[data-select-mode-inline-editor][contenteditable="true"],
|
|
13
|
-
[data-select-mode-inline-editor][contenteditable="true"] * { cursor: text !important; }
|
|
14
|
-
`;
|
|
15
|
-
document.head.append(styleElement);
|
|
16
|
-
return () => {
|
|
17
|
-
styleElement.remove();
|
|
18
|
-
};
|
|
19
|
-
}, [displayMode]);
|
|
20
|
-
}
|
|
21
|
-
export {
|
|
22
|
-
useCrosshairCursor
|
|
23
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { MutableRefObject } from "react";
|
|
2
|
-
import type { DisplayMode, SelectableData, SelectModeFormatCommand } from "../../_vendor/schemas/generated/selectMode";
|
|
3
|
-
import type { SelectModeInteractionSession } from "../selectModeSession";
|
|
4
|
-
export declare function isTextEditableElement(element: Element): element is HTMLElement;
|
|
5
|
-
export declare function useEditModeStateMachine({ displayMode, interactionSessionRef, }: {
|
|
6
|
-
displayMode: DisplayMode;
|
|
7
|
-
interactionSessionRef: MutableRefObject<SelectModeInteractionSession>;
|
|
8
|
-
}): {
|
|
9
|
-
beginEditing: ({ element, descriptorPromise, }: {
|
|
10
|
-
element: Element;
|
|
11
|
-
descriptorPromise: Promise<SelectableData>;
|
|
12
|
-
}) => void;
|
|
13
|
-
editingElementRef: import("react").RefObject<HTMLElement | null>;
|
|
14
|
-
formatCommand: (command: SelectModeFormatCommand) => void;
|
|
15
|
-
revert: () => void;
|
|
16
|
-
};
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
-
import {
|
|
3
|
-
getWebsiteBuilderRpc,
|
|
4
|
-
requestInlineTextEdit
|
|
5
|
-
} from "../iframeRpcChannel";
|
|
6
|
-
import { setSelectionFrameEditing } from "../selectionFrame";
|
|
7
|
-
import { symbolicateSelectedElement } from "../symbolicate";
|
|
8
|
-
import { createInlineTiptapEditor } from "../tiptapInlineEditor";
|
|
9
|
-
import { getRepetitionSiblingElements } from "./useSiblingResolution";
|
|
10
|
-
const EMPTY_ACTIVE_MARKS = {
|
|
11
|
-
bold: false,
|
|
12
|
-
italic: false,
|
|
13
|
-
underline: false,
|
|
14
|
-
strike: false
|
|
15
|
-
};
|
|
16
|
-
function postEditorState({
|
|
17
|
-
activeMarks,
|
|
18
|
-
isEditing
|
|
19
|
-
}) {
|
|
20
|
-
getWebsiteBuilderRpc()?.editorStateUpdate({ activeMarks, isEditing });
|
|
21
|
-
}
|
|
22
|
-
function isTextEditableElement(element) {
|
|
23
|
-
const tagName = element.tagName.toLowerCase();
|
|
24
|
-
return element instanceof HTMLElement && Boolean(element.textContent?.trim()) && ![
|
|
25
|
-
"input",
|
|
26
|
-
"textarea",
|
|
27
|
-
"select",
|
|
28
|
-
"option",
|
|
29
|
-
"img",
|
|
30
|
-
"video",
|
|
31
|
-
"canvas"
|
|
32
|
-
].includes(tagName);
|
|
33
|
-
}
|
|
34
|
-
const FOCUS_STEAL_GUARD_MS = 1500;
|
|
35
|
-
function useEditModeStateMachine({
|
|
36
|
-
displayMode,
|
|
37
|
-
interactionSessionRef
|
|
38
|
-
}) {
|
|
39
|
-
const editorRef = useRef(null);
|
|
40
|
-
const editingElementRef = useRef(null);
|
|
41
|
-
const editIdRef = useRef(null);
|
|
42
|
-
const pendingEditRef = useRef(null);
|
|
43
|
-
const originalInnerHTMLRef = useRef(null);
|
|
44
|
-
const initialHTMLRef = useRef(null);
|
|
45
|
-
const isCommittingRef = useRef(false);
|
|
46
|
-
const beginEditingAtRef = useRef(0);
|
|
47
|
-
const destroyEditor = useCallback(() => {
|
|
48
|
-
editorRef.current?.destroy();
|
|
49
|
-
editorRef.current = null;
|
|
50
|
-
editingElementRef.current = null;
|
|
51
|
-
editIdRef.current = null;
|
|
52
|
-
if (pendingEditRef.current) {
|
|
53
|
-
clearTimeout(pendingEditRef.current.timeout);
|
|
54
|
-
pendingEditRef.current = null;
|
|
55
|
-
}
|
|
56
|
-
originalInnerHTMLRef.current = null;
|
|
57
|
-
initialHTMLRef.current = null;
|
|
58
|
-
isCommittingRef.current = false;
|
|
59
|
-
setSelectionFrameEditing(false);
|
|
60
|
-
postEditorState({ activeMarks: EMPTY_ACTIVE_MARKS, isEditing: false });
|
|
61
|
-
}, []);
|
|
62
|
-
const commit = useCallback(() => {
|
|
63
|
-
const editor = editorRef.current;
|
|
64
|
-
const editingElement = editingElementRef.current;
|
|
65
|
-
const originalInnerHTML = originalInnerHTMLRef.current;
|
|
66
|
-
const editId = editIdRef.current;
|
|
67
|
-
if (!editor || !editId || originalInnerHTML === null || isCommittingRef.current) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
isCommittingRef.current = true;
|
|
71
|
-
const committedHTML = editor.getHTML();
|
|
72
|
-
if (committedHTML === initialHTMLRef.current) {
|
|
73
|
-
destroyEditor();
|
|
74
|
-
if (editingElement) {
|
|
75
|
-
editingElement.innerHTML = originalInnerHTML;
|
|
76
|
-
}
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const committedJSON = editor.getJSON();
|
|
80
|
-
getWebsiteBuilderRpc()?.textCommitted({
|
|
81
|
-
editId,
|
|
82
|
-
html: committedHTML,
|
|
83
|
-
json: committedJSON,
|
|
84
|
-
originalInnerHTML
|
|
85
|
-
});
|
|
86
|
-
destroyEditor();
|
|
87
|
-
if (editingElement) {
|
|
88
|
-
editingElement.innerHTML = committedHTML;
|
|
89
|
-
}
|
|
90
|
-
}, [destroyEditor]);
|
|
91
|
-
const revert = useCallback(() => {
|
|
92
|
-
const editingElement = editingElementRef.current;
|
|
93
|
-
const originalInnerHTML = originalInnerHTMLRef.current;
|
|
94
|
-
destroyEditor();
|
|
95
|
-
if (editingElement && originalInnerHTML !== null) {
|
|
96
|
-
editingElement.innerHTML = originalInnerHTML;
|
|
97
|
-
}
|
|
98
|
-
}, [destroyEditor]);
|
|
99
|
-
const handleBlur = useCallback(() => {
|
|
100
|
-
if (isCommittingRef.current) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (typeof document !== "undefined" && document.hasFocus()) {
|
|
104
|
-
commit();
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (Date.now() - beginEditingAtRef.current < FOCUS_STEAL_GUARD_MS) {
|
|
108
|
-
editorRef.current?.focus();
|
|
109
|
-
}
|
|
110
|
-
}, [commit]);
|
|
111
|
-
const formatCommand = useCallback((command) => {
|
|
112
|
-
editorRef.current?.runFormatCommand(command);
|
|
113
|
-
}, []);
|
|
114
|
-
const clearPendingEdit = useCallback((editId) => {
|
|
115
|
-
if (pendingEditRef.current?.editId !== editId) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
clearTimeout(pendingEditRef.current.timeout);
|
|
119
|
-
pendingEditRef.current = null;
|
|
120
|
-
}, []);
|
|
121
|
-
const resolveEditRequest = useCallback(
|
|
122
|
-
({ editId, allowed }) => {
|
|
123
|
-
const pendingEdit = pendingEditRef.current;
|
|
124
|
-
if (!pendingEdit || pendingEdit.editId !== editId) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
clearTimeout(pendingEdit.timeout);
|
|
128
|
-
pendingEditRef.current = null;
|
|
129
|
-
if (!allowed || !pendingEdit.element.isConnected) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
const element = pendingEdit.element;
|
|
133
|
-
const originalInnerHTML = element.innerHTML;
|
|
134
|
-
editingElementRef.current = element;
|
|
135
|
-
editIdRef.current = editId;
|
|
136
|
-
originalInnerHTMLRef.current = originalInnerHTML;
|
|
137
|
-
editorRef.current = createInlineTiptapEditor({
|
|
138
|
-
element,
|
|
139
|
-
content: originalInnerHTML,
|
|
140
|
-
onCommit: commit,
|
|
141
|
-
onEscape: revert,
|
|
142
|
-
onBlur: handleBlur,
|
|
143
|
-
onSelectionUpdate: (activeMarks) => {
|
|
144
|
-
postEditorState({ activeMarks, isEditing: true });
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
initialHTMLRef.current = editorRef.current.getHTML();
|
|
148
|
-
beginEditingAtRef.current = Date.now();
|
|
149
|
-
editorRef.current.focusAndSelectAll();
|
|
150
|
-
setSelectionFrameEditing(true);
|
|
151
|
-
postEditorState({ activeMarks: EMPTY_ACTIVE_MARKS, isEditing: true });
|
|
152
|
-
},
|
|
153
|
-
[commit, handleBlur, revert]
|
|
154
|
-
);
|
|
155
|
-
const beginEditing = useCallback(
|
|
156
|
-
({
|
|
157
|
-
element,
|
|
158
|
-
descriptorPromise
|
|
159
|
-
}) => {
|
|
160
|
-
if (editingElementRef.current === element) {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
if (!isTextEditableElement(element)) {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
if (pendingEditRef.current?.element === element) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
void descriptorPromise.then((descriptor) => {
|
|
170
|
-
if (descriptor.kind !== "react-element" || !element.isConnected) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
interactionSessionRef.current = {
|
|
174
|
-
type: "element",
|
|
175
|
-
element,
|
|
176
|
-
siblingElements: getRepetitionSiblingElements({ element }),
|
|
177
|
-
clickPosition: null,
|
|
178
|
-
data: descriptor
|
|
179
|
-
};
|
|
180
|
-
const editId = crypto.randomUUID();
|
|
181
|
-
if (pendingEditRef.current) {
|
|
182
|
-
clearTimeout(pendingEditRef.current.timeout);
|
|
183
|
-
}
|
|
184
|
-
const timeout = window.setTimeout(() => {
|
|
185
|
-
clearPendingEdit(editId);
|
|
186
|
-
}, 5e3);
|
|
187
|
-
pendingEditRef.current = { editId, element, timeout };
|
|
188
|
-
void symbolicateSelectedElement(descriptor).then(
|
|
189
|
-
(symbolicated) => {
|
|
190
|
-
if (pendingEditRef.current?.editId !== editId) {
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
void requestInlineTextEdit({
|
|
194
|
-
editId,
|
|
195
|
-
element: symbolicated
|
|
196
|
-
}).then(
|
|
197
|
-
({ allowed }) => {
|
|
198
|
-
resolveEditRequest({ editId, allowed });
|
|
199
|
-
},
|
|
200
|
-
() => {
|
|
201
|
-
resolveEditRequest({ editId, allowed: false });
|
|
202
|
-
}
|
|
203
|
-
);
|
|
204
|
-
},
|
|
205
|
-
() => {
|
|
206
|
-
clearPendingEdit(editId);
|
|
207
|
-
}
|
|
208
|
-
);
|
|
209
|
-
});
|
|
210
|
-
},
|
|
211
|
-
[clearPendingEdit, interactionSessionRef, resolveEditRequest]
|
|
212
|
-
);
|
|
213
|
-
useEffect(() => {
|
|
214
|
-
if (displayMode !== "selection") {
|
|
215
|
-
revert();
|
|
216
|
-
}
|
|
217
|
-
}, [displayMode, revert]);
|
|
218
|
-
return {
|
|
219
|
-
beginEditing,
|
|
220
|
-
editingElementRef,
|
|
221
|
-
formatCommand,
|
|
222
|
-
revert
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
export {
|
|
226
|
-
isTextEditableElement,
|
|
227
|
-
useEditModeStateMachine
|
|
228
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { DisplayMode, SelectModeSelectionType } from "../../_vendor/schemas/generated/selectMode";
|
|
2
|
-
import type { SelectModeInteractionSession } from "../selectModeSession";
|
|
3
|
-
export declare function useSelectModeController(): {
|
|
4
|
-
displayMode: DisplayMode;
|
|
5
|
-
interactionSessionRef: import("react").RefObject<SelectModeInteractionSession>;
|
|
6
|
-
setDisplayMode: (nextDisplayMode: DisplayMode) => void;
|
|
7
|
-
setSelectionType: ({ selectionType, selectors, }: {
|
|
8
|
-
selectionType: SelectModeSelectionType;
|
|
9
|
-
selectors?: string[];
|
|
10
|
-
}) => void;
|
|
11
|
-
};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { useCallback, useRef } from "react";
|
|
2
|
-
import { displayModeStore, useDisplayMode } from "../displayModeStore";
|
|
3
|
-
import { getWebsiteBuilderRpc } from "../iframeRpcChannel";
|
|
4
|
-
import { getDocumentScrollOffset } from "../interactions/selectionGeometry";
|
|
5
|
-
import { hideSelectionFrame, showAreaSelectionFrames } from "../selectionFrame";
|
|
6
|
-
import { IDLE_SESSION } from "../selectModeSession";
|
|
7
|
-
import { resetHoverRectDedup } from "./useSelectionHover";
|
|
8
|
-
function resetInteractionSession({
|
|
9
|
-
interactionSessionRef
|
|
10
|
-
}) {
|
|
11
|
-
const currentSession = interactionSessionRef.current;
|
|
12
|
-
if (currentSession.type === "drag" && currentSession.hasExceededThreshold) {
|
|
13
|
-
getWebsiteBuilderRpc()?.dragRectUpdated({
|
|
14
|
-
dragRect: null,
|
|
15
|
-
selectionRects: []
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
hideSelectionFrame();
|
|
19
|
-
resetHoverRectDedup();
|
|
20
|
-
interactionSessionRef.current = IDLE_SESSION;
|
|
21
|
-
}
|
|
22
|
-
function useSelectModeController() {
|
|
23
|
-
const displayMode = useDisplayMode();
|
|
24
|
-
const interactionSessionRef = useRef(IDLE_SESSION);
|
|
25
|
-
const setSelectionType = useCallback(
|
|
26
|
-
({
|
|
27
|
-
selectionType,
|
|
28
|
-
selectors
|
|
29
|
-
}) => {
|
|
30
|
-
if (selectionType === "none") {
|
|
31
|
-
resetInteractionSession({ interactionSessionRef });
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
const session = interactionSessionRef.current;
|
|
35
|
-
if (selectionType !== "area" || !selectors || session.type !== "area") {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const retainedSelectors = new Set(selectors);
|
|
39
|
-
const items = session.items.filter((item) => {
|
|
40
|
-
return item.data.kind !== "react-element" || retainedSelectors.has(item.data.selector);
|
|
41
|
-
});
|
|
42
|
-
if (items.length === session.items.length) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (items.length === 0) {
|
|
46
|
-
resetInteractionSession({ interactionSessionRef });
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
interactionSessionRef.current = { ...session, items };
|
|
50
|
-
showAreaSelectionFrames({
|
|
51
|
-
elements: items.map((item) => item.element),
|
|
52
|
-
scrollOffset: getDocumentScrollOffset()
|
|
53
|
-
});
|
|
54
|
-
},
|
|
55
|
-
[]
|
|
56
|
-
);
|
|
57
|
-
const setDisplayMode = useCallback((nextDisplayMode) => {
|
|
58
|
-
if (displayModeStore.getSnapshot() === "selection" && nextDisplayMode !== "selection") {
|
|
59
|
-
resetInteractionSession({ interactionSessionRef });
|
|
60
|
-
}
|
|
61
|
-
displayModeStore.setDisplayMode(nextDisplayMode);
|
|
62
|
-
}, []);
|
|
63
|
-
return {
|
|
64
|
-
displayMode,
|
|
65
|
-
interactionSessionRef,
|
|
66
|
-
setDisplayMode,
|
|
67
|
-
setSelectionType
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
export {
|
|
71
|
-
useSelectModeController
|
|
72
|
-
};
|