Skip to content

Commit

Permalink
feat: create ModalPageV2
Browse files Browse the repository at this point in the history
  • Loading branch information
inomdzhon committed May 16, 2024
1 parent ae5ca12 commit 7d03282
Show file tree
Hide file tree
Showing 25 changed files with 1,125 additions and 4 deletions.
6 changes: 6 additions & 0 deletions packages/vkui/src/components/AppRoot/ScrollContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const clearDisableScrollStyle = (node: HTMLElement) => {
top: '',
left: '',
right: '',
overscrollBehavior: '',
overflowY: '',
overflowX: '',
});
Expand Down Expand Up @@ -86,9 +87,12 @@ export const GlobalScrollController = ({ children }: ScrollControllerProps) => {
top: `-${scrollY}px`,
left: `-${scrollX}px`,
right: '0',
overscrollBehavior: 'none',
overflowY,
overflowX,
});
// eslint-disable-next-line no-restricted-properties
document!.documentElement.classList.add('vkui--disable-overscroll-behavior');
setScrollLock(true);
}, [document, window]);

Expand All @@ -97,6 +101,8 @@ export const GlobalScrollController = ({ children }: ScrollControllerProps) => {
const scrollX = document!.body.style.left;

clearDisableScrollStyle(document!.body);
// eslint-disable-next-line no-restricted-properties
document!.documentElement.classList.remove('vkui--disable-overscroll-behavior');
window!.scrollTo(-parseInt(scrollX || '0'), -parseInt(scrollY || '0'));
setScrollLock(false);
}, [document, window]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
block-size: 100%;
overflow: hidden;
position: relative;

/* Для срабатывания `height: 100%` у `.CustomScrollView__box`, когда не во всех родителях есть `height: 100%`. */
display: flex;
flex-direction: column;
}

.CustomScrollView__box {
Expand Down
2 changes: 0 additions & 2 deletions packages/vkui/src/components/ModalPage/ModalPageContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import * as React from 'react';

export interface ModalPageContextInterface {
labelId?: string;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.ModalPageContent {
/* см. `.ModalPageV2__children {}` */
flex-grow: 1;
flex-shrink: 1;
flex-basis: 100%;
}
15 changes: 15 additions & 0 deletions packages/vkui/src/components/ModalPageContent/ModalPageContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as React from 'react';

Check failure on line 1 in packages/vkui/src/components/ModalPageContent/ModalPageContent.tsx

View workflow job for this annotation

GitHub Actions / Run linters

'React' is declared but its value is never read.
import { classNames } from '@vkontakte/vkjs';
import type { HTMLAttributesWithRootRef } from '../../types';
import { CustomScrollView } from '../CustomScrollView/CustomScrollView';
import styles from './ModalPageContent.module.css';

export type ModalPageContentProps = HTMLAttributesWithRootRef<HTMLDivElement>;

export const ModalPageContent = ({ children, className, ...restProps }: ModalPageContentProps) => {
return (
<CustomScrollView className={classNames(className, styles.ModalPageContent)} {...restProps}>
{children}
</CustomScrollView>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.ModalPageFooter {
inline-size: 100%;

/* см. `.ModalPageV2__children {}` */
flex-grow: 0;
flex-shrink: 0;
flex-basis: auto;
background-color: var(--vkui--color_background_modal);
}

.ModalPageFooter__inner {
padding-block: 16px;
padding-inline: 24px;
}
21 changes: 21 additions & 0 deletions packages/vkui/src/components/ModalPageFooter/ModalPageFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { HTMLAttributesWithRootRef } from '../../types';
import { RootComponent } from '../RootComponent/RootComponent';
import { Separator } from '../Separator/Separator';
import styles from './ModalPageFooter.module.css';

export interface ModalPageFooterProps extends HTMLAttributesWithRootRef<HTMLDivElement> {
noSeparator?: boolean;
}

export const ModalPageFooter = ({
noSeparator = false,
children,
...restProps
}: ModalPageFooterProps) => {
return (
<RootComponent baseClassName={styles.ModalPageFooter} {...restProps}>
{!noSeparator && <Separator wide />}
<div className={styles.ModalPageFooter__inner}>{children}</div>
</RootComponent>
);
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
.ModalPageHeader {
--vkui_internal--safe_area_inset_top: 0;

/* см. `.ModalPageV2__children {}` */
flex-shrink: 0;
flex-grow: 0;
flex-basis: auto;
}

.ModalPageHeader--withGaps {
Expand Down
33 changes: 33 additions & 0 deletions packages/vkui/src/components/ModalPageV2/ModalPageRoot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useCSSKeyframesAnimationController } from '../../lib/animation';
import { AppRootPortal } from '../AppRoot/AppRootPortal';
import { ModalPageV2 } from './ModalPageV2';
import type { ModalPageV2Props } from './types';

export const ModalPageRoot = ({
open,
onOpen,
onOpened,
onClosed,
keepMounted,
dynamicContentHeight,
...restProps
}: ModalPageV2Props) => {
const [animationState, animationHandlers] = useCSSKeyframesAnimationController(
open ? 'enter' : 'exit',
{
onEnter: onOpen,
onEntered: onOpened,
onExited: onClosed,
},
);

if (!keepMounted && animationState === 'exited') {
return null;
}

return (
<AppRootPortal usePortal>
<ModalPageV2 animationState={animationState} {...animationHandlers} {...restProps} />
</AppRootPortal>
);
};
177 changes: 177 additions & 0 deletions packages/vkui/src/components/ModalPageV2/ModalPageV2.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
.ModalPageV2Root {
position: fixed;
inset: 0;
z-index: var(--vkui--z_index_modal);
overflow: hidden;
}

.ModalPageV2Root--hidden {
display: none;
}

.ModalPageV2Backdrop {
position: absolute;
inset: 0;
z-index: -1;
background-color: rgba(0, 0, 0);
opacity: 0.4;
user-select: none;
-webkit-tap-highlight-color: transparent;
}

@media (prefers-reduced-motion: no-preference) {
.ModalPageV2Backdrop {
animation: animation-fade-in 0.2s ease-in;
}

.ModalPageV2Backdrop--willBeHide {
animation: animation-fade-out 0.1s ease-out forwards;
}
}

.ModalPageV2 {
inline-size: 100%;
outline: none;

/* Для ограничения высоты `.ModalPageV2__container`. */
display: flex;
}

.ModalPageV2__container {
position: relative;
inline-size: 100%;

/* Для срабатывания `height: 100%` у `.ModalPageV2__children`. */
display: flex;
flex-direction: column;
}

.ModalPageV2__children {
border-start-start-radius: var(--vkui--size_border_radius_paper--regular);
border-start-end-radius: var(--vkui--size_border_radius_paper--regular);
background-color: var(--vkui--color_background_modal);
inline-size: 100%;
block-size: 100%;
overflow: hidden;

/*
* Удерживает высоту в пределах 100% родителя при переполнении контента.
*
* - см. `.ModalPageHeader {}`
* - см. `.ModalPageContent {}`
* - см. `.ModalPageFooter {}`
*/
display: flex;
flex-shrink: 1;
flex-direction: column;
}

/* Mobile */
@media (--viewWidth-smallTabletMinus) {
.ModalPageV2 {
position: absolute;
inset-inline: 0 0;
inset-block: auto var(--vkui_internal--safe_area_inset_bottom);
margin-inline: auto;
margin-block-start: var(--vkui_internal--safe_area_inset_top);
block-size: auto;
max-inline-size: var(--vkui--size_popup_small--regular);
max-block-size: calc(100% - var(--vkui_internal--safe_area_inset_top));
}

.ModalPageV2__container::after {
content: '';
position: absolute;
z-index: -1;
inset-block-start: 100%;
inset-inline-start: 0;
margin-block-start: -1px; /* убирает зазор под `.ModalPageV2__children` */
inline-size: 100%;
block-size: 50%;
background-color: var(--vkui--color_background_modal);
pointer-events: none;
touch-action: none;
}

.ModalCloseButton {
display: none;
}
}

/* Mobile animations */
@media (--viewWidth-smallTabletMinus) and (prefers-reduced-motion: no-preference) {
.ModalPageV2 {
animation: animation-slide-up 0.2s ease-out 0.1s backwards;
}

.ModalPageV2--willBeHide {
animation: animation-slide-down 0.1s ease-in forwards;
}
}

/* Desktop */
@media (--viewWidth-smallTabletPlus) {
.ModalPageV2Root {
display: flex;
align-items: center;
justify-content: center;
}

.ModalPageV2 {
position: relative;
max-block-size: calc(100% - 64px);
}

.ModalPageV2__container {
box-shadow: var(--vkui--elevation3);
}

.ModalPageV2__children {
border-end-end-radius: var(--vkui--size_border_radius_paper--regular);
border-end-start-radius: var(--vkui--size_border_radius_paper--regular);
}

.ModalPageV2--maxWidth-s {
max-inline-size: var(--vkui--size_popup_small--regular);
}

.ModalPageV2--maxWidth-m {
max-inline-size: var(--vkui--size_popup_medium--regular);
}

.ModalPageV2--maxWidth-l {
max-inline-size: var(--vkui--size_popup_large--regular);
}
}

/* Desktop animations */
@media (--viewWidth-smallTabletPlus) and (prefers-reduced-motion: no-preference) {
.ModalPageV2 {
animation: animation-fade-in 0.2s ease-in 0.1s backwards;
}

.ModalPageV2--willBeHide {
animation: animation-fade-out 0.1s ease-out forwards;
}
}

@keyframes animation-fade-in {
from {
opacity: 0;
}
}
@keyframes animation-fade-out {
to {
opacity: 0;
}
}
@keyframes animation-slide-up {
from {
transform: translateY(100%);
}
}
@keyframes animation-slide-down {
to {
transform: translateY(100%);
}
}
Loading

0 comments on commit 7d03282

Please sign in to comment.