🐛 (editor) Fix single block duplication
This commit is contained in:
@ -17,6 +17,7 @@ import {
|
|||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
|
|
||||||
export interface ContextMenuProps<T extends HTMLElement> {
|
export interface ContextMenuProps<T extends HTMLElement> {
|
||||||
|
onOpen?: () => void
|
||||||
renderMenu: () => JSX.Element | null
|
renderMenu: () => JSX.Element | null
|
||||||
children: (
|
children: (
|
||||||
ref: MutableRefObject<T | null>,
|
ref: MutableRefObject<T | null>,
|
||||||
@ -61,6 +62,7 @@ export function ContextMenu<T extends HTMLElement = HTMLElement>(
|
|||||||
if (e.currentTarget === targetRef.current) {
|
if (e.currentTarget === targetRef.current) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
props.onOpen?.()
|
||||||
setIsOpened(true)
|
setIsOpened(true)
|
||||||
setPosition([e.pageX, e.pageY])
|
setPosition([e.pageX, e.pageY])
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
import { Fade, Flex, FlexProps, useEventListener } from '@chakra-ui/react'
|
import { Fade, Flex, FlexProps, useEventListener } from '@chakra-ui/react'
|
||||||
import React, { useRef, useMemo, useEffect, useState } from 'react'
|
import React, { useRef, useMemo, useEffect, useState } from 'react'
|
||||||
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
|
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
|
||||||
import {
|
import { BlockV6, PublicTypebotV6, TypebotV6 } from '@typebot.io/schemas'
|
||||||
BlockV6,
|
|
||||||
GroupV6,
|
|
||||||
PublicTypebotV6,
|
|
||||||
TypebotV6,
|
|
||||||
} from '@typebot.io/schemas'
|
|
||||||
import { useDebounce } from 'use-debounce'
|
import { useDebounce } from 'use-debounce'
|
||||||
import GraphElements from './GraphElements'
|
import GraphElements from './GraphElements'
|
||||||
import { createId } from '@paralleldrive/cuid2'
|
import { createId } from '@paralleldrive/cuid2'
|
||||||
@ -56,7 +51,7 @@ export const Graph = ({
|
|||||||
draggedItem,
|
draggedItem,
|
||||||
setDraggedItem,
|
setDraggedItem,
|
||||||
} = useBlockDnd()
|
} = useBlockDnd()
|
||||||
const { pasteGroups, createGroup } = useTypebot()
|
const { createGroup } = useTypebot()
|
||||||
const { user } = useUser()
|
const { user } = useUser()
|
||||||
const {
|
const {
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
@ -84,9 +79,6 @@ export const Graph = ({
|
|||||||
setFocusedGroups: state.setFocusedGroups,
|
setFocusedGroups: state.setFocusedGroups,
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
const groupsInClipboard = useGroupsStore(
|
|
||||||
useShallow((state) => state.groupsInClipboard)
|
|
||||||
)
|
|
||||||
|
|
||||||
const [graphPosition, setGraphPosition] = useState(
|
const [graphPosition, setGraphPosition] = useState(
|
||||||
graphPositionDefaultValue(
|
graphPositionDefaultValue(
|
||||||
@ -319,27 +311,7 @@ export const Graph = ({
|
|||||||
setAutoMoveDirection(undefined)
|
setAutoMoveDirection(undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
useKeyboardShortcuts({
|
useKeyboardShortcuts({})
|
||||||
paste: () => {
|
|
||||||
if (!groupsInClipboard || isReadOnly) return
|
|
||||||
const { groups, oldToNewIdsMapping } = parseGroupsToPaste(
|
|
||||||
groupsInClipboard.groups,
|
|
||||||
lastMouseClickPosition ??
|
|
||||||
projectMouse(
|
|
||||||
{
|
|
||||||
x: window.innerWidth / 2,
|
|
||||||
y: window.innerHeight / 2,
|
|
||||||
},
|
|
||||||
graphPosition
|
|
||||||
)
|
|
||||||
)
|
|
||||||
groups.forEach((group) => {
|
|
||||||
updateGroupCoordinates(group.id, group.graphCoordinates)
|
|
||||||
})
|
|
||||||
pasteGroups(groups, groupsInClipboard.edges, oldToNewIdsMapping)
|
|
||||||
setFocusedGroups(groups.map((g) => g.id))
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
useEventListener('keydown', (e) => {
|
useEventListener('keydown', (e) => {
|
||||||
if (e.key === ' ') setIsDraggingGraph(true)
|
if (e.key === ' ') setIsDraggingGraph(true)
|
||||||
@ -383,8 +355,11 @@ export const Graph = ({
|
|||||||
{selectBoxCoordinates && <SelectBox {...selectBoxCoordinates} />}
|
{selectBoxCoordinates && <SelectBox {...selectBoxCoordinates} />}
|
||||||
<Fade in={!isReadOnly && focusedGroups.length > 1}>
|
<Fade in={!isReadOnly && focusedGroups.length > 1}>
|
||||||
<GroupSelectionMenu
|
<GroupSelectionMenu
|
||||||
|
lastMouseClickPosition={lastMouseClickPosition}
|
||||||
focusedGroups={focusedGroups}
|
focusedGroups={focusedGroups}
|
||||||
blurGroups={blurGroups}
|
blurGroups={blurGroups}
|
||||||
|
graphPosition={graphPosition}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
</Fade>
|
</Fade>
|
||||||
</>
|
</>
|
||||||
@ -453,42 +428,3 @@ const useAutoMoveBoard = (
|
|||||||
clearInterval(interval)
|
clearInterval(interval)
|
||||||
}
|
}
|
||||||
}, [autoMoveDirection, setGraphPosition])
|
}, [autoMoveDirection, setGraphPosition])
|
||||||
|
|
||||||
const parseGroupsToPaste = (
|
|
||||||
groups: GroupV6[],
|
|
||||||
mousePosition: Coordinates
|
|
||||||
): { groups: GroupV6[]; oldToNewIdsMapping: Map<string, string> } => {
|
|
||||||
const farLeftGroup = groups.sort(
|
|
||||||
(a, b) => a.graphCoordinates.x - b.graphCoordinates.x
|
|
||||||
)[0]
|
|
||||||
const farLeftGroupCoord = farLeftGroup.graphCoordinates
|
|
||||||
|
|
||||||
const oldToNewIdsMapping = new Map<string, string>()
|
|
||||||
const newGroups = groups.map((group) => {
|
|
||||||
const newId = createId()
|
|
||||||
oldToNewIdsMapping.set(group.id, newId)
|
|
||||||
|
|
||||||
return {
|
|
||||||
...group,
|
|
||||||
id: newId,
|
|
||||||
graphCoordinates:
|
|
||||||
group.id === farLeftGroup.id
|
|
||||||
? mousePosition
|
|
||||||
: {
|
|
||||||
x:
|
|
||||||
mousePosition.x +
|
|
||||||
group.graphCoordinates.x -
|
|
||||||
farLeftGroupCoord.x,
|
|
||||||
y:
|
|
||||||
mousePosition.y +
|
|
||||||
group.graphCoordinates.y -
|
|
||||||
farLeftGroupCoord.y,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
groups: newGroups,
|
|
||||||
oldToNewIdsMapping,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -12,29 +12,55 @@ import {
|
|||||||
import { useRef } from 'react'
|
import { useRef } from 'react'
|
||||||
import { useGroupsStore } from '../hooks/useGroupsStore'
|
import { useGroupsStore } from '../hooks/useGroupsStore'
|
||||||
import { toast } from 'sonner'
|
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'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
graphPosition: Coordinates & { scale: number }
|
||||||
|
isReadOnly: boolean
|
||||||
|
lastMouseClickPosition: Coordinates | undefined
|
||||||
focusedGroups: string[]
|
focusedGroups: string[]
|
||||||
blurGroups: () => void
|
blurGroups: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GroupSelectionMenu = ({ focusedGroups, blurGroups }: Props) => {
|
export const GroupSelectionMenu = ({
|
||||||
const { typebot, deleteGroups } = useTypebot()
|
graphPosition,
|
||||||
|
lastMouseClickPosition,
|
||||||
|
isReadOnly,
|
||||||
|
focusedGroups,
|
||||||
|
blurGroups,
|
||||||
|
}: Props) => {
|
||||||
|
const { typebot, deleteGroups, pasteGroups } = useTypebot()
|
||||||
const ref = useRef<HTMLDivElement>(null)
|
const ref = useRef<HTMLDivElement>(null)
|
||||||
const copyGroups = useGroupsStore((state) => state.copyGroups)
|
|
||||||
|
const groupsInClipboard = useGroupsStore(
|
||||||
|
useShallow((state) => state.groupsInClipboard)
|
||||||
|
)
|
||||||
|
const { copyGroups, setFocusedGroups, updateGroupCoordinates } =
|
||||||
|
useGroupsStore(
|
||||||
|
useShallow((state) => ({
|
||||||
|
copyGroups: state.copyGroups,
|
||||||
|
updateGroupCoordinates: state.updateGroupCoordinates,
|
||||||
|
setFocusedGroups: state.setFocusedGroups,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
useEventListener('pointerup', (e) => e.stopPropagation(), ref.current)
|
useEventListener('pointerup', (e) => e.stopPropagation(), ref.current)
|
||||||
|
|
||||||
const handleCopy = () => {
|
const handleCopy = () => {
|
||||||
if (!typebot) return
|
if (!typebot) return
|
||||||
const groups = typebot.groups.filter((g) => focusedGroups.includes(g.id))
|
const groups = typebot.groups.filter((g) => focusedGroups.includes(g.id))
|
||||||
copyGroups(
|
const edges = typebot.edges.filter((edge) =>
|
||||||
groups,
|
groups.find((g) => g.id === edge.to.groupId)
|
||||||
typebot.edges.filter((edge) =>
|
|
||||||
groups.find((g) => g.id === edge.to.groupId)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
toast('Groups copied to clipboard')
|
copyGroups(groups, edges)
|
||||||
|
return {
|
||||||
|
groups,
|
||||||
|
edges,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
@ -42,13 +68,45 @@ export const GroupSelectionMenu = ({ focusedGroups, blurGroups }: Props) => {
|
|||||||
blurGroups()
|
blurGroups()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handlePaste = (overrideClipBoard?: {
|
||||||
|
groups: GroupV6[]
|
||||||
|
edges: Edge[]
|
||||||
|
}) => {
|
||||||
|
if (!groupsInClipboard || isReadOnly) return
|
||||||
|
const clipboard = overrideClipBoard ?? groupsInClipboard
|
||||||
|
const { groups, oldToNewIdsMapping } = parseGroupsToPaste(
|
||||||
|
clipboard.groups,
|
||||||
|
lastMouseClickPosition ??
|
||||||
|
projectMouse(
|
||||||
|
{
|
||||||
|
x: window.innerWidth / 2,
|
||||||
|
y: window.innerHeight / 2,
|
||||||
|
},
|
||||||
|
graphPosition
|
||||||
|
)
|
||||||
|
)
|
||||||
|
groups.forEach((group) => {
|
||||||
|
updateGroupCoordinates(group.id, group.graphCoordinates)
|
||||||
|
})
|
||||||
|
pasteGroups(groups, clipboard.edges, oldToNewIdsMapping)
|
||||||
|
setFocusedGroups(groups.map((g) => g.id))
|
||||||
|
}
|
||||||
|
|
||||||
useKeyboardShortcuts({
|
useKeyboardShortcuts({
|
||||||
copy: handleCopy,
|
copy: () => {
|
||||||
|
handleCopy()
|
||||||
|
toast('Groups copied to clipboard')
|
||||||
|
},
|
||||||
cut: () => {
|
cut: () => {
|
||||||
handleCopy()
|
handleCopy()
|
||||||
handleDelete()
|
handleDelete()
|
||||||
},
|
},
|
||||||
|
duplicate: () => {
|
||||||
|
const clipboard = handleCopy()
|
||||||
|
handlePaste(clipboard)
|
||||||
|
},
|
||||||
backspace: handleDelete,
|
backspace: handleDelete,
|
||||||
|
paste: handlePaste,
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -95,3 +153,42 @@ export const GroupSelectionMenu = ({ focusedGroups, blurGroups }: Props) => {
|
|||||||
</HStack>
|
</HStack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parseGroupsToPaste = (
|
||||||
|
groups: GroupV6[],
|
||||||
|
mousePosition: Coordinates
|
||||||
|
): { groups: GroupV6[]; oldToNewIdsMapping: Map<string, string> } => {
|
||||||
|
const farLeftGroup = groups.sort(
|
||||||
|
(a, b) => a.graphCoordinates.x - b.graphCoordinates.x
|
||||||
|
)[0]
|
||||||
|
const farLeftGroupCoord = farLeftGroup.graphCoordinates
|
||||||
|
|
||||||
|
const oldToNewIdsMapping = new Map<string, string>()
|
||||||
|
const newGroups = groups.map((group) => {
|
||||||
|
const newId = createId()
|
||||||
|
oldToNewIdsMapping.set(group.id, newId)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...group,
|
||||||
|
id: newId,
|
||||||
|
graphCoordinates:
|
||||||
|
group.id === farLeftGroup.id
|
||||||
|
? mousePosition
|
||||||
|
: {
|
||||||
|
x:
|
||||||
|
mousePosition.x +
|
||||||
|
group.graphCoordinates.x -
|
||||||
|
farLeftGroupCoord.x,
|
||||||
|
y:
|
||||||
|
mousePosition.y +
|
||||||
|
group.graphCoordinates.y -
|
||||||
|
farLeftGroupCoord.y,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
groups: newGroups,
|
||||||
|
oldToNewIdsMapping,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,18 +10,19 @@ import {
|
|||||||
type Props = {
|
type Props = {
|
||||||
groupId: string
|
groupId: string
|
||||||
onPlayClick: () => void
|
onPlayClick: () => void
|
||||||
onDuplicateClick: () => void
|
|
||||||
onDeleteClick: () => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GroupFocusToolbar = ({
|
export const GroupFocusToolbar = ({ groupId, onPlayClick }: Props) => {
|
||||||
groupId,
|
|
||||||
onPlayClick,
|
|
||||||
onDuplicateClick,
|
|
||||||
onDeleteClick,
|
|
||||||
}: Props) => {
|
|
||||||
const { hasCopied, onCopy } = useClipboard(groupId)
|
const { hasCopied, onCopy } = useClipboard(groupId)
|
||||||
|
|
||||||
|
const dispatchCopyEvent = () => {
|
||||||
|
dispatchEvent(new KeyboardEvent('keydown', { key: 'c', metaKey: true }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const dispatchDeleteEvent = () => {
|
||||||
|
dispatchEvent(new KeyboardEvent('keydown', { key: 'Backspace' }))
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack
|
<HStack
|
||||||
rounded="md"
|
rounded="md"
|
||||||
@ -44,11 +45,11 @@ export const GroupFocusToolbar = ({
|
|||||||
borderRightWidth="1px"
|
borderRightWidth="1px"
|
||||||
borderRightRadius="none"
|
borderRightRadius="none"
|
||||||
borderLeftRadius="none"
|
borderLeftRadius="none"
|
||||||
aria-label={'Duplicate group'}
|
aria-label={'Copy group'}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
onDuplicateClick()
|
dispatchCopyEvent()
|
||||||
}}
|
}}
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
@ -72,7 +73,7 @@ export const GroupFocusToolbar = ({
|
|||||||
aria-label="Delete"
|
aria-label="Delete"
|
||||||
borderLeftRadius="none"
|
borderLeftRadius="none"
|
||||||
icon={<TrashIcon />}
|
icon={<TrashIcon />}
|
||||||
onClick={onDeleteClick}
|
onClick={dispatchDeleteEvent}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
|
@ -44,13 +44,7 @@ export const GroupNode = ({ group, groupIndex }: Props) => {
|
|||||||
isReadOnly,
|
isReadOnly,
|
||||||
graphPosition,
|
graphPosition,
|
||||||
} = useGraph()
|
} = useGraph()
|
||||||
const {
|
const { typebot, updateGroup, updateGroupsCoordinates } = useTypebot()
|
||||||
typebot,
|
|
||||||
updateGroup,
|
|
||||||
updateGroupsCoordinates,
|
|
||||||
deleteGroup,
|
|
||||||
duplicateGroup,
|
|
||||||
} = useTypebot()
|
|
||||||
const { setMouseOverGroup, mouseOverGroup } = useBlockDnd()
|
const { setMouseOverGroup, mouseOverGroup } = useBlockDnd()
|
||||||
const { setRightPanel, setStartPreviewAtGroup } = useEditor()
|
const { setRightPanel, setStartPreviewAtGroup } = useEditor()
|
||||||
|
|
||||||
@ -159,7 +153,8 @@ export const GroupNode = ({ group, groupIndex }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextMenu<HTMLDivElement>
|
<ContextMenu<HTMLDivElement>
|
||||||
renderMenu={() => <GroupNodeContextMenu groupIndex={groupIndex} />}
|
onOpen={() => focusGroup(group.id)}
|
||||||
|
renderMenu={() => <GroupNodeContextMenu />}
|
||||||
isDisabled={isReadOnly}
|
isDisabled={isReadOnly}
|
||||||
>
|
>
|
||||||
{(ref, isContextMenuOpened) => (
|
{(ref, isContextMenuOpened) => (
|
||||||
@ -241,10 +236,6 @@ export const GroupNode = ({ group, groupIndex }: Props) => {
|
|||||||
<GroupFocusToolbar
|
<GroupFocusToolbar
|
||||||
groupId={group.id}
|
groupId={group.id}
|
||||||
onPlayClick={startPreviewAtThisGroup}
|
onPlayClick={startPreviewAtThisGroup}
|
||||||
onDuplicateClick={() => {
|
|
||||||
duplicateGroup(groupIndex)
|
|
||||||
}}
|
|
||||||
onDeleteClick={() => deleteGroup(groupIndex)}
|
|
||||||
/>
|
/>
|
||||||
</SlideFade>
|
</SlideFade>
|
||||||
)}
|
)}
|
||||||
|
@ -1,24 +1,21 @@
|
|||||||
import { MenuList, MenuItem } from '@chakra-ui/react'
|
import { MenuList, MenuItem } from '@chakra-ui/react'
|
||||||
import { CopyIcon, TrashIcon } from '@/components/icons'
|
import { CopyIcon, TrashIcon } from '@/components/icons'
|
||||||
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
|
|
||||||
import { useTranslate } from '@tolgee/react'
|
import { useTranslate } from '@tolgee/react'
|
||||||
|
|
||||||
export const GroupNodeContextMenu = ({
|
export const GroupNodeContextMenu = () => {
|
||||||
groupIndex,
|
|
||||||
}: {
|
|
||||||
groupIndex: number
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslate()
|
const { t } = useTranslate()
|
||||||
const { deleteGroup, duplicateGroup } = useTypebot()
|
|
||||||
|
|
||||||
const handleDeleteClick = () => deleteGroup(groupIndex)
|
const handleDeleteClick = () =>
|
||||||
|
dispatchEvent(new KeyboardEvent('keydown', { key: 'Backspace' }))
|
||||||
|
|
||||||
const handleDuplicateClick = () => duplicateGroup(groupIndex)
|
const handleDuplicateClick = () => {
|
||||||
|
dispatchEvent(new KeyboardEvent('keydown', { key: 'c', metaKey: true }))
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<MenuItem icon={<CopyIcon />} onClick={handleDuplicateClick}>
|
<MenuItem icon={<CopyIcon />} onClick={handleDuplicateClick}>
|
||||||
{t('duplicate')}
|
{t('copy')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem icon={<TrashIcon />} onClick={handleDeleteClick}>
|
<MenuItem icon={<TrashIcon />} onClick={handleDeleteClick}>
|
||||||
{t('delete')}
|
{t('delete')}
|
||||||
|
@ -6,6 +6,7 @@ type Props = {
|
|||||||
copy?: () => void
|
copy?: () => void
|
||||||
paste?: () => void
|
paste?: () => void
|
||||||
cut?: () => void
|
cut?: () => void
|
||||||
|
duplicate?: () => void
|
||||||
backspace?: () => void
|
backspace?: () => void
|
||||||
}
|
}
|
||||||
export const useKeyboardShortcuts = ({
|
export const useKeyboardShortcuts = ({
|
||||||
@ -14,6 +15,7 @@ export const useKeyboardShortcuts = ({
|
|||||||
copy,
|
copy,
|
||||||
paste,
|
paste,
|
||||||
cut,
|
cut,
|
||||||
|
duplicate,
|
||||||
backspace,
|
backspace,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const isUndoShortcut = (event: KeyboardEvent) =>
|
const isUndoShortcut = (event: KeyboardEvent) =>
|
||||||
@ -31,6 +33,9 @@ export const useKeyboardShortcuts = ({
|
|||||||
const isCutShortcut = (event: KeyboardEvent) =>
|
const isCutShortcut = (event: KeyboardEvent) =>
|
||||||
(event.metaKey || event.ctrlKey) && event.key === 'x'
|
(event.metaKey || event.ctrlKey) && event.key === 'x'
|
||||||
|
|
||||||
|
const isDuplicateShortcut = (event: KeyboardEvent) =>
|
||||||
|
(event.metaKey || event.ctrlKey) && event.key === 'd'
|
||||||
|
|
||||||
const isBackspaceShortcut = (event: KeyboardEvent) =>
|
const isBackspaceShortcut = (event: KeyboardEvent) =>
|
||||||
event.key === 'Backspace'
|
event.key === 'Backspace'
|
||||||
|
|
||||||
@ -44,26 +49,37 @@ export const useKeyboardShortcuts = ({
|
|||||||
if (undo && isUndoShortcut(event)) {
|
if (undo && isUndoShortcut(event)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
undo()
|
undo()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if (redo && isRedoShortcut(event)) {
|
if (redo && isRedoShortcut(event)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
redo()
|
redo()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if (copy && isCopyShortcut(event)) {
|
if (copy && isCopyShortcut(event)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
copy()
|
copy()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if (paste && isPasteShortcut(event)) {
|
if (paste && isPasteShortcut(event)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
paste()
|
paste()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if (cut && isCutShortcut(event)) {
|
if (cut && isCutShortcut(event)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
cut()
|
cut()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (duplicate && isDuplicateShortcut(event)) {
|
||||||
|
event.preventDefault()
|
||||||
|
duplicate()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if (backspace && isBackspaceShortcut(event)) {
|
if (backspace && isBackspaceShortcut(event)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
backspace()
|
backspace()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user