diff --git a/packages/core/src/Calendar/Calendar.test.ts b/packages/core/src/Calendar/Calendar.test.ts index 4e1f67c3a..86dfcb1a6 100644 --- a/packages/core/src/Calendar/Calendar.test.ts +++ b/packages/core/src/Calendar/Calendar.test.ts @@ -889,6 +889,7 @@ describe('calendar - edge cases', () => { expect(heading).toHaveTextContent('January - April 2025') await user.keyboard(kbd.ARROW_RIGHT) + expect(getByTestId('heading')).toHaveTextContent('February - May 2025') expect(getByTestId('date-3-5-1')).toHaveFocus() const firstDayOfMonth = getByTestId('date-0-2-1') diff --git a/packages/core/src/Calendar/CalendarCellTrigger.vue b/packages/core/src/Calendar/CalendarCellTrigger.vue index 832496311..92602e960 100644 --- a/packages/core/src/Calendar/CalendarCellTrigger.vue +++ b/packages/core/src/Calendar/CalendarCellTrigger.vue @@ -9,9 +9,8 @@ import { isToday, } from '@internationalized/date' import { computed, nextTick } from 'vue' -import { getDaysInMonth, parseStringToDateValue, toDate } from '@/date' +import { toDate } from '@/date' import { useKbd } from '@/shared' -import { getSelectableCells } from './utils' export interface CalendarCellTriggerProps extends PrimitiveProps { /** The date value provided to the cell trigger */ @@ -112,119 +111,52 @@ function handleArrowKey(e: KeyboardEvent) { const sign = rootContext.dir.value === 'rtl' ? -1 : 1 switch (e.code) { case kbd.ARROW_RIGHT: - shiftFocus(currentElement.value, sign) + shiftFocus(props.day, sign) break case kbd.ARROW_LEFT: - shiftFocus(currentElement.value, -sign) + shiftFocus(props.day, -sign) break case kbd.ARROW_UP: - shiftFocus(currentElement.value, -indexIncrementation) + shiftFocus(props.day, -indexIncrementation) break case kbd.ARROW_DOWN: - shiftFocus(currentElement.value, indexIncrementation) + shiftFocus(props.day, indexIncrementation) break case kbd.ENTER: case kbd.SPACE_CODE: changeDate(props.day) } - function shiftFocus(node: HTMLElement, add: number) { - const allCollectionItems: HTMLElement[] = getSelectableCells(parentElement) - if (!allCollectionItems.length) - return - - const index = allCollectionItems.indexOf(node) - const newIndex = index + add - - if (newIndex >= 0 && newIndex < allCollectionItems.length) { - const newDate = allCollectionItems[newIndex].getAttribute('data-value') - const newDateValue = parseStringToDateValue(newDate!, rootContext.placeholder.value) - const minValue = rootContext.minValue.value - const maxValue = rootContext.maxValue.value - if ((minValue && newDateValue.compare(minValue) < 0) || (maxValue && newDateValue.compare(maxValue) > 0)) - return + function shiftFocus(day: DateValue, add: number) { + const candidateDayValue = day.add({ days: add }) - if (allCollectionItems[newIndex].hasAttribute('data-disabled')) { - shiftFocus(allCollectionItems[newIndex], add) - } - rootContext.onPlaceholderChange(newDateValue) - allCollectionItems[newIndex].focus() + if ((rootContext.minValue.value && candidateDayValue.compare(rootContext.minValue.value) < 0) || (rootContext.maxValue.value && candidateDayValue.compare(rootContext.maxValue.value) > 0)) return - } - if (newIndex < 0) { - if (rootContext.isPrevButtonDisabled()) - return - rootContext.prevPage() - nextTick(() => { - const newCollectionItems: HTMLElement[] = getSelectableCells(parentElement) - if (!newCollectionItems.length) + const candidateDay = parentElement.querySelector(`[data-value='${candidateDayValue.toString()}']:not([data-outside-view])`) + // If the date is not found it means we must change the page + if (!candidateDay) { + if (add > 0) { + if (rootContext.isNextButtonDisabled()) return - if (!rootContext.pagedNavigation.value && rootContext.numberOfMonths.value > 1) { - // Placeholder is set to first month of the new page - const numberOfDays = getDaysInMonth(rootContext.placeholder.value) - const computedIndex = numberOfDays - Math.abs(newIndex) - if (newCollectionItems[computedIndex].hasAttribute('data-disabled')) { - shiftFocus(newCollectionItems[computedIndex], add) - } - const newDate = newCollectionItems[computedIndex].getAttribute('data-value') - newCollectionItems[ - computedIndex - ].focus() - - rootContext.onPlaceholderChange(parseStringToDateValue(newDate!, rootContext.placeholder.value)) + rootContext.nextPage() + } + else { + if (rootContext.isPrevButtonDisabled()) return - } - const computedIndex = newCollectionItems.length - Math.abs(newIndex) - if (newCollectionItems[computedIndex].hasAttribute('data-disabled')) { - shiftFocus(newCollectionItems[computedIndex], add) - } - const newDate = newCollectionItems[computedIndex].getAttribute('data-value') - rootContext.onPlaceholderChange(parseStringToDateValue(newDate!, rootContext.placeholder.value)) - newCollectionItems[ - computedIndex - ].focus() + rootContext.prevPage() + } + nextTick(() => { + shiftFocus(day, add) }) return } - if (newIndex >= allCollectionItems.length) { - if (rootContext.isNextButtonDisabled()) - return - rootContext.nextPage() - nextTick(() => { - const newCollectionItems: HTMLElement[] = getSelectableCells(parentElement) - if (!newCollectionItems.length) - return - - if (!rootContext.pagedNavigation.value && rootContext.numberOfMonths.value > 1) { - // Placeholder is set to first month of the new page - const numberOfDays = getDaysInMonth( - rootContext.placeholder.value.add({ months: rootContext.numberOfMonths.value - 1 }), - ) - - const computedIndex = newIndex - allCollectionItems.length + (newCollectionItems.length - numberOfDays) - - if (newCollectionItems[computedIndex].hasAttribute('data-disabled')) { - shiftFocus(newCollectionItems[computedIndex], add) - } - const newDate = newCollectionItems[computedIndex].getAttribute('data-value') - rootContext.onPlaceholderChange(parseStringToDateValue(newDate!, rootContext.placeholder.value)) - newCollectionItems[computedIndex].focus() - return - } - - const computedIndex = newIndex - allCollectionItems.length - if (newCollectionItems[computedIndex].hasAttribute('data-disabled')) { - shiftFocus(newCollectionItems[computedIndex], add) - } - - const newDate = newCollectionItems[computedIndex].getAttribute('data-value') - rootContext.onPlaceholderChange(parseStringToDateValue(newDate!, rootContext.placeholder.value)) - - newCollectionItems[computedIndex].focus() - }) + if (candidateDay && candidateDay.hasAttribute('data-disabled')) { + return shiftFocus(candidateDayValue, add) } + rootContext.onPlaceholderChange(candidateDayValue) + candidateDay?.focus() } } diff --git a/packages/core/src/Calendar/utils.ts b/packages/core/src/Calendar/utils.ts deleted file mode 100644 index 71edbfdc4..000000000 --- a/packages/core/src/Calendar/utils.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const SELECTOR - = '[data-reka-calendar-cell-trigger]:not([data-outside-view]):not([data-outside-visible-view])' -export function getSelectableCells(calendar: HTMLElement): HTMLElement[] { - return Array.from(calendar.querySelectorAll(SELECTOR)) ?? [] -} diff --git a/packages/core/src/RangeCalendar/RangeCalendarCellTrigger.vue b/packages/core/src/RangeCalendar/RangeCalendarCellTrigger.vue index 723cad82f..d2665b0d9 100644 --- a/packages/core/src/RangeCalendar/RangeCalendarCellTrigger.vue +++ b/packages/core/src/RangeCalendar/RangeCalendarCellTrigger.vue @@ -9,8 +9,7 @@ import { isToday, } from '@internationalized/date' import { computed, nextTick } from 'vue' -import { getSelectableCells } from '@/Calendar/utils' -import { getDaysInMonth, isBetweenInclusive, parseStringToDateValue, toDate } from '@/date' +import { isBetweenInclusive, toDate } from '@/date' import { useKbd } from '@/shared' export interface RangeCalendarCellTriggerProps extends PrimitiveProps { @@ -179,117 +178,52 @@ function handleArrowKey(e: KeyboardEvent) { const sign = rootContext.dir.value === 'rtl' ? -1 : 1 switch (e.code) { case kbd.ARROW_RIGHT: - shiftFocus(currentElement.value, sign) + shiftFocus(props.day, sign) break case kbd.ARROW_LEFT: - shiftFocus(currentElement.value, -sign) + shiftFocus(props.day, -sign) break case kbd.ARROW_UP: - shiftFocus(currentElement.value, -indexIncrementation) + shiftFocus(props.day, -indexIncrementation) break case kbd.ARROW_DOWN: - shiftFocus(currentElement.value, indexIncrementation) + shiftFocus(props.day, indexIncrementation) break case kbd.ENTER: case kbd.SPACE_CODE: changeDate(e, props.day) } - function shiftFocus(node: HTMLElement, add: number) { - const allCollectionItems: HTMLElement[] = getSelectableCells(parentElement) - if (!allCollectionItems.length) - return - - const index = allCollectionItems.indexOf(node) - const newIndex = index + add - - if (newIndex >= 0 && newIndex < allCollectionItems.length) { - const newDate = allCollectionItems[newIndex].getAttribute('data-value') - const newDateValue = parseStringToDateValue(newDate!, rootContext.placeholder.value) - const minValue = rootContext.minValue.value - const maxValue = rootContext.maxValue.value - if ((minValue && newDateValue.compare(minValue) < 0) || (maxValue && newDateValue.compare(maxValue) > 0)) - return + function shiftFocus(day: DateValue, add: number) { + const candidateDayValue = day.add({ days: add }) - if (allCollectionItems[newIndex].hasAttribute('data-disabled')) { - shiftFocus(allCollectionItems[newIndex], add) - } - rootContext.onPlaceholderChange(newDateValue) - allCollectionItems[newIndex].focus() + if ((rootContext.minValue.value && candidateDayValue.compare(rootContext.minValue.value) < 0) || (rootContext.maxValue.value && candidateDayValue.compare(rootContext.maxValue.value) > 0)) return - } - if (newIndex < 0) { - if (rootContext.isPrevButtonDisabled()) - return - rootContext.prevPage() - nextTick(() => { - const newCollectionItems: HTMLElement[] = getSelectableCells(parentElement) - if (!newCollectionItems.length) + const candidateDay = parentElement.querySelector(`[data-value='${candidateDayValue.toString()}']:not([data-outside-view])`) + // If the date is not found it means we must change the page + if (!candidateDay) { + if (add > 0) { + if (rootContext.isNextButtonDisabled()) return - if (!rootContext.pagedNavigation.value && rootContext.numberOfMonths.value > 1) { - // Placeholder is set to first month of the new page - const numberOfDays = getDaysInMonth(rootContext.placeholder.value) - const computedIndex = numberOfDays - Math.abs(newIndex) - if (newCollectionItems[computedIndex].hasAttribute('data-disabled')) { - shiftFocus(newCollectionItems[computedIndex], add) - } - const newDate = newCollectionItems[computedIndex].getAttribute('data-value') - rootContext.onPlaceholderChange(parseStringToDateValue(newDate!, rootContext.placeholder.value)) - newCollectionItems[ - computedIndex - ].focus() + rootContext.nextPage() + } + else { + if (rootContext.isPrevButtonDisabled()) return - } - const computedIndex = newCollectionItems.length - Math.abs(newIndex) - if (newCollectionItems[computedIndex].hasAttribute('data-disabled')) { - shiftFocus(newCollectionItems[computedIndex], add) - } - const newDate = newCollectionItems[computedIndex].getAttribute('data-value') - rootContext.onPlaceholderChange(parseStringToDateValue(newDate!, rootContext.placeholder.value)) - newCollectionItems[ - computedIndex - ].focus() + rootContext.prevPage() + } + nextTick(() => { + shiftFocus(day, add) }) return } - if (newIndex >= allCollectionItems.length) { - if (rootContext.isNextButtonDisabled()) - return - rootContext.nextPage() - nextTick(() => { - const newCollectionItems: HTMLElement[] = getSelectableCells(parentElement) - if (!newCollectionItems.length) - return - - if (!rootContext.pagedNavigation.value && rootContext.numberOfMonths.value > 1) { - // Placeholder is set to first month of the new page - const numberOfDays = getDaysInMonth( - rootContext.placeholder.value.add({ months: rootContext.numberOfMonths.value - 1 }), - ) - - const computedIndex = newIndex - allCollectionItems.length + (newCollectionItems.length - numberOfDays) - - if (newCollectionItems[computedIndex].hasAttribute('data-disabled')) { - shiftFocus(newCollectionItems[computedIndex], add) - } - const newDate = newCollectionItems[computedIndex].getAttribute('data-value') - rootContext.onPlaceholderChange(parseStringToDateValue(newDate!, rootContext.placeholder.value)) - newCollectionItems[computedIndex].focus() - return - } - - const computedIndex = newIndex - allCollectionItems.length - if (newCollectionItems[computedIndex].hasAttribute('data-disabled')) { - shiftFocus(newCollectionItems[computedIndex], add) - } - - const newDate = newCollectionItems[computedIndex].getAttribute('data-value') - rootContext.onPlaceholderChange(parseStringToDateValue(newDate!, rootContext.placeholder.value)) - newCollectionItems[computedIndex].focus() - }) + if (candidateDay && candidateDay.hasAttribute('data-disabled')) { + return shiftFocus(candidateDayValue, add) } + rootContext.onPlaceholderChange(candidateDayValue) + candidateDay?.focus() } }