Разработка адаптивной формы ввода: опыт, проблемы и поиск решений
В рамках проекта на Next.js 16 и Tailwind v4 я занялся созданием формы ввода, которая должна была максимально точно повторить поведение и интерфейс веб-версии ChatGPT. Задача оказалась не такой простой, как казалось на первый взгляд.
Исходная концепция и структура
Основная идея заключалась в использовании CSS Grid для управления макетом. Я решил создать два условных «кадра» - свернутое и развернутое состояние формы, переключаемые флагом isExpanded.
- Первый кадр (isExpanded === false): Сетка из одной строки и трех колонок. Компоновка:
кнопка | textarea | кнопка кнопка. Это классический компактный вид формы, характерный для многих AI-приложений. - Второй кадр (isExpanded === true): Сетка из двух строк и трех колонок. Верхняя строка полностью занята расширенным полем ввода (
textarea), а нижняя содержит кнопки для взаимодействия.
Ключевая проблема: триггер переключения и «дребезг»
Логичным триггером для перехода в развернутое состояние (isExpanded = true) казался момент, когда содержимое textarea переполняло одну строку и начинался перенос. Однако здесь возникла основная сложность.
Как только флаг переключался и форма перестраивалась во второй кадр, ширина textarea резко увеличивалась (так как он теперь занимал верхний ряд из трех колонок). Введенный текст снова умещался в одну строку, что моментально сбрасывало флаг isExpanded обратно в false. Это вызывало циклическое переключение между состояниями («дребезг»), пока текст не заполнял новую, более широкую строку окончательно. Такое поведение было неприемлемо.
Предварительное решение: Фиксировать состояние isExpanded = true после первого же переполнения строки и сбрасывать его обратно только при полной очистке поля ввода. Это остановило «дребезг», но не решило всех проблем.
Сложности с анимацией
Простое переключение между двумя кадрами выглядело как слайд-шоу. Попытки анимировать саму CSS-сетку нативными средствами дали неудовлетворительный результат: кнопки деформировались, текст «прыгал», анимация выглядела неряшливо. Эксперименты с отступами (padding) немного сгладили визуальные артефакты, но общее впечатление осталось на уровне «5 из 10».
Поиск альтернатив: Framer Motion
Я обратился к библиотеке Framer Motion, известной своими возможностями анимации макета. Использование пропса layout на элементах позволило добиться более плавных переходов. Однако настройка потребовала значительно больше времени, чем ожидалось, а итоговый результат, хотя и был лучше CSS-анимации, все равно казался недостаточно отполированным и элегантным.
Открытые вопросы и выбор элемента ввода
Главный вопрос остается открытым: как корректно реализовать форму, которая будет вести себя абсолютно так же, как в веб-версии ChatGPT? Буду благодарен за любые идеи, рекомендации, примеры кода или ссылки на готовые решения.
Важное уточнение по элементу ввода: В качестве поля для промпта можно использовать как textarea, так и div с атрибутом contenteditable. Оба могут динамически менять высоту. В ChatGPT используется contenteditable - это обусловлено возможностью вставки изображений и файлов. В моем случае такой функционал не планировался, поэтому я выбрал более простой textarea. Стоит отметить, что в оригинальном интерфейсе ChatGPT на определенных разрешениях экрана contenteditable элемент также заменяется на стандартный textarea для улучшения адаптивности.