@node-edit-utils/core 2.2.7 → 2.2.9
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/dist/lib/node-tools/events/click/handleNodeClick.d.ts +2 -1
- package/dist/lib/node-tools/events/setupEventListener.d.ts +2 -1
- package/dist/lib/node-tools/highlight/createCornerHandles.d.ts +1 -1
- package/dist/lib/node-tools/highlight/createHighlightFrame.d.ts +1 -1
- package/dist/lib/node-tools/highlight/createToolsContainer.d.ts +1 -1
- package/dist/lib/node-tools/select/selectNode.d.ts +2 -1
- package/dist/lib/node-tools/text/helpers/enterTextEditMode.d.ts +2 -0
- package/dist/lib/node-tools/text/helpers/handleTextChange.d.ts +1 -0
- package/dist/lib/node-tools/text/helpers/shouldEnterTextEditMode.d.ts +1 -0
- package/dist/node-edit-utils.cjs.js +251 -81
- package/dist/node-edit-utils.esm.js +251 -81
- package/dist/node-edit-utils.umd.js +251 -81
- package/dist/node-edit-utils.umd.min.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/index.ts +0 -2
- package/src/lib/node-tools/createNodeTools.ts +2 -16
- package/src/lib/node-tools/events/click/handleNodeClick.ts +3 -2
- package/src/lib/node-tools/events/setupEventListener.ts +3 -2
- package/src/lib/node-tools/highlight/clearHighlightFrame.ts +7 -2
- package/src/lib/node-tools/highlight/createCornerHandles.ts +12 -6
- package/src/lib/node-tools/highlight/createHighlightFrame.ts +25 -7
- package/src/lib/node-tools/highlight/createTagLabel.ts +25 -1
- package/src/lib/node-tools/highlight/createToolsContainer.ts +4 -1
- package/src/lib/node-tools/highlight/helpers/getHighlightFrameElement.ts +5 -1
- package/src/lib/node-tools/highlight/highlightNode.ts +17 -6
- package/src/lib/node-tools/highlight/refreshHighlightFrame.ts +37 -4
- package/src/lib/node-tools/highlight/updateHighlightFrameVisibility.ts +4 -1
- package/src/lib/node-tools/select/selectNode.ts +24 -5
- package/src/lib/node-tools/text/events/setupMutationObserver.ts +53 -3
- package/src/lib/node-tools/text/helpers/enterTextEditMode.ts +9 -0
- package/src/lib/node-tools/text/helpers/handleTextChange.ts +29 -0
- package/src/lib/node-tools/text/helpers/shouldEnterTextEditMode.ts +9 -0
- package/src/lib/styles/styles.css +23 -8
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { enterTextEditMode } from "../text/helpers/enterTextEditMode";
|
|
2
|
+
import type { NodeText } from "../text/types";
|
|
1
3
|
import { IGNORED_DOM_ELEMENTS } from "./constants";
|
|
2
4
|
import { getElementsFromPoint } from "./helpers/getElementsFromPoint";
|
|
3
5
|
import { isInsideComponent } from "./helpers/isInsideComponent";
|
|
@@ -6,7 +8,9 @@ import { targetSameCandidates } from "./helpers/targetSameCandidates";
|
|
|
6
8
|
let candidateCache: Element[] = [];
|
|
7
9
|
let attempt = 0;
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
let lastSelectedNode: HTMLElement | null = null;
|
|
12
|
+
|
|
13
|
+
export const selectNode = (event: MouseEvent, nodeProvider: HTMLElement | null, text: NodeText): HTMLElement | null => {
|
|
10
14
|
let selectedNode: HTMLElement | null = null;
|
|
11
15
|
|
|
12
16
|
const clickX = event.clientX;
|
|
@@ -21,19 +25,29 @@ export const selectNode = (event: MouseEvent, editableNode: HTMLElement | null):
|
|
|
21
25
|
!isInsideComponent(element)
|
|
22
26
|
);
|
|
23
27
|
|
|
28
|
+
const editableNode = text.getEditableNode();
|
|
24
29
|
if (editableNode && candidates.includes(editableNode)) {
|
|
25
|
-
|
|
30
|
+
selectedNode = editableNode;
|
|
31
|
+
lastSelectedNode = selectedNode;
|
|
32
|
+
|
|
33
|
+
return selectedNode;
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
if (clickThrough) {
|
|
29
37
|
candidateCache = [];
|
|
30
|
-
|
|
31
38
|
selectedNode = candidates[0] as HTMLElement;
|
|
39
|
+
|
|
40
|
+
if (lastSelectedNode && lastSelectedNode === selectedNode) {
|
|
41
|
+
enterTextEditMode(selectedNode, nodeProvider, text);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
lastSelectedNode = selectedNode;
|
|
45
|
+
|
|
32
46
|
return selectedNode;
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
if (targetSameCandidates(candidateCache, candidates)) {
|
|
36
|
-
attempt <= candidates.length && attempt++;
|
|
50
|
+
attempt <= candidates.length - 2 && attempt++;
|
|
37
51
|
} else {
|
|
38
52
|
attempt = 0;
|
|
39
53
|
}
|
|
@@ -41,8 +55,13 @@ export const selectNode = (event: MouseEvent, editableNode: HTMLElement | null):
|
|
|
41
55
|
const nodeIndex = candidates.length - 1 - attempt;
|
|
42
56
|
|
|
43
57
|
selectedNode = candidates[nodeIndex] as HTMLElement;
|
|
44
|
-
|
|
45
58
|
candidateCache = candidates;
|
|
46
59
|
|
|
60
|
+
if (lastSelectedNode && lastSelectedNode === selectedNode) {
|
|
61
|
+
enterTextEditMode(selectedNode, nodeProvider, text);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
lastSelectedNode = selectedNode;
|
|
65
|
+
|
|
47
66
|
return selectedNode;
|
|
48
67
|
};
|
|
@@ -1,10 +1,60 @@
|
|
|
1
1
|
import { connectMutationObserver } from "../../../helpers/observer/connectMutationObserver";
|
|
2
2
|
import { refreshHighlightFrame } from "../../highlight/refreshHighlightFrame";
|
|
3
|
+
import { handleTextChange } from "../helpers/handleTextChange";
|
|
3
4
|
|
|
4
|
-
export const setupMutationObserver = (
|
|
5
|
-
|
|
5
|
+
export const setupMutationObserver = (
|
|
6
|
+
node: HTMLElement,
|
|
7
|
+
nodeProvider: HTMLElement,
|
|
8
|
+
canvasName: string = "canvas"
|
|
9
|
+
): (() => void) | undefined => {
|
|
10
|
+
// Accumulate mutations instead of replacing them
|
|
11
|
+
let pendingMutations: MutationRecord[] = [];
|
|
12
|
+
let rafId1: number | null = null;
|
|
13
|
+
let rafId2: number | null = null;
|
|
14
|
+
|
|
15
|
+
const processMutations = () => {
|
|
16
|
+
if (pendingMutations.length > 0) {
|
|
17
|
+
const mutationsToProcess = [...pendingMutations];
|
|
18
|
+
pendingMutations = [];
|
|
19
|
+
handleTextChange(node, mutationsToProcess);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const scheduleProcess = () => {
|
|
24
|
+
if (rafId1 === null) {
|
|
25
|
+
rafId1 = requestAnimationFrame(() => {
|
|
26
|
+
// First RAF: let browser complete layout
|
|
27
|
+
rafId2 = requestAnimationFrame(() => {
|
|
28
|
+
// Second RAF: read textContent after layout is complete
|
|
29
|
+
processMutations();
|
|
30
|
+
rafId1 = null;
|
|
31
|
+
rafId2 = null;
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const cleanup = () => {
|
|
38
|
+
if (rafId1 !== null) {
|
|
39
|
+
cancelAnimationFrame(rafId1);
|
|
40
|
+
rafId1 = null;
|
|
41
|
+
}
|
|
42
|
+
if (rafId2 !== null) {
|
|
43
|
+
cancelAnimationFrame(rafId2);
|
|
44
|
+
rafId2 = null;
|
|
45
|
+
}
|
|
46
|
+
pendingMutations = [];
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const mutationObserver = connectMutationObserver(node, (mutations) => {
|
|
50
|
+
// Accumulate mutations instead of replacing
|
|
51
|
+
pendingMutations.push(...mutations);
|
|
52
|
+
scheduleProcess();
|
|
6
53
|
refreshHighlightFrame(node, nodeProvider, canvasName);
|
|
7
54
|
});
|
|
8
55
|
|
|
9
|
-
return () =>
|
|
56
|
+
return () => {
|
|
57
|
+
mutationObserver.disconnect();
|
|
58
|
+
cleanup();
|
|
59
|
+
};
|
|
10
60
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { sendPostMessage } from "@/lib/post-message/sendPostMessage";
|
|
2
|
+
|
|
3
|
+
export const handleTextChange = (node: HTMLElement, mutations: MutationRecord[]): void => {
|
|
4
|
+
// Check if any mutation is a text content change
|
|
5
|
+
const hasTextChange = mutations.some((mutation) => {
|
|
6
|
+
return (
|
|
7
|
+
mutation.type === "characterData" ||
|
|
8
|
+
(mutation.type === "childList" && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0))
|
|
9
|
+
);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (!hasTextChange) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Get the text content of the node
|
|
17
|
+
const textContent = node.textContent ?? "";
|
|
18
|
+
|
|
19
|
+
// Get the node ID
|
|
20
|
+
const nodeId = node.getAttribute("data-node-id");
|
|
21
|
+
|
|
22
|
+
console.log("textContentChanged", nodeId, textContent);
|
|
23
|
+
|
|
24
|
+
// Send postMessage with the text change
|
|
25
|
+
sendPostMessage("textContentChanged", {
|
|
26
|
+
nodeId,
|
|
27
|
+
textContent,
|
|
28
|
+
});
|
|
29
|
+
};
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
--primary-color: oklch(0.6235 0.22 294);
|
|
3
3
|
--uncode-color: oklch(45.7% 0.24 277.023);
|
|
4
4
|
--component-color: oklch(65.6% 0.241 354.308);
|
|
5
|
+
--text-edit-color: oklch(62.3% 0.214 259.815);
|
|
6
|
+
--text-edit-selection-color: oklch(62.3% 0.214 259.815 / 0.2);
|
|
5
7
|
|
|
6
8
|
--handle-color: oklch(55.2% 0.016 285.938);
|
|
7
9
|
--handle-color-transparent: oklch(55.2% 0.016 285.938 / 0.7);
|
|
@@ -19,7 +21,7 @@
|
|
|
19
21
|
--transition-medium: 0.2s ease-in-out;
|
|
20
22
|
|
|
21
23
|
--z-index-high: 10000;
|
|
22
|
-
--z-index-highlight:
|
|
24
|
+
--z-index-highlight: 500;
|
|
23
25
|
--z-index-medium: 1000;
|
|
24
26
|
|
|
25
27
|
--letter-spacing: 0.02em;
|
|
@@ -58,7 +60,7 @@
|
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
.highlight-frame-overlay {
|
|
61
|
-
position:
|
|
63
|
+
position: absolute;
|
|
62
64
|
inset: 0;
|
|
63
65
|
width: 100vw;
|
|
64
66
|
height: 100vh;
|
|
@@ -79,6 +81,10 @@
|
|
|
79
81
|
stroke: var(--component-color);
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
.highlight-frame-overlay.is-text-edit .highlight-frame-rect {
|
|
85
|
+
stroke: var(--text-edit-color);
|
|
86
|
+
}
|
|
87
|
+
|
|
82
88
|
.highlight-frame-handle {
|
|
83
89
|
fill: white;
|
|
84
90
|
stroke: var(--primary-color);
|
|
@@ -91,6 +97,10 @@
|
|
|
91
97
|
stroke: var(--component-color);
|
|
92
98
|
}
|
|
93
99
|
|
|
100
|
+
.highlight-frame-overlay.is-text-edit .highlight-frame-handle {
|
|
101
|
+
stroke: var(--text-edit-color);
|
|
102
|
+
}
|
|
103
|
+
|
|
94
104
|
.highlight-frame-handle.handle-top-left {
|
|
95
105
|
cursor: nwse-resize;
|
|
96
106
|
}
|
|
@@ -108,7 +118,7 @@
|
|
|
108
118
|
}
|
|
109
119
|
|
|
110
120
|
.highlight-frame-tools-wrapper {
|
|
111
|
-
position:
|
|
121
|
+
position: absolute;
|
|
112
122
|
left: 0;
|
|
113
123
|
top: 0;
|
|
114
124
|
pointer-events: none;
|
|
@@ -121,14 +131,14 @@
|
|
|
121
131
|
color: var(--text-color-white);
|
|
122
132
|
font-size: 0.575rem;
|
|
123
133
|
font-weight: 500;
|
|
124
|
-
text-transform: uppercase;
|
|
125
134
|
height: 1rem;
|
|
126
135
|
padding: 0.5625rem var(--spacing-sm);
|
|
127
|
-
line-height: 1;
|
|
136
|
+
line-height: 1.4;
|
|
128
137
|
border-radius: var(--spacing-sm);
|
|
129
138
|
display: flex;
|
|
130
139
|
align-items: center;
|
|
131
140
|
justify-content: center;
|
|
141
|
+
white-space: nowrap;
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
.highlight-frame-tools-wrapper.is-instance .tag-label,
|
|
@@ -136,6 +146,11 @@
|
|
|
136
146
|
background-color: var(--component-color);
|
|
137
147
|
}
|
|
138
148
|
|
|
149
|
+
.highlight-frame-tools-wrapper.is-text-edit .tag-label,
|
|
150
|
+
.node-tools.is-text-edit .tag-label {
|
|
151
|
+
background-color: var(--text-edit-color);
|
|
152
|
+
}
|
|
153
|
+
|
|
139
154
|
.viewport {
|
|
140
155
|
position: relative;
|
|
141
156
|
width: var(--container-width);
|
|
@@ -233,14 +248,14 @@
|
|
|
233
248
|
user-select: text;
|
|
234
249
|
|
|
235
250
|
&::selection {
|
|
236
|
-
background: var(--
|
|
251
|
+
background: var(--text-edit-selection-color);
|
|
237
252
|
}
|
|
238
253
|
|
|
239
254
|
&::-moz-selection {
|
|
240
|
-
background: var(--
|
|
255
|
+
background: var(--text-edit-selection-color);
|
|
241
256
|
}
|
|
242
257
|
|
|
243
258
|
&::-webkit-selection {
|
|
244
|
-
background: var(--
|
|
259
|
+
background: var(--text-edit-selection-color);
|
|
245
260
|
}
|
|
246
261
|
}
|