-
Notifications
You must be signed in to change notification settings - Fork 185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Не скролится модалка пока открыта клавиатура #599
Comments
IMG_2463.mp4.zip Перезалил скринкаст проблемы. Доступ к трекеру продукта закрыт от всех, а ссылка на видео протухла. |
Closed
8 tasks
inomdzhon
added a commit
that referenced
this issue
Dec 2, 2024
h2. Описание <details><summary>Переписаны компоненты ModalPage/ModalCard</summary> <p>⚠️ Теперь компоненты могут использоваться без `ModalRoot`. `ModalPage` / `ModalCard`: - добавлено свойство `open`; - добавлено свойство `keepMounted`; - типа `onClose` изменён с `VoidFunction` на `(reason: ModalPageCloseReason, event?: UIEvent<HTMLElement>) => void`; - добавлено свойство `noFocusToDialog`, приоритетней чем `noFocusToDialog` в `ModalRoot`; - добавлено свойство `modalOverlayTestId`, приоритетней чем `modalOverlayTestId` в `ModalRoot`. - создан компонент `ModalOutlet`; - создан компонент `ModalOverlay`; - основной код работы модалок вынесен в отдельные компоненты `ModalPageInternal` и `ModalCardInternal`, у них есть свойство `ModalOverlay`; - создан контекст `ModalContext`, который теперь используется в компонентах `ModalPageHeader`, `Group` и `PanelHeader` вместо `ModalRootContext`. `ModalPage`: - `settlingHeight` теперь имеет значение по умолчанию `50%` вместо `75%`, обратил внимание, что в обычно `BottomSheet`'ы открываются на половину экрана; - добавлено свойство `footer`, а также создан компонент `ModalPageFooter`; - созданы компонент `ModalPageContent` – вынес, чтобы можно было в будущем перейти на сбор `ModalPage` через композицию компонентов. `lib/sheet`: В папке хранится логика отвечающая за взаимодействие с модалкой на мобильных экранах. Отдаёт хук `useBottomSheet()`. </p> </details> <details><summary>Переписан компонент ModalRoot</summary> <p>⚠️ Теперь лишь отвечает за состояние `open` компонентов `ModalCard` и `ModalPage` в зависимости от их `id`/`nav` и параметра `activeModal`, а также рендера общей `ModalOverlay` для всех модалок. `ModalRoot`: - добавлены события `onOpen`, `onOpened`, `onClosed`, которые всплывают от `activeModal`; - в `PopoutRoot` удалён `PopoutRootModal` в пользу `ModalOutlet` у `ModalPage` и `ModalCard`. `ModalRootContext`: - **⚠️ BREAKING CHANGE** в свойство `onClose` нужно теперь обязательно передавать `id` модального окна; - добавлены события `onOpen`, `onOpened`, `onClosed`, чтобы их могли вызывать `ModalPage` и `ModalCard`; - свойство `registerModal` теперь `@deprecated` – не нужно отдельно регистрировать модальное окно. - свойство `updateModalHeight` теперь `@deprecated` – задача с обновлением высоты контента при `dynamicContentHeight` решается через CSS; `ModalRootOverlayContext` / `VisuallyHiddenModalOverlay`: - чтобы создать общий `ModalOverlay` для всех модалок в контексте `ModalRoot`, происходит подмена `ModalOverlay` в `ModalPage` и `ModalCard` на `VisuallyHiddenModalOverlay`, который отвечает за приём `onClick` и `modalOverlayTestId`, а сам `ModalOverlay` попадает в начало `ModalRoot` `useModalManager()`: - отвечает за `unmounted` состояние; - отвечает за регулирования приоритета параметров из `ModalRoot` и `ModalPage`/`ModalCard`; - отвечает за подмену `ModalOverlay` на `VisuallyHiddenModalOverlay`. `withModalRootContext`: - ввиду отказа от `updateModalHeight` HOC тоже `@deprecated`. </p> </details> h2. Нюансы <details><summary>Обратная совместимость</summary> <p> Постарался сделать так, чтобы миграция прошла бесследно. Сломаются вот такой кейс: ```tsx const MyModal = ({ id }) => { // пропустили settinglingHeight / dynamicContentHeight return <ModalPage>Lorem Ipsum</ModalPage> }; const App = () => ( <ModalRoot> <MyModal id="example-1" settinglingHeight={100} // устанавливалось здесь, т.к. раньше ModalRoot итерировал по потомкам и доставал этот параметр /> <MyModal id="example-2" dynamicContentHeight // устанавливалось здесь, т.к. раньше ModalRoot итерировал по потомкам и доставал этот параметр /> </ModalRoot> ); ``` который нужно будет править руками. </p> </details> <details><summary>BottomSheet, анимации и свайп</summary> <p> Полное появление и полное скрытие происходит через `transform`, но анимация взаимодействия через свайп реализована через `height`, т.к. это оказалось самым оптимальным способом для решения задач: - закреплённый `ModalPageFooter` внизу; - возможность скроллить при `settlingHeight` меньше `100`; - обновление высоты, если задан `dynamicContentHeight`. Нашёл решение допустимым, т.к. свайп используется либо для закрытия, либо для разворачивания/сворачивания модального окна на всю или на половину страницы. В первом случае сработает закрытие через `transform`, а во втором разворачивание/сворачивание произойдёт через `height`. </p> </details> <details><summary>dynamicContentHeight</summary> <p> > см. предыдущий пункт про **BottomSheet** для контекста Обновление высоты происходит без анимации, т.к. `height: auto` не анимируется. Опустил анимирование, т.к. усложняет компонент. В теории можно прибегнуть к `useResizeObserver()`. </p> </details> <details><summary>Адативность</summary> <p> При `platform="vkcom"` и при разрешении экрана `767px` компонент теперь превращается в **BottomSheet**, но при этом логику взаимодействия через тач не имеет, т.к. `isDesktop` при `platform="vkcom"` всегда `true` вне зависимости от размера экрана. </p> </details> h2. Решения <details><summary>Выделение текста</summary> <p> С помощью функции `hasSelectionWithRangeType` определяем, что пользователь выделил текст и перестаём реагировать на `touchstart` и `touchmove` пока выделение не будет удалено. </p> </details> <details><summary>Вертикальный и горизонтальный скроллы</summary> <p> - **вертикальный скролл:**: - **основной скролл (`ModalPageContent`)**: проверяем на `scrollTop !== 0` - **другие скроллы**: при `touchstart` достаём скроллируемый элемент через `event.target` и проверяем на положение `scrollTop !== 0` и направление пальца вверх - **горизонтальный скролл:** не блочится, т.к. реагируем на события только по оси Y. </p> </details> <details><summary>Плавающие элементы внутри модалки</summary> <p> Нужно рекомендовать использовать `forcePortal` – в коде проверяем, что идёт взаимодействие с элементом вне модалки. Или нужно рекомендовать добавлять в корневой элемент плавающего элемента атрибут `data-vkui-prevent-swipe` . </p> </details> <details><summary>Поля ввода</summary> <p> Наилучшего варианта не нашёл кроме как: 1. через `useVirtualKeyboardState()` узнавать, что пользователь работает с клавиатурой, и перебивать `safe-area-inset-bottom` на ~тот, что возвращает хук~ **0** (попытка вычислять разницу высоты через `VisualViewport`, чтобы реагировать на смену размера клавиатуры, например, из-за панели эмодзи, не удалась); 2. в том же хуке слушать событие скролла на `window` и сохранять его позицию на `window.scrollTo(0, visualViewport.offsetTop)`. Так как иные решения приводят к другим проблемам (подробнее можно прочесть в **JSDoc** хука `useVirtualKeyboardState()`), следующие баги нужно закрыть: - [#338](https://github.com/VKCOM/VKUI/issues/338) - [#599](https://github.com/VKCOM/VKUI/issues/599) </p> </details> h2. Референсы - [Youtube • Универсальные попапы или UIKit против / Антон Спивак](https://www.youtube.com/watch?v=jQC_jxtf500) - [Habr • Bottom sheet: Scrolling and interactions](https://habr.com/ru/companies/koshelek/articles/703260/) - [How to present a Bottom Sheet in iOS 15 with UISheetPresentationController](https://sarunw.com/posts/bottom-sheet-in-ios-15-with-uisheetpresentationcontroller/) - [GitHub • ionic-framework/core/src/components/modal](https://github.com/ionic-team/ionic-framework/tree/main/core/src/components/modal) - [GitHub • stipsan/react-spring-bottom-sheet](https://github.com/stipsan/react-spring-bottom-sheet) - [GitHub • gorhom/react-native-bottom-sheet](https://github.com/gorhom/react-native-bottom-sheet) - [GitHub • stanleyugwu/react-native-bottom-sheet](https://github.com/stanleyugwu/react-native-bottom-sheet) - [GitHub • Temzasse/react-modal-sheet](https://github.com/Temzasse/react-modal-sheet) - [GitHub • tech-systems/panes](https://github.com/tech-systems/panes) - [#5092](https://github.com/VKCOM/VKUI/issues/5092) – Пример рефактора модалок с помощью touch-событий h2. Release notes h2. BREAKING CHANGE - ModalRoot: удалена реализация контекста через `React.cloneElement`, теперь не нужно задавать `settlingHeight` и `dynamicContentHeight` на обёртку над `ModalPage` / `ModalCard` <details> <summary>Пример миграции 1</summary> ```diff const SomeWrapper = ({ id }) => ( <Modal id={id} + settlingHeight={100} /> ); <ModalRoot activeModal="m"> <SomeWrapper id="m" - settlingHeight={100} /> </ModalRoot> ``` </details> <details> <summary>Пример миграции 2</summary> ```diff - const SomeWrapper = ({ id }) => ( + const SomeWrapper = (props) => ( <Modal - id={id} + {...props} /> ); <ModalRoot activeModal="m"> <SomeWrapper id="m" settlingHeight={100} /> </ModalRoot> ``` </details> h2. Улучшения - ModalRoot: - `updateModalHeight()` помечен как `@depreacted`, т.к. в нём больше нет необходимости – `ModalPage`, при `dynamicContentHeight`, теперь автоматически подстраиваются под контент; - `registerModal()` помечен как `@depreacted`, т.к. изменилась логика работы компонента – теперь `ModalPage` и `ModalCard` ориентируется на контекст, создаваемый `ModalRoot`; - добавлено свойство `usePortal`. - `ModalPage`: - теперь можно использовать без `ModalRoot` (для рендера в портале можно обернуть в `AppRootPortal`); - изменилось значение по умолчанию у свойств `settlingHeight` – `75` → `50` ; - добавлено свойство `keepMounted`; - добавлено свойство `footer`; - добавлено свойство `disableContentPanningGestureProp`; - расширен тип `onClose` до `(reason: ModalPageCloseReason, event?: UIEvent<HTMLElement>) => void`. - `ModalCard`: - теперь можно использовать без `ModalRoot` (для рендера в портале можно обернуть в `AppRootPortal`); - добавлено свойство `keepMounted`; - расширен тип `onClose` до `(reason: ModalPageCloseReason, event?: UIEvent<HTMLElement>) => void`.
Частично исправилось в v7.0.0 (#6759) – можно скроллить сам контент. Когда открывается клавиатура, отключается возможность закрывать свайпом модалку.
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Шаги воспроизведения:
Фактический результат: Модалка не скроллится. до тех пор, пока мы не закроем клавиатуру.
Ожидаемый результат: Модалка скроллится.
Скринкаст:
Оригинальный тикет:
The text was updated successfully, but these errors were encountered: