diff --git a/src/components/compose.jsx b/src/components/compose.jsx index 0fe06102..3188aed5 100644 --- a/src/components/compose.jsx +++ b/src/components/compose.jsx @@ -7,7 +7,7 @@ import { useEffect, useMemo, useRef, useState } from 'preact/hooks'; import { useHotkeys } from 'react-hotkeys-hook'; import stringLength from 'string-length'; import { uid } from 'uid/single'; -import { useDebouncedCallback } from 'use-debounce'; +import { useDebouncedCallback, useThrottledCallback } from 'use-debounce'; import { useSnapshot } from 'valtio'; import supportedLanguages from '../data/status-supported-languages'; @@ -132,13 +132,12 @@ function highlightText(text, { maxCharacters = Infinity }) { let leftoverHTML = ''; if (composerCharacterCount > maxCharacters) { const leftoverCount = composerCharacterCount - maxCharacters; - leftoverHTML = html.slice(-leftoverCount); - html = html.slice(0, -leftoverCount); // Highlight exceeded characters - leftoverHTML = leftoverHTML.replace( - new RegExp(`(.{${leftoverCount}})$`), - '$1', - ); + leftoverHTML = + '' + + html.slice(-leftoverCount) + + ''; + html = html.slice(0, -leftoverCount); } html = html @@ -1270,7 +1269,8 @@ function autoResizeTextarea(textarea) { // NOTE: This check is needed because the offsetHeight return 50000 (really large number) on first render // No idea why it does that, will re-investigate in far future const offset = offsetHeight - clientHeight; - textarea.style.height = value ? scrollHeight + offset + 'px' : null; + const height = value ? scrollHeight + offset + 'px' : null; + textarea.style.height = height; } } @@ -1467,7 +1467,26 @@ const Textarea = forwardRef((props, ref) => { }; }, []); + useEffect(() => { + // Resize observer for textarea + const textarea = ref.current; + if (!textarea) return; + const resizeObserver = new ResizeObserver(() => { + // Get height of textarea, set height to textExpander + const { height } = textarea.getBoundingClientRect(); + textExpanderRef.current.style.height = height + 'px'; + }); + resizeObserver.observe(textarea); + }, []); + const composeHighlightRef = useRef(); + const throttleHighlightText = useThrottledCallback((text) => { + composeHighlightRef.current.innerHTML = + highlightText(text, { + maxCharacters, + }) + '\n'; + // Newline to prevent multiple line breaks at the end from being collapsed, no idea why + }, 500); return ( { setText(text); autoResizeTextarea(target); props.onInput?.(e); - composeHighlightRef.current.innerHTML = - highlightText(text, { - maxCharacters, - }) + '\n'; - // Newline to prevent multiple line breaks at the end from being collapsed, no idea why + throttleHighlightText(text); }} style={{ width: '100%',