2
0

🐛 (editor) Fix dragging text bubble after editting

This commit is contained in:
Baptiste Arnaud
2024-01-29 12:03:51 +01:00
parent 0817fbaebb
commit 1ebfc15178
6 changed files with 48 additions and 32 deletions

View File

@ -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<HTMLDivElement | null>(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 && <SelectBox {...selectBoxCoordinates} />}
<Fade in={!isReadOnly && focusedGroups.length > 1}>
<GroupSelectionMenu
lastMouseClickPosition={lastMouseClickPosition}
graphPosition={graphPosition}
focusedGroups={focusedGroups}
blurGroups={blurGroups}
graphPosition={graphPosition}
isReadOnly={isReadOnly}
/>
</Fade>

View File

@ -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<Coordinates>()
const { typebot, deleteGroups, pasteGroups } = useTypebot()
const ref = useRef<HTMLDivElement>(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)

View File

@ -101,6 +101,7 @@ export const BlockNode = ({
ref: blockRef,
onDrag,
isDisabled: !onMouseDown,
deps: [isEditing],
})
const {

View File

@ -105,11 +105,13 @@ export const useDragDistance = ({
onDrag,
distanceTolerance = 20,
isDisabled = false,
deps = [],
}: {
ref: React.MutableRefObject<HTMLDivElement | null>
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

View File

@ -9,7 +9,8 @@ function useEventListener<K extends keyof MediaQueryListEventMap>(
eventName: K,
handler: (event: MediaQueryListEventMap[K]) => void,
element: RefObject<MediaQueryList>,
options?: Options
options?: Options,
deps?: unknown[]
): void
// Window Event based useEventListener interface
@ -17,7 +18,8 @@ function useEventListener<K extends keyof WindowEventMap>(
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<T>,
options?: Options
options?: Options,
deps?: unknown[]
): void
// Document Event based useEventListener interface
@ -36,7 +39,8 @@ function useEventListener<K extends keyof DocumentEventMap>(
eventName: K,
handler: (event: DocumentEventMap[K]) => void,
element: RefObject<Document>,
options?: Options
options?: Options,
deps?: unknown[]
): void
function useEventListener<
@ -54,7 +58,8 @@ function useEventListener<
| Event
) => void,
element?: RefObject<T>,
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 }

View File

@ -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' ||