mirror of
https://github.com/tenrok/vue2-datepicker.git
synced 2026-06-24 15:00:35 +03:00
feat: add prop appendToBody
This commit is contained in:
@@ -81,6 +81,8 @@ export default {
|
|||||||
| disabled | Boolean | false | Disable the component |
|
| disabled | Boolean | false | Disable the component |
|
||||||
| placeholder | String | | input placeholder text |
|
| placeholder | String | | input placeholder text |
|
||||||
| width | String/Number | 210 | input size |
|
| 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-before | String/Date | '' | Disable all dates before new Date(not-before) |
|
||||||
| not-after | String/Date | '' | Disable all dates after new Date(not-after) |
|
| not-after | String/Date | '' | Disable all dates after new Date(not-after) |
|
||||||
| disabled-days | Array/function| [] | Disable Days |
|
| disabled-days | Array/function| [] | Disable Days |
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ export default {
|
|||||||
| disabled | Boolean | false | 禁用组件
|
| disabled | Boolean | false | 禁用组件
|
||||||
| placeholder | String | | 输入框placeholder
|
| 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-before | String/Date | '' | 禁止选择这个时间之前的时间
|
||||||
| not-after | String/Date | '' | 禁止选择这个时间之前=后的时间
|
| not-after | String/Date | '' | 禁止选择这个时间之前=后的时间
|
||||||
| disabled-days | Array/function| [] | 自定义禁止的日期
|
| disabled-days | Array/function| [] | 自定义禁止的日期
|
||||||
|
|||||||
+2
-1
@@ -47,7 +47,7 @@ new Vue({ // eslint-disable-line
|
|||||||
render (h) {
|
render (h) {
|
||||||
const example1 = {
|
const example1 = {
|
||||||
'base': '<date-picker v-model="value1" lang="en" :not-before="new Date()"></date-picker>',
|
'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>',
|
'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>',
|
'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>'
|
'time': '<date-picker v-model="value12" lang="en" type="time" format="HH:mm:ss" placeholder="Select Time"></date-picker>'
|
||||||
@@ -64,6 +64,7 @@ new Vue({ // eslint-disable-line
|
|||||||
v-model="value4"
|
v-model="value4"
|
||||||
lang="en"
|
lang="en"
|
||||||
type="datetime"
|
type="datetime"
|
||||||
|
appendToBody
|
||||||
format="YYYY-MM-DD hh:mm:ss a"
|
format="YYYY-MM-DD hh:mm:ss a"
|
||||||
:time-picker-options="{
|
:time-picker-options="{
|
||||||
start: '00:00',
|
start: '00:00',
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export default {
|
|||||||
el['@clickoutside'] = e => {
|
el['@clickoutside'] = e => {
|
||||||
if (
|
if (
|
||||||
!el.contains(e.target) &&
|
!el.contains(e.target) &&
|
||||||
|
!(vnode.context.popupElm && vnode.context.popupElm.contains(e.target)) &&
|
||||||
binding.expression &&
|
binding.expression &&
|
||||||
vnode.context[binding.expression]
|
vnode.context[binding.expression]
|
||||||
) {
|
) {
|
||||||
|
|||||||
+64
-12
@@ -44,7 +44,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mx-datepicker-popup"
|
<div class="mx-datepicker-popup"
|
||||||
:style="position"
|
:style="innerPopupStyle"
|
||||||
v-show="popupVisible"
|
v-show="popupVisible"
|
||||||
ref="calendar">
|
ref="calendar">
|
||||||
<slot name="header">
|
<slot name="header">
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import fecha from 'fecha'
|
import fecha from 'fecha'
|
||||||
import clickoutside from '@/directives/clickoutside'
|
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 CalendarPanel from './calendar.vue'
|
||||||
import locale from '@/mixins/locale'
|
import locale from '@/mixins/locale'
|
||||||
import Languages from '@/locale/languages'
|
import Languages from '@/locale/languages'
|
||||||
@@ -183,6 +183,13 @@ export default {
|
|||||||
inputClass: {
|
inputClass: {
|
||||||
type: [String, Array],
|
type: [String, Array],
|
||||||
default: 'mx-input'
|
default: 'mx-input'
|
||||||
|
},
|
||||||
|
appendToBody: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
popupStyle: {
|
||||||
|
type: Object
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
@@ -290,8 +297,31 @@ export default {
|
|||||||
return this.format
|
return this.format
|
||||||
}
|
}
|
||||||
return this.format.replace(/[Hh]+.*[msSaAZ]|\[.*?\]/g, '').trim() || 'YYYY-MM-DD'
|
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: {
|
methods: {
|
||||||
initCalendar () {
|
initCalendar () {
|
||||||
this.handleValueChange(this.value)
|
this.handleValueChange(this.value)
|
||||||
@@ -384,31 +414,53 @@ export default {
|
|||||||
closePopup () {
|
closePopup () {
|
||||||
this.popupVisible = false
|
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 () {
|
displayPopup () {
|
||||||
const dw = document.documentElement.clientWidth
|
const dw = document.documentElement.clientWidth
|
||||||
const dh = document.documentElement.clientHeight
|
const dh = document.documentElement.clientHeight
|
||||||
const InputRect = this.$el.getBoundingClientRect()
|
const InputRect = this.$el.getBoundingClientRect()
|
||||||
const PopupRect = this.$refs.calendar.getBoundingClientRect()
|
const PopupRect = this._popupRect || (this._popupRect = this.getPopupSize(this.$refs.calendar))
|
||||||
this.position = {}
|
const position = {}
|
||||||
|
let offsetRelativeToInputX = 0
|
||||||
|
let offsetRelativeToInputY = 0
|
||||||
|
if (this.appendToBody) {
|
||||||
|
offsetRelativeToInputX = window.pageXOffset + InputRect.left
|
||||||
|
offsetRelativeToInputY = window.pageYOffset + InputRect.top
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
dw - InputRect.left < PopupRect.width &&
|
dw - InputRect.left < PopupRect.width &&
|
||||||
InputRect.right < 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) {
|
} else if (InputRect.left + InputRect.width / 2 <= dw / 2) {
|
||||||
this.position.left = 0
|
position.left = offsetRelativeToInputX + 'px'
|
||||||
} else {
|
} else {
|
||||||
this.position.right = 0
|
position.left = offsetRelativeToInputX + InputRect.width - PopupRect.width + 'px'
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
InputRect.top <= PopupRect.height + 1 &&
|
InputRect.top <= PopupRect.height &&
|
||||||
dh - InputRect.bottom <= PopupRect.height + 1
|
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) {
|
} else if (InputRect.top + InputRect.height / 2 <= dh / 2) {
|
||||||
this.position.top = '100%'
|
position.top = offsetRelativeToInputY + InputRect.height + 'px'
|
||||||
} else {
|
} 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) {
|
handleInput (event) {
|
||||||
|
|||||||
@@ -69,3 +69,25 @@ export function parseDate (value, format) {
|
|||||||
return false
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,6 +13,16 @@ afterEach(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('datepicker', () => {
|
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', () => {
|
it('click: pick date', () => {
|
||||||
wrapper = mount(DatePicker, {
|
wrapper = mount(DatePicker, {
|
||||||
propsData: {
|
propsData: {
|
||||||
|
|||||||
Reference in New Issue
Block a user