From 1ebfc15178ccd3177c4f91760d0841499d0d9f6f Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Mon, 29 Jan 2024 12:03:51 +0100 Subject: [PATCH] :bug: (editor) Fix dragging text bubble after editting --- .../src/features/graph/components/Graph.tsx | 11 ++----- .../graph/components/GroupSelectionMenu.tsx | 25 +++++++-------- .../components/nodes/block/BlockNode.tsx | 1 + .../graph/providers/GraphDndProvider.tsx | 6 ++-- apps/builder/src/hooks/useEventListener.ts | 32 ++++++++++++++----- .../builder/src/hooks/useKeyboardShortcuts.ts | 5 +++ 6 files changed, 48 insertions(+), 32 deletions(-) diff --git a/apps/builder/src/features/graph/components/Graph.tsx b/apps/builder/src/features/graph/components/Graph.tsx index 46cea048a..43402a346 100644 --- a/apps/builder/src/features/graph/components/Graph.tsx +++ b/apps/builder/src/features/graph/components/Graph.tsx @@ -101,9 +101,6 @@ export const Graph = ({ const [groupRects, setGroupRects] = useState< { groupId: string; rect: DOMRect }[] | undefined >() - const [lastMouseClickPosition, setLastMouseClickPosition] = useState< - Coordinates | undefined - >() const [isDragging, setIsDragging] = useState(false) const graphContainerRef = useRef(null) @@ -167,7 +164,7 @@ export const Graph = ({ if (isRightClick) e.stopPropagation() } - const handlePointerUp = (e: PointerEvent) => { + const handlePointerUp = () => { if (isDraggingGraph) return if ( !selectBoxCoordinates || @@ -176,9 +173,6 @@ export const Graph = ({ 5 ) { blurGroups() - setLastMouseClickPosition( - projectMouse({ x: e.clientX, y: e.clientY }, graphPosition) - ) } setSelectBoxCoordinates(undefined) setOpenedBlockId(undefined) @@ -355,10 +349,9 @@ export const Graph = ({ {selectBoxCoordinates && } 1}> diff --git a/apps/builder/src/features/graph/components/GroupSelectionMenu.tsx b/apps/builder/src/features/graph/components/GroupSelectionMenu.tsx index 0582fedda..0021af1b6 100644 --- a/apps/builder/src/features/graph/components/GroupSelectionMenu.tsx +++ b/apps/builder/src/features/graph/components/GroupSelectionMenu.tsx @@ -9,30 +9,29 @@ import { useColorModeValue, useEventListener, } from '@chakra-ui/react' -import { useRef } from 'react' +import { useRef, useState } from 'react' import { useGroupsStore } from '../hooks/useGroupsStore' import { toast } from 'sonner' import { createId } from '@paralleldrive/cuid2' import { Edge, GroupV6 } from '@typebot.io/schemas' -import { projectMouse } from '../helpers/projectMouse' import { Coordinates } from '../types' import { useShallow } from 'zustand/react/shallow' +import { projectMouse } from '../helpers/projectMouse' type Props = { graphPosition: Coordinates & { scale: number } isReadOnly: boolean - lastMouseClickPosition: Coordinates | undefined focusedGroups: string[] blurGroups: () => void } export const GroupSelectionMenu = ({ graphPosition, - lastMouseClickPosition, isReadOnly, focusedGroups, blurGroups, }: Props) => { + const [mousePosition, setMousePosition] = useState() const { typebot, deleteGroups, pasteGroups } = useTypebot() const ref = useRef(null) @@ -50,6 +49,13 @@ export const GroupSelectionMenu = ({ useEventListener('pointerup', (e) => e.stopPropagation(), ref.current) + useEventListener('mousemove', (e) => { + setMousePosition({ + x: e.clientX, + y: e.clientY, + }) + }) + const handleCopy = () => { if (!typebot) return const groups = typebot.groups.filter((g) => focusedGroups.includes(g.id)) @@ -72,18 +78,11 @@ export const GroupSelectionMenu = ({ groups: GroupV6[] edges: Edge[] }) => { - if (!groupsInClipboard || isReadOnly) return + if (!groupsInClipboard || isReadOnly || !mousePosition) return const clipboard = overrideClipBoard ?? groupsInClipboard const { groups, oldToNewIdsMapping } = parseGroupsToPaste( clipboard.groups, - lastMouseClickPosition ?? - projectMouse( - { - x: window.innerWidth / 2, - y: window.innerHeight / 2, - }, - graphPosition - ) + projectMouse(mousePosition, graphPosition) ) groups.forEach((group) => { updateGroupCoordinates(group.id, group.graphCoordinates) diff --git a/apps/builder/src/features/graph/components/nodes/block/BlockNode.tsx b/apps/builder/src/features/graph/components/nodes/block/BlockNode.tsx index 5603114ab..fe500cefb 100644 --- a/apps/builder/src/features/graph/components/nodes/block/BlockNode.tsx +++ b/apps/builder/src/features/graph/components/nodes/block/BlockNode.tsx @@ -101,6 +101,7 @@ export const BlockNode = ({ ref: blockRef, onDrag, isDisabled: !onMouseDown, + deps: [isEditing], }) const { diff --git a/apps/builder/src/features/graph/providers/GraphDndProvider.tsx b/apps/builder/src/features/graph/providers/GraphDndProvider.tsx index f27043f08..f6f24fb64 100644 --- a/apps/builder/src/features/graph/providers/GraphDndProvider.tsx +++ b/apps/builder/src/features/graph/providers/GraphDndProvider.tsx @@ -105,11 +105,13 @@ export const useDragDistance = ({ onDrag, distanceTolerance = 20, isDisabled = false, + deps = [], }: { ref: React.MutableRefObject onDrag: (position: { absolute: Coordinates; relative: Coordinates }) => void distanceTolerance?: number isDisabled: boolean + deps?: unknown[] }) => { const mouseDownPosition = useRef<{ absolute: Coordinates @@ -119,7 +121,7 @@ export const useDragDistance = ({ const handleMouseUp = () => { if (mouseDownPosition) mouseDownPosition.current = undefined } - useEventListener('mouseup', handleMouseUp) + useEventListener('mouseup', handleMouseUp, undefined, undefined, deps) const handleMouseDown = (e: MouseEvent) => { if (isDisabled || !ref.current) return @@ -133,7 +135,7 @@ export const useDragDistance = ({ }, } } - useEventListener('mousedown', handleMouseDown, ref) + useEventListener('mousedown', handleMouseDown, ref, undefined, deps) useEffect(() => { let triggered = false diff --git a/apps/builder/src/hooks/useEventListener.ts b/apps/builder/src/hooks/useEventListener.ts index 2c490c6fb..3c8a093f5 100644 --- a/apps/builder/src/hooks/useEventListener.ts +++ b/apps/builder/src/hooks/useEventListener.ts @@ -9,7 +9,8 @@ function useEventListener( eventName: K, handler: (event: MediaQueryListEventMap[K]) => void, element: RefObject, - options?: Options + options?: Options, + deps?: unknown[] ): void // Window Event based useEventListener interface @@ -17,7 +18,8 @@ function useEventListener( eventName: K, handler: (event: WindowEventMap[K]) => void, element?: undefined, - options?: Options + options?: Options, + deps?: unknown[] ): void // Element Event based useEventListener interface @@ -28,7 +30,8 @@ function useEventListener< eventName: K, handler: (event: HTMLElementEventMap[K]) => void, element: RefObject, - options?: Options + options?: Options, + deps?: unknown[] ): void // Document Event based useEventListener interface @@ -36,7 +39,8 @@ function useEventListener( eventName: K, handler: (event: DocumentEventMap[K]) => void, element: RefObject, - options?: Options + options?: Options, + deps?: unknown[] ): void function useEventListener< @@ -54,7 +58,8 @@ function useEventListener< | Event ) => void, element?: RefObject, - options?: Options + options?: Options, + deps: unknown[] = [] ) { // Create a ref that stores handler const savedHandler = useRef(handler) @@ -72,7 +77,12 @@ function useEventListener< if (!(targetElement && targetElement.addEventListener)) return if (options && typeof options !== 'boolean') - console.log('Add event listener', { eventName, element, options }) + console.log('Add event listener', { + elementText: (targetElement as HTMLElement).textContent, + eventName, + element: targetElement, + options, + }) // Create event listener that calls handler function stored in ref const listener: typeof handler = (event) => savedHandler.current(event) @@ -81,10 +91,16 @@ function useEventListener< // Remove event listener on cleanup return () => { if (options && typeof options !== 'boolean') - console.log('Remove event listener') + console.log('Remove event listener', { + elementText: (targetElement as HTMLElement).textContent, + eventName, + element: targetElement, + options, + }) targetElement.removeEventListener(eventName, listener, options) } - }, [eventName, element, options]) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [eventName, ...deps]) } export { useEventListener } diff --git a/apps/builder/src/hooks/useKeyboardShortcuts.ts b/apps/builder/src/hooks/useKeyboardShortcuts.ts index 842855b5a..9f4bb30e4 100644 --- a/apps/builder/src/hooks/useKeyboardShortcuts.ts +++ b/apps/builder/src/hooks/useKeyboardShortcuts.ts @@ -1,3 +1,4 @@ +import { isNotEmpty } from '@typebot.io/lib' import { useEventListener } from './useEventListener' type Props = { @@ -40,6 +41,10 @@ export const useKeyboardShortcuts = ({ event.key === 'Backspace' useEventListener('keydown', (event: KeyboardEvent) => { + if (!event.metaKey && !event.ctrlKey && event.key !== 'Backspace') return + // get text selection + const textSelection = window.getSelection()?.toString() + if (isNotEmpty(textSelection)) return const target = event.target as HTMLElement | null const isTyping = target?.role === 'textbox' ||