-
Notifications
You must be signed in to change notification settings - Fork 186
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
refactor: ModalRoot/ModalPage/ModalCard #6759
base: master
Are you sure you want to change the base?
Conversation
size-limit report 📦
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit bce0326:
|
e2e tests |
👀 Docs deployed
Commit bce0326 |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #6759 +/- ##
==========================================
- Coverage 95.34% 93.65% -1.69%
==========================================
Files 377 386 +9
Lines 11054 11050 -4
Branches 3673 3631 -42
==========================================
- Hits 10539 10349 -190
- Misses 515 701 +186
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
4a0d455
to
7d03282
Compare
cd0da9c
to
c382b8f
Compare
c382b8f
to
fba65d5
Compare
c1453c5
to
a1c9447
Compare
481fc96
to
f07b8b4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Грандиозная работа! 👏 👏 👏 💪
Здорово раскидал по компонентам!
Понравилось, что компоненты теперь функции как практически везде!
Офигенно как переведена анимация/перетаскивание на CSS Transition и контроллеры. (Там я, конечно, поплыл, но приятно видеть знакомое API с коллбэками и то, что всё сводится к установке css переменных)
Надо пристально пройтись по местам где коллбёки передаются в компоненты, а не напрямую в html элементы и обернуть их useCallback, и по местам, где объекты возвращаются из хуков или передаются в контекст, чтобы обернуть в useMemo и избежать лишних ререндеров.
По стайлгайду и сторибуку прошелся и так и на устройстве, ничего такого не заметил.
const context = useContext(ModalRootContext); | ||
const opened = context.isInsideModal ? context.activeModal === id : open; | ||
|
||
const [unmounted, setUnmounted] = useState(keepMounted ? false : !opened); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
А почему решил именно с unmounted
работать?
Немного тяжело читается, приходится инверсировать в голове, когда unmounted: false
, вместо mounted: true
.
Вижу плюс в том, что ниже условия при которых надо раньше выйти из рендер функции компонента пишутся без отрицания, типа if unmounted
(вместо if !mounted
или if mounted === false
.
Но, честно говоря, явное if mounted === false
читается проще.
Ещё пара легковесных аргументов за mounted
.
- нас уже есть проп
keepMounted
- в FocusTrap используется
mounted
. Мы его в этом PR трогаем и немного не консистентно смотрится.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
хороший поинт 👍
поправил, но с одним нюансом – в API получаем mounted
, а внутри оставил реализацию через unmounted
, а то у мне не получилось переписать useEffect()
так, чтобы это работало наоборот x)))
добавил мемоизацию там где можно, но во всех местах сильная завязка на пользователя, т.к. используются колбеки ( ждём React 19, где не нужно будет руками мемоизировать) |
cb1bd66
to
9473173
Compare
- `CustomSelect` жалуется на `renderOption` в 729 строке, что якобы идёт обращение к `ref.current`, но такого там нет; ```plain 729:51 error Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) react-compiler/react-compiler ``` - `ModalRootContext` и `useModalManager` переписываются в PR #6759.
9473173
to
bce0326
Compare
settlingHeight
в 0, минимальное значение ограничено до 25, а максимальное до 100)Проверить после
Описание
Переписаны компоненты ModalPage/ModalCard
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()
.Переписан компонент ModalRoot
open
компонентовModalCard
иModalPage
в зависимости от ихid
/nav
и параметраactiveModal
, а также рендера общейModalOverlay
для всех модалок.ModalRoot
:onOpen
,onOpened
,onClosed
, которые всплывают отactiveModal
;PopoutRoot
удалёнPopoutRootModal
в пользуModalOutlet
уModalPage
иModalCard
.ModalRootContext
: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
.Изменения, которые нужно вынести в отдельные PR после первичного ревью
useKeyboard()
вuseVirtualKeyboardState()
Нюансы
Обратная совместимость
Постарался сделать так, чтобы миграция прошла бесследно.
Сломаются вот такой кейс:
который нужно будет править руками.
BottomSheet, анимации и свайп
Полное появление и полное скрытие происходит через
transform
, но анимация взаимодействия через свайп реализована черезheight
, т.к. это оказалось самым оптимальным способом для решения задач:ModalPageFooter
внизу;settlingHeight
меньше100
;dynamicContentHeight
.Нашёл решение допустимым, т.к. свайп используется либо для закрытия, либо для разворачивания/сворачивания модального окна на всю или на половину страницы. В первом случае сработает закрытие через
transform
, а во втором разворачивание/сворачивание произойдёт черезheight
.dynamicContentHeight
Обновление высоты происходит без анимации, т.к.
height: auto
не анимируется. Опустил анимирование, т.к. усложняет компонент. В теории можно прибегнуть кuseResizeObserver()
.Адативность
При
platform="vkcom"
и при разрешении экрана767px
компонент теперь превращается в BottomSheet, но при этом логику взаимодействия через тач не имеет, т.к.isDesktop
приplatform="vkcom"
всегдаtrue
вне зависимости от размера экрана.Решения
Выделение текста
С помощью функции
hasSelectionWithRangeType
определяем, что пользователь выделил текст и перестаём реагировать наtouchstart
иtouchmove
пока выделение не будет удалено.Вертикальный и горизонтальный скроллы
При
touchstart
достаём скроллируемый элемент черезevent.target
и подписываемся на событие скролла, а также получаем информацию про позицию скролла.scrollTop
– отключаем взаимодействие еслиscrollTop !== 0
;Плавающие элементы внутри модалки
Нужно рекомендовать использовать
forcePortal
– в коде проверяем, что идёт взаимодействие с элементом вне модалки.Или нужно рекомендовать добавлять в корневой элемент плавающего элемента атрибут
data-vkui-prevent-swipe
.Поля ввода
Наилучшего варианта не нашёл кроме как:
useVirtualKeyboardState()
узнавать, что пользователь работает с клавиатурой, и перебиватьsafe-area-inset-bottom
на тот, что возвращает хук;window
и сохранять его позицию наwindow.scrollTo(0, visualViewport.offsetTop)
.Так как иные решения приводят к другим проблемам (подробнее можно прочесть в JSDoc хука
useVirtualKeyboardState()
), следующие баги нужно закрыть:Референсы