import * as React from 'react' import { stopPropagation } from '~components/stopPropagation' import { GHOSTED_OPACITY, LETTER_SPACING } from '~constants' import { TLDR } from '~state/TLDR' import { styled } from '~styles' import { getTextLabelSize } from './getTextSize' import { TextAreaUtils } from './TextAreaUtils' export interface TextLabelProps { font: string text: string color: string onBlur?: () => void onChange: (text: string) => void offsetY?: number offsetX?: number scale?: number isEditing?: boolean } export const TextLabel = React.memo(function TextLabel({ font, text, color, offsetX = 0, offsetY = 0, scale = 1, isEditing = false, onBlur, onChange, }: TextLabelProps) { const rInput = React.useRef(null) const rIsMounted = React.useRef(false) const handleChange = React.useCallback( (e: React.ChangeEvent) => { onChange(TLDR.normalizeText(e.currentTarget.value)) }, [onChange] ) const handleKeyDown = React.useCallback( (e: React.KeyboardEvent) => { if (e.key === 'Escape') return if (e.key === 'Tab' && text.length === 0) { e.preventDefault() return } if (!(e.key === 'Meta' || e.metaKey)) { e.stopPropagation() } else if (e.key === 'z' && e.metaKey) { if (e.shiftKey) { document.execCommand('redo', false) } else { document.execCommand('undo', false) } e.stopPropagation() e.preventDefault() return } if (e.key === 'Tab') { e.preventDefault() if (e.shiftKey) { TextAreaUtils.unindent(e.currentTarget) } else { TextAreaUtils.indent(e.currentTarget) } onChange?.(TLDR.normalizeText(e.currentTarget.value)) } }, [onChange] ) const handleBlur = React.useCallback( (e: React.FocusEvent) => { e.currentTarget.setSelectionRange(0, 0) onBlur?.() }, [onBlur] ) 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: React.PointerEvent) => { if (isEditing) { e.stopPropagation() } }, [isEditing] ) React.useEffect(() => { if (isEditing) { requestAnimationFrame(() => { rIsMounted.current = true const elm = rInput.current if (elm) { elm.focus() elm.select() } }) } else { onBlur?.() } }, [isEditing, onBlur]) const rInnerWrapper = React.useRef(null) React.useLayoutEffect(() => { const elm = rInnerWrapper.current if (!elm) return const size = getTextLabelSize(text, font) elm.style.transform = `scale(${scale}, ${scale}) translate(${offsetX}px, ${offsetY}px)` elm.style.width = size[0] + 1 + 'px' elm.style.height = size[1] + 1 + 'px' }, [text, font, offsetY, offsetX, scale]) return ( {isEditing ? (