PluginProbe
Extendify / 2.2.0
Extendify v2.2.0
3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 0.7.0 All 126 releases
extendify / src / Draft / components / DynamicTextarea.jsx

DynamicTextarea.jsx in Extendify 2.2.0, at src/Draft/components/DynamicTextarea.jsx

93 lines 2.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import {
2 useLayoutEffect,
3 useEffect,
4 useRef,
5 useState,
6 } from '@wordpress/element';
7 import { motion, AnimatePresence } from 'framer-motion';
8
9 export const DynamicTextarea = ({
10 value,
11 className,
12 onChange,
13 onKeyDown,
14 disabled,
15 placeholder,
16 }) => {
17 const ref = useRef(null);
18 const [height, setHeight] = useState('auto');
19
20 // Dynamically resize the input by creating a temporary version and measuring the height.
21 // This is a workaround for scrollHeight not reducing when text is deleted.
22 useLayoutEffect(() => {
23 const tempTextarea = document.createElement('textarea');
24 tempTextarea.value = value || placeholder;
25 tempTextarea.rows = 1; // Start at 1
26
27 const styleProps = [
28 'paddingTop',
29 'paddingBottom',
30 'paddingLeft',
31 'paddingRight',
32 'width',
33 'fontFamily',
34 'fontSize',
35 'borderWidth',
36 ];
37
38 const styles = window.getComputedStyle(ref.current);
39
40 // apply styles to the temporary textarea
41 styleProps.forEach((prop) => (tempTextarea.style[prop] = styles[prop]));
42
43 Object.assign(tempTextarea.style, {
44 position: 'absolute',
45 left: '-9999px',
46 });
47
48 document.body.appendChild(tempTextarea);
49 setHeight(`${tempTextarea.scrollHeight}px`);
50 document.body.removeChild(tempTextarea);
51 }, [value, placeholder]);
52
53 // Focus the input.
54 useEffect(() => {
55 const input = ref.current;
56 if (!input) return;
57 if (document.activeElement === input) return;
58
59 const inputLength = input.value.length;
60 input.focus();
61 input.setSelectionRange(inputLength, inputLength); // Place cursor at the end of the input.
62 }, [value]);
63
64 return (
65 <AnimatePresence>
66 <motion.div
67 className="m-0.5 w-full"
68 key="input"
69 animate={{ height }}
70 transition={{ duration: 0.2 }}
71 style={{ lineHeight: 0 }}>
72 <label htmlFor="draft-ai-textarea" className="sr-only">
73 {placeholder}
74 </label>
75 <textarea
76 ref={ref}
77 id="draft-ai-textarea"
78 disabled={disabled}
79 className={className}
80 value={value}
81 rows={1}
82 onChange={onChange}
83 onKeyDown={onKeyDown}
84 onScroll={(event) => {
85 event.target.scrollTop = 0;
86 }}
87 placeholder={placeholder}
88 />
89 </motion.div>
90 </AnimatePresence>
91 );
92 };
93