Реализация компонента с нуля на React.js

Первое, что приходит в голову, это то, что кто‑то, скорее всего, уже сделал такой компонент, и нужно адаптировать готовое решение. При быстром поиске нашел один пример, сделанный на textarea, который и работал как обычный textarea, но с возможностью выделять слова. Плюс ответы на stackoverflow, дающие понять, что это не такая уж и тривиальная задача.
Очевидное решение — это сделать все обычным input»ом с абсолютно позиционированным div»ом на заднем фоне. Скрыть текст самого поля, оставив только каретку, и сделать видимым только содержимое заднего блока. Так я уже добился того, что у меня был полностью стилизуемый текст.
Стили включать не буду, так как это займет слишком много места. Принцип прост: задать одинаковые размеры, отступы и шрифты для div»а и input»а.
const [value, setValue] = useState(''); const onChange = (e) => setValue(e.target.value); const format = (text) => // formatted jsx
<div className="container"> <div className="text">{format(value)}</div> <input type="text" className="field" value={value} onChange={onChange} /> </div>
Первая проблема

Это исправляется тем, что можно брать значение скролла поля и устанавливать его для заднего блока.
<!-- ... --> <div className="text" ref={textBlockRef}>{format(value)}</div> <input type="text" className="field" value={value} onChange={onChange} onScroll={handleScroll} /> <!-- ... -->
Получился, вроде бы, хороший результат. Компонент работал, как и было задумано.
Вторая проблема
Вторая проблема заключается в том, что мне, оказывается, не удалось полностью избавиться от первой.
В браузере Safari не работает свойство onScroll у простого input’а.
Выяснилось, что safari поддерживает onScroll у textarea. Это решило проблему. Оставалось только стилизовать textarea, чтобы он выглядел и вел себя как простой input[type=”text”], и дело с концом.
<!-- ... --> <textarea className="field" ref={ref} value={value} onChange={onChange} onScroll={handleScroll} rows={1} /> <!-- ... -->
Третья проблема (более очевидная)
При нажатии на Enter, в textarea происходил перенос ��троки. Это решается просто - нужно отловить нажатие и отменить его, плюс, если инпут находится в форме, сделать сабмит.
// ... const submitHandler = (e) => { if (e.keyCode === 13 || e.key === "Enter" || e.which === 13) { if (!e.repeat && e.target.form) { e.target.form.submit(); } e.preventDefault(); } }; // ...
<!-- ... --> <textarea className="field" ref={ref} value={value} onChange={onChange} onScroll={handleScroll} onKeyDown={submitHandler} rows={1} /> <!-- ... -->
Финальный результат можно посмотреть на CodeSandbox.
Возможно, в этом решении найдутся еще проблемы, буду рад, если кто-то их найдет и озвучит в комментариях.
