feat: update drawer (#2324)

pull/2351/head
zdw 2020-06-02 22:09:34 +08:00 committed by GitHub
parent 3c292a51e1
commit 3642b09db8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 259 additions and 161 deletions

View File

@ -0,0 +1,30 @@
import { nextTick } from 'vue';
const antvRef = {
beforeMount: function bind(el, binding, vnode) {
nextTick(function() {
binding.value(el, vnode.key);
});
binding.value(el, vnode.key);
},
updated: function update(el, binding, vnode, oldVnode) {
if (oldVnode && oldVnode.directives) {
let oldBinding = oldVnode.directives.find(function(directive) {
return directive === antvRef;
});
if (oldBinding && oldBinding.value !== binding.value) {
oldBinding && oldBinding.value(null, oldVnode.key);
binding.value(el, vnode.key);
return;
}
}
// Should not have this situation
if (vnode.el !== oldVnode.el) {
binding.value(el, vnode.key);
}
},
unmounted: function unbind(el, binding, vnode) {
binding.value(null, vnode.key);
},
};
export default antvRef;

View File

@ -203,12 +203,20 @@ const getKey = ele => {
export function getEvents(child) { export function getEvents(child) {
let events = {}; let events = {};
if (child.componentOptions && child.componentOptions.listeners) { for (let key in child) {
events = child.componentOptions.listeners; if (/^on/.test(key)) {
} else if (child.data && child.data.on) { key = key.toLowerCase();
events = child.data.on; events[key] = child[key];
}
} }
return { ...events }; return events;
// let events = {};
// if (child.componentOptions && child.componentOptions.listeners) {
// events = child.componentOptions.listeners;
// } else if (child.data && child.data.on) {
// events = child.data.on;
// }
// return { ...events };
} }
// 获取 xxx.native 或者 原生标签 事件 // 获取 xxx.native 或者 原生标签 事件

View File

@ -1,3 +1,4 @@
import { inject, provide, nextTick } from 'vue';
import classnames from 'classnames'; import classnames from 'classnames';
import omit from 'omit.js'; import omit from 'omit.js';
import VcDrawer from '../vc-drawer/src'; import VcDrawer from '../vc-drawer/src';
@ -42,17 +43,28 @@ const Drawer = {
_push: false, _push: false,
}; };
}, },
inject: { setup() {
parentDrawer: { const configProvider = inject('configProvider', ConfigConsumerProps);
default: () => null,
},
configProvider: { default: () => ConfigConsumerProps },
},
provide() {
return { return {
parentDrawer: this, configProvider,
}; };
}, },
// inject: {
// parentDrawer: {
// default: () => null,
// },
// configProvider: { default: () => ConfigConsumerProps },
// },
// provide() {
// return {
// parentDrawer: this,
// };
// },
beforeCreate() {
const parentDrawer = inject('parentDrawer', null);
provide('parentDrawer', this);
this.parentDrawer = parentDrawer;
},
mounted() { mounted() {
// fix: delete drawer in child and re-render, no push started. // fix: delete drawer in child and re-render, no push started.
// <Drawer>{show && <Drawer />}</Drawer> // <Drawer>{show && <Drawer />}</Drawer>
@ -62,7 +74,7 @@ const Drawer = {
} }
}, },
updated() { updated() {
this.$nextTick(() => { nextTick(() => {
if (this.preVisible !== this.visible && this.parentDrawer) { if (this.preVisible !== this.visible && this.parentDrawer) {
if (this.visible) { if (this.visible) {
this.parentDrawer.push(); this.parentDrawer.push();
@ -133,7 +145,9 @@ const Drawer = {
}, },
renderHeader(prefixCls) { renderHeader(prefixCls) {
const { closable, headerStyle } = this.$props; const { closable, headerStyle } = this.$props;
const title = getComponentFromProp(this, 'title'); // TODO
// const title = getComponentFromProp(this, 'title');
const title = null;
if (!title && !closable) { if (!title && !closable) {
return null; return null;
} }
@ -181,7 +195,7 @@ const Drawer = {
> >
{this.renderHeader(prefixCls)} {this.renderHeader(prefixCls)}
<div key="body" class={`${prefixCls}-body`} style={bodyStyle}> <div key="body" class={`${prefixCls}-body`} style={bodyStyle}>
{this.$slots.default} {this.$slots.default && this.$slots.default()}
</div> </div>
</div> </div>
); );
@ -206,53 +220,59 @@ const Drawer = {
} else { } else {
offsetStyle.height = typeof height === 'number' ? `${height}px` : height; offsetStyle.height = typeof height === 'number' ? `${height}px` : height;
} }
const handler = getComponentFromProp(this, 'handle') || false; // TODO
// const handler = getComponentFromProp(this, 'handle') || false;
const handler = false;
const getPrefixCls = this.configProvider.getPrefixCls; const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('drawer', customizePrefixCls); const prefixCls = getPrefixCls('drawer', customizePrefixCls);
const vcDrawerProps = { const vcDrawerProps = {
props: { ...omit(rest, [
...omit(rest, [ 'closable',
'closable', 'destroyOnClose',
'destroyOnClose', 'drawerStyle',
'drawerStyle', 'headerStyle',
'headerStyle', 'bodyStyle',
'bodyStyle', 'title',
'title', 'push',
'push', 'visible',
'visible', 'getPopupContainer',
'getPopupContainer', 'rootPrefixCls',
'rootPrefixCls', 'getPrefixCls',
'getPrefixCls', 'renderEmpty',
'renderEmpty', 'csp',
'csp', 'pageHeader',
'pageHeader', 'autoInsertSpaceInButton',
'autoInsertSpaceInButton', ]),
]), handler,
handler, ...offsetStyle,
...offsetStyle, prefixCls,
prefixCls, open: visible,
open: visible, showMask: mask,
showMask: mask, placement,
placement, className: classnames({
className: classnames({ [wrapClassName]: !!wrapClassName,
[wrapClassName]: !!wrapClassName, [haveMask]: !!haveMask,
[haveMask]: !!haveMask, }),
}), wrapStyle: this.getRcDrawerStyle(),
wrapStyle: this.getRcDrawerStyle(),
},
on: {
...getListeners(this),
},
}; };
return <VcDrawer {...vcDrawerProps}>{this.renderBody(prefixCls)}</VcDrawer>; return (
<VcDrawer
{...vcDrawerProps}
on={{
...getListeners(this), //TODO
}}
>
{this.renderBody(prefixCls)}
</VcDrawer>
);
}, },
}; };
/* istanbul ignore next */ /* istanbul ignore next */
Drawer.install = function(Vue) { Drawer.install = function(app) {
Vue.use(Base); app.use(Base);
Vue.component(Drawer.name, Drawer); app.component(Drawer.name, Drawer);
}; };
export default Drawer; export default Drawer;

View File

@ -1,9 +1,9 @@
import classnames from 'classnames'; import classnames from 'classnames';
import Vue from 'vue'; import { cloneVNode, withDirectives, Teleport, nextTick } from 'vue';
import ref from 'vue-ref'; import antRef from '../../_util/ant-ref';
import BaseMixin from '../../_util/BaseMixin'; import BaseMixin from '../../_util/BaseMixin';
import { initDefaultProps, getEvents, getListeners } from '../../_util/props-util'; import { initDefaultProps, getEvents, getListeners, getSlot } from '../../_util/props-util';
import { cloneElement } from '../../_util/vnode'; // import { cloneElement } from '../../_util/vnode';
import getScrollBarSize from '../../_util/getScrollBarSize'; import getScrollBarSize from '../../_util/getScrollBarSize';
import { IDrawerProps } from './IDrawerPropTypes'; import { IDrawerProps } from './IDrawerPropTypes';
import KeyCode from '../../_util/KeyCode'; import KeyCode from '../../_util/KeyCode';
@ -16,7 +16,7 @@ import {
transformArguments, transformArguments,
isNumeric, isNumeric,
} from './utils'; } from './utils';
import Portal from '../../_util/Portal'; // import Portal from '../../_util/Portal';
function noop() {} function noop() {}
@ -27,9 +27,10 @@ const windowIsUndefined = !(
window.document.createElement window.document.createElement
); );
Vue.use(ref, { name: 'ant-ref' }); // Vue.use(ref, { name: 'ant-ref' });
const Drawer = { const Drawer = {
mixins: [BaseMixin], mixins: [BaseMixin],
directives: { 'ant-ref': antRef },
props: initDefaultProps(IDrawerProps, { props: initDefaultProps(IDrawerProps, {
prefixCls: 'drawer', prefixCls: 'drawer',
placement: 'left', placement: 'left',
@ -65,7 +66,7 @@ const Drawer = {
}; };
}, },
mounted() { mounted() {
this.$nextTick(() => { nextTick(() => {
if (!windowIsUndefined) { if (!windowIsUndefined) {
let passiveSupported = false; let passiveSupported = false;
window.addEventListener( window.addEventListener(
@ -119,7 +120,7 @@ const Drawer = {
}, },
}, },
updated() { updated() {
this.$nextTick(() => { nextTick(() => {
// dom 没渲染时,重走一遍。 // dom 没渲染时,重走一遍。
if (!this.sFirstEnter && this.container) { if (!this.sFirstEnter && this.container) {
this.$forceUpdate(); this.$forceUpdate();
@ -362,7 +363,9 @@ const Drawer = {
} }
} }
} }
const { change } = getListeners(this); // TODO
// const { change } = getListeners(this);
const change = false;
if (change && this.isOpenChange && this.sFirstEnter) { if (change && this.isOpenChange && this.sFirstEnter) {
change(open); change(open);
this.isOpenChange = false; this.isOpenChange = false;
@ -382,7 +385,7 @@ const Drawer = {
keyboard, keyboard,
maskClosable, maskClosable,
} = this.$props; } = this.$props;
const children = this.$slots.default; const children = getSlot(this);
const wrapperClassname = classnames(prefixCls, { const wrapperClassname = classnames(prefixCls, {
[`${prefixCls}-${placement}`]: true, [`${prefixCls}-${placement}`]: true,
[`${prefixCls}-open`]: open, [`${prefixCls}-open`]: open,
@ -411,73 +414,80 @@ const Drawer = {
</div> </div>
); );
const { handler: handlerSlot } = this; const { handler: handlerSlot } = this;
const handlerSlotVnode = (handlerSlot && handlerSlot[0]) || handlerDefalut; const handlerSlotVnode = handlerSlot || handlerDefalut;
const { click: handleIconClick } = getEvents(handlerSlotVnode); const { onclick: handleIconClick } = getEvents(handlerSlotVnode);
handlerChildren = cloneElement(handlerSlotVnode, { handlerChildren = withDirectives(
on: { cloneVNode(handlerSlotVnode, {
click: e => { onClick: e => {
handleIconClick && handleIconClick(); handleIconClick && handleIconClick();
this.onIconTouchEnd(e); this.onIconTouchEnd(e);
}, },
}, }),
directives: [ [
{ [
name: 'ant-ref', antRef, //directive
value: c => { c => {
this.handlerdom = c; this.handlerdom = c;
}, }, // value
}, ],
], ],
}); );
} }
const domContProps = { const domContProps = {
class: wrapperClassname, class: wrapperClassname,
directives: [ // directives: [
{ // {
name: 'ant-ref', // name: 'ant-ref',
value: c => { // value: c => {
this.dom = c; // this.dom = c;
}, // },
}, // },
], // ],
on: { onTransitionend: this.onWrapperTransitionEnd,
transitionend: this.onWrapperTransitionEnd, onKeydown: open && keyboard ? this.onKeyDown : noop,
keydown: open && keyboard ? this.onKeyDown : noop,
},
style: wrapStyle, style: wrapStyle,
}; };
const directivesMaskDom = [ // const directivesMaskDom = [
{ // {
name: 'ant-ref', // name: 'ant-ref',
value: c => { // value: c => {
this.maskDom = c; // this.maskDom = c;
}, // },
}, // },
]; // ];
const directivesContentWrapper = [ // const directivesContentWrapper = [
{ // {
name: 'ant-ref', // name: 'ant-ref',
value: c => { // value: c => {
this.contentWrapper = c; // this.contentWrapper = c;
}, // },
}, // },
]; // ];
const directivesContentDom = [ // const directivesContentDom = [
{ // {
name: 'ant-ref', // name: 'ant-ref',
value: c => { // value: c => {
this.contentDom = c; // this.contentDom = c;
}, // },
}, // },
]; // ];
return ( return (
<div {...domContProps} tabIndex={-1}> <div
v-ant-ref={c => {
this.dom = c;
}}
{...domContProps}
tabIndex={-1}
>
{showMask && ( {showMask && (
<div <div
class={`${prefixCls}-mask`} class={`${prefixCls}-mask`}
onClick={maskClosable ? this.onMaskTouchEnd : noop} onClick={maskClosable ? this.onMaskTouchEnd : noop}
style={maskStyle} style={maskStyle}
{...{ directives: directivesMaskDom }} v-ant-ref={c => {
this.maskDom = c;
}}
// {...{ directives: directivesMaskDom }}
/> />
)} )}
<div <div
@ -488,11 +498,17 @@ const Drawer = {
width: isNumeric(width) ? `${width}px` : width, width: isNumeric(width) ? `${width}px` : width,
height: isNumeric(height) ? `${height}px` : height, height: isNumeric(height) ? `${height}px` : height,
}} }}
{...{ directives: directivesContentWrapper }} v-ant-ref={c => {
this.contentWrapper = c;
}}
// {...{ directives: directivesContentWrapper }}
> >
<div <div
class={`${prefixCls}-content`} class={`${prefixCls}-content`}
{...{ directives: directivesContentDom }} v-ant-ref={c => {
this.contentDom = c;
}}
// {...{ directives: directivesContentDom }}
onTouchstart={open ? this.removeStartHandler : noop} // 跑用例用 onTouchstart={open ? this.removeStartHandler : noop} // 跑用例用
onTouchmove={open ? this.removeMoveHandler : noop} // 跑用例用 onTouchmove={open ? this.removeMoveHandler : noop} // 跑用例用
> >
@ -604,16 +620,21 @@ const Drawer = {
currentDrawer[this.drawerId] = open ? this.container : open; currentDrawer[this.drawerId] = open ? this.container : open;
const children = this.getChildToRender(this.sFirstEnter ? open : false); const children = this.getChildToRender(this.sFirstEnter ? open : false);
if (!getContainer) { if (!getContainer) {
const directives = [ // const directives = [
{ // {
name: 'ant-ref', // name: 'ant-ref',
value: c => { // value: c => {
this.container = c; // this.container = c;
}, // },
}, // },
]; // ];
return ( return (
<div class={wrapperClassName} {...{ directives }}> <div
class={wrapperClassName}
v-ant-ref={c => {
this.container = c;
}}
>
{children} {children}
</div> </div>
); );
@ -624,7 +645,7 @@ const Drawer = {
// 如果有 handler 为内置强制渲染; // 如果有 handler 为内置强制渲染;
const $forceRender = !!handler || forceRender; const $forceRender = !!handler || forceRender;
if ($forceRender || open || this.dom) { if ($forceRender || open || this.dom) {
portal = <Portal getContainer={this.getSelfContainer} children={children}></Portal>; portal = <Teleport to={this.getSelfContainer()}>{children}</Teleport>;
} }
return portal; return portal;
}, },

View File

@ -1,47 +1,63 @@
<template> <template>
<a-config-provider> <div>
<a-button-group class="dddd" style="color: red" @click="onClick"> <a-button type="primary" @click="showDrawer">Open</a-button>
<a-button>Cancel</a-button> <a-drawer
<a-button type="primary"> title="Multi-level drawer"
OK width="520"
</a-button> :closable="false"
</a-button-group> :visible="visible"
<a-button @close="onClose"
:class="['test']"
class="aaa"
style="display: inline"
block
:type="type"
@click="onClick"
> >
Primary <a-button type="primary" @click="showChildrenDrawer">Two-level drawer</a-button>
</a-button> <a-drawer
<a-button block> title="Two-level Drawer"
Default width="320"
</a-button> :closable="false"
<a-button type="dashed" block> :visible="childrenDrawer"
Dashed @close="onChildrenDrawerClose"
</a-button> >
<a-button type="danger" block> <a-button type="primary" @click="showChildrenDrawer">This is two-level drawer</a-button>
Danger </a-drawer>
</a-button> <div
<a-button type="link" block> :style="{
Link position: 'absolute',
</a-button> bottom: 0,
</a-config-provider> width: '100%',
borderTop: '1px solid #e8e8e8',
padding: '10px 16px',
textAlign: 'right',
left: 0,
background: '#fff',
borderRadius: '0 0 4px 4px',
boxSizing: 'border-box',
}"
>
<a-button style="marginRight: 8px" @click="onClose">Cancel</a-button>
<a-button type="primary" @click="onClose">Submit</a-button>
</div>
</a-drawer>
</div>
</template> </template>
<script> <script>
export default { export default {
name: 'Demo',
data() { data() {
return { return {
type: 'primary', visible: false,
childrenDrawer: false,
}; };
}, },
methods: { methods: {
onClick() { showDrawer() {
console.log(1); this.visible = true;
},
onClose() {
this.visible = false;
},
showChildrenDrawer() {
this.childrenDrawer = true;
},
onChildrenDrawerClose() {
this.childrenDrawer = false;
}, },
}, },
}; };

View File

@ -2,10 +2,13 @@ import '@babel/polyfill';
import { createApp } from 'vue'; import { createApp } from 'vue';
import App from './App.vue'; import App from './App.vue';
import Button from 'ant-design-vue/button'; import Button from 'ant-design-vue/button';
import Drawer from 'ant-design-vue/drawer';
import ConfigProvider from 'ant-design-vue/config-provider'; import ConfigProvider from 'ant-design-vue/config-provider';
import 'ant-design-vue/button/style/index.less'; import 'ant-design-vue/button/style/index.less';
import 'ant-design-vue/drawer/style/index.less';
createApp(App) createApp(App)
.use(Button) .use(Button)
.use(ConfigProvider) .use(ConfigProvider)
.use(Drawer)
.mount('#app'); .mount('#app');