178 lines
8.8 KiB
JavaScript
178 lines
8.8 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.useAnimation = useAnimation;
|
|
const react_1 = require("react");
|
|
const UI_js_1 = require("./UI.js");
|
|
const asHtmlElement = (element) => {
|
|
if (element instanceof HTMLElement)
|
|
return element;
|
|
return null;
|
|
};
|
|
const queryMonthEls = (element) => [
|
|
...(element.querySelectorAll("[data-animated-month]") ?? [])
|
|
];
|
|
const queryMonthEl = (element) => asHtmlElement(element.querySelector("[data-animated-month]"));
|
|
const queryCaptionEl = (element) => asHtmlElement(element.querySelector("[data-animated-caption]"));
|
|
const queryWeeksEl = (element) => asHtmlElement(element.querySelector("[data-animated-weeks]"));
|
|
const queryNavEl = (element) => asHtmlElement(element.querySelector("[data-animated-nav]"));
|
|
const queryWeekdaysEl = (element) => asHtmlElement(element.querySelector("[data-animated-weekdays]"));
|
|
/**
|
|
* Handles animations for transitioning between months in the DayPicker
|
|
* component.
|
|
*
|
|
* @private
|
|
* @param rootElRef - A reference to the root element of the DayPicker
|
|
* component.
|
|
* @param enabled - Whether animations are enabled.
|
|
* @param options - Configuration options for the animation, including class
|
|
* names, months, focused day, and the date utility library.
|
|
*/
|
|
function useAnimation(rootElRef, enabled, { classNames, months, focused, dateLib }) {
|
|
const previousRootElSnapshotRef = (0, react_1.useRef)(null);
|
|
const previousMonthsRef = (0, react_1.useRef)(months);
|
|
const animatingRef = (0, react_1.useRef)(false);
|
|
(0, react_1.useLayoutEffect)(() => {
|
|
// get previous months before updating the previous months ref
|
|
const previousMonths = previousMonthsRef.current;
|
|
// update previous months ref for next effect trigger
|
|
previousMonthsRef.current = months;
|
|
if (!enabled ||
|
|
!rootElRef.current ||
|
|
// safety check because the ref can be set to anything by consumers
|
|
!(rootElRef.current instanceof HTMLElement) ||
|
|
// validation required for the animation to work as expected
|
|
months.length === 0 ||
|
|
previousMonths.length === 0 ||
|
|
months.length !== previousMonths.length) {
|
|
return;
|
|
}
|
|
const isSameMonth = dateLib.isSameMonth(months[0].date, previousMonths[0].date);
|
|
const isAfterPreviousMonth = dateLib.isAfter(months[0].date, previousMonths[0].date);
|
|
const captionAnimationClass = isAfterPreviousMonth
|
|
? classNames[UI_js_1.Animation.caption_after_enter]
|
|
: classNames[UI_js_1.Animation.caption_before_enter];
|
|
const weeksAnimationClass = isAfterPreviousMonth
|
|
? classNames[UI_js_1.Animation.weeks_after_enter]
|
|
: classNames[UI_js_1.Animation.weeks_before_enter];
|
|
// get previous root element snapshot before updating the snapshot ref
|
|
const previousRootElSnapshot = previousRootElSnapshotRef.current;
|
|
// update snapshot for next effect trigger
|
|
const rootElSnapshot = rootElRef.current.cloneNode(true);
|
|
if (rootElSnapshot instanceof HTMLElement) {
|
|
// if this effect is triggered while animating, we need to clean up the new root snapshot
|
|
// to put it in the same state as when not animating, to correctly animate the next month change
|
|
const currentMonthElsSnapshot = queryMonthEls(rootElSnapshot);
|
|
currentMonthElsSnapshot.forEach((currentMonthElSnapshot) => {
|
|
if (!(currentMonthElSnapshot instanceof HTMLElement))
|
|
return;
|
|
// remove the old month snapshots from the new root snapshot
|
|
const previousMonthElSnapshot = queryMonthEl(currentMonthElSnapshot);
|
|
if (previousMonthElSnapshot &&
|
|
currentMonthElSnapshot.contains(previousMonthElSnapshot)) {
|
|
currentMonthElSnapshot.removeChild(previousMonthElSnapshot);
|
|
}
|
|
// remove animation classes from the new month snapshots
|
|
const captionEl = queryCaptionEl(currentMonthElSnapshot);
|
|
if (captionEl) {
|
|
captionEl.classList.remove(captionAnimationClass);
|
|
}
|
|
const weeksEl = queryWeeksEl(currentMonthElSnapshot);
|
|
if (weeksEl) {
|
|
weeksEl.classList.remove(weeksAnimationClass);
|
|
}
|
|
});
|
|
previousRootElSnapshotRef.current = rootElSnapshot;
|
|
}
|
|
else {
|
|
previousRootElSnapshotRef.current = null;
|
|
}
|
|
if (animatingRef.current ||
|
|
isSameMonth ||
|
|
// skip animation if a day is focused because it can cause issues to the animation and is better for a11y
|
|
focused) {
|
|
return;
|
|
}
|
|
const previousMonthEls = previousRootElSnapshot instanceof HTMLElement
|
|
? queryMonthEls(previousRootElSnapshot)
|
|
: [];
|
|
const currentMonthEls = queryMonthEls(rootElRef.current);
|
|
if (currentMonthEls &&
|
|
currentMonthEls.every((el) => el instanceof HTMLElement) &&
|
|
previousMonthEls &&
|
|
previousMonthEls.every((el) => el instanceof HTMLElement)) {
|
|
animatingRef.current = true;
|
|
const cleanUpFunctions = [];
|
|
// set isolation to isolate to isolate the stacking context during animation
|
|
rootElRef.current.style.isolation = "isolate";
|
|
// set z-index to 1 to ensure the nav is clickable over the other elements being animated
|
|
const navEl = queryNavEl(rootElRef.current);
|
|
if (navEl) {
|
|
navEl.style.zIndex = "1";
|
|
}
|
|
currentMonthEls.forEach((currentMonthEl, index) => {
|
|
const previousMonthEl = previousMonthEls[index];
|
|
if (!previousMonthEl) {
|
|
return;
|
|
}
|
|
// animate new displayed month
|
|
currentMonthEl.style.position = "relative";
|
|
currentMonthEl.style.overflow = "hidden";
|
|
const captionEl = queryCaptionEl(currentMonthEl);
|
|
if (captionEl) {
|
|
captionEl.classList.add(captionAnimationClass);
|
|
}
|
|
const weeksEl = queryWeeksEl(currentMonthEl);
|
|
if (weeksEl) {
|
|
weeksEl.classList.add(weeksAnimationClass);
|
|
}
|
|
// animate new displayed month end
|
|
const cleanUp = () => {
|
|
animatingRef.current = false;
|
|
if (rootElRef.current) {
|
|
rootElRef.current.style.isolation = "";
|
|
}
|
|
if (navEl) {
|
|
navEl.style.zIndex = "";
|
|
}
|
|
if (captionEl) {
|
|
captionEl.classList.remove(captionAnimationClass);
|
|
}
|
|
if (weeksEl) {
|
|
weeksEl.classList.remove(weeksAnimationClass);
|
|
}
|
|
currentMonthEl.style.position = "";
|
|
currentMonthEl.style.overflow = "";
|
|
if (currentMonthEl.contains(previousMonthEl)) {
|
|
currentMonthEl.removeChild(previousMonthEl);
|
|
}
|
|
};
|
|
cleanUpFunctions.push(cleanUp);
|
|
// animate old displayed month
|
|
previousMonthEl.style.pointerEvents = "none";
|
|
previousMonthEl.style.position = "absolute";
|
|
previousMonthEl.style.overflow = "hidden";
|
|
previousMonthEl.setAttribute("aria-hidden", "true");
|
|
// hide the weekdays container of the old month and only the new one
|
|
const previousWeekdaysEl = queryWeekdaysEl(previousMonthEl);
|
|
if (previousWeekdaysEl) {
|
|
previousWeekdaysEl.style.opacity = "0";
|
|
}
|
|
const previousCaptionEl = queryCaptionEl(previousMonthEl);
|
|
if (previousCaptionEl) {
|
|
previousCaptionEl.classList.add(isAfterPreviousMonth
|
|
? classNames[UI_js_1.Animation.caption_before_exit]
|
|
: classNames[UI_js_1.Animation.caption_after_exit]);
|
|
previousCaptionEl.addEventListener("animationend", cleanUp);
|
|
}
|
|
const previousWeeksEl = queryWeeksEl(previousMonthEl);
|
|
if (previousWeeksEl) {
|
|
previousWeeksEl.classList.add(isAfterPreviousMonth
|
|
? classNames[UI_js_1.Animation.weeks_before_exit]
|
|
: classNames[UI_js_1.Animation.weeks_after_exit]);
|
|
}
|
|
currentMonthEl.insertBefore(previousMonthEl, currentMonthEl.firstChild);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
//# sourceMappingURL=useAnimation.js.map
|