Skip to content

Commit

Permalink
refactor: update ModalRoot/ModalPage/ModalCard
Browse files Browse the repository at this point in the history
  • Loading branch information
inomdzhon committed Oct 29, 2024
1 parent fe3493a commit ad3bd4e
Show file tree
Hide file tree
Showing 58 changed files with 2,193 additions and 3,043 deletions.
2 changes: 1 addition & 1 deletion packages/vkui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"test:e2e-update": "../../scripts/generate_env_docker.sh -u && docker compose --env-file=./.env.docker up --abort-on-container-exit",
"test:e2e:ci": "yarn run -T playwright test --config playwright-ct.config.ts",
"test:e2e-update:ci": "yarn run test:e2e:ci --update-snapshots",
"lint:generated-files": "yarn run -T tsc scripts/generateCSSCustomMedias.mjs --checkJs --module ESNext --moduleResolution node --resolveJsonModule --allowSyntheticDefaultImports --noEmit && yarn run generate:css-custom-medias && git diff --exit-code src/styles/customMedias.generated.css",
"lint:generated-files": "yarn run -T tsc scripts/generateCSSCustomMedias.mjs --checkJs --module ESNext --moduleResolution node --resolveJsonModule --allowSyntheticDefaultImports --jsx react-jsx --noEmit && yarn run generate:css-custom-medias && git diff --exit-code src/styles/customMedias.generated.css",
"storybook": "bash -c 'source .env && yarn run -T cross-env SANDBOX=\\.storybook storybook dev -p ${STORYBOOK_DEV_PORT:=6006}'",
"storybook:build": "yarn run -T cross-env SANDBOX=\\.storybook FROM_STORYBOOK=1 storybook build",
"generate:css-custom-medias": "node scripts/generateCSSCustomMedias.mjs"
Expand Down
55 changes: 55 additions & 0 deletions packages/vkui/src/components/Backdrop/Backdrop.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.host {
position: absolute;
inset: 0;
background-color: rgba(0, 0, 0, 0.4);
user-select: none;
-webkit-tap-highlight-color: transparent;
transition: opacity 0.5s ease;
will-change: opacity;
}

.hostStateEnter {
opacity: 0;
transition-property: none;
}

.hostStateEntering {
opacity: var(--vkui_internal--backdrop--opacity);
transition-timing-function: ease-in;
}

.hostStateEntered {
opacity: var(--vkui_internal--backdrop--opacity);
}

.hostStateExit {
opacity: var(--vkui_internal--backdrop--opacity);
transition-property: none;
}

.hostStateExiting {
opacity: 0;
transition-timing-function: ease-out;
}

.hostStateExited {
opacity: 0;
transition-property: none;
}
/* Mobile */
@media (--viewWidth-smallTabletMinus) {
.hostStateEntering,
.hostStateExiting {
transition-duration: var(--vkui--animation_duration_l);
}
}
/* Desktop */
@media (--viewWidth-smallTabletPlus) {
.hostStateEntering {
transition-duration: 0.2s;
}

.hostStateExiting {
transition-duration: 0.1s;
}
}
45 changes: 45 additions & 0 deletions packages/vkui/src/components/Backdrop/Backdrop.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use client';

import { classNames } from '@vkontakte/vkjs';
import { useExternRef } from '../../hooks/useExternRef';
import { useCSSTransition, type UseCSSTransitionState } from '../../lib/animation/useCSSTransition';
import type { HasRootRef } from '../../types';
import styles from './Backdrop.module.css';

const transitionStateClassNames: Partial<Record<UseCSSTransitionState, string>> = {
appear: styles['hostStateEnter'],
appearing: styles['hostStateEntering'],
appeared: styles['hostStateEntered'],
enter: styles['hostStateEnter'],
entering: styles['hostStateEntering'],
entered: styles['hostStateEntered'],
exit: styles['hostStateExit'],
exiting: styles['hostStateExiting'],
exited: styles['hostStateExited'],
};

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

/**
* @private
*/
export const Backdrop = ({ visible, getRootRef, onClick, ...restProps }: BackdropProps) => {
const [transitionState, { ref, onTransitionEnd }] = useCSSTransition<HTMLDivElement>(visible, {
enableAppear: true,
});
const handleRef = useExternRef(getRootRef, ref);

return (
<div
{...restProps}
ref={handleRef}
aria-hidden="true"
className={classNames(styles.host, transitionStateClassNames[transitionState])}
onClick={onClick}
onTransitionEnd={onTransitionEnd}
/>
);
};
12 changes: 3 additions & 9 deletions packages/vkui/src/components/Group/Group.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { render, screen } from '@testing-library/react';
import { classNames, noop } from '@vkontakte/vkjs';
import { ModalContext } from '../../context/ModalContext';
import type { SizeTypeValues } from '../../lib/adaptivity';
import { baselineComponent } from '../../testing/utils';
import { AdaptivityContext } from '../AdaptivityProvider/AdaptivityContext';
Expand All @@ -8,7 +9,6 @@ import {
type AppRootContextInterface,
DEFAULT_APP_ROOT_CONTEXT_VALUE,
} from '../AppRoot/AppRootContext';
import { ModalRootContext } from '../ModalRoot/ModalRootContext';
import { Group, type GroupProps } from './Group';
import styles from './Group.module.css';

Expand Down Expand Up @@ -75,17 +75,11 @@ describe('Group', () => {
}}
>
<AdaptivityContext.Provider value={{ sizeX }}>
<ModalRootContext.Provider
value={{
isInsideModal,
updateModalHeight: noop,
registerModal: noop,
}}
>
<ModalContext.Provider value={isInsideModal ? 'test' : null}>
<Group mode={mode} data-testid="group">
<div />
</Group>
</ModalRootContext.Provider>
</ModalContext.Provider>
</AdaptivityContext.Provider>
</AppRootContext.Provider>,
);
Expand Down
4 changes: 2 additions & 2 deletions packages/vkui/src/components/Group/GroupContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import * as React from 'react';
import { classNames } from '@vkontakte/vkjs';
import { useModalContext } from '../../context/ModalContext';
import { useAdaptivity } from '../../hooks/useAdaptivity';
import type { SizeTypeValues } from '../../lib/adaptivity';
import { warnOnce } from '../../lib/warnOnce';
import type { HasComponent, HTMLAttributesWithRootRef } from '../../types';
import { AppRootContext } from '../AppRoot/AppRootContext';
import { ModalRootContext } from '../ModalRoot/ModalRootContext';
import { RootComponent } from '../RootComponent/RootComponent';
import styles from './Group.module.css';

Expand Down Expand Up @@ -90,7 +90,7 @@ export const GroupContainer: React.FC<GroupContainerProps> = ({
tabIndex: tabIndexProp,
...restProps
}: GroupContainerProps) => {
const { isInsideModal } = React.useContext(ModalRootContext);
const isInsideModal = useModalContext().id !== null;
const { sizeX = 'none' } = useAdaptivity();

const mode = useGroupMode(modeProps, sizeX, isInsideModal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
import { Button } from '../Button/Button';
import { ButtonGroup } from '../ButtonGroup/ButtonGroup';
import { Image } from '../Image/Image';
import { ModalRoot } from '../ModalRoot/ModalRootAdaptive';
import { Spacing } from '../Spacing/Spacing';
import { Textarea } from '../Textarea/Textarea';
import { UsersStack } from '../UsersStack/UsersStack';
Expand Down Expand Up @@ -127,15 +126,14 @@ export const ModalCardPlayground = (props: ComponentPlaygroundProps) => {
AppWrapper={AppWrapper}
>
{(props: ModalCardProps) => (
<div style={{ height: 500, transform: 'translateZ(0)' }}>
<ModalRoot
activeModal={props.nav}
<div style={{ height: 500, overflow: 'hidden', transform: 'translateZ(0)' }}>
<ModalCard
open
// Note: с включенным фокусом ломаются скриншоты на движке Webkit из-за фокуса сразу
// на несколько окон
noFocusToDialog
>
<ModalCard {...props} />
</ModalRoot>
disableAutoFocus
{...props}
/>
</div>
)}
</ComponentPlayground>
Expand Down
105 changes: 61 additions & 44 deletions packages/vkui/src/components/ModalCard/ModalCard.module.css
Original file line number Diff line number Diff line change
@@ -1,65 +1,82 @@
.host {
box-sizing: border-box;
position: absolute;
inset-block-start: 0;
padding: 8px;
inset-inline-start: 0;
padding: var(--vkui--spacing_size_m);
margin-inline: auto;
inline-size: 100%;
block-size: 100%;
display: flex;
align-items: flex-end;
box-sizing: border-box;
}

.host:focus {
outline: none;
}

.in {
inline-size: 100%;
margin-inline: auto;
transform: translateY(calc(100% + 16px));
transition: transform 340ms var(--vkui--animation_easing_platform);
.hostMaxWidthS {
max-inline-size: calc(400px + var(--vkui--spacing_size_2xl));
}

/**
* iOS
*/
.hostMaxWidthM {
max-inline-size: calc(414px + var(--vkui--spacing_size_2xl));
}

.ios .in {
max-inline-size: 414px;
.hostMaxWidthL {
max-inline-size: calc(440px + var(--vkui--spacing_size_2xl));
}

/**
* Android + vkcom
*/
/* Mobile */
@media (--viewWidth-smallTabletMinus) {
.host {
--vkui_internal_ModalCard--snapPoint: 100%;
--vkui_internal_ModalCard--safeAreaInsetBottom: var(--vkui_internal--safe_area_inset_bottom);

.android .in {
max-inline-size: 440px;
}
position: absolute;
inset-inline: 0;
inset-block-end: 0;
margin-block-end: var(--vkui_internal_ModalCard--safeAreaInsetBottom);
transform: translate3d(0, calc(100% - var(--vkui_internal_ModalCard--snapPoint)), 0);
transition: transform var(--vkui--animation_duration_l) var(--vkui--animation_easing_platform);
}

.vkcom .in {
max-inline-size: 400px;
}
.hostStateEnter {
transform: translate3d(0, 100%, 0);
transition-property: none;
}

/**
* Desktop
*/
.hostStateEntering {
transition-property: transform;
transition-delay: 0.2s;
}

.desktop {
align-items: center;
}
.hostStateExiting {
transform: translate3d(0, 100%, 0);
transition-property: transform;
}

.desktop .in {
transform: unset;
opacity: 0;
transition: opacity 340ms var(--vkui--animation_easing_platform);
.hostStateExited {
transform: translate3d(0, 100%, 0);
transition-property: transform;
}
}
/* Desktop */
@media (--viewWidth-smallTabletPlus) {
.host {
margin-block: auto;
opacity: 1;
transition: opacity 340ms var(--vkui--animation_easing_platform);
}

.hostStateEnter {
opacity: 0;
transition-property: none;
}

.hostStateEntering {
opacity: 1;
}

.hostStateExiting {
opacity: 0;
}

/**
* CMP:
* ModalRoot
*/
/* stylelint-disable-next-line selector-pseudo-class-disallowed-list */
:global(.vkuiInternalModalRoot--touched) .in {
transition: none;
.hostStateExited {
opacity: 0;
}
}
Loading

0 comments on commit ad3bd4e

Please sign in to comment.