PluginProbe
Extendify / 3.1.5
Extendify v3.1.5
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 3.1.5, at src/Draft/components/DynamicTextarea.jsx

96 lines 2.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import {
2 useEffect,
3 useLayoutEffect,
4 useRef,
5 useState,
6 } from '@wordpress/element';
7 import { AnimatePresence, motion } 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) => {
42 tempTextarea.style[prop] = styles[prop];
43 });
44
45 Object.assign(tempTextarea.style, {
46 position: 'absolute',
47 left: '-9999px',
48 });
49
50 document.body.appendChild(tempTextarea);
51 setHeight(`${tempTextarea.scrollHeight}px`);
52 document.body.removeChild(tempTextarea);
53 }, [value, placeholder]);
54
55 // Focus the input.
56 useEffect(() => {
57 const input = ref.current;
58 if (!input) return;
59 if (document.activeElement === input) return;
60
61 const inputLength = input.value.length;
62 input.focus();
63 input.setSelectionRange(inputLength, inputLength); // Place cursor at the end of the input.
64 }, [value]);
65
66 return (
67 <AnimatePresence>
68 <motion.div
69 className="m-0.5 w-full"
70 key="input"
71 animate={{ height }}
72 transition={{ duration: 0.2 }}
73 style={{ lineHeight: 0 }}
74 >
75 <label htmlFor="draft-ai-textarea" className="sr-only">
76 {placeholder}
77 </label>
78 <textarea
79 ref={ref}
80 id="draft-ai-textarea"
81 disabled={disabled}
82 className={className}
83 value={value}
84 rows={1}
85 onChange={onChange}
86 onKeyDown={onKeyDown}
87 onScroll={(event) => {
88 event.target.scrollTop = 0;
89 }}
90 placeholder={placeholder}
91 />
92 </motion.div>
93 </AnimatePresence>
94 );
95 };
96