🐛 (editor) Fix dragging text bubble after editting
This commit is contained in:
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -101,6 +101,7 @@ export const BlockNode = ({
|
||||
ref: blockRef,
|
||||
onDrag,
|
||||
isDisabled: !onMouseDown,
|
||||
deps: [isEditing],
|
||||
})
|
||||
|
||||
const {
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
|
@ -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' ||
|
||||
|
Reference in New Issue
Block a user