import { ExtractShapeByProps, preventDefault, TLShape, TLShapeId, useEditor } from '@tldraw/editor' import { useCallback, useEffect, useRef, useState } from 'react' import { useUiEvents } from '../../context/events' import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { TldrawUiButton } from '../primitives/Button/TldrawUiButton' import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon' import { TldrawUiInput } from '../primitives/TldrawUiInput' /** @public */ export interface AltTextEditorProps { shapeId: TLShapeId onClose(): void source: 'image-toolbar' | 'video-toolbar' } /** @public @react */ export function AltTextEditor({ shapeId, onClose, source }: AltTextEditorProps) { const editor = useEditor() const [altText, setAltText] = useState(() => { const shape = editor.getShape(shapeId) if (!shape) return '' if (!('altText' in shape.props)) throw Error('Shape does not have altText property') return shape.props.altText || '' }) const msg = useTranslation() const ref = useRef(null) const trackEvent = useUiEvents() const isReadonly = editor.getIsReadonly() const handleValueChange = (value: string) => setAltText(value) const handleComplete = useCallback(() => { trackEvent('set-alt-text', { source }) const shape = editor.getShape>(shapeId) if (!shape) return editor.updateShapes([ { id: shape.id, type: shape.type, props: { altText }, }, ]) onClose() }, [trackEvent, source, editor, shapeId, altText, onClose]) const handleConfirm = () => handleComplete() const handleAltTextCancel = useCallback(() => onClose(), [onClose]) useEffect(() => { const doc = editor.getContainerDocument() ref.current?.select() function handleKeyDown(event: KeyboardEvent) { if (event.key === 'Escape') { event.stopPropagation() handleAltTextCancel() } } doc.addEventListener('keydown', handleKeyDown, { capture: true }) return () => { doc.removeEventListener('keydown', handleKeyDown, { capture: true }) } }, [editor, handleAltTextCancel]) useEffect(() => { const doc = editor.getContainerDocument() const handlePointerDown = (e: PointerEvent) => { const toolbar = doc.querySelector('.tlui-media__toolbar') if (toolbar?.contains(e.target as Node)) return // If the pointer down is not in the toolbar, complete the alt text handleComplete() } doc.addEventListener('pointerdown', handlePointerDown, { capture: true }) return () => { doc.removeEventListener('pointerdown', handlePointerDown, { capture: true }) } }, [editor, handleComplete]) return ( <> {!isReadonly && ( )} ) }