diff --git a/components/drawer/demo/basic.md b/components/drawer/demo/basic.md new file mode 100644 index 0000000000..0d7f56886b --- /dev/null +++ b/components/drawer/demo/basic.md @@ -0,0 +1,53 @@ +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- + +## zh-CN + +第一个对话框。 + +## en-US + +Basic modal. + +```jsx +import { Drawer, Button } from 'antd'; + +class App extends React.Component { + state = { visible: false }; + showDrawer = () => { + this.setState({ + visible: true, + }); + }; + onClose = () => { + this.setState({ + visible: false, + }); + }; + render() { + return ( +
+ + +

Some contents...

+

Some contents...

+

Some contents...

+
+
+ ); + } +} + +ReactDOM.render(, mountNode); +``` diff --git a/components/drawer/index.en-US.md b/components/drawer/index.en-US.md new file mode 100644 index 0000000000..4802e32a7d --- /dev/null +++ b/components/drawer/index.en-US.md @@ -0,0 +1,32 @@ +--- +type: Feedback +category: Components +subtitle: Drawer +title: Drawer +--- + +Drawer container + +## When To Use + + +## API + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| closable | Whether a close (x) button is visible on top right of the Drawer dialog or not | boolean | true | +| destroyOnClose | Whether to unmount child compenents on onClose | boolean | false | +| getContainer | Return the mount node for Drawer | (instance): HTMLElement | () => document.body | +| mask | Whether show mask or not. | Boolean | true | +| maskClosable | Whether to close the Drawer dialog when the mask (area outside the Drawer) is clicked | boolean | true | +| maskStyle | Style for Drawer's mask element. | object | {} | +| style | Style of floating layer, typically used at least for adjusting the position. | object | - | +| title | The Drawer dialog's title | string\|ReactNode | - | +| visible | Whether the Drawer dialog is visible or not | boolean | false | +| width | Width of the Drawer dialog | string\|number | 520 | +| wrapClassName | The class name of the container of the Drawer dialog | string | - | +| zIndex | The `z-index` of the Drawer | Number | 1000 | +| placement | The direction of the Drawer | 'left' | 'right' | 'left' +| onClose | Specify a function that will be called when a user clicks mask, close button on top right or Cancel button | function(e) | - | + + diff --git a/components/drawer/index.md b/components/drawer/index.md deleted file mode 100644 index 48253e0ea6..0000000000 --- a/components/drawer/index.md +++ /dev/null @@ -1,28 +0,0 @@ - ---- -category: Components -type: Other -title: Drawer -subtitle: 抽屉 ---- - -## API -| 参数 | 说明 | 类型 | 默认值 | -| --- | --- | --- | --- | -| className | 抽屉的类属性 | string | `drawer` | -| openClassName | 打开抽屉时的类属性 | string | `drawer-open`| -| bodyStyle | Drawer 面板 样式 | object | {} | -| mask | 是否展示遮罩 | Boolean | true | -| maskClosable | 点击蒙层是否允许关闭 | boolean | true | -| maskStyle | 遮罩样式 | object | {} | -| showIcon | 是否显示抽屉的 icon | boolean | true | -| icon | 抽屉的 icon | ReactNode | node | -| visible | 抽屉是否可见 | boolean | false | -| getContainer | 指定 抽屉挂载节点 | (instance)=> HTMLElement | `body` | -| width | 抽屉的宽度 | string | `60vw` | -| placement | 抽屉的方向 | `left` 或 `right` | `left` | -| onChange | 面板状态改变事件 | (state:boolean)=>void | null | -| onIconClick | icon 点击事件 | (e:event)=>void | null -| destroyOnClose | 关闭时销毁 Drawer 里的子元素 | boolean | false | - - diff --git a/components/drawer/index.tsx b/components/drawer/index.tsx new file mode 100644 index 0000000000..1ea063fa16 --- /dev/null +++ b/components/drawer/index.tsx @@ -0,0 +1,120 @@ +import * as React from 'react'; +import RcDrawer from 'rc-drawer-menu'; +import { isNull } from 'util'; + +type EventType = + | React.MouseEvent + | React.MouseEvent; + +export interface IDrawerProps { + closable?: boolean; + // @todo 下一步增加 + destroyOnClose?: boolean; + getContainer?: HTMLElement; + maskClosable?: boolean; + mask?: boolean; + maskStyle?: React.CSSProperties; + style?: React.CSSProperties; + title?: React.ReactNode; + visible?: boolean; + width?: number | string; + wrapClassName?: string; + // @todo 下一步增加 + zIndex?: number; + prefixCls?: string; + placement?: 'left' | 'right' ; + onClose?: (e: EventType) => void; +} + +export interface IDrawerState { + visible?: boolean; +} + +export default class Drawer extends React.Component< + IDrawerProps, + IDrawerState +> { + static defaultProps = { + prefixCls: 'ant-drawer', + width: 325, + closable: true, + }; + static getDerivedStateFromProps( + nextProps: IDrawerProps, + prevState: IDrawerState, + ) { + const nextState: IDrawerState = {}; + if (!isNull(nextProps.visible) && nextProps.visible !== prevState.visible) { + nextState.visible = nextProps.visible; + } + return nextState; + } + constructor(props: IDrawerProps) { + super(props); + this.state = { + visible: false, + }; + } + close = (e: EventType) => { + if (!isNull(this.props.visible)) { + if (this.props.onClose) { + this.props.onClose(e); + } + return; + } + this.setState({ + visible: false, + }); + } + onMaskClick = (e: EventType) => { + if (!this.props.maskClosable) { + return; + } + this.close(e); + } + renderBody = () => { + const { prefixCls, title, closable } = this.props; + let header; + if (title) { + header = ( +
+
{title}
+
+ ); + } + let closer; + if (closable) { + closer = ( + + ); + } + const containerStyle = { width: this.props.width }; + return ( +
+ {header} + {closer} +
{this.props.children}
; +
+ ); + } + render() { + return ( + + {this.renderBody()} + + ); + } +} diff --git a/components/drawer/index.zh-CN.md b/components/drawer/index.zh-CN.md new file mode 100644 index 0000000000..5d675ca0c9 --- /dev/null +++ b/components/drawer/index.zh-CN.md @@ -0,0 +1,32 @@ +--- +type: Feedback +category: Components +subtitle: 抽屉 +title: Drawer +--- + +抽屉容器 + +## 何时使用 + + +## API + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| closable | 是否显示右上角的关闭按钮 | boolean | true | +| destroyOnClose | 关闭时销毁 Drawer 里的子元素 | boolean | false | +| getContainer | 指定 Drawer 挂载的 HTML 节点 | (instance): HTMLElement | () => document.body | +| maskClosable | 点击蒙层是否允许关闭 | boolean | true | +| mask | 是否展示遮罩 | Boolean | true | +| maskStyle | 遮罩样式 | object | {} | +| style | 可用于设置 Drawer 的样式,调整浮层位置等 | object | - | +| title | 标题 | string\|ReactNode | 无 | +| visible | Drawer 是否可见 | boolean | 无 | +| width | 宽度 | string\|number | 325 | +| wrapClassName | 对话框外层容器的类名 | string | - | +| zIndex | 设置 Drawer 的 `z-index` | Number | 1000 | +| placement | 抽屉的方向 | 'left' | 'right' | 'left' +| onClose | 点击遮罩层或右上角叉或取消按钮的回调 | function(e) | 无 | + + diff --git a/components/drawer/style/drawer.less b/components/drawer/style/drawer.less new file mode 100644 index 0000000000..dac3bf324e --- /dev/null +++ b/components/drawer/style/drawer.less @@ -0,0 +1,237 @@ +@dawer-prefix-cls: ~"@{ant-prefix}-drawer"; + +.@{dawer-prefix-cls} { + position: fixed; + top: 0; + width: 100%; + height: 100%; + pointer-events: none; + + &-content-wrapper { + position: absolute; + z-index: @zindex-modal-mask + 1; + } + + &-left, + &-right { + .@{dawer-prefix-cls}-content-wrapper, + .@{dawer-prefix-cls}-content { + height: 100%; + } + } + &-left { + .@{dawer-prefix-cls} { + &-handle { + right: -40px; + box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15); + border-radius: 0 4px 4px 0; + } + } + &.@{dawer-prefix-cls}-open { + .@{dawer-prefix-cls} { + &-wrapper { + box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15); + } + } + } + } + &-right { + .@{dawer-prefix-cls} { + &-content-wrapper { + right: 0; + } + &-handle { + left: -40px; + box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15); + border-radius: 4px 0 0 4px; + } + } + &.@{dawer-prefix-cls}-open { + & .@{dawer-prefix-cls} { + &-wrapper { + box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15); + } + } + } + } + &-top, + &-bottom { + .@{dawer-prefix-cls}-content-wrapper, + .@{dawer-prefix-cls}-content { + width: 100%; + } + .@{dawer-prefix-cls} { + &-handle { + left: 50%; + margin-left: -20px; + } + } + } + &-top { + .@{dawer-prefix-cls} { + &-handle { + top: auto; + bottom: -40px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + border-radius: 0 0 4px 4px; + } + } + &.@{dawer-prefix-cls}-open { + .@{dawer-prefix-cls} { + &-wrapper { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + } + } + } + } + &-bottom { + .@{dawer-prefix-cls} { + &-content-wrapper { + bottom: 0; + } + &-handle { + top: -40px; + box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.15); + border-radius: 4px 4px 0 0; + } + } + &.@{dawer-prefix-cls}-open { + .@{dawer-prefix-cls} { + &-wrapper { + box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.15); + } + } + } + } + &.@{dawer-prefix-cls}-open { + > * { + pointer-events: auto; + } + .@{dawer-prefix-cls} { + &-mask { + opacity: 0.3; + display: block; + } + &-handle { + &-icon { + background: transparent; + &:before { + transform: translateY(5px) rotate(45deg); + } + &:after { + transform: translateY(-5px) rotate(-45deg); + } + } + } + } + } + + &-title { + margin: 0; + font-size: @font-size-lg; + line-height: 22px; + font-weight: 500; + color: @heading-color; + } + + &-content { + position: relative; + background-color: @component-background; + border: 0; + background-clip: padding-box; + box-shadow: @shadow-2; + } + + &-close { + cursor: pointer; + border: 0; + background: transparent; + position: absolute; + right: 0; + top: 0; + z-index: 10; + font-weight: 700; + line-height: 1; + text-decoration: none; + transition: color 0.3s; + color: @text-color-secondary; + outline: 0; + padding: 0; + + &-x { + display: block; + font-style: normal; + text-align: center; + text-transform: none; + text-rendering: auto; + width: 56px; + height: 56px; + line-height: 56px; + font-size: @font-size-lg; + + &:before { + content: "\e633"; + display: block; + font-family: "anticon" !important; + } + } + + &:focus, + &:hover { + color: #444; + text-decoration: none; + } + } + + &-header { + padding: 16px 24px; + border-radius: @border-radius-base @border-radius-base 0 0; + background: @component-background; + color: @text-color; + border-bottom: @border-width-base @border-style-base @border-color-split; + } + + &-body { + padding: 24px; + font-size: @font-size-base; + line-height: @line-height-base; + word-wrap: break-word; + } + + &.zoom-enter, + &.zoom-appear { + animation-duration: @animation-duration-slow; + transform: none; // reset scale avoid mousePosition bug + opacity: 0; + } + + &-mask { + position: fixed; + top: 0; + right: 0; + left: 0; + bottom: 0; + display: none; + background-color: #373737; + background-color: @modal-mask-bg; // lesshint duplicateProperty: false + height: 100%; + z-index: @zindex-modal-mask; + filter: ~"alpha(opacity=50)"; + } + + &-open { + overflow: hidden; + } +} + +@media (max-width: @screen-md) { + .@{dawer-prefix-cls} { + width: auto !important; + margin: 10px; + } + .vertical-center-modal { + .@{dawer-prefix-cls} { + flex: 1; + } + } +} diff --git a/components/drawer/style/index.less b/components/drawer/style/index.less new file mode 100644 index 0000000000..303b8ee832 --- /dev/null +++ b/components/drawer/style/index.less @@ -0,0 +1,4 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; +@import "./drawer"; + diff --git a/components/drawer/style/index.tsx b/components/drawer/style/index.tsx new file mode 100644 index 0000000000..416ec0177e --- /dev/null +++ b/components/drawer/style/index.tsx @@ -0,0 +1,5 @@ +import '../../style/index.less'; +import './index.less'; + +// style dependencies +import '../../button/style'; diff --git a/components/index.tsx b/components/index.tsx index 4a06cf350e..bbb3c7bebf 100644 --- a/components/index.tsx +++ b/components/index.tsx @@ -51,6 +51,8 @@ export { default as Divider } from './divider'; export { default as Dropdown } from './dropdown'; +export { default as Drawer } from './drawer'; + export { default as Form } from './form'; export { default as Icon } from './icon'; diff --git a/package.json b/package.json index 2ff0d6de24..1cb25feafc 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,7 @@ "preact": "^8.2.5", "preact-compat": "^3.17.0", "querystring": "^0.2.0", - "rc-drawer-menu": "^0.5.3", + "rc-drawer-menu": "^1.0.2", "rc-queue-anim": "^1.4.1", "rc-scroll-anim": "^2.2.1", "rc-tween-one": "^2.0.1", diff --git a/typings/custom-typings.d.ts b/typings/custom-typings.d.ts index 3d3e1c46a3..83636f3c95 100644 --- a/typings/custom-typings.d.ts +++ b/typings/custom-typings.d.ts @@ -40,6 +40,8 @@ declare module 'rc-progress'; declare module 'rc-menu'; +declare module 'rc-drawer-menu'; + declare module 'rc-tabs*'; declare module 'rc-tree';