ant-design/components/drawer/index.tsx

237 lines
6.1 KiB
TypeScript
Raw Normal View History

import CloseOutlined from '@ant-design/icons/CloseOutlined';
2018-08-05 07:08:34 +08:00
import classNames from 'classnames';
import RcDrawer from 'rc-drawer';
import type { DrawerProps as RcDrawerProps } from 'rc-drawer';
import type { CSSMotionProps } from 'rc-motion';
import * as React from 'react';
2022-03-23 22:46:15 +08:00
import { ConfigContext } from '../config-provider';
import { NoFormStyle } from '../form/context';
import { getTransitionName } from '../_util/motion';
import { tuple } from '../_util/type';
import warning from '../_util/warning';
2022-03-24 23:33:51 +08:00
// CSSINJS
import useStyle from './style';
const SizeTypes = tuple('default', 'large');
type sizeType = typeof SizeTypes[number];
2020-07-12 18:47:14 +08:00
export interface PushState {
distance: string | number;
}
2022-08-01 17:06:19 +08:00
// Drawer diff props: 'open' | 'motion' | 'maskMotion' | 'wrapperClassName'
export interface DrawerProps extends RcDrawerProps {
size?: sizeType;
2018-05-23 10:56:37 +08:00
closable?: boolean;
closeIcon?: React.ReactNode;
2022-08-01 17:06:19 +08:00
/** Wrapper dom node style of header and body */
drawerStyle?: React.CSSProperties;
headerStyle?: React.CSSProperties;
2019-01-11 22:45:01 +08:00
bodyStyle?: React.CSSProperties;
2022-08-01 17:06:19 +08:00
footerStyle?: React.CSSProperties;
2018-05-23 10:56:37 +08:00
title?: React.ReactNode;
open?: boolean;
2022-08-01 17:06:19 +08:00
footer?: React.ReactNode;
2022-08-01 17:06:19 +08:00
extra?: React.ReactNode;
afterOpenChange?: (open: boolean) => void;
// Deprecated
/** @deprecated Please use `open` instead */
visible?: boolean;
/** @deprecated Please use `afterOpenChange` instead */
afterVisibleChange?: (open: boolean) => void;
}
2020-07-12 18:47:14 +08:00
const defaultPushState: PushState = { distance: 180 };
function Drawer(props: DrawerProps) {
const {
rootClassName,
width,
height,
size = 'default',
closable = true,
mask = true,
push = defaultPushState,
closeIcon = <CloseOutlined />,
bodyStyle,
drawerStyle,
open,
afterOpenChange,
children,
title,
headerStyle,
onClose,
footer,
footerStyle,
prefixCls: customizePrefixCls,
getContainer: customizeGetContainer,
extra,
// Deprecated
visible,
afterVisibleChange,
...rest
} = props;
const { getPopupContainer, getPrefixCls, direction } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('drawer', customizePrefixCls);
2022-07-29 15:56:11 +08:00
// Style
const [wrapSSR, hashId] = useStyle(prefixCls);
const getContainer =
// 有可能为 false所以不能直接判断
customizeGetContainer === undefined && getPopupContainer
? () => getPopupContainer(document.body)
: customizeGetContainer;
const closeIconNode = closable && (
<button type="button" onClick={onClose} aria-label="Close" className={`${prefixCls}-close`}>
{closeIcon}
</button>
);
function renderHeader() {
if (!title && !closable) {
return null;
}
return (
<div
className={classNames(`${prefixCls}-header`, {
[`${prefixCls}-header-close-only`]: closable && !title && !extra,
})}
style={headerStyle}
>
<div className={`${prefixCls}-header-title`}>
{closeIconNode}
{title && <div className={`${prefixCls}-title`}>{title}</div>}
</div>
{extra && <div className={`${prefixCls}-extra`}>{extra}</div>}
</div>
);
}
function renderFooter() {
if (!footer) {
return null;
2018-07-19 15:39:47 +08:00
}
const footerClassName = `${prefixCls}-footer`;
return (
<div className={footerClassName} style={footerStyle}>
{footer}
</div>
);
}
const drawerClassName = classNames(
{
'no-mask': !mask,
[`${prefixCls}-rtl`]: direction === 'rtl',
},
rootClassName,
2022-07-29 15:56:11 +08:00
hashId,
);
// ========================== Warning ===========================
if (process.env.NODE_ENV !== 'production') {
[
['visible', 'open'],
['afterVisibleChange', 'afterOpenChange'],
].forEach(([deprecatedName, newName]) => {
warning(
!(deprecatedName in props),
'Drawer',
`\`${deprecatedName}\` is deprecated, please use \`${newName}\` instead.`,
);
});
}
// ============================ Size ============================
const mergedWidth = React.useMemo(() => width ?? (size === 'large' ? 736 : 378), [width, size]);
const mergedHeight = React.useMemo(
() => height ?? (size === 'large' ? 736 : 378),
[height, size],
);
// =========================== Motion ===========================
const maskMotion: CSSMotionProps = {
motionName: getTransitionName(prefixCls, 'mask-motion'),
motionAppear: true,
motionEnter: true,
motionLeave: true,
motionDeadline: 500,
};
const panelMotion: RcDrawerProps['motion'] = motionPlacement => ({
motionName: getTransitionName(prefixCls, `panel-motion-${motionPlacement}`),
motionAppear: true,
motionEnter: true,
motionLeave: true,
motionDeadline: 500,
});
// =========================== Render ===========================
2022-07-29 15:56:11 +08:00
return wrapSSR(
<NoFormStyle status override>
<RcDrawer
prefixCls={prefixCls}
onClose={onClose}
maskMotion={maskMotion}
motion={panelMotion}
{...rest}
open={open ?? visible}
mask={mask}
push={push}
width={mergedWidth}
height={mergedHeight}
rootClassName={drawerClassName}
getContainer={getContainer}
afterOpenChange={afterOpenChange ?? afterVisibleChange}
>
<div className={`${prefixCls}-wrapper-body`} style={{ ...drawerStyle }}>
{renderHeader()}
<div className={`${prefixCls}-body`} style={bodyStyle}>
{children}
</div>
{renderFooter()}
2019-01-11 22:45:01 +08:00
</div>
</RcDrawer>
2022-07-29 15:56:11 +08:00
</NoFormStyle>,
);
}
if (process.env.NODE_ENV !== 'production') {
Drawer.displayName = 'Drawer';
}
2022-08-25 10:17:29 +08:00
function PurePanel({ style, ...restProps }: DrawerProps) {
const containerRef = React.useRef<HTMLDivElement>(null);
return (
<div
ref={containerRef}
style={{
position: 'relative',
minHeight: 100,
overflow: 'hidden',
...style,
}}
>
<Drawer {...restProps} getContainer={false} maskMotion={{}} motion={{}} open />
</div>
);
}
Drawer._InternalPanelDoNotUseOrYouWillBeFired = PurePanel;
2022-03-23 22:46:15 +08:00
export default Drawer;