kui-vue
Version:
A high quality UI Toolkit built on Vue.js 2.0
365 lines (333 loc) • 11.4 kB
JSX
import { getPosition } from '../_tool/utils'
import transfer from "../_tool/transfer";
import resize from "../_tool/resize";
import outsideclick from "../_tool/outsiteclick";
export default {
name: 'Drop',
directives: { transfer, resize, outsideclick },
props: {
transfer: Boolean,
value: Boolean,
className: [String, Array],
width: Number,
placement: String,
trigger: { type: String, default: "click" },
transitionName: { type: String, default: 'dropdown' },
selection: { validator: v => true },
updateKey: [String, Object, Array],
offsetLeft: { type: Number, default: 0 },
extendWidth: Boolean
},
watch: {
updateKey(o, v) {
if (o != v) {
this.$nextTick(e => {
this.setPosition()
})
}
},
rendered(value) {
this.$emit('render')
},
value(value) {
this.rendered = true
this.visible = value
if (value) {
this.left = 0
this.top = 0
this.$nextTick(e => {
this.setPosition()
})
}
},
placement(value) {
this.currentPlacement = value
}
},
data() {
return {
left: 0,
top: 0,
minWidth: 0,
mousedownIn: false,
transformOrigin: '',
currentPlacement: this.placement,
rendered: this.value === true,
visible: this.value
}
},
mounted() {
this.$nextTick(() => this.setPosition())
!this.$isServer && document.addEventListener('mousedown', this.onMouseDown)
},
beforeDestroy() {
!this.$isServer && document.removeEventListener('mousedown', this.onMouseDown)
},
render() {
const props = {
key: this.$vnode.key,
class: this.className,
style: {
left: `${this.left}px`,
top: `${this.top}px`,
width: `${this.width}px`,
minWidth: `${this.minWidth}px`,
transformOrigin: this.transformOrigin
},
attrs: {
'k-placement': this.currentPlacement
},
on: {
...this.$listeners
},
}
return this.rendered ? <transition name={this.transitionName}>
<div {...props} v-show={this.visible}
v-transfer={this.transfer}
v-outsideclick={this.hide}
v-resize={this.resize}>
{this.$slots.default}
</div>
</transition> : null
},
methods: {
showContextmenu(e) {
let pickerHeight = this.$el.offsetHeight
let pickerWidth = this.$el.offsetWidth
let clientHeight = document.documentElement.clientHeight
let clientWidth = document.documentElement.clientWidth
let offsetTop = document.body.scrollTop || document.documentElement.scrollTop || window.scrollY;
let offsetLeft = document.body.scrollLeft || document.documentElement.scrollLeft || window.scrollX;
let left = e.clientX + offsetLeft;
let top = e.clientY + offsetTop;
let showInRight = clientWidth - e.clientX > pickerWidth
let showInBottom = clientHeight - e.clientY > pickerHeight
let transformOrigin = 'top center';
if (!showInRight) {
left -= pickerWidth
}
if (!showInBottom) {
top -= pickerHeight
transformOrigin = 'bottom center'
}
this.left = left
this.top = top
this.transformOrigin = transformOrigin
},
onMouseDown({ target }) {
this.mousedownIn = this.visible && this.$el.contains(target)
},
setPosition() {
if (this.trigger == 'contextmenu' || !this.rendered || !this.value) {
return;
}
let { selection, placement = 'bottom', offsetLeft } = this
let picker = this.$el
if (this.extendWidth) {
picker.style.width = selection.offsetWidth + 'px'
}
let top = 0, left = 0, offset = 3;
let origin = ["", ""]
if (picker && selection) {
let selectionRect = selection.getBoundingClientRect();
let pickerHeight = picker.offsetHeight
let pickerWidth = picker.offsetWidth
let clientHeight = document.documentElement.clientHeight
let clientWidth = document.documentElement.clientWidth
let scrollTop = document.documentElement.scrollTop
let scrollLeft = document.documentElement.scrollLeft
//是否有足够的空间
//底部
let enoughBottom = clientHeight - selectionRect.bottom > pickerHeight
let enoughBottomC = clientHeight - selectionRect.top > pickerHeight
//上面
let enoughTop = selectionRect.top > pickerHeight
let enoughTopC = selectionRect.bottom > pickerHeight
//左边
let enoughLeft = selectionRect.left > pickerWidth
let enoughLeftC = selectionRect.right > pickerWidth
//右边
let enoughRight = clientWidth - selectionRect.right > pickerWidth
let enoughRightC = clientWidth - selectionRect.left > pickerWidth
// console.log(showInRight, selectionRect.left > pickerWidth, selectionRect.right, pickerWidth)
// console.log(selectionRect.right, pickerWidth, placement, ', bottom:', enoughBottom, 'top:', enoughTop, 'left:', enoughLeft, 'leftC:', enoughLeftC, 'right:', enoughRight, 'rightC:', enoughRightC)
// console.log(placement, 'showInTop:', showInTop, 'showInBottom:', showInBottom, clientHeight, scrollTop, selectionRect.top, pickerHeight)
//reset placement
if (placement.startsWith('top')) {
if (!enoughTop) {
placement = placement.replace('top', 'bottom')
}
if (!enoughLeftC) {
placement = placement.replace('right', 'left')
if (placement == 'top') {
placement = "top-left"
}
if (placement == 'bottom') {
placement = "bottom-left"
}
}
if (!enoughRightC) {
placement = placement.replace('left', 'right')
if (placement == 'bottom') {
placement = "bottom-right"
}
if (placement == 'top') {
placement = "top-right"
}
}
} else if (placement.startsWith('right')) {
if (!enoughRight) {
placement = placement.replace('right', 'left')
}
if (!enoughBottomC) {
placement = placement.replace('top', 'bottom')
if (placement == 'right') {
placement = 'right-bottom'
}
if (placement == 'left') {
placement = 'left-bottom'
}
}
if (!enoughTopC) {
placement = placement.replace('bottom', 'top')// ok
if (placement == 'left') {
placement = 'left-top'
}
if (placement == 'right') {
placement = 'right-top'
}
}
} else if (placement.startsWith('bottom')) {
if (!enoughBottom) {
placement = placement.replace('bottom', 'top')
}
if (!enoughLeftC) {
placement = placement.replace('right', 'left')
if (placement == 'bottom') {
placement = "bottom-left"
}
if (placement == 'top') {
placement = "top-left"
}
}
if (!enoughRightC) {
placement = placement.replace('left', 'right')
if (placement == 'top') {
placement = "top-right"
}
if (placement == 'bottom') {
placement = "bottom-right"
}
}
} else if (placement.startsWith('left')) {
if (!enoughLeft) {
placement = placement.replace('left', 'right')
}
if (!enoughTopC) {
placement = placement.replace('bottom', 'top')
if (placement == 'right') {
placement = 'right-top'
}
if (placement == 'left') {
placement = 'left-top'
}
}
if (!enoughBottomC) {
placement = placement.replace('top', 'bottom')
if (placement == 'right') {
placement = 'right-bottom'
}
if (placement == 'left') {
placement = 'left-bottom'
}
}
}
// console.log('result:' + placement)
// set postion
if (!placement.includes('-')) {
//equal
if (placement == 'top') {
top = selectionRect.top - pickerHeight - offset + scrollTop
left = selectionRect.left + (selectionRect.width - pickerWidth) / 2 + scrollLeft
origin = ['center', 'bottom']
}
if (placement == 'bottom') {
top = selectionRect.bottom + offset + scrollTop
left = selectionRect.left + (selectionRect.width - pickerWidth) / 2 + scrollLeft
origin = ['center', 'top']
}
if (placement == 'left') {
left = selectionRect.left - pickerWidth - offset + scrollLeft
top = selectionRect.top + (selectionRect.height - pickerHeight) / 2 + scrollTop
origin = ['right', 'center']
}
if (placement == 'right') {
left = selectionRect.right + offset + scrollLeft
top = selectionRect.top + (selectionRect.height - pickerHeight) / 2 + scrollTop
origin = ['left', 'center']
}
} else {
//start
if (placement.startsWith('bottom')) {
top = selectionRect.bottom + offset + scrollTop
origin[1] = 'top'
}
if (placement.startsWith('right')) {
left = selectionRect.right + offset + scrollLeft
origin[0] = 'left'
}
if (placement.startsWith('top')) {
top = selectionRect.top - pickerHeight - offset + scrollTop
origin[1] = 'bottom'
}
if (placement.startsWith('left')) {
left = selectionRect.left - pickerWidth - offset + scrollLeft
origin[0] = 'right'
}
//end
if (placement.endsWith('left')) {
left = selectionRect.left + scrollLeft
origin[0] = 'left'
}
if (placement.endsWith('right')) {
left = selectionRect.right - pickerWidth + scrollLeft - 0.5
origin[0] = 'right'
}
if (placement.endsWith('bottom')) {
top = selectionRect.bottom - pickerHeight + scrollTop
origin[1] = 'bottom'
}
if (placement.endsWith('top')) {
top = selectionRect.top + scrollTop
origin[1] = 'top'
}
}
}
// console.log(placement, origin)
this.top = top
this.left = left + (placement.includes('right') ? offsetLeft : -offsetLeft)
this.transformOrigin = origin.join(' ')
this.currentPlacement = placement
},
hide(e) {
let { target } = e
e.stopPropagation()
if (this.visible &&
this.selection &&
target.parentNode != null && target.parentNode.parentNode != null &&
!this.selection.contains(target) &&
!this.$el.contains(target) &&
!this.mousedownIn
) {
// this.visible = false
this.$emit('hide', false)
}
},
resize() {
if (this.visible) {
this.$emit('resize')
this.setPosition()
}
}
}
}