Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat) Add animate prop #2608

Closed
wants to merge 14 commits into from
Prev Previous commit
Next Next commit
Use Transition component
gpbl committed Nov 12, 2024
commit 791a625fde2900898ff634d057c6b9c73b84c499
449 changes: 239 additions & 210 deletions src/DayPicker.tsx

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/UI.ts
Original file line number Diff line number Diff line change
@@ -100,6 +100,14 @@ export enum SelectionState {
selected = "selected"
}

/** The transition that can be applied to the DayPicker component. */
export enum TransitionType {
/** Slide the element horizontally. */
Slide = "slide",
/** Animate the opacity. */
Fade = "fade"
}

/**
* Deprecated UI elements and flags.
*
43 changes: 2 additions & 41 deletions src/components/MonthCaption.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import React from "react";

import { CSSTransition, TransitionGroup } from "react-transition-group";

import type { CalendarMonth } from "../classes/index.js";

/**
@@ -16,47 +14,10 @@ export function MonthCaption(
calendarMonth: CalendarMonth;
/** The index where this month is displayed. */
displayIndex: number;
animate?: boolean;
transitionDirection?: "next" | "prev";
transitionDuration?: number;
onTransitionEnter?: () => void;
onTransitionEntered?: () => void;
} & JSX.IntrinsicElements["div"]
) {
const {
calendarMonth,
displayIndex,
animate,
transitionDuration,
transitionDirection: animateDirection,
onTransitionEnter: onEnter,
onTransitionEntered: onEntered,
...divProps
} = props;

if (animate) {
return (
<div style={{ overflow: "hidden", position: "relative", zIndex: 0 }}>
<TransitionGroup component={null}>
<CSSTransition
key={calendarMonth.date.toISOString()}
timeout={transitionDuration ?? 200}
classNames={
animateDirection === "next"
? "rdp-opacity-next"
: "rdp-opacity-prev"
}
onEnter={onEnter}
onEntered={onEntered}
>
<div {...divProps} />
</CSSTransition>
</TransitionGroup>
</div>
);
} else {
return <div {...divProps} />;
}
const { calendarMonth, displayIndex, ...divProps } = props;
return <div {...divProps} />;
}

export type MonthCaptionProps = Parameters<typeof MonthCaption>[0];
40 changes: 40 additions & 0 deletions src/components/Transition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from "react";

import { CSSTransition, TransitionGroup } from "react-transition-group";

/**
* When enabled, animate the children with a CSS transition.
*
* @group Components
* @see https://daypicker.dev/guides/custom-components
*/
export function Transition(props: {
transitionKey: string;
enabled?: boolean;
className: string;
direction?: "next" | "previous";
duration?: number;
onEnter?: () => void;
onEntered?: () => void;
children: React.ReactNode;
}) {
if (!props.enabled) {
return <>{props.children}</>;
}
const { direction = "next", duration = 200, className } = props;
return (
<TransitionGroup component={null}>
<CSSTransition
key={props.transitionKey}
timeout={duration ?? 200}
classNames={`${className}-${direction}`}
onEnter={props.onEnter}
onEntered={props.onEntered}
>
{props.children}
</CSSTransition>
</TransitionGroup>
);
}

export type TransitionProps = Parameters<typeof Transition>[0];
38 changes: 2 additions & 36 deletions src/components/Weeks.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import React from "react";

import { CSSTransition, TransitionGroup } from "react-transition-group";

/**
* Render the weeks in the month grid.
*
@@ -11,42 +9,10 @@ import { CSSTransition, TransitionGroup } from "react-transition-group";
export function Weeks(
props: JSX.IntrinsicElements["tbody"] & {
month: Date;
animate?: boolean;
transitionDirection?: "next" | "prev";
transitionDuration?: number;
onTransitionEnter?: () => void;
onTransitionEntered?: () => void;
}
) {
const {
animate,
transitionDirection: animateDirection,
onTransitionEnter: onEnter,
onTransitionEntered: onEntered,
month,
transitionDuration,
...tbodyProps
} = props;

if (animate) {
return (
<TransitionGroup component={null}>
<CSSTransition
key={month.toISOString()}
timeout={transitionDuration ?? 200}
classNames={
animateDirection === "next" ? "rdp-slide-next" : "rdp-slide-prev"
}
onEnter={onEnter}
onEntered={onEntered}
>
<tbody {...tbodyProps}>{props.children}</tbody>
</CSSTransition>
</TransitionGroup>
);
} else {
return <tbody {...tbodyProps} />;
}
const { month, ...tbodyProps } = props;
return <tbody {...tbodyProps} />;
}

export type WeeksProps = Parameters<typeof Weeks>[0];
1 change: 1 addition & 0 deletions src/components/custom-components.tsx
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ export * from "./Option.js";
export * from "./PreviousMonthButton.js";
export * from "./Root.js";
export * from "./Select.js";
export * from "./Transition.js";
export * from "./Week.js";
export * from "./Weekday.js";
export * from "./Weekdays.js";
7 changes: 6 additions & 1 deletion src/helpers/getDefaultClassNames.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UI, DayFlag, SelectionState } from "../UI.js";
import { UI, DayFlag, SelectionState, TransitionType } from "../UI.js";
import type { ClassNames } from "../types/index.js";

/**
@@ -24,5 +24,10 @@ export function getDefaultClassNames(): ClassNames {
`rdp-${SelectionState[key as keyof typeof SelectionState]}`;
}

for (const key in TransitionType) {
classNames[TransitionType[key as keyof typeof TransitionType]] =
`rdp-${TransitionType[key as keyof typeof TransitionType]}`;
}

return classNames as Required<ClassNames>;
}
56 changes: 12 additions & 44 deletions src/style.css
Original file line number Diff line number Diff line change
@@ -365,79 +365,47 @@
transition: transform var(--rdp-transition-duration) ease-in-out;
}

.rdp-slide-prev-enter {
.rdp-slide-previous-enter {
transform: translateX(-100%);
}
.rdp-slide-prev-enter-active {
.rdp-slide-previous-enter-active {
transform: translateX(0);
transition: transform var(--rdp-transition-duration) ease-in-out;
}
.rdp-slide-prev-exit {
.rdp-slide-previous-exit {
transform: translateX(0);
}
.rdp-slide-prev-exit-active {
.rdp-slide-previous-exit-active {
transform: translateX(100%);
transition: transform var(--rdp-transition-duration) ease-in-out;
}

.rdp-opacity-next-enter {
.rdp-fade-next-enter {
opacity: 0;
}
.rdp-opacity-next-enter-active {
.rdp-fade-next-enter-active {
opacity: 1;
transition: opacity var(--rdp-transition-duration) ease-in-out;
}
.rdp-opacity-next-exit {
.rdp-fade-next-exit {
opacity: 1;
}
.rdp-opacity-next-exit-active {
.rdp-fade-next-exit-active {
opacity: 0;
transition: opacity var(--rdp-transition-duration) ease-in-out;
}

.rdp-opacity-prev-enter {
.rdp-fade-previous-enter {
opacity: 0;
}
.rdp-opacity-prev-enter-active {
.rdp-fade-previous-enter-active {
opacity: 1;
transition: opacity var(--rdp-transition-duration) ease-in-out;
}
.rdp-opacity-prev-exit {
.rdp-fade-previous-exit {
opacity: 1;
}
.rdp-opacity-prev-exit-active {
.rdp-fade-previous-exit-active {
opacity: 0;
transition: opacity var(--rdp-transition-duration) ease-in-out;
}

.rdp-slide-up-next-enter {
transform: translateY(100%);
}
.rdp-slide-up-next-enter-active {
transform: translateY(0);
opacity: 1;
transition: transform var(--rdp-transition-duration) ease-in-out;
}
.rdp-slide-up-next-exit {
opacity: 1;
transform: translateY(0);
}
.rdp-slide-up-next-exit-active {
transform: translateY(-100%);
transition: transform var(--rdp-transition-duration) ease-in-out;
}

.rdp-slide-up-prev-enter {
transform: translateY(-100%);
}
.rdp-slide-up-prev-enter-active {
transform: translateY(0);
transition: transform var(--rdp-transition-duration) ease-in-out;
}
.rdp-slide-up-prev-exit {
transform: translateY(0);
}
.rdp-slide-up-prev-exit-active {
transform: translateY(100%);
transition: transform var(--rdp-transition-duration) ease-in-out;
}
Loading