import type { FC, PointerEvent as ReactPointerEvent } from 'react' import { memo, useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Avatar from '@/app/components/base/avatar' import { useAppContext } from '@/context/app-context' import { cn } from '@/utils/classnames' import { MentionInput } from './mention-input' type CommentInputProps = { position: { x: number, y: number } onSubmit: (content: string, mentionedUserIds: string[]) => void onCancel: () => void onPositionChange?: (position: { pageX: number pageY: number elementX: number elementY: number }) => void } export const CommentInput: FC = memo(({ position, onSubmit, onCancel, onPositionChange }) => { const [content, setContent] = useState('') const { t } = useTranslation() const { userProfile } = useAppContext() const dragStateRef = useRef<{ pointerId: number | null startPointerX: number startPointerY: number startX: number startY: number active: boolean } & { endHandler?: (event: PointerEvent) => void }>({ pointerId: null, startPointerX: 0, startPointerY: 0, startX: 0, startY: 0, active: false, endHandler: undefined, }) useEffect(() => { const handleGlobalKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') { e.preventDefault() e.stopPropagation() onCancel() } } document.addEventListener('keydown', handleGlobalKeyDown, true) return () => { document.removeEventListener('keydown', handleGlobalKeyDown, true) } }, [onCancel]) const handleMentionSubmit = useCallback((content: string, mentionedUserIds: string[]) => { onSubmit(content, mentionedUserIds) setContent('') }, [onSubmit]) const handleDragPointerMove = useCallback((event: PointerEvent) => { const state = dragStateRef.current if (!state.active || (state.pointerId !== null && event.pointerId !== state.pointerId)) return if (!onPositionChange) return event.preventDefault() const deltaX = event.clientX - state.startPointerX const deltaY = event.clientY - state.startPointerY onPositionChange({ pageX: event.clientX, pageY: event.clientY, elementX: state.startX + deltaX, elementY: state.startY + deltaY, }) }, [onPositionChange]) const stopDragging = useCallback((event?: PointerEvent) => { const state = dragStateRef.current if (!state.active) return if (event && state.pointerId !== null && event.pointerId !== state.pointerId) return state.active = false state.pointerId = null window.removeEventListener('pointermove', handleDragPointerMove) if (state.endHandler) { window.removeEventListener('pointerup', state.endHandler) window.removeEventListener('pointercancel', state.endHandler) state.endHandler = undefined } }, [handleDragPointerMove]) const handleDragPointerDown = useCallback((event: ReactPointerEvent) => { if (event.button !== 0) return event.stopPropagation() event.preventDefault() if (!onPositionChange) return const endHandler = (pointerEvent: PointerEvent) => { stopDragging(pointerEvent) } dragStateRef.current = { pointerId: event.pointerId, startPointerX: event.clientX, startPointerY: event.clientY, startX: position.x, startY: position.y, active: true, endHandler, } window.addEventListener('pointermove', handleDragPointerMove, { passive: false }) window.addEventListener('pointerup', endHandler) window.addEventListener('pointercancel', endHandler) }, [handleDragPointerMove, onPositionChange, position.x, position.y, stopDragging]) useEffect(() => () => { stopDragging() }, [stopDragging]) return (
) }) CommentInput.displayName = 'CommentInput'