@base44-preview/vite-plugin 0.2.26-pr.42.7e00d38 → 0.2.26-pr.43.554d4c7
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/consts.d.ts +12 -0
- package/dist/consts.d.ts.map +1 -0
- package/dist/consts.js +12 -0
- package/dist/consts.js.map +1 -0
- package/dist/injections/visual-edit-agent.d.ts.map +1 -1
- package/dist/injections/visual-edit-agent.js +10 -303
- package/dist/injections/visual-edit-agent.js.map +1 -1
- package/dist/jsx-processor.d.ts +4 -1
- package/dist/jsx-processor.d.ts.map +1 -1
- package/dist/jsx-processor.js +33 -6
- package/dist/jsx-processor.js.map +1 -1
- package/dist/jsx-utils.d.ts +9 -0
- package/dist/jsx-utils.d.ts.map +1 -1
- package/dist/jsx-utils.js +86 -0
- package/dist/jsx-utils.js.map +1 -1
- package/dist/processors/collection-id-processor.d.ts +20 -0
- package/dist/processors/collection-id-processor.d.ts.map +1 -0
- package/dist/processors/collection-id-processor.js +182 -0
- package/dist/processors/collection-id-processor.js.map +1 -0
- package/dist/processors/collection-item-field-processor.d.ts +22 -0
- package/dist/processors/collection-item-field-processor.d.ts.map +1 -0
- package/dist/processors/collection-item-field-processor.js +206 -0
- package/dist/processors/collection-item-field-processor.js.map +1 -0
- package/dist/processors/collection-item-id-processor.d.ts +12 -0
- package/dist/processors/collection-item-id-processor.d.ts.map +1 -0
- package/dist/processors/collection-item-id-processor.js +50 -0
- package/dist/processors/collection-item-id-processor.js.map +1 -0
- package/dist/processors/collection-reference-field-processor.d.ts +31 -0
- package/dist/processors/collection-reference-field-processor.d.ts.map +1 -0
- package/dist/processors/collection-reference-field-processor.js +174 -0
- package/dist/processors/collection-reference-field-processor.js.map +1 -0
- package/dist/processors/collection-tracing-utils.d.ts +31 -0
- package/dist/processors/collection-tracing-utils.d.ts.map +1 -0
- package/dist/processors/collection-tracing-utils.js +326 -0
- package/dist/processors/collection-tracing-utils.js.map +1 -0
- package/dist/processors/shared-utils.d.ts +64 -0
- package/dist/processors/shared-utils.d.ts.map +1 -1
- package/dist/processors/shared-utils.js +464 -0
- package/dist/processors/shared-utils.js.map +1 -1
- package/dist/processors/static-array-processor.d.ts +2 -3
- package/dist/processors/static-array-processor.d.ts.map +1 -1
- package/dist/processors/static-array-processor.js +2 -3
- package/dist/processors/static-array-processor.js.map +1 -1
- package/dist/statics/index.mjs +1 -5
- package/dist/statics/index.mjs.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/visual-edit-plugin.d.ts +0 -1
- package/dist/visual-edit-plugin.d.ts.map +1 -1
- package/dist/visual-edit-plugin.js +29 -178
- package/dist/visual-edit-plugin.js.map +1 -1
- package/package.json +1 -1
- package/src/consts.ts +12 -0
- package/src/injections/visual-edit-agent.ts +10 -369
- package/src/jsx-processor.ts +41 -14
- package/src/jsx-utils.ts +116 -0
- package/src/processors/collection-id-processor.ts +261 -0
- package/src/processors/collection-item-field-processor.ts +309 -0
- package/src/processors/collection-item-id-processor.ts +69 -0
- package/src/processors/collection-reference-field-processor.ts +225 -0
- package/src/processors/collection-tracing-utils.ts +436 -0
- package/src/processors/shared-utils.ts +595 -0
- package/src/processors/static-array-processor.ts +6 -3
- package/src/types.ts +4 -0
- package/src/visual-edit-plugin.ts +34 -215
|
@@ -11,15 +11,6 @@ export function setupVisualEditAgent() {
|
|
|
11
11
|
let selectedOverlays: HTMLDivElement[] = [];
|
|
12
12
|
let currentHighlightedElements: Element[] = [];
|
|
13
13
|
let selectedElementId: string | null = null;
|
|
14
|
-
let currentEditingElement: HTMLElement | null = null;
|
|
15
|
-
let debouncedSendTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
16
|
-
let isInlineEditExperimentEnabled = false;
|
|
17
|
-
|
|
18
|
-
const INLINE_EDIT_DEBOUNCE_MS = 500;
|
|
19
|
-
const REPOSITION_DELAY_MS = 50;
|
|
20
|
-
|
|
21
|
-
// WeakMap to track AbortControllers for each element's input listener
|
|
22
|
-
const listenerAbortControllers = new WeakMap<HTMLElement, AbortController>();
|
|
23
14
|
|
|
24
15
|
// Create overlay element
|
|
25
16
|
const createOverlay = (isSelected = false): HTMLDivElement => {
|
|
@@ -78,258 +69,6 @@ export function setupVisualEditAgent() {
|
|
|
78
69
|
}
|
|
79
70
|
};
|
|
80
71
|
|
|
81
|
-
// --- Inline editing utilities ---
|
|
82
|
-
|
|
83
|
-
const injectFocusOutlineCSS = () => {
|
|
84
|
-
const existingStyle = document.getElementById("visual-edit-focus-styles");
|
|
85
|
-
if (existingStyle) return;
|
|
86
|
-
|
|
87
|
-
const style = document.createElement("style");
|
|
88
|
-
style.id = "visual-edit-focus-styles";
|
|
89
|
-
style.textContent = `
|
|
90
|
-
[data-selected="true"][contenteditable="true"]:focus {
|
|
91
|
-
outline: none !important;
|
|
92
|
-
}
|
|
93
|
-
`;
|
|
94
|
-
document.head.appendChild(style);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const removeFocusOutlineCSS = () => {
|
|
98
|
-
const style = document.getElementById("visual-edit-focus-styles");
|
|
99
|
-
if (style) {
|
|
100
|
-
style.remove();
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const selectText = (element: HTMLElement) => {
|
|
105
|
-
const range = document.createRange();
|
|
106
|
-
range.selectNodeContents(element);
|
|
107
|
-
const selection = window.getSelection();
|
|
108
|
-
selection?.removeAllRanges();
|
|
109
|
-
selection?.addRange(range);
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const isEditableTextElement = (element: Element): boolean => {
|
|
113
|
-
if (!(element instanceof HTMLElement)) {
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const allowedTags = [
|
|
118
|
-
"div", "p", "h1", "h2", "h3", "h4", "h5", "h6",
|
|
119
|
-
"span", "li", "td", "a", "button", "label",
|
|
120
|
-
];
|
|
121
|
-
if (!allowedTags.includes(element.tagName.toLowerCase())) {
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const textContent = element.textContent?.trim() || "";
|
|
126
|
-
if (textContent.length === 0) {
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (element.querySelector("img, video, canvas, svg") !== null) {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (element.children.length > 0) {
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (element.dataset.dynamicContent === "true") {
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return true;
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const shouldEnterInlineEditingMode = (element: Element): boolean => {
|
|
146
|
-
if (!(element instanceof HTMLElement) || element.dataset.selected !== "true") {
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
return isEditableTextElement(element);
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const handleInputEvent = function (this: HTMLElement) {
|
|
153
|
-
onTextInputChange(this);
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const enterInlineEditingMode = (element: HTMLElement) => {
|
|
157
|
-
injectFocusOutlineCSS();
|
|
158
|
-
|
|
159
|
-
element.dataset.originalTextContent = element.textContent || "";
|
|
160
|
-
element.dataset.originalCursor = element.style.cursor;
|
|
161
|
-
|
|
162
|
-
element.contentEditable = "true";
|
|
163
|
-
|
|
164
|
-
const abortController = new AbortController();
|
|
165
|
-
listenerAbortControllers.set(element, abortController);
|
|
166
|
-
element.addEventListener("input", handleInputEvent, { signal: abortController.signal });
|
|
167
|
-
|
|
168
|
-
element.style.cursor = "text";
|
|
169
|
-
selectText(element);
|
|
170
|
-
|
|
171
|
-
setTimeout(() => {
|
|
172
|
-
element.focus();
|
|
173
|
-
}, 0);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const clearInlineEditingMode = (element: HTMLElement) => {
|
|
177
|
-
const abortController = listenerAbortControllers.get(element);
|
|
178
|
-
if (abortController) {
|
|
179
|
-
abortController.abort();
|
|
180
|
-
listenerAbortControllers.delete(element);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (!element.isConnected) {
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
removeFocusOutlineCSS();
|
|
188
|
-
element.contentEditable = "false";
|
|
189
|
-
delete element.dataset.originalTextContent;
|
|
190
|
-
|
|
191
|
-
if (element.dataset.originalCursor !== undefined) {
|
|
192
|
-
element.style.cursor = element.dataset.originalCursor;
|
|
193
|
-
delete element.dataset.originalCursor;
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
const repositionSelectedOverlays = () => {
|
|
198
|
-
if (selectedElementId) {
|
|
199
|
-
const elements = findElementsById(selectedElementId);
|
|
200
|
-
selectedOverlays.forEach((overlay, index) => {
|
|
201
|
-
if (index < elements.length) {
|
|
202
|
-
positionOverlay(overlay, elements[index]!);
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
const handleEnterInlineEditingMode = (element: HTMLElement) => {
|
|
209
|
-
currentEditingElement = element;
|
|
210
|
-
|
|
211
|
-
selectedOverlays.forEach((overlay) => {
|
|
212
|
-
overlay.style.display = "none";
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
enterInlineEditingMode(element);
|
|
216
|
-
|
|
217
|
-
window.parent.postMessage(
|
|
218
|
-
{
|
|
219
|
-
type: "content-editing-started",
|
|
220
|
-
visualSelectorId: selectedElementId,
|
|
221
|
-
},
|
|
222
|
-
"*"
|
|
223
|
-
);
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const handleClearInlineEditingMode = () => {
|
|
227
|
-
if (!currentEditingElement) return;
|
|
228
|
-
|
|
229
|
-
if (debouncedSendTimeout) {
|
|
230
|
-
clearTimeout(debouncedSendTimeout);
|
|
231
|
-
debouncedSendTimeout = null;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const element = currentEditingElement;
|
|
235
|
-
clearInlineEditingMode(element);
|
|
236
|
-
|
|
237
|
-
selectedOverlays.forEach((overlay) => {
|
|
238
|
-
overlay.style.display = "";
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
repositionSelectedOverlays();
|
|
242
|
-
|
|
243
|
-
window.parent.postMessage(
|
|
244
|
-
{
|
|
245
|
-
type: "content-editing-ended",
|
|
246
|
-
visualSelectorId: selectedElementId,
|
|
247
|
-
},
|
|
248
|
-
"*"
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
currentEditingElement = null;
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
const reportInlineEdit = (element: HTMLElement) => {
|
|
255
|
-
const originalContent = element.dataset.originalTextContent;
|
|
256
|
-
const newContent = element.textContent;
|
|
257
|
-
|
|
258
|
-
const svgElement = element as unknown as SVGElement;
|
|
259
|
-
const elementInfo = {
|
|
260
|
-
tagName: element.tagName,
|
|
261
|
-
classes:
|
|
262
|
-
(svgElement.className as unknown as SVGAnimatedString)?.baseVal ||
|
|
263
|
-
element.className ||
|
|
264
|
-
"",
|
|
265
|
-
visualSelectorId: selectedElementId,
|
|
266
|
-
content: newContent,
|
|
267
|
-
dataSourceLocation: element.dataset.sourceLocation,
|
|
268
|
-
isDynamicContent: element.dataset.dynamicContent === "true",
|
|
269
|
-
linenumber: element.dataset.linenumber,
|
|
270
|
-
filename: element.dataset.filename,
|
|
271
|
-
position: (() => {
|
|
272
|
-
const rect = element.getBoundingClientRect();
|
|
273
|
-
return {
|
|
274
|
-
top: rect.top,
|
|
275
|
-
left: rect.left,
|
|
276
|
-
right: rect.right,
|
|
277
|
-
bottom: rect.bottom,
|
|
278
|
-
width: rect.width,
|
|
279
|
-
height: rect.height,
|
|
280
|
-
centerX: rect.left + rect.width / 2,
|
|
281
|
-
centerY: rect.top + rect.height / 2,
|
|
282
|
-
};
|
|
283
|
-
})(),
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
window.parent.postMessage(
|
|
287
|
-
{
|
|
288
|
-
type: "inline-edit",
|
|
289
|
-
elementInfo,
|
|
290
|
-
originalContent,
|
|
291
|
-
newContent,
|
|
292
|
-
},
|
|
293
|
-
"*"
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
element.dataset.originalTextContent = newContent || "";
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
const debouncedSendInlineEditMessage = (element: HTMLElement) => {
|
|
300
|
-
if (debouncedSendTimeout) {
|
|
301
|
-
clearTimeout(debouncedSendTimeout);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
debouncedSendTimeout = setTimeout(() => {
|
|
305
|
-
reportInlineEdit(element);
|
|
306
|
-
}, INLINE_EDIT_DEBOUNCE_MS);
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
const onTextInputChange = (element: HTMLElement) => {
|
|
310
|
-
repositionSelectedOverlays();
|
|
311
|
-
debouncedSendInlineEditMessage(element);
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
const clearSelection = () => {
|
|
315
|
-
if (selectedElementId) {
|
|
316
|
-
const elements = findElementsById(selectedElementId);
|
|
317
|
-
elements.forEach((el) => {
|
|
318
|
-
if (el instanceof HTMLElement) {
|
|
319
|
-
delete el.dataset.selected;
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
selectedOverlays.forEach((overlay) => {
|
|
325
|
-
if (overlay && overlay.parentNode) {
|
|
326
|
-
overlay.remove();
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
selectedOverlays = [];
|
|
330
|
-
selectedElementId = null;
|
|
331
|
-
};
|
|
332
|
-
|
|
333
72
|
// Clear hover overlays
|
|
334
73
|
const clearHoverOverlays = () => {
|
|
335
74
|
hoverOverlays.forEach((overlay) => {
|
|
@@ -415,9 +154,6 @@ export function setupVisualEditAgent() {
|
|
|
415
154
|
const handleMouseOver = (e: MouseEvent) => {
|
|
416
155
|
if (!isVisualEditMode || isPopoverDragging) return;
|
|
417
156
|
|
|
418
|
-
// Skip hover effects when inline editing
|
|
419
|
-
if (currentEditingElement) return;
|
|
420
|
-
|
|
421
157
|
const target = e.target as Element;
|
|
422
158
|
|
|
423
159
|
// Prevent hover effects when a dropdown is open
|
|
@@ -485,20 +221,6 @@ export function setupVisualEditAgent() {
|
|
|
485
221
|
// Let layer dropdown clicks pass through without interference
|
|
486
222
|
if (target.closest(`[${LAYER_DROPDOWN_ATTR}]`)) return;
|
|
487
223
|
|
|
488
|
-
// Allow normal editing when clicking inside a contentEditable element
|
|
489
|
-
if (isInlineEditExperimentEnabled && target instanceof HTMLElement && target.contentEditable === "true") {
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// If currently editing, clicking outside should exit editing mode
|
|
494
|
-
if (currentEditingElement) {
|
|
495
|
-
e.preventDefault();
|
|
496
|
-
e.stopPropagation();
|
|
497
|
-
e.stopImmediatePropagation();
|
|
498
|
-
handleClearInlineEditingMode();
|
|
499
|
-
return;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
224
|
// Close dropdowns when clicking anywhere in iframe if a dropdown is open
|
|
503
225
|
if (isDropdownOpen) {
|
|
504
226
|
e.preventDefault();
|
|
@@ -527,45 +249,14 @@ export function setupVisualEditAgent() {
|
|
|
527
249
|
return;
|
|
528
250
|
}
|
|
529
251
|
|
|
530
|
-
const htmlElement = element as HTMLElement;
|
|
531
|
-
const visualSelectorId = getElementSelectorId(element);
|
|
532
|
-
|
|
533
|
-
// Check if this element is already selected (second click scenario)
|
|
534
|
-
const isAlreadySelected =
|
|
535
|
-
selectedElementId === visualSelectorId &&
|
|
536
|
-
htmlElement.dataset.selected === "true";
|
|
537
|
-
|
|
538
|
-
if (isAlreadySelected) {
|
|
539
|
-
if (isInlineEditExperimentEnabled && shouldEnterInlineEditingMode(htmlElement)) {
|
|
540
|
-
handleEnterInlineEditingMode(htmlElement);
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// Select the element, mark for inline editing, and attach layer dropdown
|
|
546
|
-
if (currentEditingElement) {
|
|
547
|
-
handleClearInlineEditingMode();
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
if (isInlineEditExperimentEnabled) {
|
|
551
|
-
const elements = findElementsById(visualSelectorId);
|
|
552
|
-
elements.forEach((el) => {
|
|
553
|
-
if (el instanceof HTMLElement) {
|
|
554
|
-
el.dataset.selected = "true";
|
|
555
|
-
}
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
|
|
559
252
|
const selectedOverlay = selectElement(element);
|
|
560
253
|
layerController.attachToOverlay(selectedOverlay, element);
|
|
561
254
|
};
|
|
562
255
|
|
|
563
|
-
//
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
}
|
|
568
|
-
clearSelection();
|
|
256
|
+
// Clear the current selection
|
|
257
|
+
const clearSelection = () => {
|
|
258
|
+
clearSelectedOverlays();
|
|
259
|
+
selectedElementId = null;
|
|
569
260
|
};
|
|
570
261
|
|
|
571
262
|
const updateElementClassesAndReposition = (visualSelectorId: string, classes: string) => {
|
|
@@ -597,7 +288,7 @@ export function setupVisualEditAgent() {
|
|
|
597
288
|
});
|
|
598
289
|
}
|
|
599
290
|
}
|
|
600
|
-
},
|
|
291
|
+
}, 50);
|
|
601
292
|
};
|
|
602
293
|
|
|
603
294
|
// Update element attribute by visual selector ID
|
|
@@ -620,7 +311,7 @@ export function setupVisualEditAgent() {
|
|
|
620
311
|
}
|
|
621
312
|
});
|
|
622
313
|
}
|
|
623
|
-
},
|
|
314
|
+
}, 50);
|
|
624
315
|
};
|
|
625
316
|
|
|
626
317
|
// Update element content by visual selector ID
|
|
@@ -643,7 +334,7 @@ export function setupVisualEditAgent() {
|
|
|
643
334
|
}
|
|
644
335
|
});
|
|
645
336
|
}
|
|
646
|
-
},
|
|
337
|
+
}, 50);
|
|
647
338
|
};
|
|
648
339
|
|
|
649
340
|
// --- Layer dropdown controller ---
|
|
@@ -665,15 +356,12 @@ export function setupVisualEditAgent() {
|
|
|
665
356
|
isVisualEditMode = isEnabled;
|
|
666
357
|
|
|
667
358
|
if (!isEnabled) {
|
|
668
|
-
if (currentEditingElement) {
|
|
669
|
-
handleClearInlineEditingMode();
|
|
670
|
-
}
|
|
671
|
-
clearSelection();
|
|
672
359
|
layerController.cleanup();
|
|
673
360
|
clearHoverOverlays();
|
|
674
361
|
clearSelectedOverlays();
|
|
675
362
|
|
|
676
363
|
currentHighlightedElements = [];
|
|
364
|
+
selectedElementId = null;
|
|
677
365
|
document.body.style.cursor = "default";
|
|
678
366
|
|
|
679
367
|
document.removeEventListener("mouseover", handleMouseOver);
|
|
@@ -734,9 +422,6 @@ export function setupVisualEditAgent() {
|
|
|
734
422
|
switch (message.type) {
|
|
735
423
|
case "toggle-visual-edit-mode":
|
|
736
424
|
toggleVisualEditMode(message.data.enabled);
|
|
737
|
-
if (message.data.specs?.newInlineEditEnabled !== undefined) {
|
|
738
|
-
isInlineEditExperimentEnabled = message.data.specs.newInlineEditEnabled;
|
|
739
|
-
}
|
|
740
425
|
break;
|
|
741
426
|
|
|
742
427
|
case "update-classes":
|
|
@@ -774,7 +459,7 @@ export function setupVisualEditAgent() {
|
|
|
774
459
|
break;
|
|
775
460
|
|
|
776
461
|
case "unselect-element":
|
|
777
|
-
|
|
462
|
+
clearSelection();
|
|
778
463
|
break;
|
|
779
464
|
|
|
780
465
|
case "refresh-page":
|
|
@@ -852,50 +537,6 @@ export function setupVisualEditAgent() {
|
|
|
852
537
|
}
|
|
853
538
|
break;
|
|
854
539
|
|
|
855
|
-
case "toggle-inline-edit-mode":
|
|
856
|
-
if (!isInlineEditExperimentEnabled) break;
|
|
857
|
-
if (!message.data || !message.data.dataSourceLocation) break;
|
|
858
|
-
|
|
859
|
-
{
|
|
860
|
-
const elements = findElementsById(message.data.dataSourceLocation);
|
|
861
|
-
if (elements.length === 0 || !(elements[0] instanceof HTMLElement)) break;
|
|
862
|
-
|
|
863
|
-
const element = elements[0];
|
|
864
|
-
|
|
865
|
-
if (message.data.inlineEditingMode) {
|
|
866
|
-
if (shouldEnterInlineEditingMode(element)) {
|
|
867
|
-
// Select the element first if not already selected
|
|
868
|
-
if (selectedElementId !== message.data.dataSourceLocation) {
|
|
869
|
-
if (currentEditingElement) {
|
|
870
|
-
handleClearInlineEditingMode();
|
|
871
|
-
}
|
|
872
|
-
clearSelection();
|
|
873
|
-
|
|
874
|
-
elements.forEach((el) => {
|
|
875
|
-
if (el instanceof HTMLElement) {
|
|
876
|
-
el.dataset.selected = "true";
|
|
877
|
-
}
|
|
878
|
-
});
|
|
879
|
-
|
|
880
|
-
elements.forEach((el) => {
|
|
881
|
-
const overlay = createOverlay(true);
|
|
882
|
-
document.body.appendChild(overlay);
|
|
883
|
-
selectedOverlays.push(overlay);
|
|
884
|
-
positionOverlay(overlay, el, true);
|
|
885
|
-
});
|
|
886
|
-
|
|
887
|
-
selectedElementId = message.data.dataSourceLocation;
|
|
888
|
-
}
|
|
889
|
-
handleEnterInlineEditingMode(element);
|
|
890
|
-
}
|
|
891
|
-
} else {
|
|
892
|
-
if (currentEditingElement === element) {
|
|
893
|
-
handleClearInlineEditingMode();
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
break;
|
|
898
|
-
|
|
899
540
|
default:
|
|
900
541
|
break;
|
|
901
542
|
}
|
|
@@ -960,7 +601,7 @@ export function setupVisualEditAgent() {
|
|
|
960
601
|
});
|
|
961
602
|
|
|
962
603
|
if (needsUpdate) {
|
|
963
|
-
setTimeout(handleResize,
|
|
604
|
+
setTimeout(handleResize, 50);
|
|
964
605
|
}
|
|
965
606
|
});
|
|
966
607
|
|
package/src/jsx-processor.ts
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import type { NodePath } from "@babel/traverse";
|
|
2
2
|
import type * as t from "@babel/types";
|
|
3
|
+
import { CollectionIdProcessor } from "./processors/collection-id-processor.js";
|
|
4
|
+
import { ReferenceFieldProcessor } from "./processors/collection-reference-field-processor.js";
|
|
5
|
+
import { DataItemIdProcessor } from "./processors/collection-item-id-processor.js";
|
|
6
|
+
import { DataItemFieldProcessor } from "./processors/collection-item-field-processor.js";
|
|
3
7
|
import { StaticArrayProcessor } from "./processors/static-array-processor.js";
|
|
4
8
|
|
|
5
9
|
export class JSXProcessor {
|
|
10
|
+
private collectionIdProcessor: CollectionIdProcessor;
|
|
11
|
+
private referenceFieldProcessor: ReferenceFieldProcessor;
|
|
12
|
+
private dataItemIdProcessor: DataItemIdProcessor;
|
|
13
|
+
private dataItemFieldProcessor: DataItemFieldProcessor;
|
|
6
14
|
private staticArrayProcessor: StaticArrayProcessor;
|
|
7
15
|
|
|
8
16
|
constructor(
|
|
9
17
|
private types: typeof t,
|
|
10
18
|
private filename: string
|
|
11
19
|
) {
|
|
20
|
+
this.collectionIdProcessor = new CollectionIdProcessor(types);
|
|
21
|
+
this.referenceFieldProcessor = new ReferenceFieldProcessor(types);
|
|
22
|
+
this.dataItemIdProcessor = new DataItemIdProcessor(types);
|
|
23
|
+
this.dataItemFieldProcessor = new DataItemFieldProcessor(types);
|
|
12
24
|
this.staticArrayProcessor = new StaticArrayProcessor(types);
|
|
13
25
|
}
|
|
14
26
|
|
|
@@ -17,8 +29,13 @@ export class JSXProcessor {
|
|
|
17
29
|
|
|
18
30
|
this.addSourceLocationAttribute(path);
|
|
19
31
|
this.addDynamicContentAttribute(path);
|
|
20
|
-
|
|
32
|
+
|
|
33
|
+
this.collectionIdProcessor.process(path);
|
|
34
|
+
this.referenceFieldProcessor.process(path);
|
|
35
|
+
this.dataItemIdProcessor.process(path);
|
|
36
|
+
this.dataItemFieldProcessor.process(path);
|
|
21
37
|
this.staticArrayProcessor.process(path);
|
|
38
|
+
|
|
22
39
|
}
|
|
23
40
|
|
|
24
41
|
private addSourceLocationAttribute(
|
|
@@ -27,7 +44,7 @@ export class JSXProcessor {
|
|
|
27
44
|
const { line, column } = path.node.loc?.start || { line: 1, column: 0 };
|
|
28
45
|
const value = `${this.filename}:${line}:${column}`;
|
|
29
46
|
|
|
30
|
-
path.node.attributes.
|
|
47
|
+
path.node.attributes.unshift(
|
|
31
48
|
this.types.jsxAttribute(
|
|
32
49
|
this.types.jsxIdentifier("data-source-location"),
|
|
33
50
|
this.types.stringLiteral(value)
|
|
@@ -45,21 +62,19 @@ export class JSXProcessor {
|
|
|
45
62
|
parentElement.node as t.JSXElement
|
|
46
63
|
);
|
|
47
64
|
|
|
48
|
-
path.node.attributes.
|
|
49
|
-
|
|
50
|
-
this.types.
|
|
51
|
-
this.types.
|
|
52
|
-
|
|
65
|
+
const sourceLocIdx = path.node.attributes.findIndex(
|
|
66
|
+
(attr) =>
|
|
67
|
+
this.types.isJSXAttribute(attr) &&
|
|
68
|
+
this.types.isJSXIdentifier(attr.name) &&
|
|
69
|
+
attr.name.name === "data-source-location"
|
|
53
70
|
);
|
|
54
|
-
}
|
|
55
71
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
path.node.attributes.push(
|
|
72
|
+
path.node.attributes.splice(
|
|
73
|
+
sourceLocIdx + 1,
|
|
74
|
+
0,
|
|
60
75
|
this.types.jsxAttribute(
|
|
61
|
-
this.types.jsxIdentifier("content
|
|
62
|
-
this.types.stringLiteral("true")
|
|
76
|
+
this.types.jsxIdentifier("data-dynamic-content"),
|
|
77
|
+
this.types.stringLiteral(isDynamic ? "true" : "false")
|
|
63
78
|
)
|
|
64
79
|
);
|
|
65
80
|
}
|
|
@@ -137,6 +152,18 @@ export class JSXProcessor {
|
|
|
137
152
|
}
|
|
138
153
|
};
|
|
139
154
|
|
|
155
|
+
const attributes = jsxElement.openingElement?.attributes || [];
|
|
156
|
+
for (const attr of attributes) {
|
|
157
|
+
if (hasDynamicContent) break;
|
|
158
|
+
if (this.types.isJSXSpreadAttribute(attr)) {
|
|
159
|
+
hasDynamicContent = true;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
if (this.types.isJSXAttribute(attr) && attr.value) {
|
|
163
|
+
traverseNode(attr.value as unknown as Record<string, unknown>);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
140
167
|
for (const child of jsxElement.children) {
|
|
141
168
|
if (hasDynamicContent) break;
|
|
142
169
|
traverseNode(child as unknown as Record<string, unknown>);
|
package/src/jsx-utils.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type * as t from "@babel/types";
|
|
2
|
+
import { ALLOWED_CUSTOM_COMPONENTS } from "./consts.js";
|
|
2
3
|
|
|
3
4
|
export class JSXUtils {
|
|
4
5
|
private static types: typeof t;
|
|
@@ -12,4 +13,119 @@ export class JSXUtils {
|
|
|
12
13
|
? `${attr.name.namespace.name}:${attr.name.name.name}`
|
|
13
14
|
: attr.name.name;
|
|
14
15
|
}
|
|
16
|
+
|
|
17
|
+
static getElementName(node: t.JSXOpeningElement): string | null {
|
|
18
|
+
const name = node.name;
|
|
19
|
+
|
|
20
|
+
if (this.types.isJSXIdentifier(name)) {
|
|
21
|
+
return name.name;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (this.types.isJSXNamespacedName(name)) {
|
|
25
|
+
return `${name.namespace.name}:${name.name.name}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (this.types.isJSXMemberExpression(name)) {
|
|
29
|
+
return this.collectJSXMemberName(name);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private static collectJSXMemberName(
|
|
36
|
+
node: t.JSXMemberExpression
|
|
37
|
+
): string {
|
|
38
|
+
const parts: string[] = [node.property.name];
|
|
39
|
+
let current: t.JSXMemberExpression["object"] = node.object;
|
|
40
|
+
|
|
41
|
+
while (this.types.isJSXMemberExpression(current)) {
|
|
42
|
+
parts.unshift(current.property.name);
|
|
43
|
+
current = current.object;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (this.types.isJSXIdentifier(current)) {
|
|
47
|
+
parts.unshift(current.name);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return parts.join(".");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static isCustomComponent(name: string): boolean {
|
|
54
|
+
return name.length > 0 && name.charAt(0) === name.charAt(0).toUpperCase();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static isAllowedCustomComponent(name: string): boolean {
|
|
58
|
+
return ALLOWED_CUSTOM_COMPONENTS.includes(name);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static isDOMElement(name: string): boolean {
|
|
62
|
+
return name.length > 0 && name.charAt(0) === name.charAt(0).toLowerCase();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static isDOMOrAllowed(name: string): boolean {
|
|
66
|
+
return this.isDOMElement(name) || this.isAllowedCustomComponent(name);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static producesJSX(node: t.Expression | t.Node): boolean {
|
|
70
|
+
const types = this.types;
|
|
71
|
+
|
|
72
|
+
if (types.isJSXElement(node) || types.isJSXFragment(node)) return true;
|
|
73
|
+
|
|
74
|
+
if (types.isConditionalExpression(node)) {
|
|
75
|
+
return (
|
|
76
|
+
this.producesJSX(node.consequent) || this.producesJSX(node.alternate)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (types.isLogicalExpression(node)) {
|
|
81
|
+
return this.producesJSX(node.left) || this.producesJSX(node.right);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (types.isCallExpression(node)) {
|
|
85
|
+
if (types.isMemberExpression(node.callee)) {
|
|
86
|
+
const prop = node.callee.property;
|
|
87
|
+
if (
|
|
88
|
+
types.isIdentifier(prop) &&
|
|
89
|
+
(prop.name === "map" || prop.name === "flatMap")
|
|
90
|
+
) {
|
|
91
|
+
const callback = node.arguments[0];
|
|
92
|
+
if (callback) return this.callbackProducesJSX(callback);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (types.isParenthesizedExpression(node)) {
|
|
98
|
+
return this.producesJSX(node.expression);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private static callbackProducesJSX(
|
|
105
|
+
callback: t.Expression | t.SpreadElement | t.ArgumentPlaceholder
|
|
106
|
+
): boolean {
|
|
107
|
+
const types = this.types;
|
|
108
|
+
|
|
109
|
+
if (types.isArrowFunctionExpression(callback)) {
|
|
110
|
+
if (types.isBlockStatement(callback.body)) {
|
|
111
|
+
return this.doesBlockReturnJSX(callback.body);
|
|
112
|
+
}
|
|
113
|
+
return this.producesJSX(callback.body);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (types.isFunctionExpression(callback)) {
|
|
117
|
+
return this.doesBlockReturnJSX(callback.body);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static doesBlockReturnJSX(block: t.BlockStatement): boolean {
|
|
124
|
+
for (const stmt of block.body) {
|
|
125
|
+
if (this.types.isReturnStatement(stmt) && stmt.argument) {
|
|
126
|
+
if (this.producesJSX(stmt.argument)) return true;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
15
131
|
}
|