/* eslint-disable @typescript-eslint/no-explicit-any */ import * as React from 'react' import { HTMLContainer, TLComponentProps, TLTextMeasure } from '@tldraw/react' import { TextUtils, TLBounds, TLResizeStartInfo, TLTextShape, TLTextShapeProps } from '@tldraw/core' import { observer } from 'mobx-react-lite' import { CustomStyleProps, withClampedStyles } from './style-props' export interface TextShapeProps extends TLTextShapeProps, CustomStyleProps { borderRadius: number fontFamily: string fontSize: number fontWeight: number lineHeight: number padding: number type: 'text' } export class TextShape extends TLTextShape { static id = 'text' static defaultProps: TextShapeProps = { id: 'box', parentId: 'page', type: 'text', point: [0, 0], size: [100, 100], isSizeLocked: true, text: '', lineHeight: 1.2, fontSize: 20, fontWeight: 400, padding: 4, fontFamily: "'Helvetica Neue', Helvetica, Arial, sans-serif", borderRadius: 0, stroke: 'var(--tl-foreground)', fill: '#ffffff', strokeWidth: 2, opacity: 1, } ReactComponent = observer(({ events, isErasing, isEditing, onEditingEnd }: TLComponentProps) => { const { props: { opacity, fontFamily, fontSize, fontWeight, lineHeight, text, stroke, padding }, } = this const rInput = React.useRef(null) const rIsMounted = React.useRef(false) const rInnerWrapper = React.useRef(null) // When the text changes, update the text—and, const handleChange = React.useCallback((e: React.ChangeEvent) => { const { isSizeLocked } = this.props const text = TextUtils.normalizeText(e.currentTarget.value) if (isSizeLocked) { this.update({ text, size: this.getAutoSizedBoundingBox({ text }) }) return } // If not autosizing, update just the text this.update({ text }) }, []) const handleKeyDown = React.useCallback((e: React.KeyboardEvent) => { if (e.metaKey) e.stopPropagation() switch (e.key) { case 'Meta': { e.stopPropagation() break } case 'z': { if (e.metaKey) { if (e.shiftKey) { document.execCommand('redo', false) } else { document.execCommand('undo', false) } e.preventDefault() } break } case 'Enter': { if (e.ctrlKey || e.metaKey) { e.currentTarget.blur() } break } case 'Tab': { e.preventDefault() if (e.shiftKey) { TextUtils.unindent(e.currentTarget) } else { TextUtils.indent(e.currentTarget) } this.update({ text: TextUtils.normalizeText(e.currentTarget.value) }) break } } }, []) const handleBlur = React.useCallback( (e: React.FocusEvent) => { e.currentTarget.setSelectionRange(0, 0) onEditingEnd?.() }, [onEditingEnd] ) const handleFocus = React.useCallback( (e: React.FocusEvent) => { if (!isEditing) return if (!rIsMounted.current) return if (document.activeElement === e.currentTarget) { e.currentTarget.select() } }, [isEditing] ) const handlePointerDown = React.useCallback( e => { if (isEditing) e.stopPropagation() }, [isEditing] ) React.useEffect(() => { if (isEditing) { requestAnimationFrame(() => { rIsMounted.current = true const elm = rInput.current if (elm) { elm.focus() elm.select() } }) } else { onEditingEnd?.() } }, [isEditing, onEditingEnd]) React.useLayoutEffect(() => { const { fontFamily, fontSize, fontWeight, lineHeight, padding } = this.props const { width, height } = this.measure.measureText( text, { fontFamily, fontSize, fontWeight, lineHeight }, padding ) this.update({ size: [width, height] }) }, []) return (
{isEditing ? (