Skip to content

Commit

Permalink
refactor: portal
Browse files Browse the repository at this point in the history
  • Loading branch information
tangjinzhou committed May 13, 2020
1 parent 8d050f1 commit 529f81c
Show file tree
Hide file tree
Showing 19 changed files with 1,038 additions and 200 deletions.
2 changes: 1 addition & 1 deletion antdv-demo
52 changes: 52 additions & 0 deletions components/_util/Portal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import PropTypes from './vue-types';
import { cloneElement } from './vnode';

export default {
name: 'Portal',
props: {
getContainer: PropTypes.func.isRequired,
children: PropTypes.any.isRequired,
didUpdate: PropTypes.func,
},
mounted() {
this.createContainer();
},
updated() {
const { didUpdate } = this.$props;
if (didUpdate) {
this.$nextTick(() => {
didUpdate(this.$props);
});
}
},

beforeDestroy() {
this.removeContainer();
},
methods: {
createContainer() {
this._container = this.$props.getContainer();
this.$forceUpdate();
},

removeContainer() {
if (this._container) {
this._container.parentNode.removeChild(this._container);
}
},
},

render() {
if (this._container) {
return cloneElement(this.$props.children, {
directives: [
{
name: 'ant-portal',
value: this._container,
},
],
});
}
return null;
},
};
156 changes: 156 additions & 0 deletions components/_util/PortalWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import PropTypes from './vue-types';
import switchScrollingEffect from './switchScrollingEffect';
import setStyle from './setStyle';
import Portal from './Portal';

let openCount = 0;
const windowIsUndefined = !(
typeof window !== 'undefined' &&
window.document &&
window.document.createElement
);
// https://github.com/ant-design/ant-design/issues/19340
// https://github.com/ant-design/ant-design/issues/19332
let cacheOverflow = {};

export default {
name: 'PortalWrapper',
props: {
wrapperClassName: PropTypes.string,
forceRender: PropTypes.bool,
getContainer: PropTypes.any,
children: PropTypes.func,
visible: PropTypes.bool,
},
data() {
const { visible } = this.$props;
openCount = visible ? openCount + 1 : openCount;
return {};
},
updated() {
this.setWrapperClassName();
},
watch: {
visible(val) {
openCount = val ? openCount + 1 : openCount - 1;
},
getContainer(getContainer, prevGetContainer) {
const getContainerIsFunc =
typeof getContainer === 'function' && typeof prevGetContainer === 'function';
if (
getContainerIsFunc
? getContainer.toString() !== prevGetContainer.toString()
: getContainer !== prevGetContainer
) {
this.removeCurrentContainer(false);
}
},
},
beforeDestroy() {
const { visible } = this.$props;
// 离开时不会 render, 导到离开时数值不变,改用 func 。。
openCount = visible && openCount ? openCount - 1 : openCount;
this.removeCurrentContainer(visible);
},
methods: {
getParent() {
const { getContainer } = this.$props;
if (getContainer) {
if (typeof getContainer === 'string') {
return document.querySelectorAll(getContainer)[0];
}
if (typeof getContainer === 'function') {
return getContainer();
}
if (typeof getContainer === 'object' && getContainer instanceof window.HTMLElement) {
return getContainer;
}
}
return document.body;
},

getDomContainer() {
if (windowIsUndefined) {
return null;
}
if (!this.container) {
this.container = document.createElement('div');
const parent = this.getParent();
if (parent) {
parent.appendChild(this.container);
}
}
this.setWrapperClassName();
return this.container;
},

setWrapperClassName() {
const { wrapperClassName } = this.$props;
if (this.container && wrapperClassName && wrapperClassName !== this.container.className) {
this.container.className = wrapperClassName;
}
},

savePortal(c) {
// Warning: don't rename _component
// https://github.com/react-component/util/pull/65#discussion_r352407916
this._component = c;
},

removeCurrentContainer() {
this.container = null;
this._component = null;
},

/**
* Enhance ./switchScrollingEffect
* 1. Simulate document body scroll bar with
* 2. Record body has overflow style and recover when all of PortalWrapper invisible
* 3. Disable body scroll when PortalWrapper has open
*
* @memberof PortalWrapper
*/
switchScrollingEffect() {
if (openCount === 1 && !Object.keys(cacheOverflow).length) {
switchScrollingEffect();
// Must be set after switchScrollingEffect
cacheOverflow = setStyle({
overflow: 'hidden',
overflowX: 'hidden',
overflowY: 'hidden',
});
} else if (!openCount) {
setStyle(cacheOverflow);
cacheOverflow = {};
switchScrollingEffect(true);
}
},
},

render() {
const { children, forceRender, visible } = this.$props;
let portal = null;
const childProps = {
getOpenCount: () => openCount,
getContainer: this.getDomContainer,
switchScrollingEffect: this.switchScrollingEffect,
};
if (forceRender || visible || this._component) {
portal = (
<Portal
getContainer={this.getDomContainer}
children={children(childProps)}
{...{
directives: [
{
name: 'ant-ref',
value: this.savePortal,
},
],
}}
></Portal>
);
}
return portal;
},
};
2 changes: 2 additions & 0 deletions components/_util/antDirective.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import ref from 'vue-ref';
import { antInput } from './antInputDirective';
import { antDecorator } from './FormDecoratorDirective';
import { antPortal } from './portalDirective';

export default {
install: Vue => {
Vue.use(ref, { name: 'ant-ref' });
antInput(Vue);
antDecorator(Vue);
antPortal(Vue);
},
};
24 changes: 24 additions & 0 deletions components/_util/portalDirective.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export function antPortal(Vue) {
return Vue.directive('ant-portal', {
inserted(el, binding) {
const { value } = binding;
const parentNode = typeof value === 'function' ? value(el) : value;
if (parentNode !== el.parentNode) {
parentNode.appendChild(el);
}
},
componentUpdated(el, binding) {
const { value } = binding;
const parentNode = typeof value === 'function' ? value(el) : value;
if (parentNode !== el.parentNode) {
parentNode.appendChild(el);
}
},
});
}

export default {
install: Vue => {
antPortal(Vue);
},
};
25 changes: 25 additions & 0 deletions components/_util/setStyle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Easy to set element style, return previous style
* IE browser compatible(IE browser doesn't merge overflow style, need to set it separately)
* https://github.com/ant-design/ant-design/issues/19393
*
*/
function setStyle(style, options = {}) {
const { element = document.body } = options;
const oldStyle = {};

const styleKeys = Object.keys(style);

// IE browser compatible
styleKeys.forEach(key => {
oldStyle[key] = element.style[key];
});

styleKeys.forEach(key => {
element.style[key] = style[key];
});

return oldStyle;
}

export default setStyle;
3 changes: 3 additions & 0 deletions components/_util/vnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ export function cloneElement(n, nodeProps = {}, deep) {
node.componentOptions.children = children;
}
} else {
if (children) {
node.children = children;
}
node.data.on = { ...(node.data.on || {}), ...on };
}

Expand Down
12 changes: 6 additions & 6 deletions components/drawer/__tests__/__snapshots__/Drawer.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Drawer class is test_drawer 1`] = `
<div tabindex="-1" class="">
<div class="">
<div tabindex="-1" class="ant-drawer ant-drawer-right test_drawer">
<div class="ant-drawer-mask"></div>
<div class="ant-drawer-content-wrapper" style="transform: translateX(100%); width: 256px;">
Expand All @@ -19,7 +19,7 @@ exports[`Drawer class is test_drawer 1`] = `
`;

exports[`Drawer closable is false 1`] = `
<div tabindex="-1" class="">
<div class="">
<div tabindex="-1" class="ant-drawer ant-drawer-right">
<div class="ant-drawer-mask"></div>
<div class="ant-drawer-content-wrapper" style="transform: translateX(100%); width: 256px;">
Expand All @@ -34,7 +34,7 @@ exports[`Drawer closable is false 1`] = `
`;

exports[`Drawer destroyOnClose is true 1`] = `
<div tabindex="-1" class="">
<div class="">
<div tabindex="-1" class="ant-drawer ant-drawer-right">
<div class="ant-drawer-mask"></div>
<div class="ant-drawer-content-wrapper" style="transform: translateX(100%); width: 256px;">
Expand All @@ -49,7 +49,7 @@ exports[`Drawer destroyOnClose is true 1`] = `
`;

exports[`Drawer have a title 1`] = `
<div tabindex="-1" class="">
<div class="">
<div tabindex="-1" class="ant-drawer ant-drawer-right">
<div class="ant-drawer-mask"></div>
<div class="ant-drawer-content-wrapper" style="transform: translateX(100%); width: 256px;">
Expand All @@ -69,7 +69,7 @@ exports[`Drawer have a title 1`] = `
`;

exports[`Drawer render correctly 1`] = `
<div tabindex="-1" class="">
<div class="">
<div tabindex="-1" class="ant-drawer ant-drawer-right">
<div class="ant-drawer-mask"></div>
<div class="ant-drawer-content-wrapper" style="transform: translateX(100%); width: 400px;">
Expand All @@ -87,7 +87,7 @@ exports[`Drawer render correctly 1`] = `
`;

exports[`Drawer render top drawer 1`] = `
<div tabindex="-1" class="">
<div class="">
<div tabindex="-1" class="ant-drawer ant-drawer-top">
<div class="ant-drawer-mask"></div>
<div class="ant-drawer-content-wrapper" style="transform: translateY(-100%); height: 400px;">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`Drawer render correctly 1`] = `
<div><button type="button" class="ant-btn" ant-click-animating-without-extra-node="false"><span>open</span></button>
<div tabindex="-1" class="">
<div class="">
<div tabindex="-1" class="ant-drawer ant-drawer-right">
<div class="ant-drawer-mask"></div>
<div class="ant-drawer-content-wrapper" style="transform: translateX(100%); width: 256px;">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ exports[`renders ./antdv-demo/docs/drawer/demo/render-in-current.md correctly 1`
<div style="height: 200px; overflow: hidden; position: relative; border: 1px solid #ebedf0; border-radius: 2px; padding: 48px; text-align: center; background: rgb(250, 250, 250);">
Render in this
<div style="margin-top: 16px;"><button type="button" class="ant-btn ant-btn-primary"><span>Open</span></button></div>
<div tabindex="-1" class="">
<div class="">
<div tabindex="-1" class="ant-drawer ant-drawer-right" style="position: absolute;">
<div class="ant-drawer-mask"></div>
<div class="ant-drawer-content-wrapper" style="transform: translateX(100%); width: 256px;">
Expand Down
Loading

0 comments on commit 529f81c

Please sign in to comment.