move more of the code into EditPopup

This commit is contained in:
Abi Raja 2024-05-29 16:53:36 -04:00
parent b0e7ae35af
commit 2709312943
2 changed files with 105 additions and 99 deletions

View File

@ -19,103 +19,19 @@ function Preview({ code, device, doUpdate, inSelectAndEditMode }: Props) {
// Don't update code more often than every 200ms. // Don't update code more often than every 200ms.
const throttledCode = useThrottle(code, 200); const throttledCode = useThrottle(code, 200);
const inSelectAndEditModeRef = useRef(inSelectAndEditMode); // Create a ref for the state // Select and edit functionality
const [clickEvent, setClickEvent] = useState<MouseEvent | null>(null);
const [selectedElement, setSelectedElement] = useState<
HTMLElement | undefined
>(undefined);
const [popupVisible, setPopupVisible] = useState(false);
const [popupPosition, setPopupPosition] = useState({ x: 0, y: 0 });
function removeHighlight(element: HTMLElement) {
element.style.outline = "";
element.style.backgroundColor = "";
return element;
}
function updateHighlight(targetElement: HTMLElement) {
setSelectedElement((prev) => {
// Remove style from previous element
if (prev) {
removeHighlight(prev);
}
// Add style to new element
targetElement.style.outline = "2px dashed #1846db";
targetElement.style.backgroundColor = "#bfcbf5";
return targetElement;
});
}
function handleClick(event: MouseEvent) {
// Return if not in select and edit mode
if (!inSelectAndEditModeRef.current) {
return;
}
const { clientX, clientY } = event;
// Prevent default to avoid issues like label clicks triggering textareas, etc.
event.preventDefault();
const targetElement = event.target as HTMLElement;
// Return if no target element
if (!targetElement) {
return;
}
// Highlight the selected element
updateHighlight(targetElement);
// Show popup at click position, slightly offset to be right under the cursor
setPopupVisible(false);
// Calculate offsets
const rect = iframeRef.current?.getBoundingClientRect();
const offsetX = rect ? rect.left : 0;
const offsetY = rect ? rect.top : 0;
// Adjust for scale
const scale = 1; // the scale factor applied to the iframe
const scaledX = clientX / scale + offsetX;
const scaledY = clientY / scale + offsetY;
setPopupPosition({ x: scaledX, y: scaledY });
setPopupVisible(true);
}
function handleEditSubmit(editText: string) {
doUpdate(
editText,
selectedElement ? removeHighlight(selectedElement) : selectedElement
);
setSelectedElement(undefined);
setPopupVisible(false);
}
useEffect(() => {
if (!inSelectAndEditMode) {
if (selectedElement) removeHighlight(selectedElement);
setSelectedElement(undefined);
setPopupVisible(false);
}
}, [inSelectAndEditMode, selectedElement]);
// Update the ref whenever the state changes
useEffect(() => {
inSelectAndEditModeRef.current = inSelectAndEditMode;
}, [inSelectAndEditMode]);
useEffect(() => { useEffect(() => {
const iframe = iframeRef.current; const iframe = iframeRef.current;
if (iframe) { if (iframe) {
iframe.srcdoc = throttledCode; iframe.srcdoc = throttledCode;
// Related to // Set up click handler for select and edit funtionality
iframe.addEventListener("load", function () { iframe.addEventListener("load", function () {
iframe.contentWindow?.document.body.addEventListener( iframe.contentWindow?.document.body.addEventListener(
"click", "click",
handleClick setClickEvent
); );
}); });
} }
@ -137,11 +53,10 @@ function Preview({ code, device, doUpdate, inSelectAndEditMode }: Props) {
)} )}
></iframe> ></iframe>
<EditPopup <EditPopup
{...{ event={clickEvent}
popupVisible, iframeRef={iframeRef}
popupPosition, inSelectAndEditMode={inSelectAndEditMode}
handleEditSubmit, doUpdate={doUpdate}
}}
/> />
</div> </div>
); );

View File

@ -2,20 +2,60 @@ import React, { useEffect, useRef, useState } from "react";
import { Textarea } from "../ui/textarea"; import { Textarea } from "../ui/textarea";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
function removeHighlight(element: HTMLElement) {
element.style.outline = "";
element.style.backgroundColor = "";
return element;
}
interface EditPopupProps { interface EditPopupProps {
popupVisible: boolean; event: MouseEvent | null;
popupPosition: { x: number; y: number }; inSelectAndEditMode: boolean;
handleEditSubmit: (editText: string) => void; doUpdate: (
selectedUpdateInstruction?: string,
selectedElement?: HTMLElement
) => void;
iframeRef: React.RefObject<HTMLIFrameElement>;
} }
const EditPopup: React.FC<EditPopupProps> = ({ const EditPopup: React.FC<EditPopupProps> = ({
popupVisible, event,
popupPosition, inSelectAndEditMode,
handleEditSubmit, doUpdate,
iframeRef,
}) => { }) => {
const [editText, setEditText] = useState(""); const [editText, setEditText] = useState("");
const [selectedElement, setSelectedElement] = useState<
HTMLElement | undefined
>(undefined);
const [popupVisible, setPopupVisible] = useState(false);
const [popupPosition, setPopupPosition] = useState({ x: 0, y: 0 });
const inSelectAndEditModeRef = useRef(inSelectAndEditMode); // Create a ref for the state
const textareaRef = useRef<HTMLTextAreaElement | null>(null); const textareaRef = useRef<HTMLTextAreaElement | null>(null);
function updateHighlight(targetElement: HTMLElement) {
setSelectedElement((prev) => {
// Remove style from previous element
if (prev) {
removeHighlight(prev);
}
// Add style to new element
targetElement.style.outline = "2px dashed #1846db";
targetElement.style.backgroundColor = "#bfcbf5";
return targetElement;
});
}
function handleEditSubmit(editText: string) {
doUpdate(
editText,
selectedElement ? removeHighlight(selectedElement) : selectedElement
);
setSelectedElement(undefined);
setPopupVisible(false);
}
useEffect(() => { useEffect(() => {
if (popupVisible && textareaRef.current) { if (popupVisible && textareaRef.current) {
textareaRef.current.focus(); textareaRef.current.focus();
@ -28,6 +68,57 @@ const EditPopup: React.FC<EditPopupProps> = ({
} }
}, [popupVisible, setEditText]); }, [popupVisible, setEditText]);
useEffect(() => {
if (!inSelectAndEditMode) {
if (selectedElement) removeHighlight(selectedElement);
setSelectedElement(undefined);
setPopupVisible(false);
}
}, [inSelectAndEditMode, selectedElement]);
useEffect(() => {
// Return if not in select and edit mode
if (!inSelectAndEditModeRef.current || !event) {
return;
}
const { clientX, clientY } = event;
// Prevent default to avoid issues like label clicks triggering textareas, etc.
event.preventDefault();
const targetElement = event.target as HTMLElement;
// Return if no target element
if (!targetElement) {
return;
}
// Highlight the selected element
updateHighlight(targetElement);
// Show popup at click position, slightly offset to be right under the cursor
setPopupVisible(false);
// Calculate offsets
const rect = iframeRef.current?.getBoundingClientRect();
const offsetX = rect ? rect.left : 0;
const offsetY = rect ? rect.top : 0;
// Adjust for scale
const scale = 1; // the scale factor applied to the iframe
const scaledX = clientX / scale + offsetX;
const scaledY = clientY / scale + offsetY;
setPopupPosition({ x: scaledX, y: scaledY });
setPopupVisible(true);
}, [event]);
// Update the ref whenever the state changes
useEffect(() => {
inSelectAndEditModeRef.current = inSelectAndEditMode;
}, [inSelectAndEditMode]);
if (!popupVisible) return; if (!popupVisible) return;
return ( return (