UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

54 lines (49 loc) 2.41 kB
import { useState, useLayoutEffect } from 'react'; import { getCharacterCoordinates } from '../utils/character-coordinates.js'; /** * Calculates the optimal height of the textarea according to its content, automatically * resizing it as the user types. If the user manually resizes the textarea, their setting * will be respected. * * Returns an object to spread to the component's `sx` prop. If you are using `Textarea`, * apply this to the child `textarea` element: `<Textarea sx={{'& textarea': resultOfThisHook}} />`. * * NOTE: for the most accurate results, be sure that the `lineHeight` of the element is * explicitly set in CSS. */ const useDynamicTextareaHeight = ({ minHeightLines, maxHeightLines, elementRef, value }) => { const [height, setHeight] = useState(undefined); const [minHeight, setMinHeight] = useState(undefined); const [maxHeight, setMaxHeight] = useState(undefined); useLayoutEffect(() => { const element = elementRef.current; if (!element) return; const computedStyles = getComputedStyle(element); const pt = computedStyles.paddingTop; // The calculator gives us the distance from the top border to the bottom of the caret, including // any top padding, so we need to delete the top padding to accurately get the height // We could also parse and subtract the top padding, but this is more reliable (no chance of NaN) element.style.paddingTop = '0'; const lastCharacterCoords = getCharacterCoordinates(element, element.value.length); // Somehow we come up 1 pixel too short and the scrollbar appears, so just add one setHeight(`${lastCharacterCoords.top + lastCharacterCoords.height + 1}px`); element.style.paddingTop = pt; const lineHeight = computedStyles.lineHeight === 'normal' ? `1.2 * ${computedStyles.fontSize}` : computedStyles.lineHeight; // Using CSS calculations is fast and prevents us from having to parse anything if (minHeightLines !== undefined) setMinHeight(`calc(${minHeightLines} * ${lineHeight})`); if (maxHeightLines !== undefined) setMaxHeight(`calc(${maxHeightLines} * ${lineHeight})`); // `value` is an unnecessary dependency but it enables us to recalculate as the user types }, [minHeightLines, maxHeightLines, value, elementRef]); return { height, minHeight, maxHeight, boxSizing: 'content-box' }; }; export { useDynamicTextareaHeight };