UNPKG

tldraw

Version:

A tiny little drawing editor.

63 lines (55 loc) 2.05 kB
import { ExtractShapeByProps, TLRichText, TLShapeId, isAccelKey, useEditor } from '@tldraw/editor' import { useCallback, useEffect, useRef } from 'react' import { isEmptyRichText } from '../../utils/text/richText' import { useEditableTextCommon } from './useEditablePlainText' /** @public */ export function useEditableRichText( shapeId: TLShapeId, type: ExtractShapeByProps<{ richText: TLRichText }>['type'], richText?: TLRichText ) { const commonUseEditableTextHandlers = useEditableTextCommon(shapeId) const isEditing = commonUseEditableTextHandlers.isEditing const editor = useEditor() const rInput = useRef<HTMLDivElement>(null) const isEmpty = richText && isEmptyRichText(richText) useEffect(() => { if (!isEditing) return // N.B. In Development mode you need to ensure you're testing this without StrictMode on. // Otherwise it's not gonna work as expected on iOS. const contentEditable = rInput.current?.querySelector('[contenteditable]') if (contentEditable && document.activeElement !== rInput.current) { // This is a crucial difference with useEditablePlainText, that we need to select the // child contentEditable <div> not rInput.current directly. // Specifically, this is to ensure iOS works. Otherwise, we could just use rInput.current. ;(contentEditable as HTMLElement).focus() } }, [editor, isEditing]) // When the user presses ctrl / meta enter, complete the editing state. const handleKeyDown = useCallback( (e: KeyboardEvent) => { if (editor.getEditingShapeId() !== shapeId) return if (e.key === 'Enter' && isAccelKey(e)) editor.complete() }, [editor, shapeId] ) // When the text changes, update the text value. const handleChange = useCallback( ({ richText }: { richText: TLRichText }) => { if (editor.getEditingShapeId() !== shapeId) return editor.updateShape({ id: shapeId, type, props: { richText }, }) }, [editor, shapeId, type] ) return { rInput, handleKeyDown, handleChange, isEmpty, ...commonUseEditableTextHandlers, } }