diff --git a/build/bin/build-entry.js b/build/bin/build-entry.js
index 2419df433..a976ee92e 100644
--- a/build/bin/build-entry.js
+++ b/build/bin/build-entry.js
@@ -51,7 +51,7 @@ ComponentNames.forEach(name => {
'Lazyload',
// services
- 'MessageBox',
+ 'Dialog',
'Toast',
'Indicator'
].indexOf(componentName) === -1) {
diff --git a/components.json b/components.json
index fa7c0daea..1ed3e41c2 100644
--- a/components.json
+++ b/components.json
@@ -5,5 +5,7 @@
"radio": "./packages/radio/index.js",
"cell": "./packages/cell/index.js",
"icon": "./packages/icon/index.js",
- "cell-group": "./packages/cell-group/index.js"
+ "cell-group": "./packages/cell-group/index.js",
+ "popup": "./packages/popup/index.js",
+ "dialog": "./packages/dialog/index.js"
}
diff --git a/docs/examples/dialog.md b/docs/examples/dialog.md
new file mode 100644
index 000000000..46305c766
--- /dev/null
+++ b/docs/examples/dialog.md
@@ -0,0 +1,46 @@
+
+
+## Dialog组件
+
+### 基础用法
+
+:::demo
+```html
+alert
+
+confirm
+```
+:::
+
+### API
+
+| 参数 | 说明 | 类型 | 默认值 | 可选值 |
+|-----------|-----------|-----------|-------------|-------------|
+| title | 标题 | String | '' | |
+| message | 内容 | String | '' | |
diff --git a/docs/examples/popup.md b/docs/examples/popup.md
new file mode 100644
index 000000000..3778b530e
--- /dev/null
+++ b/docs/examples/popup.md
@@ -0,0 +1,84 @@
+
+
+
+
+## Popup组件
+
+### 基础用法
+
+:::demo
+```html
+从下方弹出popup
+
+
+从上方方弹出popup
+
+
+从右方弹出popup
+
+
+从中间弹出popup
+
+```
+:::
+
+### API
+
+| 参数 | 说明 | 类型 | 默认值 | 可选值 |
+|-----------|-----------|-----------|-------------|-------------|
+| value | 利用`v-model`绑定当前组件是否显示 | Boolean | '' | |
diff --git a/docs/nav.config.json b/docs/nav.config.json
index 28b555022..6046705d1 100644
--- a/docs/nav.config.json
+++ b/docs/nav.config.json
@@ -77,8 +77,12 @@
"title": "Lazyload"
},
{
- "path": "/pop",
- "title": "Pop"
+ "path": "/popup",
+ "title": "Popup"
+ },
+ {
+ "path": "/dialog",
+ "title": "Dialog"
},
{
"path": "/swipe",
diff --git a/packages/dialog/CHANGELOG.md b/packages/dialog/CHANGELOG.md
new file mode 100644
index 000000000..e88c472b3
--- /dev/null
+++ b/packages/dialog/CHANGELOG.md
@@ -0,0 +1,8 @@
+## 0.0.2 (2017-01-20)
+
+* 改了bug A
+* 加了功能B
+
+## 0.0.1 (2017-01-10)
+
+* 第一版
diff --git a/packages/dialog/README.md b/packages/dialog/README.md
new file mode 100644
index 000000000..4c6172563
--- /dev/null
+++ b/packages/dialog/README.md
@@ -0,0 +1,26 @@
+# @youzan/<%= name %>
+
+!!! 请在此处填写你的文档最简单描述 !!!
+
+[![version][version-image]][download-url]
+[![download][download-image]][download-url]
+
+[version-image]: http://npm.qima-inc.com/badge/v/@youzan/<%= name %>.svg?style=flat-square
+[download-image]: http://npm.qima-inc.com/badge/d/@youzan/<%= name %>.svg?style=flat-square
+[download-url]: http://npm.qima-inc.com/package/@youzan/<%= name %>
+
+## Demo
+
+## Usage
+
+## API
+
+| 参数 | 说明 | 类型 | 默认值 | 可选值 |
+|-----------|-----------|-----------|-------------|-------------|
+| className | 自定义额外类名 | string | '' | '' |
+
+
+
+
+## License
+[MIT](https://opensource.org/licenses/MIT)
diff --git a/packages/dialog/index.js b/packages/dialog/index.js
new file mode 100644
index 000000000..cb8dcd8d5
--- /dev/null
+++ b/packages/dialog/index.js
@@ -0,0 +1,3 @@
+import Dialog from './src/dialog.js';
+
+export default Dialog;
diff --git a/packages/dialog/package.json b/packages/dialog/package.json
new file mode 100644
index 000000000..7dbfa2900
--- /dev/null
+++ b/packages/dialog/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "<%= name %>",
+ "version": "<%= version %>",
+ "description": "<%= description %>",
+ "main": "./lib/index.js",
+ "author": "<%= author %>",
+ "license": "<%= license %>",
+ "devDependencies": {},
+ "dependencies": {}
+}
diff --git a/packages/dialog/src/dialog.js b/packages/dialog/src/dialog.js
new file mode 100644
index 000000000..fa295ac15
--- /dev/null
+++ b/packages/dialog/src/dialog.js
@@ -0,0 +1,98 @@
+import Vue from 'vue';
+import Dialog from './dialog.vue';
+import merge from 'src/utils/merge';
+
+const DialogConstructor = Vue.extend(Dialog);
+
+let currentDialog;
+let instance;
+let dialogQueue = [];
+
+const defaultCallback = action => {
+ if (currentDialog) {
+ let callback = currentDialog.callback;
+
+ if (typeof callback === 'function') {
+ callback(action);
+ }
+
+ if (currentDialog.resolve && action === 'confirm') {
+ currentDialog.resolve(action);
+ } else if (currentDialog.reject && action === 'cancel') {
+ currentDialog.reject(action);
+ }
+ }
+};
+
+const initInstance = () => {
+ instance = new DialogConstructor({
+ el: document.createElement('div')
+ });
+
+ instance.callback = defaultCallback;
+};
+
+const showNextDialog = () => {
+ if (!instance) {
+ initInstance();
+ }
+
+ if (!instance.value && dialogQueue.length > 0) {
+ currentDialog = dialogQueue.shift();
+
+ let options = currentDialog.options;
+
+ for (let prop in options) {
+ if (options.hasOwnProperty(prop)) {
+ instance[prop] = options[prop];
+ }
+ }
+
+ if (options.callback === undefined) {
+ instance.callback = defaultCallback;
+ }
+
+ document.body.appendChild(instance.$el);
+
+ Vue.nextTick(() => {
+ instance.value = true;
+ });
+ }
+};
+
+var DialogBox = options => {
+ return new Promise((resolve, reject) => { // eslint-disable-line
+ dialogQueue.push({
+ options: merge({}, options),
+ callback: options.callback,
+ resolve: resolve,
+ reject: reject
+ });
+
+ showNextDialog();
+ });
+};
+
+DialogBox.alert = function(options) {
+ return DialogBox(merge({
+ type: 'alert',
+ closeOnClickOverlay: false,
+ showCancelButton: false
+ }, options));
+};
+
+DialogBox.confirm = function(options) {
+ return DialogBox(merge({
+ type: 'confirm',
+ closeOnClickOverlay: true,
+ showCancelButton: true
+ }, options));
+};
+
+DialogBox.close = function() {
+ instance.value = false;
+ dialogQueue = [];
+ currentDialog = null;
+};
+
+export default DialogBox;
diff --git a/packages/dialog/src/dialog.vue b/packages/dialog/src/dialog.vue
new file mode 100644
index 000000000..bac5b3437
--- /dev/null
+++ b/packages/dialog/src/dialog.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
diff --git a/packages/popup/CHANGELOG.md b/packages/popup/CHANGELOG.md
new file mode 100644
index 000000000..e88c472b3
--- /dev/null
+++ b/packages/popup/CHANGELOG.md
@@ -0,0 +1,8 @@
+## 0.0.2 (2017-01-20)
+
+* 改了bug A
+* 加了功能B
+
+## 0.0.1 (2017-01-10)
+
+* 第一版
diff --git a/packages/popup/README.md b/packages/popup/README.md
new file mode 100644
index 000000000..4c6172563
--- /dev/null
+++ b/packages/popup/README.md
@@ -0,0 +1,26 @@
+# @youzan/<%= name %>
+
+!!! 请在此处填写你的文档最简单描述 !!!
+
+[![version][version-image]][download-url]
+[![download][download-image]][download-url]
+
+[version-image]: http://npm.qima-inc.com/badge/v/@youzan/<%= name %>.svg?style=flat-square
+[download-image]: http://npm.qima-inc.com/badge/d/@youzan/<%= name %>.svg?style=flat-square
+[download-url]: http://npm.qima-inc.com/package/@youzan/<%= name %>
+
+## Demo
+
+## Usage
+
+## API
+
+| 参数 | 说明 | 类型 | 默认值 | 可选值 |
+|-----------|-----------|-----------|-------------|-------------|
+| className | 自定义额外类名 | string | '' | '' |
+
+
+
+
+## License
+[MIT](https://opensource.org/licenses/MIT)
diff --git a/packages/popup/index.js b/packages/popup/index.js
new file mode 100644
index 000000000..31857d9ef
--- /dev/null
+++ b/packages/popup/index.js
@@ -0,0 +1,3 @@
+import Popup from './src/popup';
+
+export default Popup;
diff --git a/packages/popup/package.json b/packages/popup/package.json
new file mode 100644
index 000000000..7dbfa2900
--- /dev/null
+++ b/packages/popup/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "<%= name %>",
+ "version": "<%= version %>",
+ "description": "<%= description %>",
+ "main": "./lib/index.js",
+ "author": "<%= author %>",
+ "license": "<%= license %>",
+ "devDependencies": {},
+ "dependencies": {}
+}
diff --git a/packages/popup/src/popup.vue b/packages/popup/src/popup.vue
new file mode 100644
index 000000000..1740fc049
--- /dev/null
+++ b/packages/popup/src/popup.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
diff --git a/packages/zanui/src/dialog.pcss b/packages/zanui/src/dialog.pcss
new file mode 100644
index 000000000..a8ed0db0a
--- /dev/null
+++ b/packages/zanui/src/dialog.pcss
@@ -0,0 +1,95 @@
+@import "./mixins/border_retina.pcss";
+
+@component-namespace o2 {
+ @component dialog-wrapper {
+ position: absolute;
+ }
+
+ @component dialog {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate3d(-50%, -50%, 0);
+ background-color: #fff;
+ width: 85%;
+ border-radius: 4px;
+ font-size: 16px;
+ overflow: hidden;
+ backface-visibility: hidden;
+ transition: .2s;
+
+ @descendent header {
+ padding: 15px 0 0;
+ }
+
+ @descendent content {
+ padding: 15px 20px;
+ min-height: 36px;
+ position: relative;
+
+ &::after {
+ @mixin border-retina (bottom);
+ }
+ }
+
+ @descendent title {
+ text-align: center;
+ padding-left: 0;
+ margin-bottom: 0;
+ font-size: 16px;
+ color: #333;
+ }
+
+ @descendent message {
+ color: #999;
+ margin: 0;
+ font-size: 14px;
+ line-height: 1.5;
+ }
+
+ @descendent footer {
+ font-size: 14px;
+ overflow: hidden;
+ }
+
+ .is-twobtn {
+ .o2-dialog-btn {
+ width: 50%;
+ }
+
+ .o2-dialog-cancel {
+ &::after {
+ @mixin border-retina (right);
+ }
+ }
+ }
+
+ @descendent btn {
+ line-height: 40px;
+ border: 0;
+ background-color: #fff;
+ float: left;
+ box-sizing: border-box;
+ text-align: center;
+ position: relative;
+ }
+
+ @descendent cancel {
+ color: #333;
+ }
+
+ @descendent confirm {
+ color: #00C000;
+ width: 100%;
+ }
+ }
+}
+
+.dialog-bounce-enter {
+ opacity: 0;
+ transform: translate3d(-50%, -50%, 0) scale(0.7);
+}
+.dialog-bounce-leave-active {
+ opacity: 0;
+ transform: translate3d(-50%, -50%, 0) scale(0.9);
+}
diff --git a/packages/zanui/src/index.pcss b/packages/zanui/src/index.pcss
index 469e089fc..64007adf1 100644
--- a/packages/zanui/src/index.pcss
+++ b/packages/zanui/src/index.pcss
@@ -3,6 +3,8 @@
*/
@import './button.pcss';
@import './cell.pcss';
+@import './dialog.pcss';
@import './field.pcss';
@import './icon.pcss';
+@import './popup.pcss';
@import './switch.pcss';
diff --git a/packages/zanui/src/popup.pcss b/packages/zanui/src/popup.pcss
new file mode 100644
index 000000000..52798154f
--- /dev/null
+++ b/packages/zanui/src/popup.pcss
@@ -0,0 +1,75 @@
+.v-modal {
+ position: fixed;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ background-color: rgba(0, 0, 0, 0.701961);
+}
+
+@component-namespace o2 {
+ @component popup {
+ position: fixed;
+ background-color: #fff;
+ top: 50%;
+ left: 50%;
+ transform: translate3d(-50%, -50%, 0);
+ backface-visibility: hidden;
+ transition: .2s ease-out;
+
+ @modifier top {
+ top: 0;
+ right: auto;
+ bottom: auto;
+ left: 50%;
+ transform: translate3d(-50%, 0, 0);
+ }
+
+ @modifier right {
+ top: 50%;
+ right: 0;
+ bottom: auto;
+ left: auto;
+ transform: translate3d(0, -50%, 0);
+ }
+
+ @modifier bottom {
+ top: auto;
+ bottom: 0;
+ right: auto;
+ left: 50%;
+ transform: translate3d(-50%, 0, 0);
+ }
+
+ @modifier left {
+ top: 50%;
+ right: auto;
+ bottom: auto;
+ left: 0;
+ transform: translate3d(0, -50%, 0);
+ }
+ }
+}
+
+.popup-slide-top-enter,
+.popup-slide-top-leave-active {
+ transform: translate3d(-50%, -100%, 0);
+}
+
+.popup-slide-right-enter,
+.popup-slide-right-leave-active {
+ transform: translate3d(100%, -50%, 0);
+}
+
+.popup-slide-bottom-enter,
+.popup-slide-bottom-leave-active {
+ transform: translate3d(-50%, 100%, 0);
+}
+
+.popup-slide-left-enter, .popup-slide-left-leave-active {
+ transform: translate3d(-100%, -50%, 0);
+}
+
+.popup-fade-enter, .popup-fade-leave-active {
+ opacity: 0;
+}
diff --git a/src/index.js b/src/index.js
index 3ffa774fd..b3dd83c70 100644
--- a/src/index.js
+++ b/src/index.js
@@ -5,6 +5,8 @@ import Radio from '../packages/radio/index.js';
import Cell from '../packages/cell/index.js';
import Icon from '../packages/icon/index.js';
import CellGroup from '../packages/cell-group/index.js';
+import Popup from '../packages/popup/index.js';
+import Dialog from '../packages/dialog/index.js';
// zanui
import '../packages/zanui/src/index.pcss';
@@ -18,6 +20,8 @@ const install = function(Vue) {
Vue.component(Cell.name, Cell);
Vue.component(Icon.name, Icon);
Vue.component(CellGroup.name, CellGroup);
+ Vue.component(Popup.name, Popup);
+ // Vue.component(Dialog.name, Dialog);
};
// auto install
@@ -34,5 +38,7 @@ module.exports = {
Radio,
Cell,
Icon,
- CellGroup
+ CellGroup,
+ Popup,
+ Dialog
};
diff --git a/src/mixins/popup/index.js b/src/mixins/popup/index.js
new file mode 100644
index 000000000..cf0636e9e
--- /dev/null
+++ b/src/mixins/popup/index.js
@@ -0,0 +1,195 @@
+import Vue from 'vue';
+import merge from 'src/utils/merge';
+import PopupManager from './popup-manager';
+
+let idSeed = 1;
+
+const getDOM = function(dom) {
+ if (dom.nodeType === 3) {
+ dom = dom.nextElementSibling || dom.nextSibling;
+ getDOM(dom);
+ }
+ return dom;
+};
+
+let scrollBarWidth;
+const getScrollBarWidth = () => {
+ if (scrollBarWidth !== undefined) return scrollBarWidth;
+
+ const outer = document.createElement('div');
+ outer.style.visibility = 'hidden';
+ outer.style.width = '100px';
+ outer.style.position = 'absolute';
+ outer.style.top = '-9999px';
+ document.body.appendChild(outer);
+
+ const widthNoScroll = outer.offsetWidth;
+ outer.style.overflow = 'scroll';
+
+ const inner = document.createElement('div');
+ inner.style.width = '100%';
+ outer.appendChild(inner);
+
+ const widthWithScroll = inner.offsetWidth;
+ outer.parentNode.removeChild(outer);
+
+ return widthNoScroll - widthWithScroll;
+};
+
+export default {
+ props: {
+ /**
+ * popup当前显示状态
+ */
+ value: {
+ type: Boolean,
+ default: false
+ },
+ /**
+ * 是否显示遮罩层
+ */
+ overlay: {
+ type: Boolean,
+ default: false
+ },
+ /**
+ * 点击遮罩层是否关闭popup
+ */
+ closeOnClickOverlay: {
+ type: Boolean,
+ default: false
+ },
+ zIndex: [String, Number],
+ /**
+ * popup滚动时是否body内容也滚动
+ * 默认为不滚动
+ */
+ lockOnScroll: {
+ type: Boolean,
+ default: true
+ }
+ },
+
+ watch: {
+ value(val) {
+ if (val) {
+ if (this.opening) return;
+ this.open();
+ } else {
+ if (this.closing) return;
+ this.close();
+ }
+ }
+ },
+
+ beforeMount() {
+ this._popupId = 'popup-' + idSeed++;
+ PopupManager.register(this._popupId, this);
+ },
+
+ data() {
+ return {
+ opening: false,
+ opened: false,
+ closing: false,
+ bodyOverflow: null,
+ bodyPaddingRight: null
+ };
+ },
+
+ methods: {
+ /**
+ * 显示popup
+ */
+ open(options) {
+ if (this.opened) return;
+
+ this.opening = true;
+
+ this.$emit('input', true);
+
+ const dom = getDOM(this.$el);
+ const props = merge({}, this, options);
+ const overlay = props.overlay;
+ const zIndex = props.zIndex;
+
+ // 如果属性中传入了`zIndex`,则覆盖`PopupManager`中对应的`zIndex`
+ if (zIndex) {
+ PopupManager.zIndex = zIndex;
+ }
+
+ // 如果显示遮罩层
+ if (overlay) {
+ if (this.closing) {
+ PopupManager.closeModal(this._popupId);
+ this.closing = false;
+ }
+ PopupManager.openModal(this._popupId, PopupManager.nextZIndex(), dom);
+
+ // 如果滚动时需要锁定
+ if (props.lockOnScroll) {
+ // 将原来的`bodyOverflow`和`bodyPaddingRight`存起来
+ if (!this.bodyOverflow) {
+ this.bodyPaddingRight = document.body.style.paddingRight;
+ this.bodyOverflow = document.body.style.overflow;
+ }
+
+ scrollBarWidth = getScrollBarWidth();
+
+ // 页面是否`overflow`
+ let bodyHasOverflow = document.documentElement.clientHeight < document.body.scrollHeight;
+ if (scrollBarWidth > 0 && bodyHasOverflow) {
+ document.body.style.paddingRight = scrollBarWidth + 'px';
+ }
+ document.body.style.overlay = 'hidden';
+ }
+ }
+
+ dom.style.zIndex = PopupManager.nextZIndex();
+ this.opened = true;
+ this.opening = false;
+ },
+
+ /**
+ * 关闭popup
+ */
+ close() {
+ if (this.closing) return;
+
+ this.closing = true;
+
+ this.$emit('input', false);
+
+ if (this.lockOnScroll) {
+ setTimeout(() => {
+ if (this.modal && this.bodyOverflow !== 'hidden') {
+ document.body.style.overflow = this.bodyOverflow;
+ document.body.style.paddingRight = this.bodyPaddingRight;
+ }
+ this.bodyOverflow = null;
+ this.bodyPaddingRight = null;
+ }, 200);
+ }
+
+ this.opened = false;
+ this.doAfterClose();
+ },
+
+ doAfterClose() {
+ this.closing = false;
+ PopupManager.closeModal(this._popupId);
+ }
+ },
+
+ beforeDestroy() {
+ PopupManager.deregister(this._popupId);
+ PopupManager.closeModal(this._popupId);
+
+ if (this.modal && this.bodyOverflow !== null && this.bodyOverflow !== 'hidden') {
+ document.body.style.overflow = this.bodyOverflow;
+ document.body.style.paddingRight = this.bodyPaddingRight;
+ }
+ this.bodyOverflow = null;
+ this.bodyPaddingRight = null;
+ }
+};
diff --git a/src/mixins/popup/popup-manager.js b/src/mixins/popup/popup-manager.js
new file mode 100644
index 000000000..9ba799094
--- /dev/null
+++ b/src/mixins/popup/popup-manager.js
@@ -0,0 +1,136 @@
+import { addClass, removeClass } from 'src/utils/dom';
+
+let hasModal = false;
+
+const getModal = function() {
+ let modalDom = PopupManager.modalDom;
+ if (modalDom) {
+ hasModal = true;
+ } else {
+ hasModal = false;
+ modalDom = document.createElement('div');
+ PopupManager.modalDom = modalDom;
+
+ modalDom.addEventListener('touchmove', function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+ });
+
+ modalDom.addEventListener('click', function() {
+ PopupManager.handleOverlayClick && PopupManager.handleOverlayClick();
+ });
+ }
+
+ return modalDom;
+};
+
+const instances = {};
+
+const PopupManager = {
+ zIndex: 2000,
+
+ modalStack: [],
+
+ nextZIndex() {
+ return this.zIndex++;
+ },
+
+ getInstance(id) {
+ return instances[id];
+ },
+
+ register(id, instance) {
+ if (id && instance) {
+ instances[id] = instance;
+ }
+ },
+
+ deregister(id) {
+ if (id) {
+ instances[id] = null;
+ delete instances[id];
+ }
+ },
+
+ /**
+ * 遮罩层点击回调,`closeOnClickOverlay`为`true`时会关闭当前`popup`
+ */
+ handleOverlayClick() {
+ const topModal = PopupManager.modalStack[PopupManager.modalStack.length - 1];
+ if (!topModal) return;
+
+ const instance = PopupManager.getInstance(topModal.id);
+ if (instance && instance.closeOnClickOverlay) {
+ instance.close();
+ }
+ },
+
+ openModal(id, zIndex, dom) {
+ if (!id || zIndex === undefined) return;
+
+ const modalStack = this.modalStack;
+
+ for (let i = 0, j = modalStack.length; i < j; i++) {
+ const item = modalStack[i];
+ if (item.id === id) {
+ return;
+ }
+ }
+
+ const modalDom = getModal();
+
+ addClass(modalDom, 'v-modal');
+ setTimeout(() => {
+ removeClass(modalDom, 'v-modal-enter');
+ }, 200);
+
+ if (dom && dom.parentNode && dom.parentNode.nodeType !== 11) {
+ dom.parentNode.appendChild(modalDom);
+ } else {
+ document.body.appendChild(modalDom);
+ }
+
+ if (zIndex) {
+ modalDom.style.zIndex = zIndex;
+ }
+ modalDom.style.display = '';
+
+ this.modalStack.push({ id: id, zIndex: zIndex });
+ },
+
+ closeModal(id) {
+ const modalStack = this.modalStack;
+ const modalDom = getModal();
+
+ if (modalStack.length > 0) {
+ const topItem = modalStack[modalStack.length - 1];
+ if (topItem.id === id) {
+ modalStack.pop();
+ if (modalStack.length > 0) {
+ modalDom.style.zIndex = modalStack[modalStack.length - 1].zIndex;
+ }
+ } else {
+ for (let i = modalStack.length - 1; i >= 0; i--) {
+ if (modalStack[i].id === id) {
+ modalStack.splice(i, 1);
+ break;
+ }
+ }
+ }
+ }
+
+ if (modalStack.length === 0) {
+ setTimeout(() => {
+ if (modalStack.length === 0) {
+ if (modalDom.parentNode) modalDom.parentNode.removeChild(modalDom);
+
+ modalDom.style.display = 'none';
+ this.modalDom = undefined;
+ }
+ removeClass(modalDom, 'v-modal-leave');
+ }, 200);
+ }
+ }
+};
+
+export default PopupManager;
diff --git a/src/utils/dom.js b/src/utils/dom.js
new file mode 100644
index 000000000..ede0951ec
--- /dev/null
+++ b/src/utils/dom.js
@@ -0,0 +1,57 @@
+const trim = function(string) {
+ return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
+};
+
+export function hasClass(el, cls) {
+ if (!el || !cls) return false;
+ if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
+ if (el.classList) {
+ return el.classList.contains(cls);
+ } else {
+ return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
+ }
+};
+
+export function addClass(el, cls) {
+ if (!el) return;
+ var curClass = el.className;
+ var classes = (cls || '').split(' ');
+
+ for (var i = 0, j = classes.length; i < j; i++) {
+ var clsName = classes[i];
+ if (!clsName) continue;
+
+ if (el.classList) {
+ el.classList.add(clsName);
+ } else {
+ if (!hasClass(el, clsName)) {
+ curClass += ' ' + clsName;
+ }
+ }
+ }
+ if (!el.classList) {
+ el.className = curClass;
+ }
+};
+
+export function removeClass(el, cls) {
+ if (!el || !cls) return;
+ var classes = cls.split(' ');
+ var curClass = ' ' + el.className + ' ';
+
+ for (var i = 0, j = classes.length; i < j; i++) {
+ var clsName = classes[i];
+ if (!clsName) continue;
+
+ if (el.classList) {
+ el.classList.remove(clsName);
+ } else {
+ if (hasClass(el, clsName)) {
+ curClass = curClass.replace(' ' + clsName + ' ', ' ');
+ }
+ }
+ }
+ if (!el.classList) {
+ el.className = trim(curClass);
+ }
+};
diff --git a/src/utils/merge.js b/src/utils/merge.js
new file mode 100644
index 000000000..564a989a4
--- /dev/null
+++ b/src/utils/merge.js
@@ -0,0 +1,15 @@
+export default function(target, ...sources) {
+ for (let i = 0; i < sources.length; i++) {
+ let source = sources[i] || {};
+ for (let prop in source) {
+ if (source.hasOwnProperty(prop)) {
+ let value = source[prop];
+ if (value !== undefined) {
+ target[prop] = value;
+ }
+ }
+ }
+ }
+
+ return target;
+};