Skip to content

Commit

Permalink
feat: create ModalPageV2
Browse files Browse the repository at this point in the history
  • Loading branch information
inomdzhon committed Aug 1, 2024
1 parent 14a35a6 commit c382b8f
Show file tree
Hide file tree
Showing 25 changed files with 1,162 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 @@ -93,9 +94,12 @@ export const GlobalScrollController = ({ children }: ScrollControllerProps): Rea
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 @@ -104,6 +108,8 @@ export const GlobalScrollController = ({ children }: ScrollControllerProps): Rea
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
4 changes: 2 additions & 2 deletions packages/vkui/src/components/ModalPage/ModalPageContext.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from 'react';
import { createContext } from 'react';

export interface ModalPageContextInterface {
labelId?: string;
}

export const ModalPageContext: React.Context<ModalPageContextInterface> =
React.createContext<ModalPageContextInterface>({});
createContext<ModalPageContextInterface>({});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.ModalPageBackdrop {
position: absolute;
inset: 0;
z-index: -1;
background-color: rgba(0, 0, 0);
opacity: 0.4;
user-select: none;
-webkit-tap-highlight-color: transparent;
}

.ModalPageBackdrop--visible {
animation: modal-page-backdrop-fade-in 0.2s ease-in;
}

.ModalPageBackdrop--invisible {
animation: modal-page-backdrop-fade-out 0.1s ease-out forwards;
}

@keyframes modal-page-backdrop-fade-in {
from {
opacity: 0;
}
}
@keyframes modal-page-backdrop-fade-out {
to {
opacity: 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { classNames } from '@vkontakte/vkjs';
import type { HasRootRef } from '../../types';
import styles from './ModalPageBackdrop.module.css';

export interface ModalPageBackdropProps extends HasRootRef<HTMLDivElement> {
visible: boolean;
onClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
}

export const ModalPageBackdrop = ({ visible, getRootRef, onClick }: ModalPageBackdropProps) => {
return (
<div
ref={getRootRef}
aria-hidden="true"
className={classNames(
styles['ModalPageBackdrop'],
visible ? styles['ModalPageBackdrop--visible'] : styles['ModalPageBackdrop--invisible'],
)}
onClick={onClick}
/>
);
};
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
150 changes: 150 additions & 0 deletions packages/vkui/src/components/ModalPageV2/ModalPageV2.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
.ModalPageV2Root:not([hidden]) {
position: fixed;
inset: 0;
z-index: var(--vkui--z_index_modal);
overflow: hidden;
}

.ModalPageV2 {
--vkui_internal_ModalPage--transform-default: none;

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;
transform: var(--vkui_internal_ModalPage--transform-default);
}

.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;
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--enter {
animation: animation-slide-up 0.2s ease-out 0.1s backwards;
}

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

.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;
}

.Modal__CloseButton {
display: none;
}
}

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

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

.ModalPageV2--enter {
animation: animation-fade-in 0.2s ease-in 0.1s backwards;
}

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

.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);
}
}

@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 c382b8f

Please sign in to comment.