From e7c80606ee84c586439c69b6fc06a03437cfcfe8 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Thu, 13 Feb 2020 19:58:19 +0800 Subject: [PATCH] feat: update drawer --- build/config.js | 2 +- .../__snapshots__/Drawer.test.js.snap | 34 ++++---- .../__snapshots__/DrawerEvent.test.js.snap | 8 +- .../__tests__/__snapshots__/demo.test.js.snap | 28 ++++++- components/drawer/demo/basic-right.md | 5 ++ components/drawer/demo/form-in-drawer.md | 5 +- components/drawer/demo/index.vue | 16 ++-- components/drawer/demo/multi-level-drawer.md | 2 +- components/drawer/demo/placement.md | 2 +- components/drawer/demo/render-in-current.md | 64 ++++++++++++++ components/drawer/demo/user-profile.md | 8 +- components/drawer/index.en-US.md | 3 + components/drawer/index.jsx | 83 +++++++++++++------ components/drawer/index.zh-CN.md | 3 + components/vc-drawer/src/Drawer.js | 32 +++++-- .../{drawerProps.js => IDrawerPropTypes.js} | 31 +++++-- components/vc-drawer/src/utils.js | 45 ++++++++++ types/drawer.d.ts | 5 +- 18 files changed, 292 insertions(+), 84 deletions(-) create mode 100644 components/drawer/demo/render-in-current.md rename components/vc-drawer/src/{drawerProps.js => IDrawerPropTypes.js} (66%) diff --git a/build/config.js b/build/config.js index f404819ae..47d935825 100644 --- a/build/config.js +++ b/build/config.js @@ -1,5 +1,5 @@ module.exports = { dev: { - componentName: 'divider', // dev components + componentName: 'drawer', // dev components }, }; diff --git a/components/drawer/__tests__/__snapshots__/Drawer.test.js.snap b/components/drawer/__tests__/__snapshots__/Drawer.test.js.snap index 356cff59b..0a6a84cf3 100644 --- a/components/drawer/__tests__/__snapshots__/Drawer.test.js.snap +++ b/components/drawer/__tests__/__snapshots__/Drawer.test.js.snap @@ -1,12 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Drawer class is test_drawer 1`] = ` -
-
+
+
-
+
@@ -19,12 +19,12 @@ exports[`Drawer class is test_drawer 1`] = ` `; exports[`Drawer closable is false 1`] = ` -
-
+
+
-
+
Here is content of Drawer
@@ -34,12 +34,12 @@ exports[`Drawer closable is false 1`] = ` `; exports[`Drawer destroyOnClose is true 1`] = ` -
-
+
+
-
+
Here is content of Drawer
@@ -49,12 +49,12 @@ exports[`Drawer destroyOnClose is true 1`] = ` `; exports[`Drawer have a title 1`] = ` -
-
+
+
-
+
Test Title
@@ -87,8 +87,8 @@ exports[`Drawer render correctly 1`] = ` `; exports[`Drawer render top drawer 1`] = ` -
-
+
+
diff --git a/components/drawer/__tests__/__snapshots__/DrawerEvent.test.js.snap b/components/drawer/__tests__/__snapshots__/DrawerEvent.test.js.snap index c42b044f9..e95ed2aa6 100644 --- a/components/drawer/__tests__/__snapshots__/DrawerEvent.test.js.snap +++ b/components/drawer/__tests__/__snapshots__/DrawerEvent.test.js.snap @@ -1,13 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Drawer render correctly 1`] = ` -
-
-
+
+
+
-
+
diff --git a/components/drawer/__tests__/__snapshots__/demo.test.js.snap b/components/drawer/__tests__/__snapshots__/demo.test.js.snap index 947218b7d..b20622905 100644 --- a/components/drawer/__tests__/__snapshots__/demo.test.js.snap +++ b/components/drawer/__tests__/__snapshots__/demo.test.js.snap @@ -28,6 +28,30 @@ exports[`renders ./components/drawer/demo/placement.md correctly 1`] = `
`; +exports[`renders ./components/drawer/demo/render-in-current.md correctly 1`] = ` +
+ Render in this +
+
+
+
+
+
+
+
+
Basic Drawer
+
+
+

Some contents...

+
+
+
+
+
+
+
+`; + exports[`renders ./components/drawer/demo/user-profile.md correctly 1`] = `
@@ -38,7 +62,7 @@ exports[`renders ./components/drawer/demo/user-profile.md correctly 1`] = `

Lily

-
Progresser AFX
+
Progresser XTech
    @@ -50,7 +74,7 @@ exports[`renders ./components/drawer/demo/user-profile.md correctly 1`] = `

    Lily

    -
    Progresser AFX
    +
    Progresser XTech
    diff --git a/components/drawer/demo/basic-right.md b/components/drawer/demo/basic-right.md index b77766262..3269607da 100644 --- a/components/drawer/demo/basic-right.md +++ b/components/drawer/demo/basic-right.md @@ -20,6 +20,8 @@ Basic drawer. :closable="false" @close="onClose" :visible="visible" + :maskClosable="false" + :afterVisibleChange="afterVisibleChange" >

    Some contents...

    Some contents...

    @@ -35,6 +37,9 @@ Basic drawer. }; }, methods: { + afterVisibleChange(val) { + console.log('visible', val) + }, showDrawer() { this.visible = true; }, diff --git a/components/drawer/demo/form-in-drawer.md b/components/drawer/demo/form-in-drawer.md index bbc723c83..b37ba2149 100644 --- a/components/drawer/demo/form-in-drawer.md +++ b/components/drawer/demo/form-in-drawer.md @@ -17,7 +17,7 @@ Use form in drawer with submit button. :width="720" @close="onClose" :visible="visible" - :wrapStyle="{height: 'calc(100% - 108px)',overflow: 'auto',paddingBottom: '108px'}" + :bodyStyle="{paddingBottom: '80px'}" > @@ -116,13 +116,14 @@ Use form in drawer with submit button.
    diff --git a/components/drawer/demo/index.vue b/components/drawer/demo/index.vue index 515dbc2d9..00a177c1a 100644 --- a/components/drawer/demo/index.vue +++ b/components/drawer/demo/index.vue @@ -4,6 +4,7 @@ import Placement from './placement'; import UserProfile from './user-profile'; import MultiLevelDrawer from './multi-level-drawer'; import FormInDrawer from './form-in-drawer'; +import RenderInCurrent from './render-in-current'; // import CustomPaging from './customPaging' // import CustomArrows from './customArrows' @@ -20,21 +21,21 @@ const md = { 抽屉从父窗体边缘滑入,覆盖住部分父窗体内容。用户在抽屉内操作时不必离开当前任务,操作完成后,可以平滑地回到到原任务。 -* 当需要一个附加的面板来控制父窗体内容,这个面板在需要时呼出。比如,控制界面展示样式,往界面中添加内容。 -* 当需要在当前任务流中插入临时任务,创建或预览附加内容。比如展示协议条款,创建子对象。 +- 当需要一个附加的面板来控制父窗体内容,这个面板在需要时呼出。比如,控制界面展示样式,往界面中添加内容。 +- 当需要在当前任务流中插入临时任务,创建或预览附加内容。比如展示协议条款,创建子对象。 ## 代码演示 `, us: `# Drawer - Panel slides from screen edge. + A panel which slides in from the edge of the screen. ## When To Use -A Drawer is a panel that is typically overlaid on top of a page and slides in from the side. It contains a set of information or actions. Since that user can interact with the Drawer without leaving the current page, tasks can be achieved more efficient within the same context. +A Drawer is a panel that is typically overlaid on top of a page and slides in from the side. It contains a set of information or actions. Since the user can interact with the Drawer without leaving the current page, tasks can be achieved more efficiently within the same context. -* Use a Form to create or edit a set of information. -* Processing subtasks. When subtasks are too heavy for Popover and we still want to keep the subtasks in the context of the main task, Drawer comes very handy. -* When a same Form is needed in multiple places. +- Use a Form to create or edit a set of information. +- Processing subtasks. When subtasks are too heavy for a Popover and we still want to keep the subtasks in the context of the main task, Drawer comes very handy. +- When the same Form is needed in multiple places. ## Examples `, }; @@ -53,6 +54,7 @@ export default { + diff --git a/components/drawer/demo/multi-level-drawer.md b/components/drawer/demo/multi-level-drawer.md index 35b74b1e9..b0e0c6f68 100644 --- a/components/drawer/demo/multi-level-drawer.md +++ b/components/drawer/demo/multi-level-drawer.md @@ -5,7 +5,7 @@ #### Multi-level drawer -Open a new drawer on top of an existing drawer to handle multi branch tasks +Open a new drawer on top of an existing drawer to handle multi branch tasks. ```tpl diff --git a/components/drawer/demo/placement.md b/components/drawer/demo/placement.md index 85a33a94e..e1beb355e 100644 --- a/components/drawer/demo/placement.md +++ b/components/drawer/demo/placement.md @@ -5,7 +5,7 @@ #### Custom Placement -Basic drawer. +The Drawer can appear from any edge of the screen. ```tpl diff --git a/components/drawer/demo/render-in-current.md b/components/drawer/demo/render-in-current.md new file mode 100644 index 000000000..fab4fb308 --- /dev/null +++ b/components/drawer/demo/render-in-current.md @@ -0,0 +1,64 @@ + +#### 渲染在当前 DOM +渲染在当前 dom 里。自定义容器,查看 getContainer。 + + + +#### Render in current dom +Render in current dom. custom container, check getContainer. + + +```tpl + + +``` diff --git a/components/drawer/demo/user-profile.md b/components/drawer/demo/user-profile.md index a0670e5fd..d83373617 100644 --- a/components/drawer/demo/user-profile.md +++ b/components/drawer/demo/user-profile.md @@ -5,7 +5,7 @@ #### Preview drawer -Use when you need to quickly preview the outline of the object. Such as list item preview. +Use Drawer to quickly preview details of an object, such as those in a list. ```tpl @@ -22,9 +22,9 @@ Use when you need to quickly preview the outline of the object. Such as list ite ]" bordered > - + View Profile - + {{item.name}} - + diff --git a/components/drawer/index.en-US.md b/components/drawer/index.en-US.md index 8d758f208..bcc37ac38 100644 --- a/components/drawer/index.en-US.md +++ b/components/drawer/index.en-US.md @@ -13,12 +13,15 @@ | wrapClassName | The class name of the container of the Drawer dialog. | string | - | | | wrapStyle | Style of wrapper element which **contains mask** compare to `drawerStyle` | object | - | | | drawerStyle | Style of the popup layer element | object | - | 1.4.11 | +| headerStyle | Style of the drawer header part | object | - | 1.5.0 | | bodyStyle | Style of the drawer content part | object | - | | | width | Width of the Drawer dialog. | string\|number | 256 | | | height | placement is `top` or `bottom`, height of the Drawer dialog. | string\|number | - | | | zIndex | The `z-index` of the Drawer. | Number | 1000 | | | placement | The placement of the Drawer. | 'top' \| 'right' \| 'bottom' \| 'left' | 'right' | | | handle | After setting, the drawer is directly mounted on the DOM, and you can control the drawer to open or close through this `handle`. | VNode \| slot | - | | +| afterVisibleChange | Callback after the animation ends when switching drawers. | function(visible) | - | 1.5.0 | +| keyboard | Whether support press esc to close | Boolean | true | 1.5.0 | ## Methods diff --git a/components/drawer/index.jsx b/components/drawer/index.jsx index 80277b7b5..5f6a2d0b0 100644 --- a/components/drawer/index.jsx +++ b/components/drawer/index.jsx @@ -1,4 +1,5 @@ import classnames from 'classnames'; +import omit from 'omit.js'; import VcDrawer from '../vc-drawer/src'; import PropTypes from '../_util/vue-types'; import BaseMixin from '../_util/BaseMixin'; @@ -18,6 +19,7 @@ const Drawer = { maskStyle: PropTypes.object, wrapStyle: PropTypes.object, bodyStyle: PropTypes.object, + headerStyle: PropTypes.object, drawerStyle: PropTypes.object, title: PropTypes.any, visible: PropTypes.bool, @@ -29,6 +31,8 @@ const Drawer = { level: PropTypes.any.def(null), wrapClassName: PropTypes.string, // not use class like react, vue will add class to root dom handle: PropTypes.any, + afterVisibleChange: PropTypes.func, + keyboard: PropTypes.bool.def(true), }, mixins: [BaseMixin], data() { @@ -49,6 +53,14 @@ const Drawer = { parentDrawer: this, }; }, + mounted() { + // fix: delete drawer in child and re-render, no push started. + // {show && } + const { visible } = this; + if (visible && this.parentDrawer) { + this.parentDrawer.push(); + } + }, updated() { this.$nextTick(() => { if (this.preVisible !== this.visible && this.parentDrawer) { @@ -61,19 +73,23 @@ const Drawer = { this.preVisible = this.visible; }); }, + beforeDestroy() { + // unmount drawer in child, clear push. + if (this.parentDrawer) { + this.parentDrawer.pull(); + this.parentDrawer = null; + } + }, methods: { close(e) { - if (this.visible !== undefined) { - this.$emit('close', e); - return; - } - }, - onMaskClick(e) { - if (!this.maskClosable) { - return; - } - this.close(e); + this.$emit('close', e); }, + // onMaskClick(e) { + // if (!this.maskClosable) { + // return; + // } + // this.close(e); + // }, push() { this.setState({ _push: true, @@ -117,7 +133,7 @@ const Drawer = { }; }, renderHeader(prefixCls) { - const { closable } = this.$props; + const { closable, headerStyle } = this.$props; const title = getComponentFromProp(this, 'title'); if (!title && !closable) { return null; @@ -125,17 +141,20 @@ const Drawer = { const headerClassName = title ? `${prefixCls}-header` : `${prefixCls}-header-no-title`; return ( -
    +
    {title &&
    {title}
    } {closable ? this.renderCloseIcon(prefixCls) : null}
    ); }, renderCloseIcon(prefixCls) { + const { closable } = this; return ( - + closable && ( + + ) ); }, // render drawer body dom @@ -144,15 +163,9 @@ const Drawer = { return null; } this.destroyClose = false; - const { placement, bodyStyle, drawerStyle } = this.$props; + const { bodyStyle, drawerStyle } = this.$props; - const containerStyle = - placement === 'left' || placement === 'right' - ? { - overflow: 'auto', - height: '100%', - } - : {}; + const containerStyle = {}; const isDestroyOnClose = this.getDestroyOnClose(); if (isDestroyOnClose) { @@ -184,9 +197,10 @@ const Drawer = { visible, placement, wrapClassName, + mask, ...rest } = props; - const haveMask = rest.mask ? '' : 'no-mask'; + const haveMask = mask ? '' : 'no-mask'; const offsetStyle = {}; if (placement === 'left' || placement === 'right') { offsetStyle.width = typeof width === 'number' ? `${width}px` : width; @@ -199,12 +213,28 @@ const Drawer = { const vcDrawerProps = { props: { - ...rest, + ...omit(rest, [ + 'closable', + 'destroyOnClose', + 'drawerStyle', + 'headerStyle', + 'bodyStyle', + 'title', + 'push', + 'visible', + 'getPopupContainer', + 'rootPrefixCls', + 'getPrefixCls', + 'renderEmpty', + 'csp', + 'pageHeader', + 'autoInsertSpaceInButton', + ]), handler, ...offsetStyle, prefixCls, open: visible, - showMask: props.mask, + showMask: mask, placement, className: classnames({ [wrapClassName]: !!wrapClassName, @@ -213,7 +243,6 @@ const Drawer = { wrapStyle: this.getRcDrawerStyle(), }, on: { - maskClick: this.onMaskClick, ...getListeners(this), }, }; diff --git a/components/drawer/index.zh-CN.md b/components/drawer/index.zh-CN.md index 7219675ae..5f6cbc8d5 100644 --- a/components/drawer/index.zh-CN.md +++ b/components/drawer/index.zh-CN.md @@ -13,12 +13,15 @@ | wrapClassName | 对话框外层容器的类名 | string | - | | | wrapStyle | 可用于设置 Drawer 最外层容器的样式,和 `drawerStyle` 的区别是作用节点包括 `mask` | object | - | | | drawerStyle | 用于设置 Drawer 弹出层的样式 | object | - | 1.4.11 | +| headerStyle | 用于设置 Drawer 头部的样式 | object | - | 1.5.0 | | bodyStyle | 可用于设置 Drawer 内容部分的样式 | object | - | | | width | 宽度 | string \| number | 256 | | | height | 高度, 在 `placement` 为 `top` 或 `bottom` 时使用 | string \| number | 256 | | | zIndex | 设置 Drawer 的 `z-index` | Number | 1000 | | | placement | 抽屉的方向 | 'top' \| 'right' \| 'bottom' \| 'left' | 'right' | | | handle | 设置后抽屉直接挂载到 DOM 上,你可以通过该 handle 控制抽屉打开关闭 | VNode \| slot | - | | +| afterVisibleChange | 切换抽屉时动画结束后的回调 | function(visible) | 无 | 1.5.0 | +| keyboard | 是否支持键盘 esc 关闭 | boolean | true | 1.5.0 | ## 方法 diff --git a/components/vc-drawer/src/Drawer.js b/components/vc-drawer/src/Drawer.js index 6743b3b99..bb30a7e54 100644 --- a/components/vc-drawer/src/Drawer.js +++ b/components/vc-drawer/src/Drawer.js @@ -6,7 +6,8 @@ import { initDefaultProps, getEvents, getListeners } from '../../_util/props-uti import { cloneElement } from '../../_util/vnode'; import ContainerRender from '../../_util/ContainerRender'; import getScrollBarSize from '../../_util/getScrollBarSize'; -import drawerProps from './drawerProps'; +import { IDrawerProps } from './IDrawerPropTypes'; +import KeyCode from '../../_util/KeyCode'; import { dataToArray, transitionEnd, @@ -29,7 +30,7 @@ const windowIsUndefined = !( Vue.use(ref, { name: 'ant-ref' }); const Drawer = { mixins: [BaseMixin], - props: initDefaultProps(drawerProps, { + props: initDefaultProps(IDrawerProps, { prefixCls: 'drawer', placement: 'left', getContainer: 'body', @@ -151,8 +152,14 @@ const Drawer = { } }, methods: { + onKeyDown(e) { + if (e.keyCode === KeyCode.ESC) { + e.stopPropagation(); + this.$emit('close', e); + } + }, onMaskTouchEnd(e) { - this.$emit('maskClick', e); + this.$emit('close', e); this.onTouchEnd(e, true); }, onIconTouchEnd(e) { @@ -170,15 +177,19 @@ const Drawer = { }); }, onWrapperTransitionEnd(e) { - if (e.target === this.contentWrapper) { + if (e.target === this.contentWrapper && e.propertyName.match(/transform$/)) { + const open = this.getOpen(); this.dom.style.transition = ''; - if (!this.sOpen && this.getCurrentDrawerSome()) { + if (!open && this.getCurrentDrawerSome()) { document.body.style.overflowX = ''; if (this.maskDom) { this.maskDom.style.left = ''; this.maskDom.style.width = ''; } } + if (this.afterVisibleChange) { + this.afterVisibleChange(!!open); + } } }, getDefault(props) { @@ -380,12 +391,15 @@ const Drawer = { width, height, wrapStyle, + keyboard, + maskClosable, } = this.$props; const children = this.$slots.default; const wrapperClassname = classnames(prefixCls, { [`${prefixCls}-${placement}`]: true, [`${prefixCls}-open`]: open, [className]: !!className, + 'no-mask': !showMask, }); const isOpenChange = this.isOpenChange; const isHorizontal = placement === 'left' || placement === 'right'; @@ -428,7 +442,6 @@ const Drawer = { ], }); } - const domContProps = { class: wrapperClassname, directives: [ @@ -441,6 +454,7 @@ const Drawer = { ], on: { transitionend: this.onWrapperTransitionEnd, + keydown: open && keyboard ? this.onKeyDown : noop, }, style: wrapStyle, }; @@ -469,11 +483,11 @@ const Drawer = { }, ]; return ( -
    +
    {showMask && (
    @@ -610,7 +624,7 @@ const Drawer = { }, ]; return ( -
    +
    {children}
    ); diff --git a/components/vc-drawer/src/drawerProps.js b/components/vc-drawer/src/IDrawerPropTypes.js similarity index 66% rename from components/vc-drawer/src/drawerProps.js rename to components/vc-drawer/src/IDrawerPropTypes.js index 28fedb2b8..6e0cc4bb1 100644 --- a/components/vc-drawer/src/drawerProps.js +++ b/components/vc-drawer/src/IDrawerPropTypes.js @@ -1,7 +1,6 @@ import PropTypes from '../../_util/vue-types'; -export default { - wrapperClassName: PropTypes.string, +const IProps = { width: PropTypes.any, height: PropTypes.any, defaultOpen: PropTypes.bool, @@ -13,15 +12,33 @@ export default { levelMove: PropTypes.oneOfType([PropTypes.number, PropTypes.func, PropTypes.array]), ease: PropTypes.string, duration: PropTypes.string, + handler: PropTypes.any, + showMask: PropTypes.bool, + maskStyle: PropTypes.object, + className: PropTypes.string, + wrapStyle: PropTypes.object, + maskClosable: PropTypes.bool, + afterVisibleChange: PropTypes.func, + keyboard: PropTypes.bool, +}; + +const IDrawerProps = { + ...IProps, + wrapperClassName: PropTypes.string, + forceRender: PropTypes.bool, getContainer: PropTypes.oneOfType([ PropTypes.string, PropTypes.func, PropTypes.object, PropTypes.bool, ]), - handler: PropTypes.any, - showMask: PropTypes.bool, - maskStyle: PropTypes.object, - className: PropTypes.string, - wrapStyle: PropTypes.object, }; + +const IDrawerChildProps = { + ...IProps, + getContainer: PropTypes.func, + getOpenCount: PropTypes.func, + switchScrollingEffect: PropTypes.func, +}; + +export { IDrawerProps, IDrawerChildProps }; diff --git a/components/vc-drawer/src/utils.js b/components/vc-drawer/src/utils.js index cf612ea34..7da5b91e3 100644 --- a/components/vc-drawer/src/utils.js +++ b/components/vc-drawer/src/utils.js @@ -54,3 +54,48 @@ export function transformArguments(arg, cb) { export const isNumeric = value => { return !isNaN(parseFloat(value)) && isFinite(value); // eslint-disable-line }; + +export const windowIsUndefined = !( + typeof window !== 'undefined' && + window.document && + window.document.createElement +); + +export const getTouchParentScroll = (root, currentTarget, differX, differY) => { + if (!currentTarget || currentTarget === document || currentTarget instanceof Document) { + return false; + } + // root 为 drawer-content 设定了 overflow, 判断为 root 的 parent 时结束滚动; + if (currentTarget === root.parentNode) { + return true; + } + + const isY = Math.max(Math.abs(differX), Math.abs(differY)) === Math.abs(differY); + const isX = Math.max(Math.abs(differX), Math.abs(differY)) === Math.abs(differX); + + const scrollY = currentTarget.scrollHeight - currentTarget.clientHeight; + const scrollX = currentTarget.scrollWidth - currentTarget.clientWidth; + + const style = document.defaultView.getComputedStyle(currentTarget); + const overflowY = style.overflowY === 'auto' || style.overflowY === 'scroll'; + const overflowX = style.overflowX === 'auto' || style.overflowX === 'scroll'; + + const y = scrollY && overflowY; + const x = scrollX && overflowX; + + if ( + (isY && + (!y || + (y && + ((currentTarget.scrollTop >= scrollY && differY < 0) || + (currentTarget.scrollTop <= 0 && differY > 0))))) || + (isX && + (!x || + (x && + ((currentTarget.scrollLeft >= scrollX && scrollX < 0) || + (currentTarget.scrollLeft <= 0 && scrollX > 0))))) + ) { + return getTouchParentScroll(root, currentTarget.parentNode, differX, differY); + } + return false; +}; diff --git a/types/drawer.d.ts b/types/drawer.d.ts index fb78ebd2d..3bf9e0ba9 100644 --- a/types/drawer.d.ts +++ b/types/drawer.d.ts @@ -83,7 +83,7 @@ export declare class Drawer extends AntdComponent { * @type object */ bodyStyle: object; - + headerStyle: object; /** * Width of the Drawer dialog. * @default 256 @@ -110,7 +110,8 @@ export declare class Drawer extends AntdComponent { * @type string */ placement: 'top' | 'right' | 'bottom' | 'left'; - + afterVisibleChange?: (visible: boolean) => void; + keyboard?: boolean; /** * Specify a callback that will be called when a user clicks mask, close button or Cancel button. */