|
29 | 29 | :class="theme" |
30 | 30 | :uid="uid" |
31 | 31 | v-click-outside-directive.dp__menu="closeMenu" |
| 32 | + :open-on-top="openOnTop" |
32 | 33 | :enable-time-picker="enableTimePicker" |
33 | 34 | :week-numbers="weekNumbers" |
34 | 35 | :week-start="weekStart" |
|
60 | 61 | v-model:rangeModelValue="rangeModelValue" |
61 | 62 | @closePicker="closeMenu" |
62 | 63 | @selectDate="selectDate" |
63 | | - @openToTop="recalculatePosition" |
| 64 | + @dpOpen="recalculatePosition" |
64 | 65 | v-if="isOpen" |
65 | 66 | /> |
66 | 67 | </teleport> |
|
121 | 122 | hideInputIcon: { type: Boolean as PropType<boolean>, default: false }, |
122 | 123 | state: { type: Boolean as PropType<boolean>, default: null }, |
123 | 124 | clearable: { type: Boolean as PropType<boolean>, default: true }, |
124 | | - closeOnScroll: { type: Boolean as PropType<boolean>, default: true }, |
| 125 | + closeOnScroll: { type: Boolean as PropType<boolean>, default: false }, |
125 | 126 | autoApply: { type: Boolean as PropType<boolean>, default: false }, |
126 | 127 | filters: { type: Object as PropType<IDateFilter>, default: () => ({}) }, |
127 | 128 | disableMonthYearSelect: { type: Boolean as PropType<boolean>, default: false }, |
|
130 | 131 | inline: { type: Boolean as PropType<boolean>, default: false }, |
131 | 132 | selectText: { type: String as PropType<string>, default: 'Select' }, |
132 | 133 | cancelText: { type: String as PropType<string>, default: 'Cancel' }, |
| 134 | + autoPosition: { type: Boolean as PropType<boolean>, default: true }, |
133 | 135 | }, |
134 | 136 | setup(props: RDatepickerProps, { emit }) { |
135 | 137 | const isOpen = ref(false); |
136 | 138 | const menuPosition = ref({ top: '0', left: '0', transform: 'none' }); |
137 | 139 | const internalValue = ref<string | string[]>(''); |
138 | 140 | const singleModelValue = ref(); |
139 | 141 | const rangeModelValue = ref(); |
| 142 | + const openOnTop = ref(false); |
140 | 143 | const valueCleared = ref(false); |
141 | 144 | const modelValue = toRef(props, 'modelValue'); |
142 | 145 |
|
|
156 | 159 |
|
157 | 160 | onMounted(() => { |
158 | 161 | mapExternalToInternalValue(); |
159 | | - if (props.closeOnScroll) { |
160 | | - window.addEventListener('scroll', closeMenu); |
161 | | - } |
162 | | - window.addEventListener('resize', setMenuPosition); |
| 162 | + window.addEventListener('scroll', onScroll); |
| 163 | + window.addEventListener('resize', onResize); |
163 | 164 | if (props.inline) { |
164 | 165 | isOpen.value = true; |
165 | 166 | } |
166 | 167 | }); |
167 | 168 |
|
168 | 169 | onUnmounted(() => { |
169 | | - if (props.closeOnScroll) { |
170 | | - window.removeEventListener('scroll', closeMenu); |
171 | | - } |
172 | | - window.removeEventListener('resize', setMenuPosition); |
| 170 | + window.removeEventListener('scroll', onScroll); |
| 171 | + window.removeEventListener('resize', onResize); |
173 | 172 | }); |
174 | 173 |
|
175 | 174 | const wrapperClass = computed( |
|
279 | 278 | closeMenu(); |
280 | 279 | }; |
281 | 280 |
|
282 | | - const setMenuPosition = (): void => { |
| 281 | + const onScroll = (): void => { |
| 282 | + if (props.closeOnScroll) { |
| 283 | + closeMenu(); |
| 284 | + } else if (props.autoPosition) { |
| 285 | + setMenuPosition(); |
| 286 | + } else { |
| 287 | + window.removeEventListener('scroll', onScroll); |
| 288 | + } |
| 289 | + }; |
| 290 | +
|
| 291 | + const onResize = (): void => { |
| 292 | + setMenuPosition(); |
| 293 | + }; |
| 294 | +
|
| 295 | + const setMenuPosition = (recalculate = true): void => { |
283 | 296 | const el = document.getElementById(`dp__input_${props.uid}`); |
284 | 297 | if (el) { |
285 | 298 | const { left, width, height } = el.getBoundingClientRect(); |
|
298 | 311 | position.transform = `translateX(-50%)`; |
299 | 312 | } |
300 | 313 | menuPosition.value = position; |
| 314 | + if (recalculate) { |
| 315 | + recalculatePosition(); |
| 316 | + } |
301 | 317 | } |
302 | 318 | }; |
303 | 319 |
|
304 | | - const recalculatePosition = (height: number) => { |
| 320 | + const recalculatePosition = (): void => { |
305 | 321 | const el = document.getElementById(`dp__input_${props.uid}`); |
306 | 322 | if (el) { |
307 | | - menuPosition.value.top = `${el.offsetTop - height - 12}px`; |
| 323 | + const { height: inputHeight, top } = el.getBoundingClientRect(); |
| 324 | + const fullHeight = window.innerHeight; |
| 325 | + const freeSpace = fullHeight - top - inputHeight; |
| 326 | + const menuEl = document.getElementById(`dp__menu_${props.uid}`); |
| 327 | +
|
| 328 | + if (menuEl) { |
| 329 | + const { height } = menuEl.getBoundingClientRect(); |
| 330 | + const menuHeight = height + inputHeight; |
| 331 | + if (menuHeight > freeSpace) { |
| 332 | + menuPosition.value.top = `${el.offsetTop - height - 12}px`; |
| 333 | + openOnTop.value = true; |
| 334 | + } else { |
| 335 | + setMenuPosition(false); |
| 336 | + openOnTop.value = false; |
| 337 | + } |
| 338 | + } |
308 | 339 | } |
309 | 340 | }; |
310 | 341 |
|
|
344 | 375 | internalValue, |
345 | 376 | isOpen, |
346 | 377 | isSingle, |
| 378 | + theme, |
| 379 | + wrapperClass, |
| 380 | + openOnTop, |
347 | 381 | clearValue, |
348 | 382 | openMenu, |
349 | 383 | closeMenu, |
350 | 384 | selectDate, |
351 | | - theme, |
352 | | - wrapperClass, |
353 | 385 | recalculatePosition, |
354 | 386 | }; |
355 | 387 | }, |
|
0 commit comments