diff --git a/docs/.vuepress/components/Demo.vue b/docs/.vuepress/components/Demo.vue index 8d123f61..84b9cfee 100755 --- a/docs/.vuepress/components/Demo.vue +++ b/docs/.vuepress/components/Demo.vue @@ -134,6 +134,40 @@ <datepicker :calendar-button="true" :show-calendar-on-button-click="true"></datepicker> + +
+

Append datepicker to body

+ +

Don't append datepicker to body

+ + + <datepicker :append-to-body="true"></datepicker> + +
+ +
+

Fixed positions

+ + + <datepicker :fixed-position="fixedPosition"></datepicker> + +
+
Settings
+ +
+
@@ -146,6 +180,15 @@ export default { format: 'd MMMM yyyy', openDate: null, vModelExample: null, + fixedPositions: [ + 'bottom', + 'bottom-left', + 'bottom-right', + 'top', + 'top-left', + 'top-right', + ], + fixedPosition: 'bottom', } }, } diff --git a/docs/.vuepress/components/style.css b/docs/.vuepress/components/style.css index df8d89d8..b8d27ca3 100644 --- a/docs/.vuepress/components/style.css +++ b/docs/.vuepress/components/style.css @@ -38,3 +38,7 @@ .error { color: red; } + +.overflow-scroll { + overflow:scroll +} diff --git a/docs/guide/Props/README.md b/docs/guide/Props/README.md index 4c7031c5..ab4f6067 100755 --- a/docs/guide/Props/README.md +++ b/docs/guide/Props/README.md @@ -3,6 +3,7 @@ | Prop | Type | Default | Description | | ----------------------------- | -----------------| ----------- | ----------------------------------------------- | +| append-to-body | Boolean | false | Append datepicker calendar to body | | autofocus | String | | Sets html `autofocus` attribute on input | | bootstrap-styling | Boolean | false | Use bootstrap v4 styling classes. | | calendar-button | Boolean | false | Show an icon that that can be clicked | diff --git a/docs/guide/README.md b/docs/guide/README.md index be7e9f2e..1f4ea15f 100644 --- a/docs/guide/README.md +++ b/docs/guide/README.md @@ -55,6 +55,6 @@ If you use [SASS](https://sass-lang.com/) you can directly import the src file. ```vue ``` diff --git a/example/Demo.vue b/example/Demo.vue index 9d5d4cbd..bdb83a78 100755 --- a/example/Demo.vue +++ b/example/Demo.vue @@ -3,7 +3,10 @@

Datepicker Examples

Default datepicker...

- + <datepicker placeholder="Select Date"></datepicker> @@ -37,6 +40,7 @@ <datepicker placeholder="Select Date" v-model="vmodelexample"></datepicker> @@ -45,6 +49,18 @@

{{ vModelExample }}

+
+

Append datepicker to body

+ +

Don't append datepicker to body

+ + + <datepicker :append-to-body="true"></datepicker> + +
+

Format datepicker

@@ -294,6 +310,29 @@ :initialView="'year'"></datepicker>
+ +
+

Fixed positions

+ + + <datepicker :fixed-position="fixedPosition"></datepicker> + +
+
Settings
+ +
+
@@ -369,6 +408,15 @@ export default { vModelExample: null, languages: lang, language: 'en', + fixedPositions: [ + 'bottom', + 'bottom-left', + 'bottom-right', + 'top', + 'top-left', + 'top-right', + ], + fixedPosition: 'bottom', } }, computed: { @@ -499,4 +547,7 @@ h5 { font-size: 80%; display: block; } +.overflow-scroll { + overflow:scroll +} diff --git a/src/components/Calendar.vue b/src/components/Calendar.vue new file mode 100644 index 00000000..e5410cff --- /dev/null +++ b/src/components/Calendar.vue @@ -0,0 +1,312 @@ + + + + diff --git a/src/components/DateInput.vue b/src/components/DateInput.vue index 00b057db..da81e69e 100644 --- a/src/components/DateInput.vue +++ b/src/components/DateInput.vue @@ -4,8 +4,7 @@ @@ -63,11 +62,13 @@ + diff --git a/src/components/Popup.vue b/src/components/Popup.vue new file mode 100644 index 00000000..9e1b3745 --- /dev/null +++ b/src/components/Popup.vue @@ -0,0 +1,94 @@ + diff --git a/src/mixins/calendarProps.vue b/src/mixins/calendarProps.vue new file mode 100644 index 00000000..e8e506a6 --- /dev/null +++ b/src/mixins/calendarProps.vue @@ -0,0 +1,62 @@ + diff --git a/src/mixins/inputProps.vue b/src/mixins/inputProps.vue index 64253691..e7062d91 100644 --- a/src/mixins/inputProps.vue +++ b/src/mixins/inputProps.vue @@ -24,19 +24,6 @@ export default ({ type: String, default: '', }, - openDate: { - type: [ - String, - Date, - Number, - ], - default: null, - validator: - (val) => val === null - || val instanceof Date - || typeof val === 'string' - || typeof val === 'number', - }, placeholder: { type: String, default: null, @@ -48,10 +35,6 @@ export default ({ ], default: null, }, - inline: { - type: Boolean, - default: false, - }, inputClass: { type: [ String, @@ -96,10 +79,6 @@ export default ({ type: Boolean, default: false, }, - useUtc: { - type: Boolean, - default: false, - }, showCalendarOnFocus: { type: Boolean, default: false, diff --git a/src/mixins/sharedProps.vue b/src/mixins/sharedProps.vue new file mode 100644 index 00000000..027826a7 --- /dev/null +++ b/src/mixins/sharedProps.vue @@ -0,0 +1,26 @@ + diff --git a/src/styles/calendar.scss b/src/styles/calendar.scss new file mode 100644 index 00000000..42bebcdf --- /dev/null +++ b/src/styles/calendar.scss @@ -0,0 +1,155 @@ +.vdp-datepicker__calendar { + position: absolute; + z-index: 10000; + background: #fff; + width: 300px; + border: 1px solid #ccc; + + header { + display: block; + line-height: 40px; + + span { + display: inline-block; + text-align: center; + width: 71.42857142857143%; + float: left; + } + + .prev, + .next { + max-height: 40px; + width: 14.285714285714286%; + float: left; + position: relative; + + .default { + text-indent: -10000px; + + &:after { + content: ''; + position: absolute; + left: 50%; + top: 50%; + transform: translateX(-50%) translateY(-50%); + border: 6px solid transparent; + } + } + } + + .prev { + .default { + &:after { + border-right: 10px solid #000; + margin-left: -5px; + } + + &.disabled:after { + border-right: 10px solid #ddd; + } + } + } + + .next { + .default { + &:after { + border-left: 10px solid #000; + margin-left: 5px; + } + + &.disabled:after { + border-left: 10px solid #ddd; + } + } + } + + .prev:not(.disabled), + .next:not(.disabled), + .up:not(.disabled) { + cursor: pointer; + + &:hover { + background: #eee; + } + } + } + + .disabled { + color: #ddd; + cursor: default; + } + + .flex-rtl { + display: flex; + width: inherit; + flex-wrap: wrap; + } + + .cell { + display: inline-block; + padding: 0 5px; + width: 14.285714285714286%; + height: 40px; + line-height: 40px; + text-align: center; + vertical-align: middle; + border: 1px solid transparent; + + &:not(.blank):not(.disabled).day, + &:not(.blank):not(.disabled).month, + &:not(.blank):not(.disabled).year { + cursor: pointer; + + &:hover { + border: 1px solid #4bd; + } + } + + &.selected { + background: #4bd; + + &:hover { + background: #4bd; + } + + &.highlighted { + background: #4bd; + } + } + + &.highlighted { + background: #cae5ed; + + &.disabled { + color: #a3a3a3; + } + } + + &.grey { + color: #888; + + &:hover { + background: inherit; + } + } + + &.day-header { + font-size: 75%; + white-space: nowrap; + cursor: inherit; + + &:hover { + background: inherit; + } + } + } + + .month, + .year { + width: 33.333%; + } + + .picker-view { + width: inherit; + } +} diff --git a/src/styles/style.scss b/src/styles/style.scss index 6a8bf7c0..ecc781da 100755 --- a/src/styles/style.scss +++ b/src/styles/style.scss @@ -11,163 +11,6 @@ } } -.vdp-datepicker__calendar { - position: absolute; - z-index: 10000; - background: #fff; - width: 300px; - border: 1px solid #ccc; - - header { - display: block; - line-height: 40px; - - span { - display: inline-block; - text-align: center; - width: 71.42857142857143%; - float: left; - } - - .prev, - .next { - max-height: 40px; - width: 14.285714285714286%; - float: left; - position: relative; - - .default { - text-indent: -10000px; - - &:after { - content: ''; - position: absolute; - left: 50%; - top: 50%; - transform: translateX(-50%) translateY(-50%); - border: 6px solid transparent; - } - } - } - - .prev { - .default { - &:after { - border-right: 10px solid #000; - margin-left: -5px; - } - - &.disabled:after { - border-right: 10px solid #ddd; - } - } - } - - .next { - .default { - &:after { - border-left: 10px solid #000; - margin-left: 5px; - } - - &.disabled:after { - border-left: 10px solid #ddd; - } - } - } - - .prev:not(.disabled), - .next:not(.disabled), - .up:not(.disabled) { - cursor: pointer; - - &:hover { - background: #eee; - } - } - } - - .disabled { - color: #ddd; - cursor: default; - } - - .flex-rtl { - display: flex; - width: inherit; - flex-wrap: wrap; - } - - .cell { - display: inline-block; - padding: 0 5px; - width: 14.285714285714286%; - height: 40px; - line-height: 40px; - text-align: center; - vertical-align: middle; - border: 1px solid transparent; - - &:not(.blank):not(.disabled).day, - &:not(.blank):not(.disabled).month, - &:not(.blank):not(.disabled).year { - cursor: pointer; - - &:hover { - border: 1px solid #4bd; - } - } - - &.selected { - background: #4bd; - - &:hover { - background: #4bd; - } - - &.highlighted { - background: #4bd; - } - } - - &.highlighted { - background: #cae5ed; - - &.disabled { - color: #a3a3a3; - } - } - - &.grey { - color: #888; - - &:hover { - background: inherit; - } - } - - &.day-header { - font-size: 75%; - white-space: nowrap; - cursor: inherit; - - &:hover { - background: inherit; - } - } - } - - .month, - .year { - width: 33.333%; - } - - .picker-view { - width: inherit; - } -} - - .vdp-datepicker__clear-button, .vdp-datepicker__calendar-button { cursor: pointer; diff --git a/src/utils/calendarSlots.js b/src/utils/calendarSlots.js new file mode 100755 index 00000000..0383cf83 --- /dev/null +++ b/src/utils/calendarSlots.js @@ -0,0 +1,10 @@ +export default [ + 'beforeCalendarHeaderDay', + 'calendarFooterDay', + 'beforeCalendarHeaderMonth', + 'calendarFooterMonth', + 'beforeCalendarHeaderYear', + 'calendarFooterYear', + 'nextIntervalBtn', + 'prevIntervalBtn', +] diff --git a/src/utils/dom.js b/src/utils/dom.js new file mode 100644 index 00000000..ccc27679 --- /dev/null +++ b/src/utils/dom.js @@ -0,0 +1,123 @@ +/* eslint no-param-reassign: 0 */ +/** + * get the hidden element width, height + * @param {HTMLElement} element dom + */ +export function getPopupElementSize(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, 10) + parseInt( + styles.marginRight, + 10, + ) + const height = element.offsetHeight + parseInt( + styles.marginTop, + 10, + ) + parseInt(styles.marginBottom, 10) + element.style.display = originalDisplay + element.style.visibility = originalVisibility + + return { + width, + height, + } +} + +/** + * get the popup position + * @param {Element} el element + * @param {Element} elRelative relative element + * @param {Number} targetWidth target element's width + * @param {Number} targetHeight target element's height + * @param {Boolean} fixed + * @param {String} fixedPosition + * @param {Boolean} rtl + */ +export function getRelativePosition({ + el, + elRelative, + targetWidth, + targetHeight, + fixed, + fixedPosition, + rtl, +}) { + let left = 0 + let top = 0 + let offsetX = 0 + let offsetY = 0 + const relativeRect = elRelative.getBoundingClientRect() + const documentWidth = document.documentElement.clientWidth + const documentHeight = document.documentElement.clientHeight + if (fixed) { + offsetX = window.pageXOffset + relativeRect.left + offsetY = window.pageYOffset + relativeRect.top + } + + const calendarBounding = el.getBoundingClientRect() + const outOfBoundsRight = calendarBounding.right > window.innerWidth + const outOfBoundsBottom = calendarBounding.bottom > window.innerHeight + + const fixedPositionRight = fixedPosition && fixedPosition.indexOf('right') !== -1 + const fixedPositionTop = fixedPosition && fixedPosition.indexOf('top') !== -1 + + const setLeft = () => { + left = offsetX + } + const setRight = () => { + left = offsetX + relativeRect.width - targetWidth + } + const setBottom = () => { + top = offsetY + relativeRect.height + } + const setTop = () => { + top = offsetY - targetHeight + } + + if (fixedPosition === '') { + if (outOfBoundsRight || rtl) { + setRight() + } else { + setLeft() + } + + if (outOfBoundsBottom) { + setTop() + } else { + setBottom() + } + const hasRelativWidth = documentWidth - relativeRect.left < targetWidth + && relativeRect.right < targetWidth + + const hasRelativHeight = relativeRect.top <= targetHeight + && documentHeight - relativeRect.bottom <= targetHeight + + if (hasRelativWidth) { + left = offsetX - relativeRect.left + 1 + } + + if (hasRelativHeight) { + top = offsetY + documentHeight - relativeRect.top - targetHeight + } + } else { + if (fixedPositionRight) { + setRight() + } else { + setLeft() + } + + if (fixedPositionTop) { + setTop() + } else { + setBottom() + } + } + + return { + left: `${left}px`, + top: `${top}px`, + } +}