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..ea74b6b4f 100644 --- a/components/_util/props-util.js +++ b/components/_util/props-util.js @@ -111,7 +111,14 @@ export function getClass (ele) { } else if (ele.$vnode && ele.$vnode.data) { data = ele.$vnode.data } - return data.class || data.staticClass + const tempCls = data.class || data.staticClass + let cls = {} + if (typeof tempCls === 'string') { + tempCls.split(' ').forEach(c => { cls[c.trim()] = true }) + } else { + cls = tempCls + } + return cls } export function getStyle (ele) { let data = {} @@ -138,6 +145,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 +161,6 @@ export { getAttrs, getValueByProp, parseStyleText, + initDefaultProps, } export default hasProp diff --git a/components/index.js b/components/index.js index 6b22f7e23..04b0ca7b7 100644 --- a/components/index.js +++ b/components/index.js @@ -72,12 +72,6 @@ import message from './message' export { default as Spin } from './spin' -const api = { - notification, - message, -} -export { api } - import Select from './select' const SelectOption = Select.Option const SelectOptGroup = Select.OptGroup @@ -93,4 +87,24 @@ 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' +import { + info, + success, + error, + warning, + warn, + confirm, +} from './modal' +const api = { + notification, + message, + modalInfo: info, + modalSuccess: success, + modalError: error, + modalWarning: warning, + modalWarn: warn, + modalConfirm: confirm, +} +export { api } 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..bec01ce92 --- /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..81ff819d2 --- /dev/null +++ b/components/modal/Modal.vue @@ -0,0 +1,153 @@ + 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 + + +``` + diff --git a/components/modal/demo/confirm.md b/components/modal/demo/confirm.md new file mode 100644 index 000000000..b5db87850 --- /dev/null +++ b/components/modal/demo/confirm.md @@ -0,0 +1,58 @@ + + +#### 确认对话框 +使用 `confirm()` 可以快捷地弹出确认框。 + + + +#### Confirmation modal dialog +To use `confirm()` to popup a confirmation modal dialog. + + +```html + + +``` + diff --git a/components/modal/index.js b/components/modal/index.js new file mode 100644 index 000000000..032891f54 --- /dev/null +++ b/components/modal/index.js @@ -0,0 +1,66 @@ +import Modal from './Modal' +import modalConfirm from './confirm' + +// export { ActionButtonProps } from './ActionButton' +// export { ModalProps, ModalFuncProps } from './Modal' + +const info = function (props) { + const config = { + type: 'info', + iconType: 'info-circle', + okCancel: false, + ...props, + } + return modalConfirm(config) +} + +const success = function (props) { + const config = { + type: 'success', + iconType: 'check-circle', + okCancel: false, + ...props, + } + return modalConfirm(config) +} + +const error = function (props) { + const config = { + type: 'error', + iconType: 'cross-circle', + okCancel: false, + ...props, + } + return modalConfirm(config) +} + +const warning = function (props) { + const config = { + type: 'warning', + iconType: 'exclamation-circle', + okCancel: false, + ...props, + } + return modalConfirm(config) +} +const warn = warning + +const confirm = function (props) { + const config = { + type: 'confirm', + okCancel: true, + ...props, + } + return modalConfirm(config) +} + +export { + info, + success, + error, + warning, + warn, + confirm, +} + +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/pagination/Pagination.vue b/components/pagination/Pagination.vue index ca5691f88..b2e95f583 100644 --- a/components/pagination/Pagination.vue +++ b/components/pagination/Pagination.vue @@ -40,12 +40,6 @@ export default { model: { prop: 'current', }, - data () { - const { current } = this - return { - stateCurrent: +current, - } - }, methods: { renderPagination (locale) { const { buildOptionText, size, ...restProps } = getOptionProps(this) 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..c2b081775 100644 --- a/components/vc-dialog/Dialog.vue +++ b/components/vc-dialog/Dialog.vue @@ -11,7 +11,7 @@ let uuid = 0 let openCount = 0 /* eslint react/no-is-mounted:0 */ - +function noop () {} function getScroll (w, top) { let ret = w[`page${top ? 'Y' : 'X'}Offset`] const method = `scroll${top ? 'Top' : 'Left'}` @@ -62,6 +62,11 @@ export default { prefixCls: 'rc-dialog', }), }, + data () { + return { + destroyPopup: false, + } + }, // private inTransition: boolean; // private titleId: string; @@ -85,13 +90,16 @@ export default { watch: { visible (val) { + if (val) { + this.destroyPopup = false + } this.$nextTick(() => { - this.updatedCallback(val) + this.updatedCallback(!val) }) }, }, 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) { @@ -169,32 +180,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 = (
- {props.footer} + {tempFooter}
) } let header - if (props.title) { + if (title) { header = (
- {props.title} + {title}
) @@ -205,7 +215,7 @@ export default { closer = ( ) } - 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 +264,7 @@ export default { key='dialog' {...dialogTransitionProps} > - {(props.visible || !props.destroyOnClose) ? dialogElement : null} + {(visible || !this.destroyPopup) ? dialogElement : null} ) }, @@ -387,7 +401,7 @@ export default { onKeydown={this.onKeydown} class={`${prefixCls}-wrap ${wrapClassName || ''}`} ref='wrap' - onClick={maskClosable ? this.onMaskClick : undefined} + onClick={maskClosable ? this.onMaskClick : noop} role='dialog' aria-labelledby={title ? this.titleId : null} style={style} 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 ( - {this.$slots.default} + {$slots.default} ) }, 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/components/vc-pagination/Options.vue b/components/vc-pagination/Options.vue index b1f735432..11cbb2484 100644 --- a/components/vc-pagination/Options.vue +++ b/components/vc-pagination/Options.vue @@ -101,7 +101,7 @@ export default { gotoButton = ( {goButton} ) } diff --git a/components/vc-pagination/Pager.vue b/components/vc-pagination/Pager.vue index 24813e270..81bbfd0b6 100644 --- a/components/vc-pagination/Pager.vue +++ b/components/vc-pagination/Pager.vue @@ -22,9 +22,6 @@ export default { if (this.active) { cls = `${cls} ${prefixCls}-active` } - if (this.className) { - cls = `${cls} ${this.className}` - } return cls }, }, @@ -33,7 +30,7 @@ export default { this.$emit('click', this.page) }, handleKeyPress (event) { - this.$emit('keyPress', event, this.handleClick, this.page) + this.$emit('keypress', event, this.handleClick, this.page) }, }, render () { diff --git a/components/vc-pagination/Pagination.vue b/components/vc-pagination/Pagination.vue index 796d629a0..9e37ec597 100644 --- a/components/vc-pagination/Pagination.vue +++ b/components/vc-pagination/Pagination.vue @@ -84,7 +84,7 @@ export default { let current = this.current const newCurrent = this.calculatePage(val) current = current > newCurrent ? newCurrent : current - if (hasProp(this, 'current')) { + if (!hasProp(this, 'current')) { newState.stateCurrent = current newState.stateCurrentInputValue = current } @@ -188,7 +188,7 @@ export default { stateCurrentInputValue: page, }) } - this.$emit('input', page) + // this.$emit('input', page) this.$emit('change', page, this.statePageSize) return page } @@ -254,7 +254,7 @@ export default { @@ -263,7 +263,7 @@ export default { gotoButton = ( {goButton} ) }