Skip to content

Commit

Permalink
feat: add prop appendToBody
Browse files Browse the repository at this point in the history
  • Loading branch information
mengxiong10 committed Oct 11, 2018
1 parent 8ebb016 commit e26e1f5
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 14 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ export default {
| disabled | Boolean | false | Disable the component |
| placeholder | String | | input placeholder text |
| width | String/Number | 210 | input size |
| append-to-body | Boolean | false | append the popup to body |
| popupStyle | Object | | popup style(override the top, left style) |
| not-before | String/Date | '' | Disable all dates before new Date(not-before) |
| not-after | String/Date | '' | Disable all dates after new Date(not-after) |
| disabled-days | Array/function| [] | Disable Days |
Expand Down
4 changes: 3 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ export default {
| editable | Boolean | true | 如果是false, 用户不能手动输入更新日期
| disabled | Boolean | false | 禁用组件
| placeholder | String | | 输入框placeholder
| width | String/Number | 210 | 设置宽度
| width | String/Number | 210 | 设置宽度
| append-to-body | Boolean | false | 弹出层放到body下面
| popup-style | Object | | 弹出层的样式(可以覆盖left,top样式)
| not-before | String/Date | '' | 禁止选择这个时间之前的时间
| not-after | String/Date | '' | 禁止选择这个时间之前=后的时间
| disabled-days | Array/function| [] | 自定义禁止的日期
Expand Down
3 changes: 2 additions & 1 deletion demo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ new Vue({ // eslint-disable-line
render (h) {
const example1 = {
'base': '<date-picker v-model="value1" lang="en" :not-before="new Date()"></date-picker>',
'range': '<date-picker v-model="value2" range ></date-picker>',
'range': '<date-picker v-model="value2" range appendToBody></date-picker>',
'month': '<date-picker v-model="value10" lang="en" type="month" format="YYYY-MM"></date-picker>',
'year': '<date-picker v-model="value11" lang="en" type="year" format="YYYY"></date-picker>',
'time': '<date-picker v-model="value12" lang="en" type="time" format="HH:mm:ss" placeholder="Select Time"></date-picker>'
Expand All @@ -64,6 +64,7 @@ new Vue({ // eslint-disable-line
v-model="value4"
lang="en"
type="datetime"
appendToBody
format="YYYY-MM-DD hh:mm:ss a"
:time-picker-options="{
start: '00:00',
Expand Down
1 change: 1 addition & 0 deletions src/directives/clickoutside.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default {
el['@clickoutside'] = e => {
if (
!el.contains(e.target) &&
!(vnode.context.popupElm && vnode.context.popupElm.contains(e.target)) &&
binding.expression &&
vnode.context[binding.expression]
) {
Expand Down
76 changes: 64 additions & 12 deletions src/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
</span>
</div>
<div class="mx-datepicker-popup"
:style="position"
:style="innerPopupStyle"
v-show="popupVisible"
ref="calendar">
<slot name="header">
Expand Down Expand Up @@ -106,7 +106,7 @@
<script>
import fecha from 'fecha'
import clickoutside from '@/directives/clickoutside'
import { isValidDate, isValidRange, isDateObejct, isPlainObject, formatDate, parseDate } from '@/utils/index'
import { isValidDate, isValidRange, isDateObejct, isPlainObject, formatDate, parseDate, throttle } from '@/utils/index'
import CalendarPanel from './calendar.vue'
import locale from '@/mixins/locale'
import Languages from '@/locale/languages'
Expand Down Expand Up @@ -183,6 +183,13 @@ export default {
inputClass: {
type: [String, Array],
default: 'mx-input'
},
appendToBody: {
type: Boolean,
default: false
},
popupStyle: {
type: Object
}
},
data () {
Expand Down Expand Up @@ -290,8 +297,31 @@ export default {
return this.format
}
return this.format.replace(/[Hh]+.*[msSaAZ]|\[.*?\]/g, '').trim() || 'YYYY-MM-DD'
},
innerPopupStyle () {
return { ...this.position, ...this.popupStyle }
}
},
mounted () {
if (this.appendToBody) {
this.popupElm = this.$refs.calendar
document.body.appendChild(this.popupElm)
}
this._displayPopup = throttle(() => {
if (this.popupVisible) {
this.displayPopup()
}
}, 200)
window.addEventListener('resize', this._displayPopup)
window.addEventListener('scroll', this._displayPopup)
},
beforeDestroy () {
if (this.popupElm && this.popupElm.parentNode === document.body) {
document.body.removeChild(this.popupElm)
}
window.removeEventListener('resize', this._displayPopup)
window.removeEventListener('scroll', this._displayPopup)
},
methods: {
initCalendar () {
this.handleValueChange(this.value)
Expand Down Expand Up @@ -384,31 +414,53 @@ export default {
closePopup () {
this.popupVisible = false
},
getPopupSize (element) {
const originalDisplay = element.style.display
const originalVisibility = element.style.visibility
element.style.display = 'block'
element.style.visibility = 'hidden'
const styles = window.getComputedStyle(element)
const width = element.offsetWidth + parseInt(styles.marginLeft) + parseInt(styles.marginRight)
const height = element.offsetHeight + parseInt(styles.marginTop) + parseInt(styles.marginBottom)
const result = { width, height }
element.style.display = originalDisplay
element.style.visibility = originalVisibility
return result
},
displayPopup () {
const dw = document.documentElement.clientWidth
const dh = document.documentElement.clientHeight
const InputRect = this.$el.getBoundingClientRect()
const PopupRect = this.$refs.calendar.getBoundingClientRect()
this.position = {}
const PopupRect = this._popupRect || (this._popupRect = this.getPopupSize(this.$refs.calendar))
const position = {}
let offsetRelativeToInputX = 0
let offsetRelativeToInputY = 0
if (this.appendToBody) {
offsetRelativeToInputX = window.pageXOffset + InputRect.left
offsetRelativeToInputY = window.pageYOffset + InputRect.top
}
if (
dw - InputRect.left < PopupRect.width &&
InputRect.right < PopupRect.width
) {
this.position.left = 1 - InputRect.left + 'px'
position.left = offsetRelativeToInputX - InputRect.left + 1 + 'px'
} else if (InputRect.left + InputRect.width / 2 <= dw / 2) {
this.position.left = 0
position.left = offsetRelativeToInputX + 'px'
} else {
this.position.right = 0
position.left = offsetRelativeToInputX + InputRect.width - PopupRect.width + 'px'
}
if (
InputRect.top <= PopupRect.height + 1 &&
dh - InputRect.bottom <= PopupRect.height + 1
InputRect.top <= PopupRect.height &&
dh - InputRect.bottom <= PopupRect.height
) {
this.position.top = dh - InputRect.top - PopupRect.height - 1 + 'px'
position.top = offsetRelativeToInputY + dh - InputRect.top - PopupRect.height + 'px'
} else if (InputRect.top + InputRect.height / 2 <= dh / 2) {
this.position.top = '100%'
position.top = offsetRelativeToInputY + InputRect.height + 'px'
} else {
this.position.bottom = '100%'
position.top = offsetRelativeToInputY - PopupRect.height + 'px'
}
if (position.top !== this.position.top || position.left !== this.position.left) {
this.position = position
}
},
handleInput (event) {
Expand Down
22 changes: 22 additions & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,25 @@ export function parseDate (value, format) {
return false
}
}

export function throttle (action, delay) {
let lastRun = 0
let timeout = null
return function () {
if (timeout) {
return
}
const args = arguments
const elapsed = Date.now() - lastRun
const callBack = () => {
lastRun = Date.now()
timeout = null
action.apply(this, args)
}
if (elapsed >= delay) {
callBack()
} else {
timeout = setTimeout(callBack, delay)
}
}
}
10 changes: 10 additions & 0 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ afterEach(() => {
})

describe('datepicker', () => {
it('prop: appendToBody', () => {
wrapper = mount(DatePicker, {
propsData: {
appendToBody: true
}
})
const popup = wrapper.find('.mx-datepicker-popup')
expect(popup.element.parentNode).toBe(document.body)
})

it('click: pick date', () => {
wrapper = mount(DatePicker, {
propsData: {
Expand Down

0 comments on commit e26e1f5

Please sign in to comment.