overleaf/services/web/frontend/js/shared/components/auto-expanding-text-area.tsx
Tim Down 98a847ae75 Merge pull request #14059 from overleaf/td-review-panel-smoother-chrome
React review panel: fix regression that made animation jerky on Chrome

GitOrigin-RevId: c1d9fa00d478fa71d94ff705ddfcc12bab0b2767
2023-07-28 08:04:10 +00:00

78 lines
2 KiB
TypeScript

import { useEffect, useRef } from 'react'
import { callFnsInSequence } from '../../utils/functions'
import { MergeAndOverride } from '../../../../types/utils'
export const resetHeight = (
e:
| React.ChangeEvent<HTMLTextAreaElement>
| React.KeyboardEvent<HTMLTextAreaElement>
) => {
const el = e.target as HTMLTextAreaElement
window.requestAnimationFrame(() => {
const curHeight = el.offsetHeight
const fitHeight = el.scrollHeight
// clear height if text area is empty
if (!el.value.length) {
el.style.removeProperty('height')
}
// otherwise expand to fit text
else if (fitHeight > curHeight) {
el.style.height = `${fitHeight}px`
}
})
}
type AutoExpandingTextAreaProps = MergeAndOverride<
React.ComponentProps<'textarea'>,
{
onResize?: () => void
}
>
function AutoExpandingTextArea({
onChange,
onResize,
...rest
}: AutoExpandingTextAreaProps) {
const ref = useRef<HTMLTextAreaElement>(null)
const isFirstResizeRef = useRef(true)
useEffect(() => {
if (!ref.current || !onResize || !('ResizeObserver' in window)) {
return
}
const resizeObserver = new ResizeObserver(() => {
// Ignore the resize that is triggered when the element is first
// inserted into the DOM
if (isFirstResizeRef.current) {
isFirstResizeRef.current = false
} else {
// Prevent errors like "ResizeObserver loop completed with undelivered
// notifications" that occur if onResize does something complicated.
// The cost of this is that onResize lags one frame behind, but it's
// unlikely to matter.
// Wrap onResize to prevent extra parameters being passed
window.requestAnimationFrame(() => onResize())
}
})
resizeObserver.observe(ref.current)
return () => {
resizeObserver.disconnect()
}
}, [onResize])
return (
<textarea
onChange={callFnsInSequence(onChange, resetHeight)}
{...rest}
ref={ref}
/>
)
}
export default AutoExpandingTextArea