Skip to content

Commit

Permalink
Render popouts and modals in portal
Browse files Browse the repository at this point in the history
Components like Alert/ScreenSpinner/ActionSheet and
ModalRootDesktop
  • Loading branch information
andrey-medvedev-vk committed Oct 11, 2024
1 parent 5fc39b7 commit 1812193
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 81 deletions.
77 changes: 40 additions & 37 deletions packages/vkui/src/components/ModalRoot/ModalRootDesktop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useWaitTransitionFinish } from '../../hooks/useWaitTransitionFinish';
import { useDOM } from '../../lib/dom';
import { getNavId } from '../../lib/getNavId';
import { warnOnce } from '../../lib/warnOnce';
import { ModalPopoutPortal } from '../AppRoot/ModalPopoutPortal';
import { useConfigProvider } from '../ConfigProvider/ConfigProviderContext';
import { FocusTrap } from '../FocusTrap/FocusTrap';
import { ModalRootContext, type ModalRootContextInterface } from './ModalRootContext';
Expand Down Expand Up @@ -200,44 +201,46 @@ export const ModalRootDesktop = ({
}

return (
<ModalRootContext.Provider value={modalRootContext}>
<div
className={classNames(
styles.host,
hasCustomPanelHeaderAfter && styles.hasCustomPanelHeaderAfterSlot,
styles.desktop,
)}
>
<ModalPopoutPortal>
<ModalRootContext.Provider value={modalRootContext}>
<div
data-testid={modalOverlayTestId}
className={styles.mask}
ref={maskElementRef}
onClick={onExit}
/>
<div className={styles.viewport}>
{modals.map((Modal: React.ReactElement) => {
const modalId = getNavId(Modal.props, warn);
if (modalId !== activeModal && modalId !== exitingModal) {
return null;
}

const key = `modal-${modalId}`;

return (
<FocusTrap
autoFocus={false}
restoreFocus={false}
onClose={onExit}
timeout={timeout}
key={key}
className={styles.modal}
>
{Modal}
</FocusTrap>
);
})}
className={classNames(
styles.host,
hasCustomPanelHeaderAfter && styles.hasCustomPanelHeaderAfterSlot,
styles.desktop,
)}
>
<div
data-testid={modalOverlayTestId}
className={styles.mask}
ref={maskElementRef}
onClick={onExit}
/>
<div className={styles.viewport}>
{modals.map((Modal: React.ReactElement) => {
const modalId = getNavId(Modal.props, warn);
if (modalId !== activeModal && modalId !== exitingModal) {
return null;
}

const key = `modal-${modalId}`;

return (
<FocusTrap
autoFocus={false}
restoreFocus={false}
onClose={onExit}
timeout={timeout}
key={key}
className={styles.modal}
>
{Modal}
</FocusTrap>
);
})}
</div>
</div>
</div>
</ModalRootContext.Provider>
</ModalRootContext.Provider>
</ModalPopoutPortal>
);
};
13 changes: 0 additions & 13 deletions packages/vkui/src/components/PopoutRoot/PopoutRoot.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,3 @@
block-size: 100%;
position: relative;
}

.popout {
position: fixed;
inset-inline-start: 0;
inset-block-start: 0;
z-index: var(--vkui--z_index_popout);
inline-size: 100%;
block-size: 100%;
}

.popout:empty {
display: none;
}
10 changes: 1 addition & 9 deletions packages/vkui/src/components/PopoutRoot/PopoutRoot.test.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import { baselineComponent } from '../../testing/utils';
import { PopoutRoot, PopoutRootModal, PopoutRootPopout } from './PopoutRoot';
import { PopoutRoot } from './PopoutRoot';

describe(PopoutRoot, () => {
baselineComponent(PopoutRoot);
});

describe(PopoutRootModal, () => {
baselineComponent(PopoutRootModal, { getRootRef: false });
});

describe(PopoutRootPopout, () => {
baselineComponent(PopoutRootPopout, { getRootRef: false });
});
23 changes: 1 addition & 22 deletions packages/vkui/src/components/PopoutRoot/PopoutRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,11 @@
'use client';

import * as React from 'react';
import { classNames } from '@vkontakte/vkjs';
import type { HTMLAttributesWithRootRef } from '../../types';
import { AppRootContext } from '../AppRoot/AppRootContext';
import { RootComponent } from '../RootComponent/RootComponent';
import styles from './PopoutRoot.module.css';

/**
* @private
*/
export const PopoutRootPopout = ({
className,
...restProps
}: React.HTMLAttributes<HTMLDivElement>): React.ReactNode => (
<div className={classNames(styles.popout, className)} {...restProps} />
);

/**
* @private
*/
export const PopoutRootModal = ({
className,
...restProps
}: React.HTMLAttributes<HTMLDivElement>): React.ReactNode => (
<div className={classNames(styles.modal, className)} {...restProps} />
);

export interface PopoutRootProps extends HTMLAttributesWithRootRef<HTMLDivElement> {
popout?: React.ReactNode;
modal?: React.ReactNode;
Expand All @@ -47,7 +26,7 @@ export const PopoutRoot = ({
<RootComponent {...restProps} baseClassName={styles.host}>
{children}
<div ref={popoutModalRoot}>
{!!popout && <PopoutRootPopout>{popout}</PopoutRootPopout>}
{popout}
{modal}
</div>
</RootComponent>
Expand Down
9 changes: 9 additions & 0 deletions packages/vkui/src/components/PopoutWrapper/PopoutWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export interface PopoutWrapperProps extends HTMLAttributesWithRootRef<HTMLDivEle
* Спрячет компонент через fade-out анимацию.
*/
closing?: boolean;
/**
* Позволяет задать z-index через токен или числом
*/
zIndex?: number | string;
}

/**
Expand All @@ -54,6 +58,7 @@ export const PopoutWrapper = ({
fixed = true,
children,
onClick,
zIndex = 'var(--vkui--z_index_popout)',
...restProps
}: PopoutWrapperProps): React.ReactNode => {
return (
Expand All @@ -67,6 +72,10 @@ export const PopoutWrapper = ({
fixed && styles.fixed,
!noBackground && styles.masked,
)}
style={{
zIndex,
...restProps.style,
}}
>
<div className={styles.container}>
<div className={styles.overlay} onClick={onClick} />
Expand Down

0 comments on commit 1812193

Please sign in to comment.