Packages
noora
0.49.0
0.84.1
0.84.0
0.83.2
0.83.1
0.83.0
0.82.7
0.82.6
0.82.5
0.82.4
0.82.3
0.82.2
0.82.1
0.82.0
0.81.4
0.81.3
0.81.2
0.81.1
0.81.0
0.80.1
0.80.0
0.79.1
0.79.0
0.78.1
0.78.0
0.77.4
0.77.3
0.77.2
0.77.1
0.77.0
0.76.0
0.75.0
0.74.0
0.73.0
0.72.0
0.71.0
0.70.0
0.69.1
0.69.0
0.68.0
0.67.0
0.66.0
0.65.0
0.64.2
0.64.1
0.64.0
0.63.2
0.63.1
0.63.0
0.62.0
0.61.0
0.60.0
0.59.0
0.58.0
0.57.0
0.56.1
0.56.0
0.55.0
0.54.0
0.53.1
0.53.0
0.52.1
0.52.0
0.51.0
0.50.1
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.3
0.44.2
0.44.1
0.44.0
0.43.0
0.42.0
0.41.0
0.40.6
0.40.5
0.40.4
0.40.3
0.40.2
0.40.1
0.40.0
0.39.1
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.1
0.32.0
0.31.0
0.30.0
0.29.2
0.29.1
0.29.0
0.28.5
0.28.4
0.28.3
0.28.2
0.28.1
0.28.0
0.27.0
0.26.1
0.26.0
0.25.0
0.24.0
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.1
0.12.0
0.11.2
0.11.1
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.2
0.3.1
0.3.0
0.2.2
0.2.0
0.1.0
0.1.0-rc.2
0.1.0-rc.1
0.1.0-alpha.6
0.1.0-alpha.5
0.1.0-alpha.4
0.1.0-alpha.3
0.1.0-alpha.2
0.1.0-alpha.1
A component library for Phoenix LiveView applications
Current section
Files
Jump to
Current section
Files
js/DatePicker/CalendarNavigation.js
import {
adjustMonth,
compareMonths,
initCalendarMonthsFromRange,
} from "./dateUtils.js";
/**
* Manages calendar month navigation state for a dual-calendar date picker.
* Each calendar (start/end) can be navigated independently while maintaining
* the constraint that the start calendar must always show a month before the end calendar.
*/
export class CalendarNavigation {
/**
* @param {{year: number, month: number}|null} startDate
* @param {{year: number, month: number}|null} endDate
* @param {function} onNavigate - Callback when navigation changes
*/
constructor(startDate, endDate, onNavigate) {
const { startCalendarMonth, endCalendarMonth } =
initCalendarMonthsFromRange(startDate, endDate);
this.startCalendarMonth = startCalendarMonth;
this.endCalendarMonth = endCalendarMonth;
this.onNavigate = onNavigate;
}
/**
* Check if we can navigate the start (left) calendar backwards.
* @param {{year: number, month: number, day: number}|null} minDate
* @returns {boolean}
*/
canNavigatePrevStart(minDate) {
if (!this.startCalendarMonth) return false;
if (!minDate) return true;
return (
this.startCalendarMonth.year > minDate.year ||
(this.startCalendarMonth.year === minDate.year &&
this.startCalendarMonth.month > minDate.month)
);
}
/**
* Check if we can navigate the start (left) calendar forwards.
* Must stay before the end calendar.
* @returns {boolean}
*/
canNavigateNextStart() {
if (!this.startCalendarMonth || !this.endCalendarMonth) return false;
const nextMonth = adjustMonth(
this.startCalendarMonth.year,
this.startCalendarMonth.month,
1,
);
return compareMonths(nextMonth, this.endCalendarMonth) < 0;
}
/**
* Check if we can navigate the end (right) calendar backwards.
* Must stay after the start calendar.
* @returns {boolean}
*/
canNavigatePrevEnd() {
if (!this.startCalendarMonth || !this.endCalendarMonth) return false;
const prevMonth = adjustMonth(
this.endCalendarMonth.year,
this.endCalendarMonth.month,
-1,
);
return compareMonths(prevMonth, this.startCalendarMonth) > 0;
}
/**
* Check if we can navigate the end (right) calendar forwards.
* @param {{year: number, month: number, day: number}|null} maxDate
* @returns {boolean}
*/
canNavigateNextEnd(maxDate) {
if (!this.endCalendarMonth) return false;
if (!maxDate) return true;
return (
this.endCalendarMonth.year < maxDate.year ||
(this.endCalendarMonth.year === maxDate.year &&
this.endCalendarMonth.month < maxDate.month)
);
}
/**
* Navigate the start (left) calendar backwards one month.
*/
navigatePrevStart() {
if (!this.startCalendarMonth) return;
this.startCalendarMonth = adjustMonth(
this.startCalendarMonth.year,
this.startCalendarMonth.month,
-1,
);
this.onNavigate();
}
/**
* Navigate the start (left) calendar forwards one month.
*/
navigateNextStart() {
if (!this.canNavigateNextStart()) return;
this.startCalendarMonth = adjustMonth(
this.startCalendarMonth.year,
this.startCalendarMonth.month,
1,
);
this.onNavigate();
}
/**
* Navigate the end (right) calendar backwards one month.
*/
navigatePrevEnd() {
if (!this.canNavigatePrevEnd()) return;
this.endCalendarMonth = adjustMonth(
this.endCalendarMonth.year,
this.endCalendarMonth.month,
-1,
);
this.onNavigate();
}
/**
* Navigate the end (right) calendar forwards one month.
*/
navigateNextEnd() {
if (!this.endCalendarMonth) return;
this.endCalendarMonth = adjustMonth(
this.endCalendarMonth.year,
this.endCalendarMonth.month,
1,
);
this.onNavigate();
}
/**
* Update calendar months to show a selected range.
* @param {{year: number, month: number}} startDate
* @param {{year: number, month: number}} endDate
*/
updateForSelection(startDate, endDate) {
const sameMonth =
startDate.year === endDate.year && startDate.month === endDate.month;
if (sameMonth) {
const prev = adjustMonth(endDate.year, endDate.month, -1);
this.startCalendarMonth = prev;
this.endCalendarMonth = { year: endDate.year, month: endDate.month };
} else {
this.startCalendarMonth = {
year: startDate.year,
month: startDate.month,
};
this.endCalendarMonth = { year: endDate.year, month: endDate.month };
}
}
}