ant-design/components/drawer/index.tsx

216 lines
5.8 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';
import { NoCompactStyle } from '../space/Compact';
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;
/**
* @deprecated `visible` is deprecated which will be removed in next major version. Please use
* `open` instead.
*/
2018-05-23 10:56:37 +08:00
visible?: boolean;
open?: boolean;
2022-08-01 17:06:19 +08:00
footer?: React.ReactNode;
2022-08-01 17:06:19 +08:00
extra?: React.ReactNode;
/**
* @deprecated `afterVisibleChange` is deprecated which will be removed in next major version.
* Please use `afterOpenChange` instead.
*/
2022-08-01 17:06:19 +08:00
afterVisibleChange?: (visible: boolean) => void;
afterOpenChange?: (open: boolean) => void;
}
2020-07-12 18:47:14 +08:00
const defaultPushState: PushState = { distance: 180 };
function Drawer(props: DrawerProps) {
const {
width,
height,
size = 'default',
closable = true,
mask = true,
push = defaultPushState,
closeIcon = <CloseOutlined />,
bodyStyle,
drawerStyle,
className,
visible,
open,
children,
style,
title,
headerStyle,
onClose,
footer,
footerStyle,
prefixCls: customizePrefixCls,
getContainer: customizeGetContainer,
extra,
afterVisibleChange,
afterOpenChange,
...rest
} = props;
const { getPopupContainer, getPrefixCls, direction } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('drawer', customizePrefixCls);
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>
);
[
['visible', 'open'],
['afterVisibleChange', 'afterOpenChange'],
].forEach(([deprecatedName, newName]) => {
warning(
!(deprecatedName in props),
'Drawer',
`\`${deprecatedName}\` is deprecated which will be removed in next major version, please use \`${newName}\` instead.`,
);
});
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',
},
className,
);
// ============================ 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 ===========================
return (
<NoCompactStyle>
<NoFormStyle status override>
<RcDrawer
prefixCls={prefixCls}
onClose={onClose}
{...rest}
open={open || visible}
mask={mask}
push={push}
width={mergedWidth}
height={mergedHeight}
rootClassName={drawerClassName}
getContainer={getContainer}
afterOpenChange={(isOpen) => {
afterOpenChange?.(isOpen);
afterVisibleChange?.(isOpen);
}}
maskMotion={maskMotion}
motion={panelMotion}
rootStyle={style}
>
<div className={`${prefixCls}-wrapper-body`} style={{ ...drawerStyle }}>
{renderHeader()}
<div className={`${prefixCls}-body`} style={bodyStyle}>
{children}
</div>
{renderFooter()}
</div>
</RcDrawer>
</NoFormStyle>
</NoCompactStyle>
);
}
if (process.env.NODE_ENV !== 'production') {
Drawer.displayName = 'Drawer';
}
2022-03-23 22:46:15 +08:00
export default Drawer;