Commit d4b59a9adbeacacf4ed8c4e2d55f3954227bc4da
1 parent
1c7289e9
Modal add dragable prop
Showing
3 changed files
with
94 additions
and
6 deletions
Show diff stats
examples/routers/modal.vue
| @@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
| 5 | <Modal | 5 | <Modal |
| 6 | v-model="modal1" | 6 | v-model="modal1" |
| 7 | title="Common Modal dialog box title" | 7 | title="Common Modal dialog box title" |
| 8 | - :mask="false" | 8 | + dragable |
| 9 | @on-ok="ok" | 9 | @on-ok="ok" |
| 10 | @on-cancel="cancel"> | 10 | @on-cancel="cancel"> |
| 11 | <p>Content of dialog</p> | 11 | <p>Content of dialog</p> |
src/components/modal/modal.vue
| @@ -6,13 +6,16 @@ | @@ -6,13 +6,16 @@ | ||
| 6 | <div :class="wrapClasses" @click="handleWrapClick"> | 6 | <div :class="wrapClasses" @click="handleWrapClick"> |
| 7 | <transition :name="transitionNames[0]" @after-leave="animationFinish"> | 7 | <transition :name="transitionNames[0]" @after-leave="animationFinish"> |
| 8 | <div :class="classes" :style="mainStyles" v-show="visible"> | 8 | <div :class="classes" :style="mainStyles" v-show="visible"> |
| 9 | - <div :class="contentClasses"> | 9 | + <div :class="contentClasses" ref="content" :style="contentStyles"> |
| 10 | <a :class="[prefixCls + '-close']" v-if="closable" @click="close"> | 10 | <a :class="[prefixCls + '-close']" v-if="closable" @click="close"> |
| 11 | <slot name="close"> | 11 | <slot name="close"> |
| 12 | <Icon type="ios-close"></Icon> | 12 | <Icon type="ios-close"></Icon> |
| 13 | </slot> | 13 | </slot> |
| 14 | </a> | 14 | </a> |
| 15 | - <div :class="[prefixCls + '-header']" v-if="showHead"><slot name="header"><div :class="[prefixCls + '-header-inner']">{{ title }}</div></slot></div> | 15 | + <div :class="[prefixCls + '-header']" |
| 16 | + @mousedown="handleMoveStart" | ||
| 17 | + v-if="showHead" | ||
| 18 | + ><slot name="header"><div :class="[prefixCls + '-header-inner']">{{ title }}</div></slot></div> | ||
| 16 | <div :class="[prefixCls + '-body']"><slot></slot></div> | 19 | <div :class="[prefixCls + '-body']"><slot></slot></div> |
| 17 | <div :class="[prefixCls + '-footer']" v-if="!footerHide"> | 20 | <div :class="[prefixCls + '-footer']" v-if="!footerHide"> |
| 18 | <slot name="footer"> | 21 | <slot name="footer"> |
| @@ -34,6 +37,8 @@ | @@ -34,6 +37,8 @@ | ||
| 34 | import Emitter from '../../mixins/emitter'; | 37 | import Emitter from '../../mixins/emitter'; |
| 35 | import ScrollbarMixins from './mixins-scrollbar'; | 38 | import ScrollbarMixins from './mixins-scrollbar'; |
| 36 | 39 | ||
| 40 | + import { on, off } from '../../utils/dom'; | ||
| 41 | + | ||
| 37 | const prefixCls = 'ivu-modal'; | 42 | const prefixCls = 'ivu-modal'; |
| 38 | 43 | ||
| 39 | export default { | 44 | export default { |
| @@ -115,7 +120,14 @@ | @@ -115,7 +120,14 @@ | ||
| 115 | wrapShow: false, | 120 | wrapShow: false, |
| 116 | showHead: true, | 121 | showHead: true, |
| 117 | buttonLoading: false, | 122 | buttonLoading: false, |
| 118 | - visible: this.value | 123 | + visible: this.value, |
| 124 | + dragData: { | ||
| 125 | + x: null, | ||
| 126 | + y: null, | ||
| 127 | + dragX: null, | ||
| 128 | + dragY: null, | ||
| 129 | + dragging: false | ||
| 130 | + } | ||
| 119 | }; | 131 | }; |
| 120 | }, | 132 | }, |
| 121 | computed: { | 133 | computed: { |
| @@ -146,7 +158,9 @@ | @@ -146,7 +158,9 @@ | ||
| 146 | return [ | 158 | return [ |
| 147 | `${prefixCls}-content`, | 159 | `${prefixCls}-content`, |
| 148 | { | 160 | { |
| 149 | - [`${prefixCls}-content-no-mask`]: !this.showMask | 161 | + [`${prefixCls}-content-no-mask`]: !this.showMask, |
| 162 | + [`${prefixCls}-content-drag`]: this.dragable, | ||
| 163 | + [`${prefixCls}-content-dragging`]: this.dragable && this.dragData.dragging | ||
| 150 | } | 164 | } |
| 151 | ]; | 165 | ]; |
| 152 | }, | 166 | }, |
| @@ -154,7 +168,9 @@ | @@ -154,7 +168,9 @@ | ||
| 154 | let style = {}; | 168 | let style = {}; |
| 155 | 169 | ||
| 156 | const width = parseInt(this.width); | 170 | const width = parseInt(this.width); |
| 157 | - const styleWidth = { | 171 | + const styleWidth = this.dragData.x !== null ? { |
| 172 | + top: 0 | ||
| 173 | + } : { | ||
| 158 | width: width <= 100 ? `${width}%` : `${width}px` | 174 | width: width <= 100 ? `${width}%` : `${width}px` |
| 159 | }; | 175 | }; |
| 160 | 176 | ||
| @@ -164,6 +180,22 @@ | @@ -164,6 +180,22 @@ | ||
| 164 | 180 | ||
| 165 | return style; | 181 | return style; |
| 166 | }, | 182 | }, |
| 183 | + contentStyles () { | ||
| 184 | + let style = {}; | ||
| 185 | + | ||
| 186 | + if (this.dragable) { | ||
| 187 | + if (this.dragData.x !== null) style.left = `${this.dragData.x}px`; | ||
| 188 | + if (this.dragData.y !== null) style.top = `${this.dragData.y}px`; | ||
| 189 | + const width = parseInt(this.width); | ||
| 190 | + const styleWidth = { | ||
| 191 | + width: width <= 100 ? `${width}%` : `${width}px` | ||
| 192 | + }; | ||
| 193 | + | ||
| 194 | + Object.assign(style, styleWidth); | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + return style; | ||
| 198 | + }, | ||
| 167 | localeOkText () { | 199 | localeOkText () { |
| 168 | if (this.okText === undefined) { | 200 | if (this.okText === undefined) { |
| 169 | return this.t('i.modal.okText'); | 201 | return this.t('i.modal.okText'); |
| @@ -219,6 +251,51 @@ | @@ -219,6 +251,51 @@ | ||
| 219 | }, | 251 | }, |
| 220 | animationFinish() { | 252 | animationFinish() { |
| 221 | this.$emit('on-hidden'); | 253 | this.$emit('on-hidden'); |
| 254 | + }, | ||
| 255 | + handleMoveStart (event) { | ||
| 256 | + if (!this.dragable) return false; | ||
| 257 | + | ||
| 258 | + const $content = this.$refs.content; | ||
| 259 | + const rect = $content.getBoundingClientRect(); | ||
| 260 | + this.dragData.x = rect.x; | ||
| 261 | + this.dragData.y = rect.y; | ||
| 262 | + | ||
| 263 | + const distance = { | ||
| 264 | + x: event.clientX, | ||
| 265 | + y: event.clientY | ||
| 266 | + }; | ||
| 267 | + | ||
| 268 | + this.dragData.dragX = distance.x; | ||
| 269 | + this.dragData.dragY = distance.y; | ||
| 270 | + | ||
| 271 | + this.dragData.dragging = true; | ||
| 272 | + | ||
| 273 | + on(window, 'mousemove', this.handleMoveMove); | ||
| 274 | + on(window, 'mouseup', this.handleMoveEnd); | ||
| 275 | + }, | ||
| 276 | + handleMoveMove (event) { | ||
| 277 | + if (!this.dragData.dragging) return false; | ||
| 278 | + | ||
| 279 | + const distance = { | ||
| 280 | + x: event.clientX, | ||
| 281 | + y: event.clientY | ||
| 282 | + }; | ||
| 283 | + | ||
| 284 | + const diff_distance = { | ||
| 285 | + x: distance.x - this.dragData.dragX, | ||
| 286 | + y: distance.y - this.dragData.dragY | ||
| 287 | + }; | ||
| 288 | + | ||
| 289 | + this.dragData.x += diff_distance.x; | ||
| 290 | + this.dragData.y += diff_distance.y; | ||
| 291 | + | ||
| 292 | + this.dragData.dragX = distance.x; | ||
| 293 | + this.dragData.dragY = distance.y; | ||
| 294 | + }, | ||
| 295 | + handleMoveEnd (event) { | ||
| 296 | + this.dragData.dragging = false; | ||
| 297 | + off(window, 'mousemove', this.handleMoveMove); | ||
| 298 | + off(window, 'mouseup', this.handleMoveEnd); | ||
| 222 | } | 299 | } |
| 223 | }, | 300 | }, |
| 224 | mounted () { | 301 | mounted () { |
src/styles/components/modal.less
| @@ -44,6 +44,17 @@ | @@ -44,6 +44,17 @@ | ||
| 44 | &-no-mask{ | 44 | &-no-mask{ |
| 45 | pointer-events: auto; | 45 | pointer-events: auto; |
| 46 | } | 46 | } |
| 47 | + &-drag{ | ||
| 48 | + position: absolute; | ||
| 49 | + .@{modal-prefix-cls}-header{ | ||
| 50 | + cursor: move; | ||
| 51 | + } | ||
| 52 | + } | ||
| 53 | + &-dragging{ | ||
| 54 | + -webkit-user-select: none; | ||
| 55 | + -moz-user-select: none; | ||
| 56 | + user-select: none; | ||
| 57 | + } | ||
| 47 | } | 58 | } |
| 48 | 59 | ||
| 49 | &-header { | 60 | &-header { |