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
src/components/modal/modal.vue
| ... | ... | @@ -6,13 +6,16 @@ |
| 6 | 6 | <div :class="wrapClasses" @click="handleWrapClick"> |
| 7 | 7 | <transition :name="transitionNames[0]" @after-leave="animationFinish"> |
| 8 | 8 | <div :class="classes" :style="mainStyles" v-show="visible"> |
| 9 | - <div :class="contentClasses"> | |
| 9 | + <div :class="contentClasses" ref="content" :style="contentStyles"> | |
| 10 | 10 | <a :class="[prefixCls + '-close']" v-if="closable" @click="close"> |
| 11 | 11 | <slot name="close"> |
| 12 | 12 | <Icon type="ios-close"></Icon> |
| 13 | 13 | </slot> |
| 14 | 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 | 19 | <div :class="[prefixCls + '-body']"><slot></slot></div> |
| 17 | 20 | <div :class="[prefixCls + '-footer']" v-if="!footerHide"> |
| 18 | 21 | <slot name="footer"> |
| ... | ... | @@ -34,6 +37,8 @@ |
| 34 | 37 | import Emitter from '../../mixins/emitter'; |
| 35 | 38 | import ScrollbarMixins from './mixins-scrollbar'; |
| 36 | 39 | |
| 40 | + import { on, off } from '../../utils/dom'; | |
| 41 | + | |
| 37 | 42 | const prefixCls = 'ivu-modal'; |
| 38 | 43 | |
| 39 | 44 | export default { |
| ... | ... | @@ -115,7 +120,14 @@ |
| 115 | 120 | wrapShow: false, |
| 116 | 121 | showHead: true, |
| 117 | 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 | 133 | computed: { |
| ... | ... | @@ -146,7 +158,9 @@ |
| 146 | 158 | return [ |
| 147 | 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 | 168 | let style = {}; |
| 155 | 169 | |
| 156 | 170 | const width = parseInt(this.width); |
| 157 | - const styleWidth = { | |
| 171 | + const styleWidth = this.dragData.x !== null ? { | |
| 172 | + top: 0 | |
| 173 | + } : { | |
| 158 | 174 | width: width <= 100 ? `${width}%` : `${width}px` |
| 159 | 175 | }; |
| 160 | 176 | |
| ... | ... | @@ -164,6 +180,22 @@ |
| 164 | 180 | |
| 165 | 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 | 199 | localeOkText () { |
| 168 | 200 | if (this.okText === undefined) { |
| 169 | 201 | return this.t('i.modal.okText'); |
| ... | ... | @@ -219,6 +251,51 @@ |
| 219 | 251 | }, |
| 220 | 252 | animationFinish() { |
| 221 | 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 | 301 | mounted () { | ... | ... |
src/styles/components/modal.less
| ... | ... | @@ -44,6 +44,17 @@ |
| 44 | 44 | &-no-mask{ |
| 45 | 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 | 60 | &-header { | ... | ... |