🚸 (editor) Improve block dragging behavior
This commit is contained in:
@@ -87,6 +87,7 @@ export const AutocompleteInput = ({
|
||||
useOutsideClick({
|
||||
ref: dropdownRef,
|
||||
handler: onClose,
|
||||
isEnabled: isOpen,
|
||||
})
|
||||
|
||||
useEffect(
|
||||
@@ -110,7 +111,9 @@ export const AutocompleteInput = ({
|
||||
inputRef.current?.focus()
|
||||
}
|
||||
|
||||
const handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
const updateFocusedDropdownItem = (
|
||||
e: React.KeyboardEvent<HTMLInputElement>
|
||||
) => {
|
||||
if (e.key === 'Enter' && isDefined(keyboardFocusIndex)) {
|
||||
handleItemClick(filteredItems[keyboardFocusIndex])()
|
||||
return setKeyboardFocusIndex(undefined)
|
||||
@@ -179,7 +182,7 @@ export const AutocompleteInput = ({
|
||||
onChange={(e) => changeValue(e.target.value)}
|
||||
onFocus={onOpen}
|
||||
onBlur={updateCarretPosition}
|
||||
onKeyDown={handleKeyUp}
|
||||
onKeyDown={updateFocusedDropdownItem}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</PopoverAnchor>
|
||||
|
||||
@@ -61,10 +61,6 @@ export const Select = <T extends Item>({
|
||||
)
|
||||
)
|
||||
|
||||
const closeDropwdown = () => {
|
||||
onClose()
|
||||
}
|
||||
|
||||
const [keyboardFocusIndex, setKeyboardFocusIndex] = useState<
|
||||
number | undefined
|
||||
>()
|
||||
@@ -85,12 +81,20 @@ export const Select = <T extends Item>({
|
||||
: items
|
||||
).slice(0, 50)
|
||||
|
||||
const closeDropdown = () => {
|
||||
onClose()
|
||||
setTimeout(() => {
|
||||
setIsTouched(false)
|
||||
}, dropdownCloseAnimationDuration)
|
||||
}
|
||||
|
||||
useOutsideClick({
|
||||
ref: dropdownRef,
|
||||
handler: closeDropwdown,
|
||||
handler: closeDropdown,
|
||||
isEnabled: isOpen,
|
||||
})
|
||||
|
||||
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const updateInputValue = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!isOpen) onOpen()
|
||||
if (!isTouched) setIsTouched(true)
|
||||
setInputValue(e.target.value)
|
||||
@@ -101,10 +105,12 @@ export const Select = <T extends Item>({
|
||||
setInputValue(getItemLabel(item))
|
||||
onSelect?.(getItemValue(item), item)
|
||||
setKeyboardFocusIndex(undefined)
|
||||
closeDropwdown()
|
||||
closeDropdown()
|
||||
}
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
const updateFocusedDropdownItem = (
|
||||
e: React.KeyboardEvent<HTMLInputElement>
|
||||
) => {
|
||||
if (e.key === 'Enter' && isDefined(keyboardFocusIndex)) {
|
||||
e.preventDefault()
|
||||
handleItemClick(filteredItems[keyboardFocusIndex])()
|
||||
@@ -138,13 +144,7 @@ export const Select = <T extends Item>({
|
||||
setInputValue('')
|
||||
onSelect?.(undefined)
|
||||
setKeyboardFocusIndex(undefined)
|
||||
closeDropwdown()
|
||||
}
|
||||
|
||||
const resetIsTouched = () => {
|
||||
setTimeout(() => {
|
||||
setIsTouched(false)
|
||||
}, dropdownCloseAnimationDuration)
|
||||
closeDropdown()
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -183,10 +183,9 @@ export const Select = <T extends Item>({
|
||||
placeholder={
|
||||
!isTouched && inputValue !== '' ? undefined : placeholder
|
||||
}
|
||||
onBlur={resetIsTouched}
|
||||
onChange={handleInputChange}
|
||||
onChange={updateInputValue}
|
||||
onFocus={onOpen}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyDown={updateFocusedDropdownItem}
|
||||
pr={selectedItem ? 16 : undefined}
|
||||
/>
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ export const VariableSearchInput = ({
|
||||
useOutsideClick({
|
||||
ref: dropdownRef,
|
||||
handler: onClose,
|
||||
isEnabled: isOpen,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -9,6 +9,7 @@ export const ImageBubbleContent = ({ block }: { block: ImageBubbleBlock }) => {
|
||||
) : (
|
||||
<Box w="full">
|
||||
<Image
|
||||
pointerEvents="none"
|
||||
src={
|
||||
containsVariables ? '/images/dynamic-image.png' : block.content?.url
|
||||
}
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
Flex,
|
||||
Stack,
|
||||
useColorModeValue,
|
||||
useEventListener,
|
||||
} from '@chakra-ui/react'
|
||||
import { Flex, Stack, useColorModeValue } from '@chakra-ui/react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { Plate, PlateProvider, usePlateEditorRef } from '@udecode/plate-core'
|
||||
import { editorStyle, platePlugins } from '@/lib/plate'
|
||||
@@ -84,14 +79,6 @@ const TextBubbleEditorContent = ({
|
||||
}
|
||||
}
|
||||
|
||||
useEventListener(
|
||||
'pointerdown',
|
||||
(e) => {
|
||||
e.stopPropagation()
|
||||
},
|
||||
textEditorRef.current
|
||||
)
|
||||
|
||||
const handleVariableSelected = (variable?: Variable) => {
|
||||
setIsVariableDropdownOpen(false)
|
||||
if (!rememberedSelection.current || !variable) return
|
||||
@@ -115,6 +102,7 @@ const TextBubbleEditorContent = ({
|
||||
pos="relative"
|
||||
spacing={0}
|
||||
cursor="text"
|
||||
className="prevent-group-drag"
|
||||
sx={{
|
||||
'.slate-ToolbarButton-active': {
|
||||
color: useColorModeValue('blue.500', 'blue.300') + ' !important',
|
||||
|
||||
@@ -204,6 +204,7 @@ export const BlockNode = ({
|
||||
onClick={handleClick}
|
||||
data-testid={`block`}
|
||||
w="full"
|
||||
className="prevent-group-drag"
|
||||
>
|
||||
<HStack
|
||||
flex="1"
|
||||
|
||||
@@ -96,6 +96,7 @@ const NonMemoizedDraggableGroupNode = ({
|
||||
handler: () => setIsFocused(false),
|
||||
ref: groupRef,
|
||||
capture: true,
|
||||
isEnabled: isFocused,
|
||||
})
|
||||
|
||||
// When the group is moved from external action (e.g. undo/redo), update the current coordinates
|
||||
@@ -154,8 +155,13 @@ const NonMemoizedDraggableGroupNode = ({
|
||||
useDrag(
|
||||
({ first, last, offset: [offsetX, offsetY], event, target }) => {
|
||||
event.stopPropagation()
|
||||
if ((target as HTMLElement).classList.contains('prevent-group-drag'))
|
||||
if (
|
||||
(target as HTMLElement)
|
||||
.closest('.prevent-group-drag')
|
||||
?.classList.contains('prevent-group-drag')
|
||||
)
|
||||
return
|
||||
|
||||
if (first) {
|
||||
setIsFocused(true)
|
||||
setIsMouseDown(true)
|
||||
|
||||
@@ -44,6 +44,7 @@ export const AvatarForm = ({
|
||||
useOutsideClick({
|
||||
ref: popoverContainerRef,
|
||||
handler: onClose,
|
||||
isEnabled: isOpen,
|
||||
})
|
||||
|
||||
const isDefaultAvatar = !avatarProps?.url || avatarProps.url.includes('{{')
|
||||
|
||||
@@ -28,6 +28,7 @@ export const VariablesButton = ({ onSelectVariable, ...props }: Props) => {
|
||||
useOutsideClick({
|
||||
ref: popoverRef,
|
||||
handler: onClose,
|
||||
isEnabled: isOpen,
|
||||
})
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useEventListener } from '@chakra-ui/react'
|
||||
import { RefObject } from 'react'
|
||||
import { RefObject, useEffect } from 'react'
|
||||
|
||||
type Handler = (event: MouseEvent) => void
|
||||
|
||||
@@ -7,22 +6,32 @@ type Props<T> = {
|
||||
ref: RefObject<T>
|
||||
handler: Handler
|
||||
capture?: boolean
|
||||
isEnabled?: boolean
|
||||
}
|
||||
|
||||
export const useOutsideClick = <T extends HTMLElement = HTMLElement>({
|
||||
ref,
|
||||
handler,
|
||||
capture,
|
||||
isEnabled,
|
||||
}: Props<T>): void => {
|
||||
const triggerHandlerIfOutside = (event: MouseEvent) => {
|
||||
const el = ref?.current
|
||||
if (!el || el.contains(event.target as Node)) {
|
||||
return
|
||||
useEffect(() => {
|
||||
if (isEnabled === false) return
|
||||
const triggerHandlerIfOutside = (event: MouseEvent) => {
|
||||
const el = ref?.current
|
||||
if (!el || el.contains(event.target as Node)) {
|
||||
return
|
||||
}
|
||||
handler(event)
|
||||
}
|
||||
handler(event)
|
||||
}
|
||||
|
||||
useEventListener('pointerdown', triggerHandlerIfOutside, null, {
|
||||
capture,
|
||||
})
|
||||
document.addEventListener('pointerdown', triggerHandlerIfOutside, {
|
||||
capture,
|
||||
})
|
||||
return () => {
|
||||
document.removeEventListener('pointerdown', triggerHandlerIfOutside, {
|
||||
capture,
|
||||
})
|
||||
}
|
||||
}, [capture, handler, isEnabled, ref])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user