import React, { createContext, ForwardedRef, forwardRef, useContext, useMemo, KeyboardEvent, useRef, useEffect, } from 'react'; import { RtFormField, RtData } from '@dpa/react-form-field-text'; import { RtText } from '@dpa/react-text'; import { RtFormFieldTextareaInternalProps, RtFormFieldTextareaProps, } from './rt-form-field-textarea.types'; import mergeRefs from 'merge-refs'; import styles from './styles/rt-form-field-textarea.module.css'; import classNames from 'classnames/bind'; import { Resize } from './assets/resize'; import { mapTextareaRows } from './utils/map-textarea-rows'; import { computeMaxMinHeight } from './utils/compute-max-min-height'; const cn = classNames.bind(styles); const RtTextareaContext = createContext({ rows: 1, resize: 'vertical', }); const RtDataComponent: RtData = (props) => { const textareaProps = useContext(RtTextareaContext); const { minRows, maxRows } = textareaProps; const localRef = useRef(null); const resizeRef = useRef(null); const touchStartData = useRef<{ y: number; height: number }>({ y: 0, height: 0, }); const rows = useMemo(() => { return mapTextareaRows(textareaProps.rows); }, [textareaProps.rows]); const computeAutoHeight = () => { if (!localRef.current) return; if (textareaProps.resize !== 'none') { const { totalMaxHeight, totalMinHeight } = computeMaxMinHeight( textareaProps, localRef.current ); if (textareaProps.resize === 'auto') { localRef.current.style.height = 'auto'; localRef.current.style.overflowY = 'hidden'; const height = localRef.current.scrollHeight; if (height < totalMinHeight) { localRef.current.style.height = `${totalMinHeight}px`; } else if (totalMaxHeight && height > totalMaxHeight) { localRef.current.style.height = `${totalMaxHeight}px`; localRef.current.style.overflowY = 'auto'; } else { localRef.current.style.height = `${height}px`; } } else { localRef.current.style.minHeight = `${totalMinHeight}px`; localRef.current.style.maxHeight = `${totalMaxHeight}px`; } } }; useEffect(() => { const resizeElement = resizeRef.current; const touchMove = (e: MouseEvent | TouchEvent) => { e.stopPropagation(); e.preventDefault(); window.requestAnimationFrame(() => { const y = (e as MouseEvent).clientY || ((e as TouchEvent).touches ? (e as TouchEvent).touches[0]?.clientY : 0); const dY = y - touchStartData.current.y; localRef.current!.style.height = `${ touchStartData.current.height + dY }px`; }); }; const touchEnd = (e: MouseEvent | TouchEvent) => { document.removeEventListener('mousemove', touchMove); document.removeEventListener('touchmove', touchMove); document.removeEventListener('mouseup', touchEnd); document.removeEventListener('touchend', touchEnd); }; const touchStart = (e: MouseEvent | TouchEvent) => { e.preventDefault(); e.stopPropagation(); touchStartData.current = { y: (e as MouseEvent).clientY || ((e as TouchEvent).touches ? (e as TouchEvent).touches[0]?.clientY : 0), height: localRef.current!.clientHeight, }; document.addEventListener('mousemove', touchMove); document.addEventListener('touchmove', touchMove); document.addEventListener('mouseup', touchEnd); document.addEventListener('touchend', touchEnd); }; resizeElement?.addEventListener('mousedown', touchStart); resizeElement?.addEventListener('touchstart', touchStart); return () => { resizeElement?.removeEventListener('mousedown', touchStart); resizeElement?.removeEventListener('touchstart', touchStart); }; }, []); useEffect(() => { window.requestAnimationFrame(computeAutoHeight); }, [props.value]); useEffect(() => { const timer = setTimeout(computeAutoHeight, 10); return () => clearTimeout(timer); }, []); useEffect(() => { if (typeof document === 'undefined') return; if (document.readyState === 'complete') { computeAutoHeight(); } else { window.addEventListener('load', computeAutoHeight); return () => window.removeEventListener('load', computeAutoHeight); } }, []); return ( <> {textareaProps.resize === 'vertical' && ( )} ); }; export const RtFormFieldTextarea = forwardRef(function RtFormFieldTextarea( props: RtFormFieldTextareaProps, ref: ForwardedRef ) { const { resize = 'vertical', rows = 'min', minRows, maxRows } = props; const textareaProps = useMemo(() => { return { rows, resize, minRows, maxRows }; }, [rows, resize, minRows, maxRows]); return ( ); }); RtFormFieldTextarea.displayName = 'RtFormFieldTextarea';