diff --git a/app/canvas/features/wrapper-component/wrapper-component.tsx b/app/canvas/features/wrapper-component/wrapper-component.tsx index 22d053fa1e..a316969df0 100644 --- a/app/canvas/features/wrapper-component/wrapper-component.tsx +++ b/app/canvas/features/wrapper-component/wrapper-component.tsx @@ -70,10 +70,6 @@ export const WrapperComponentDev = ({ "data-id": instance.id, ref: refCallback, onClick: (event: MouseEvent) => { - // Prevent click handler for element selection - if (isEditing) { - event.stopPropagation(); - } if (instance.component === "Link") { event.preventDefault(); } diff --git a/app/canvas/shared/use-shortcuts.ts b/app/canvas/shared/use-shortcuts.ts index df9f480c74..35cc52d80d 100644 --- a/app/canvas/shared/use-shortcuts.ts +++ b/app/canvas/shared/use-shortcuts.ts @@ -9,6 +9,8 @@ import { useTextEditingInstanceId, } from "~/shared/nano-states"; +const inputTags = ["INPUT", "SELECT", "TEXTAREA"] as const; + type HandlerEvent = { key: string; preventDefault?: () => void; @@ -73,7 +75,7 @@ export const useShortcuts = () => { useHotkeys( "backspace, delete", shortcutHandlerMap.delete, - { ...options, enableOnTags: ["INPUT", "SELECT", "TEXTAREA"] }, + { ...options, enableOnTags: [...inputTags] }, [shortcutHandlerMap.delete] ); @@ -89,7 +91,7 @@ export const useShortcuts = () => { setSelectedInstance(undefined); publish<"selectInstance">({ type: "selectInstance" }); }, - options, + { ...options, enableOnContentEditable: true, enableOnTags: [...inputTags] }, [selectedInstance, editingInstanceId] ); diff --git a/app/canvas/shared/use-track-selected-element.ts b/app/canvas/shared/use-track-selected-element.ts index afc0e5bd35..15529e9b5f 100644 --- a/app/canvas/shared/use-track-selected-element.ts +++ b/app/canvas/shared/use-track-selected-element.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect } from "react"; +import { useCallback, useEffect, useRef } from "react"; import { type Instance, publish, useSubscribe } from "@webstudio-is/sdk"; import { useSelectedElement, useSelectedInstance } from "./nano-states"; import { @@ -15,7 +15,9 @@ const eventOptions = { export const useTrackSelectedElement = () => { const [selectedElement, setSelectedElement] = useSelectedElement(); const [selectedInstance, setSelectedInstance] = useSelectedInstance(); - const [, setEditingInstanceId] = useTextEditingInstanceId(); + const [editingInstanceId, setEditingInstanceId] = useTextEditingInstanceId(); + const editingInstanceIdRef = useRef(editingInstanceId); + editingInstanceIdRef.current = editingInstanceId; const [rootInstance] = useRootInstance(); const selectInstance = useCallback( (id) => { @@ -45,17 +47,40 @@ export const useTrackSelectedElement = () => { } }, [selectedInstance, selectedElement, setSelectedElement]); + useEffect(() => { + if ( + editingInstanceIdRef.current !== undefined && + selectedInstance?.id !== editingInstanceIdRef.current + ) { + setEditingInstanceId(undefined); + } + }, [selectedInstance, setEditingInstanceId]); + useEffect(() => { const handleClick = (event: MouseEvent) => { // Notify in general that document was clicked // e.g. to hide the side panel publish<"clickCanvas">({ type: "clickCanvas" }); + let element = event.target as HTMLElement; - if (!(event.target instanceof HTMLElement)) return; + // If we click on an element that is not a component, we search for a parent component. + if (element.dataset.component === undefined) { + const instanceElement = + element.closest("[data-component]"); + if (instanceElement === null) { + return; + } + element = instanceElement; + } - const { id, dataset } = event.target; + const { id, dataset } = element; - // It's a second click in a double click. + // Enable clicking inside of content editable without trying to select the element as an instance. + if (editingInstanceIdRef.current === id) { + return; + } + + // It's the second click in a double click. if (event.detail === 2) { const component = dataset.component as Instance["component"]; if (component === undefined || component in primitives === false) { @@ -65,7 +90,7 @@ export const useTrackSelectedElement = () => { // When user double clicks on an inline instance, we need to select the parent instance and put it indo text editing mode. // Inline instances are not directly, only through parent instance. if (isInlineOnly) { - const parentId = event.target.parentElement?.id; + const parentId = element.parentElement?.id; if (parentId) { selectInstance(parentId); setEditingInstanceId(parentId);