@hackplan/polaris
Version:
Shopify’s product component library
75 lines (74 loc) • 3.01 kB
JavaScript
import React, { useCallback, useContext, useEffect, useState } from 'react';
import { CSSTransition } from 'react-transition-group';
import debounce from 'lodash/debounce';
import { classNames } from '../../utilities/css';
import { navigationBarCollapsed } from '../../utilities/breakpoints';
import { Key } from '../../types';
import { layer, overlay, Duration } from '../shared';
import { FrameContext } from '../Frame';
import { withAppProvider } from '../AppProvider';
import { usePolaris } from '../../hooks';
import Backdrop from '../Backdrop';
import TrapFocus from '../TrapFocus';
import Portal from '../Portal';
import KeypressListener from '../KeypressListener';
import EventListener from '../EventListener';
import styles from './Sheet.scss';
export const BOTTOM_CLASS_NAMES = {
enter: classNames(styles.Bottom, styles.enterBottom),
enterActive: classNames(styles.Bottom, styles.enterBottomActive),
exit: classNames(styles.Bottom, styles.exitBottom),
exitActive: classNames(styles.Bottom, styles.exitBottomActive),
};
export const RIGHT_CLASS_NAMES = {
enter: classNames(styles.Right, styles.enterRight),
enterActive: classNames(styles.Right, styles.enterRightActive),
exit: classNames(styles.Right, styles.exitRight),
exitActive: classNames(styles.Right, styles.exitRightActive),
};
export function Sheet({ children, open, onClose }) {
const [mobile, setMobile] = useState(false);
const frame = useContext(FrameContext);
const { intl } = usePolaris();
const handleResize = useCallback(debounce(() => {
if (mobile !== isMobile()) {
handleToggleMobile();
}
}, 40, { leading: true, trailing: true, maxWait: 40 }), [mobile]);
useEffect(() => {
if (frame == null) {
// eslint-disable-next-line no-console
console.warn(intl.translate('Polaris.Sheet.warningMessage'));
}
if (mobile !== isMobile()) {
handleToggleMobile();
}
}, [frame, intl, mobile]);
if (frame == null) {
return null;
}
return (<Portal idPrefix="sheet">
<CSSTransition classNames={mobile ? BOTTOM_CLASS_NAMES : RIGHT_CLASS_NAMES} timeout={Duration.Slow} in={open} mountOnEnter unmountOnExit>
<Container open={open}>{children}</Container>
</CSSTransition>
<KeypressListener keyCode={Key.Escape} handler={onClose}/>
<EventListener event="resize" handler={handleResize}/>
{open && <Backdrop transparent onClick={onClose}/>}
</Portal>);
function handleToggleMobile() {
setMobile((mobile) => !mobile);
}
}
function isMobile() {
return navigationBarCollapsed().matches;
}
function Container(props) {
return (<div className={styles.Container} {...layer.props} {...overlay.props}>
<TrapFocus trapping={props.open}>
<div role="dialog" tabIndex={-1} className={styles.Sheet}>
{props.children}
</div>
</TrapFocus>
</div>);
}
export default withAppProvider()(Sheet);