Files
Housekeeping-web-admin/node_modules/@base44/vite-plugin/dist/capabilities/inline-edit/controller.js
T
2026-06-24 09:48:54 +02:00

203 lines
7.5 KiB
JavaScript

import { injectFocusOutlineCSS, removeFocusOutlineCSS, selectText, shouldEnterInlineEditingMode, isStaticArrayTextElement, } from "./dom-utils.js";
import { PLUGIN_ELEMENT_ATTR } from "../../injections/utils.js";
const DEBOUNCE_MS = 500;
export function createInlineEditController(host) {
let currentEditingElement = null;
let debouncedSendTimeout = null;
let enabled = false;
const listenerAbortControllers = new WeakMap();
// --- Private helpers ---
const repositionOverlays = () => {
const selectedId = host.getSelectedElementId();
if (!selectedId)
return;
const elements = host.findElementsById(selectedId);
const overlays = host.getSelectedOverlays();
overlays.forEach((overlay, i) => {
if (i < elements.length && elements[i]) {
host.positionOverlay(overlay, elements[i]);
}
});
};
const reportEdit = (element) => {
const originalContent = element.dataset.originalTextContent;
const newContent = element.textContent;
const svgElement = element;
const rect = element.getBoundingClientRect();
const message = {
type: "inline-edit",
elementInfo: {
tagName: element.tagName,
classes: svgElement.className?.baseVal ||
element.className ||
"",
visualSelectorId: host.getSelectedElementId(),
content: newContent,
dataSourceLocation: element.dataset.sourceLocation,
isDynamicContent: element.dataset.dynamicContent === "true",
linenumber: element.dataset.linenumber,
filename: element.dataset.filename,
position: {
top: rect.top,
left: rect.left,
right: rect.right,
bottom: rect.bottom,
width: rect.width,
height: rect.height,
centerX: rect.left + rect.width / 2,
centerY: rect.top + rect.height / 2,
},
},
originalContent,
newContent,
};
if (isStaticArrayTextElement(element)) {
message.arrIndex = element.dataset.arrIndex;
message.arrVariableName = element.dataset.arrVariableName;
message.arrField = element.dataset.arrField;
}
window.parent.postMessage(message, "*");
element.dataset.originalTextContent = newContent || "";
};
const debouncedReport = (element) => {
if (debouncedSendTimeout)
clearTimeout(debouncedSendTimeout);
debouncedSendTimeout = setTimeout(() => reportEdit(element), DEBOUNCE_MS);
};
const onTextInput = (element) => {
repositionOverlays();
debouncedReport(element);
};
const handleInputEvent = function () {
onTextInput(this);
};
const makeEditable = (element) => {
injectFocusOutlineCSS();
element.dataset.originalTextContent = element.textContent || "";
element.dataset.originalCursor = element.style.cursor;
element.contentEditable = "true";
element.setAttribute(PLUGIN_ELEMENT_ATTR, "true");
const abortController = new AbortController();
listenerAbortControllers.set(element, abortController);
element.addEventListener("input", handleInputEvent, {
signal: abortController.signal,
});
element.style.cursor = "text";
selectText(element);
setTimeout(() => {
if (element.isConnected) {
element.focus();
}
}, 0);
};
const makeNonEditable = (element) => {
const abortController = listenerAbortControllers.get(element);
if (abortController) {
abortController.abort();
listenerAbortControllers.delete(element);
}
if (!element.isConnected)
return;
removeFocusOutlineCSS();
element.contentEditable = "false";
element.removeAttribute(PLUGIN_ELEMENT_ATTR);
delete element.dataset.originalTextContent;
if (element.dataset.originalCursor !== undefined) {
element.style.cursor = element.dataset.originalCursor;
delete element.dataset.originalCursor;
}
};
// --- Public API ---
return {
get enabled() {
return enabled;
},
set enabled(value) {
enabled = value;
},
isEditing() {
return currentEditingElement !== null;
},
getCurrentElement() {
return currentEditingElement;
},
canEdit(element) {
return shouldEnterInlineEditingMode(element);
},
startEditing(element) {
currentEditingElement = element;
host.getSelectedOverlays().forEach((o) => {
o.style.display = "none";
});
makeEditable(element);
window.parent.postMessage({
type: "content-editing-started",
visualSelectorId: host.getSelectedElementId(),
}, "*");
},
stopEditing() {
if (!currentEditingElement)
return;
if (debouncedSendTimeout) {
clearTimeout(debouncedSendTimeout);
debouncedSendTimeout = null;
}
const element = currentEditingElement;
makeNonEditable(element);
host.getSelectedOverlays().forEach((o) => {
o.style.display = "";
});
repositionOverlays();
window.parent.postMessage({
type: "content-editing-ended",
visualSelectorId: host.getSelectedElementId(),
}, "*");
currentEditingElement = null;
},
markElementsSelected(elements) {
elements.forEach((el) => {
if (el instanceof HTMLElement) {
el.dataset.selected = "true";
}
});
},
clearSelectedMarks(elementId) {
if (!elementId)
return;
host.findElementsById(elementId).forEach((el) => {
if (el instanceof HTMLElement) {
delete el.dataset.selected;
}
});
},
handleToggleMessage(data) {
if (!enabled)
return;
const elements = host.findElementsById(data.dataSourceLocation);
if (elements.length === 0 || !(elements[0] instanceof HTMLElement))
return;
const element = elements[0];
if (data.inlineEditingMode) {
if (!shouldEnterInlineEditingMode(element))
return;
// Select the element first if not already selected
if (host.getSelectedElementId() !== data.dataSourceLocation) {
this.stopEditing();
host.clearSelection();
this.markElementsSelected(elements);
host.createSelectionOverlays(elements, data.dataSourceLocation);
}
this.startEditing(element);
}
else {
if (currentEditingElement === element) {
this.stopEditing();
}
}
},
cleanup() {
this.stopEditing();
},
};
}
//# sourceMappingURL=controller.js.map