From c67e4103f4716ce8e6be130fe3795dbf05673689 Mon Sep 17 00:00:00 2001
From: tangjinzhou <415800467@qq.com>
Date: Tue, 6 Mar 2018 19:14:41 +0800
Subject: [PATCH] add modal
---
components/_util/ContainerRender.vue | 3 -
components/_util/props-util.js | 11 +-
components/index.js | 1 +
components/modal/ActionButton.vue | 70 +++++++++++
components/modal/ConfirmDialog.vue | 71 +++++++++++
components/modal/Modal.vue | 149 +++++++++++++++++++++++
components/modal/confirm.js | 39 ++++++
components/modal/demo/basic.md | 51 ++++++++
components/modal/index.js | 56 +++++++++
components/modal/locale.js | 28 +++++
components/modal/style/confirm.less | 71 +++++++++++
components/modal/style/index.js | 5 +
components/modal/style/index.less | 4 +
components/modal/style/modal.less | 146 ++++++++++++++++++++++
components/style.js | 1 +
components/trigger/Popup.vue | 4 +-
components/vc-dialog/Dialog.vue | 53 +++++---
components/vc-dialog/DialogWrap.vue | 11 +-
components/vc-dialog/IDialogPropTypes.js | 6 +-
components/vc-dialog/demo/ant-design.vue | 5 +-
components/vc-dialog/index.js | 2 +
examples/routes.js | 2 +-
22 files changed, 756 insertions(+), 33 deletions(-)
create mode 100644 components/modal/ActionButton.vue
create mode 100644 components/modal/ConfirmDialog.vue
create mode 100644 components/modal/Modal.vue
create mode 100644 components/modal/confirm.js
create mode 100644 components/modal/demo/basic.md
create mode 100644 components/modal/index.js
create mode 100644 components/modal/locale.js
create mode 100644 components/modal/style/confirm.less
create mode 100644 components/modal/style/index.js
create mode 100644 components/modal/style/index.less
create mode 100644 components/modal/style/modal.less
create mode 100644 components/vc-dialog/index.js
diff --git a/components/_util/ContainerRender.vue b/components/_util/ContainerRender.vue
index 839c4f7d7..d949112bc 100644
--- a/components/_util/ContainerRender.vue
+++ b/components/_util/ContainerRender.vue
@@ -60,12 +60,9 @@ export default {
})
},
render () {
- console.log(props)
return getComponent(props)
},
})
- } else {
- this._component.$forceUpdate()
}
}
},
diff --git a/components/_util/props-util.js b/components/_util/props-util.js
index 6ee8a3f30..d44419859 100644
--- a/components/_util/props-util.js
+++ b/components/_util/props-util.js
@@ -111,7 +111,11 @@ export function getClass (ele) {
} else if (ele.$vnode && ele.$vnode.data) {
data = ele.$vnode.data
}
- return data.class || data.staticClass
+ let cls = data.class || data.staticClass || {}
+ if (typeof cls === 'string') {
+ cls = cls.split(' ').forEach(c => { cls[c.trim()] = true })
+ }
+ return cls
}
export function getStyle (ele) {
let data = {}
@@ -138,6 +142,10 @@ export function isEmptyElement (ele) {
export function filterEmpty (children = []) {
return children.filter(c => c.tag || c.text.trim() !== '')
}
+const initDefaultProps = (propTypes, defaultProps) => {
+ Object.keys(defaultProps).forEach(k => { propTypes[k] = propTypes[k].def(defaultProps[k]) })
+ return propTypes
+}
export {
hasProp,
filterProps,
@@ -150,5 +158,6 @@ export {
getAttrs,
getValueByProp,
parseStyleText,
+ initDefaultProps,
}
export default hasProp
diff --git a/components/index.js b/components/index.js
index 6b22f7e23..c04262eb5 100644
--- a/components/index.js
+++ b/components/index.js
@@ -93,4 +93,5 @@ export { default as Affix } from './affix'
export { default as Cascader } from './cascader'
export { default as BackTop } from './back-top'
+export { default as Modal } from './modal'
diff --git a/components/modal/ActionButton.vue b/components/modal/ActionButton.vue
new file mode 100644
index 000000000..9ec46761a
--- /dev/null
+++ b/components/modal/ActionButton.vue
@@ -0,0 +1,70 @@
+
diff --git a/components/modal/ConfirmDialog.vue b/components/modal/ConfirmDialog.vue
new file mode 100644
index 000000000..5528e7f22
--- /dev/null
+++ b/components/modal/ConfirmDialog.vue
@@ -0,0 +1,71 @@
+
diff --git a/components/modal/Modal.vue b/components/modal/Modal.vue
new file mode 100644
index 000000000..d437232f3
--- /dev/null
+++ b/components/modal/Modal.vue
@@ -0,0 +1,149 @@
+
diff --git a/components/modal/confirm.js b/components/modal/confirm.js
new file mode 100644
index 000000000..23365b493
--- /dev/null
+++ b/components/modal/confirm.js
@@ -0,0 +1,39 @@
+import Vue from 'vue'
+import ConfirmDialog from './ConfirmDialog'
+export default function confirm (config) {
+ const div = document.createElement('div')
+ document.body.appendChild(div)
+ let confirmDialogInstance = null
+ function close (...args) {
+ destroy(...args)
+ }
+
+ function destroy (...args) {
+ if (confirmDialogInstance && div.parentNode) {
+ confirmDialogInstance.$destroy()
+ confirmDialogInstance = null
+ div.parentNode.removeChild(div)
+ }
+ const triggerCancel = args && args.length &&
+ args.some(param => param && param.triggerCancel)
+ if (config.onCancel && triggerCancel) {
+ config.onCancel(...args)
+ }
+ }
+
+ function render (props) {
+ return new Vue({
+ el: div,
+ render () {
+ return
+ },
+ })
+ }
+
+ confirmDialogInstance = render({ ...config, visible: true, close })
+
+ return {
+ destroy: close,
+ }
+}
+
diff --git a/components/modal/demo/basic.md b/components/modal/demo/basic.md
new file mode 100644
index 000000000..f955e32db
--- /dev/null
+++ b/components/modal/demo/basic.md
@@ -0,0 +1,51 @@
+
+
+#### 基本
+第一个对话框。
+
+
+
+#### Basic
+Basic modal.
+
+
+```html
+
+
+
Open
+
+ Some contents...
+ Some contents...
+ Some contents...
+
+
+
+
+```
+
diff --git a/components/modal/index.js b/components/modal/index.js
new file mode 100644
index 000000000..146ec0bef
--- /dev/null
+++ b/components/modal/index.js
@@ -0,0 +1,56 @@
+import Modal from './Modal'
+import confirm from './confirm'
+
+// export { ActionButtonProps } from './ActionButton'
+// export { ModalProps, ModalFuncProps } from './Modal'
+
+Modal.info = function (props) {
+ const config = {
+ type: 'info',
+ iconType: 'info-circle',
+ okCancel: false,
+ ...props,
+ }
+ return confirm(config)
+}
+
+Modal.success = function (props) {
+ const config = {
+ type: 'success',
+ iconType: 'check-circle',
+ okCancel: false,
+ ...props,
+ }
+ return confirm(config)
+}
+
+Modal.error = function (props) {
+ const config = {
+ type: 'error',
+ iconType: 'cross-circle',
+ okCancel: false,
+ ...props,
+ }
+ return confirm(config)
+}
+
+Modal.warning = Modal.warn = function (props) {
+ const config = {
+ type: 'warning',
+ iconType: 'exclamation-circle',
+ okCancel: false,
+ ...props,
+ }
+ return confirm(config)
+}
+
+Modal.confirm = function (props) {
+ const config = {
+ type: 'confirm',
+ okCancel: true,
+ ...props,
+ }
+ return confirm(config)
+}
+
+export default Modal
diff --git a/components/modal/locale.js b/components/modal/locale.js
new file mode 100644
index 000000000..e6d7d3de5
--- /dev/null
+++ b/components/modal/locale.js
@@ -0,0 +1,28 @@
+import defaultLocale from '../locale-provider/default'
+
+// export interface ModalLocale {
+// okText: string;
+// cancelText: string;
+// justOkText: string;
+// }
+
+let runtimeLocale = {
+ ...defaultLocale.Modal,
+}
+
+export function changeConfirmLocale (newLocale) {
+ if (newLocale) {
+ runtimeLocale = {
+ ...runtimeLocale,
+ ...newLocale,
+ }
+ } else {
+ runtimeLocale = {
+ ...defaultLocale.Modal,
+ }
+ }
+}
+
+export function getConfirmLocale () {
+ return runtimeLocale
+}
diff --git a/components/modal/style/confirm.less b/components/modal/style/confirm.less
new file mode 100644
index 000000000..5618cb00b
--- /dev/null
+++ b/components/modal/style/confirm.less
@@ -0,0 +1,71 @@
+@import "../../style/mixins/index";
+
+@confirm-prefix-cls: ~"@{ant-prefix}-confirm";
+
+.@{confirm-prefix-cls} {
+ .@{ant-prefix}-modal-header {
+ display: none;
+ }
+
+ .@{ant-prefix}-modal-close {
+ display: none;
+ }
+
+ .@{ant-prefix}-modal-body {
+ padding: 32px 32px 24px;
+ }
+
+ &-body-wrapper {
+ .clearfix();
+ }
+
+ &-body {
+ .@{confirm-prefix-cls}-title {
+ color: @heading-color;
+ font-weight: 500;
+ font-size: @font-size-lg;
+ line-height: 22px;
+ }
+
+ .@{confirm-prefix-cls}-content {
+ margin-left: 38px;
+ font-size: @font-size-base;
+ color: @text-color;
+ margin-top: 8px;
+ }
+
+ > .@{iconfont-css-prefix} {
+ font-size: 22px;
+ margin-right: 16px;
+ float: left;
+ min-height: 48px;
+ }
+ }
+
+ .@{confirm-prefix-cls}-btns {
+ margin-top: 24px;
+ float: right;
+
+ button + button {
+ margin-left: 8px;
+ margin-bottom: 0;
+ }
+ }
+
+ &-error &-body > .@{iconfont-css-prefix} {
+ color: @error-color;
+ }
+
+ &-warning &-body > .@{iconfont-css-prefix},
+ &-confirm &-body > .@{iconfont-css-prefix} {
+ color: @warning-color;
+ }
+
+ &-info &-body > .@{iconfont-css-prefix} {
+ color: @info-color;
+ }
+
+ &-success &-body > .@{iconfont-css-prefix} {
+ color: @success-color;
+ }
+}
diff --git a/components/modal/style/index.js b/components/modal/style/index.js
new file mode 100644
index 000000000..81f6a07af
--- /dev/null
+++ b/components/modal/style/index.js
@@ -0,0 +1,5 @@
+import '../../style/index.less'
+import './index.less'
+
+// style dependencies
+import '../../button/style'
diff --git a/components/modal/style/index.less b/components/modal/style/index.less
new file mode 100644
index 000000000..6f5d0b74d
--- /dev/null
+++ b/components/modal/style/index.less
@@ -0,0 +1,4 @@
+@import "../../style/themes/default";
+@import "../../style/mixins/index";
+@import "./modal";
+@import "./confirm";
diff --git a/components/modal/style/modal.less b/components/modal/style/modal.less
new file mode 100644
index 000000000..60f6a5433
--- /dev/null
+++ b/components/modal/style/modal.less
@@ -0,0 +1,146 @@
+@dialog-prefix-cls: ~"@{ant-prefix}-modal";
+
+.@{dialog-prefix-cls} {
+ .reset-component;
+ position: relative;
+ width: auto;
+ margin: 0 auto;
+ top: 100px;
+ padding-bottom: 24px;
+
+ &-wrap {
+ position: fixed;
+ overflow: auto;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: @zindex-modal;
+ -webkit-overflow-scrolling: touch;
+ outline: 0;
+ }
+
+ &-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;
+ border-radius: @border-radius-base;
+ 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 .3s;
+ color: @text-color-secondary;
+ outline: 0;
+ padding: 0;
+
+ &-x {
+ display: block;
+ font-style: normal;
+ vertical-align: baseline;
+ 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;
+ }
+
+ &-footer {
+ border-top: @border-width-base @border-style-base @border-color-split;
+ padding: 10px 16px;
+ text-align: right;
+ border-radius: 0 0 @border-radius-base @border-radius-base;
+ button + button {
+ margin-left: 8px;
+ margin-bottom: 0;
+ }
+ }
+
+ &.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;
+ background-color: #373737;
+ background-color: @modal-mask-bg; // lesshint duplicateProperty: false
+ height: 100%;
+ z-index: @zindex-modal-mask;
+ filter: ~"alpha(opacity=50)";
+
+ &-hidden {
+ display: none;
+ }
+ }
+
+ &-open {
+ overflow: hidden;
+ }
+}
+
+@media (max-width: @screen-md) {
+ .@{dialog-prefix-cls} {
+ width: auto !important;
+ margin: 10px;
+ }
+ .vertical-center-modal {
+ .@{dialog-prefix-cls} {
+ flex: 1;
+ }
+ }
+}
diff --git a/components/style.js b/components/style.js
index dc38a8430..82f50e0d0 100644
--- a/components/style.js
+++ b/components/style.js
@@ -27,3 +27,4 @@ import './auto-complete/style'
import './affix/style'
import './cascader/style'
import './back-top/style'
+import './modal/style'
diff --git a/components/trigger/Popup.vue b/components/trigger/Popup.vue
index 38e1b7ac1..da1adbb33 100644
--- a/components/trigger/Popup.vue
+++ b/components/trigger/Popup.vue
@@ -218,11 +218,11 @@ export default {
},
render () {
- const { destroyPopup, getMaskElement, getPopupElement } = this
+ const { destroyPopup, getMaskElement, getPopupElement, visible } = this
return (
{getMaskElement()}
- { destroyPopup ? null : getPopupElement()}
+ {(visible || !destroyPopup) ? getPopupElement() : null}
)
},
diff --git a/components/vc-dialog/Dialog.vue b/components/vc-dialog/Dialog.vue
index d850b3bf5..36a4bd300 100644
--- a/components/vc-dialog/Dialog.vue
+++ b/components/vc-dialog/Dialog.vue
@@ -62,6 +62,11 @@ export default {
prefixCls: 'rc-dialog',
}),
},
+ data () {
+ return {
+ destroyPopup: false,
+ }
+ },
// private inTransition: boolean;
// private titleId: string;
@@ -88,10 +93,13 @@ export default {
this.$nextTick(() => {
this.updatedCallback(val)
})
+ if (val) {
+ this.destroyPopup = false
+ }
},
},
beforeDestroy () {
- if (this.props.visible || this.inTransition) {
+ if (this.visible || this.inTransition) {
this.removeScrollingEffect()
}
},
@@ -127,12 +135,15 @@ export default {
}
},
onAnimateLeave () {
- const { afterClose } = this
+ const { afterClose, destroyOnClose } = this
// need demo?
// https://github.com/react-component/dialog/pull/28
if (this.$refs.wrap) {
this.$refs.wrap.style.display = 'none'
}
+ if (destroyOnClose) {
+ this.destroyPopup = true
+ }
this.inTransition = false
this.removeScrollingEffect()
if (afterClose) {
@@ -149,6 +160,7 @@ export default {
}
},
onKeydown (e) {
+ console.log('keydown')
const props = this.$props
if (props.keyboard && e.keyCode === KeyCode.ESC) {
this.close(e)
@@ -169,32 +181,31 @@ export default {
}
},
getDialogElement () {
- const props = this.$props
- const closable = props.closable
- const prefixCls = props.prefixCls
+ const { closable, prefixCls, width, height,
+ title, footer: tempFooter, bodyStyle, visible, bodyProps } = this
const dest = {}
- if (props.width !== undefined) {
- dest.width = props.width
+ if (width !== undefined) {
+ dest.width = typeof width === 'number' ? `${width}px` : width
}
- if (props.height !== undefined) {
- dest.height = props.height
+ if (height !== undefined) {
+ dest.height = typeof height === 'number' ? `${height}px` : height
}
let footer
- if (props.footer) {
+ if (tempFooter) {
footer = (
)
}
let header
- if (props.title) {
+ if (title) {
header = (
)
@@ -213,25 +224,29 @@ export default {
)
}
- const style = { ...dest }
+ const style = { ...this.dialogStyle, ...dest }
+ const cls = {
+ [prefixCls]: true,
+ ...this.dialogClass,
+ }
const transitionName = this.getTransitionName()
const dialogElement = (
{closer}
{header}
{this.$slots.default}
@@ -250,7 +265,7 @@ export default {
key='dialog'
{...dialogTransitionProps}
>
- {(props.visible || !props.destroyOnClose) ? dialogElement : null}
+ {(visible || !this.destroyPopup) ? dialogElement : null}
)
},
diff --git a/components/vc-dialog/DialogWrap.vue b/components/vc-dialog/DialogWrap.vue
index 26cb3d1b4..567b42e55 100644
--- a/components/vc-dialog/DialogWrap.vue
+++ b/components/vc-dialog/DialogWrap.vue
@@ -2,6 +2,7 @@
import Dialog from './Dialog'
import ContainerRender from '../_util/ContainerRender'
import getDialogPropTypes from './IDialogPropTypes'
+import { getStyle, getClass } from '../_util/props-util'
const IDialogPropTypes = getDialogPropTypes()
const DialogWrap = {
props: {
@@ -29,16 +30,22 @@ const DialogWrap = {
},
methods: {
getComponent (extra = {}) {
+ const { $attrs, $listeners, $props, $slots } = this
+
const dialogProps = {
props: {
- ...this.$props,
+ ...$props,
+ dialogClass: getClass(this),
+ dialogStyle: getStyle(this),
...extra,
},
+ attrs: $attrs,
ref: '_component',
key: 'dialog',
+ on: $listeners,
}
return (
-
+
)
},
diff --git a/components/vc-dialog/IDialogPropTypes.js b/components/vc-dialog/IDialogPropTypes.js
index 64b148c99..b0e6e397d 100644
--- a/components/vc-dialog/IDialogPropTypes.js
+++ b/components/vc-dialog/IDialogPropTypes.js
@@ -25,13 +25,15 @@ function IDialogPropTypes () {
maskStyle: PropTypes.object,
prefixCls: PropTypes.string,
wrapClassName: PropTypes.string,
- width: PropTypes.number,
- height: PropTypes.number,
+ width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
zIndex: PropTypes.number,
bodyProps: PropTypes.any,
maskProps: PropTypes.any,
wrapProps: PropTypes.any,
getContainer: PropTypes.func,
+ dialogStyle: PropTypes.object.def({}),
+ dialogClass: PropTypes.object.def({}),
}
}
diff --git a/components/vc-dialog/demo/ant-design.vue b/components/vc-dialog/demo/ant-design.vue
index 310f4d17f..9e5189fdf 100644
--- a/components/vc-dialog/demo/ant-design.vue
+++ b/components/vc-dialog/demo/ant-design.vue
@@ -3,7 +3,7 @@
import '../assets/index.less'
// use import Dialog from 'rc-dialog'
-import Dialog from '../DialogWrap'
+import Dialog from '../index'
export default {
data () {
@@ -34,7 +34,6 @@ export default {
},
changeWidth (e) {
- console.log(e)
this.width = this.width === 600 ? 800 : 600
},
@@ -110,4 +109,4 @@ export default {
)
},
}
-
\ No newline at end of file
+
diff --git a/components/vc-dialog/index.js b/components/vc-dialog/index.js
new file mode 100644
index 000000000..d545f04b4
--- /dev/null
+++ b/components/vc-dialog/index.js
@@ -0,0 +1,2 @@
+import DialogWrap from './DialogWrap'
+export default DialogWrap
diff --git a/examples/routes.js b/examples/routes.js
index 8fcd8f9f9..e96bbe787 100644
--- a/examples/routes.js
+++ b/examples/routes.js
@@ -3,7 +3,7 @@ const AsyncComp = () => {
const hashs = window.location.hash.split('/')
const d = hashs[hashs.length - 1]
return {
- component: import(`../components/vc-dialog/demo/${d}.vue`),
+ component: import(`../components/modal/demo/${d}`),
}
}
export default [