mirror of
https://github.com/lukevella/rallly.git
synced 2025-05-21 04:46:22 +02:00
⚡️ Lazy load animation library to help reduce bundle size (#502)
This commit is contained in:
parent
c2c000f770
commit
696cd44ba1
14 changed files with 115 additions and 83 deletions
|
@ -1,4 +1,4 @@
|
|||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { AnimatePresence, m } from "framer-motion";
|
||||
import { useRouter } from "next/router";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import posthog from "posthog-js";
|
||||
|
@ -97,7 +97,7 @@ export const AdminControls = (props: { children?: React.ReactNode }) => {
|
|||
</div>
|
||||
<AnimatePresence initial={false}>
|
||||
{isSharingVisible ? (
|
||||
<motion.div
|
||||
<m.div
|
||||
initial={{
|
||||
opacity: 0,
|
||||
scale: 0.8,
|
||||
|
@ -122,13 +122,13 @@ export const AdminControls = (props: { children?: React.ReactNode }) => {
|
|||
setIsSharingVisible(false);
|
||||
}}
|
||||
/>
|
||||
</motion.div>
|
||||
</m.div>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
<motion.div className="relative z-10 space-y-4" layout="position">
|
||||
<m.div className="relative z-10 space-y-4" layout="position">
|
||||
{poll.verified === false ? <UnverifiedPollNotice /> : null}
|
||||
{props.children}
|
||||
</motion.div>
|
||||
</m.div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { AnimatePresence, m } from "framer-motion";
|
||||
import Cookies from "js-cookie";
|
||||
import Link from "next/link";
|
||||
import * as React from "react";
|
||||
|
@ -14,7 +14,7 @@ const CookieConsentPopover: React.VoidFunctionComponent = () => {
|
|||
return ReactDOM.createPortal(
|
||||
<AnimatePresence>
|
||||
{visible ? (
|
||||
<motion.div
|
||||
<m.div
|
||||
variants={{
|
||||
enter: {
|
||||
opacity: 1,
|
||||
|
@ -54,7 +54,7 @@ const CookieConsentPopover: React.VoidFunctionComponent = () => {
|
|||
OK
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
</m.div>
|
||||
) : null}
|
||||
</AnimatePresence>,
|
||||
getPortal(),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-disable @next/next/no-html-link-for-pages */
|
||||
import { motion } from "framer-motion";
|
||||
import { m } from "framer-motion";
|
||||
import { Trans, useTranslation } from "next-i18next";
|
||||
import * as React from "react";
|
||||
|
||||
|
@ -46,13 +46,13 @@ const Hero: React.VoidFunctionComponent = () => {
|
|||
<UserAvatarProvider seed="mock" names={names}>
|
||||
<DayjsProvider>
|
||||
<div className="relative inline-block">
|
||||
<motion.div
|
||||
<m.div
|
||||
className="absolute z-20 h-full rounded-2xl border-4 border-primary-500 bg-primary-200/10 shadow-md"
|
||||
initial={{ opacity: 0, width: 100, scale: 1.1, x: 480 }}
|
||||
animate={{ opacity: 1, x: 381 }}
|
||||
transition={{ type: "spring", delay: 1 }}
|
||||
/>
|
||||
<motion.div
|
||||
<m.div
|
||||
className="absolute z-20 rounded-full bg-primary-500 py-1 px-3 text-sm text-slate-100"
|
||||
initial={{
|
||||
opacity: 0,
|
||||
|
@ -65,15 +65,15 @@ const Hero: React.VoidFunctionComponent = () => {
|
|||
>
|
||||
{t("perfect")} 🤩
|
||||
<ScribbleArrow className="absolute -right-8 top-3 text-slate-400" />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
</m.div>
|
||||
<m.div
|
||||
className="rounded-lg"
|
||||
transition={{ type: "spring", delay: 0.5 }}
|
||||
initial={{ opacity: 0, translateY: -100 }}
|
||||
animate={{ opacity: 1, translateY: 0 }}
|
||||
>
|
||||
<PollDemo />
|
||||
</motion.div>
|
||||
</m.div>
|
||||
</div>
|
||||
</DayjsProvider>
|
||||
</UserAvatarProvider>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import clsx from "clsx";
|
||||
import { domAnimation, LazyMotion } from "framer-motion";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { Trans, useTranslation } from "next-i18next";
|
||||
|
@ -62,33 +63,35 @@ const PageLayout: React.VoidFunctionComponent<PageLayoutProps> = ({
|
|||
}) => {
|
||||
const { t } = useTranslation("homepage");
|
||||
return (
|
||||
<div className="bg-pattern min-h-full overflow-x-hidden">
|
||||
<div className="mx-auto flex max-w-7xl items-center py-8 px-8">
|
||||
<div className="grow">
|
||||
<div className="relative inline-block">
|
||||
<Link className="inline-block rounded" href="/">
|
||||
<Logo className="w-40 text-primary-500" alt="Rallly" />
|
||||
</Link>
|
||||
<span className="absolute -bottom-6 right-0 text-sm text-slate-400 transition-colors">
|
||||
<Trans t={t} i18nKey="3Ls" components={{ e: <em /> }} />
|
||||
</span>
|
||||
<LazyMotion features={domAnimation}>
|
||||
<div className="bg-pattern min-h-full overflow-x-hidden">
|
||||
<div className="mx-auto flex max-w-7xl items-center py-8 px-8">
|
||||
<div className="grow">
|
||||
<div className="relative inline-block">
|
||||
<Link className="inline-block rounded" href="/">
|
||||
<Logo className="w-40 text-primary-500" alt="Rallly" />
|
||||
</Link>
|
||||
<span className="absolute -bottom-6 right-0 text-sm text-slate-400 transition-colors">
|
||||
<Trans t={t} i18nKey="3Ls" components={{ e: <em /> }} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<Menu className="hidden items-center space-x-8 sm:flex" />
|
||||
<Popover>
|
||||
<PopoverTrigger asChild={true}>
|
||||
<button className="text-gray-400 transition-colors hover:text-primary-500 hover:no-underline hover:underline-offset-2 sm:hidden">
|
||||
<DotsVertical className="w-5" />
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="end">
|
||||
<Menu className="flex flex-col space-y-2 p-2" />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<Menu className="hidden items-center space-x-8 sm:flex" />
|
||||
<Popover>
|
||||
<PopoverTrigger asChild={true}>
|
||||
<button className="text-gray-400 transition-colors hover:text-primary-500 hover:no-underline hover:underline-offset-2 sm:hidden">
|
||||
<DotsVertical className="w-5" />
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="end">
|
||||
<Menu className="flex flex-col space-y-2 p-2" />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<div className="md:min-h-[calc(100vh-460px)]">{children}</div>
|
||||
<Footer />
|
||||
</div>
|
||||
<div className="md:min-h-[calc(100vh-460px)]">{children}</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</LazyMotion>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,23 +1,42 @@
|
|||
import { AnimatePresence, domAnimation, LazyMotion, m } from "framer-motion";
|
||||
import { useRouter } from "next/router";
|
||||
import React from "react";
|
||||
|
||||
import { DayjsProvider } from "@/utils/dayjs";
|
||||
|
||||
import { NextPageWithLayout } from "../../types";
|
||||
import ModalProvider from "../modal/modal-provider";
|
||||
import { UserProvider } from "../user-provider";
|
||||
import { MobileNavigation } from "./standard-layout/mobile-navigation";
|
||||
|
||||
const StandardLayout: React.VoidFunctionComponent<{
|
||||
children?: React.ReactNode;
|
||||
}> = ({ children, ...rest }) => {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<UserProvider>
|
||||
<DayjsProvider>
|
||||
<div className={"bg-pattern relative min-h-full"} {...rest}>
|
||||
<MobileNavigation />
|
||||
<div className="mx-auto max-w-4xl">{children}</div>
|
||||
</div>
|
||||
</DayjsProvider>
|
||||
</UserProvider>
|
||||
<LazyMotion features={domAnimation}>
|
||||
<UserProvider>
|
||||
<DayjsProvider>
|
||||
<ModalProvider>
|
||||
<div className={"bg-pattern relative min-h-full"} {...rest}>
|
||||
<MobileNavigation />
|
||||
<div className="mx-auto max-w-4xl">
|
||||
<AnimatePresence initial={false} exitBeforeEnter={true}>
|
||||
<m.div
|
||||
key={router.asPath}
|
||||
initial={{ opacity: 0, y: -50 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -50 }}
|
||||
>
|
||||
{children}
|
||||
</m.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</ModalProvider>
|
||||
</DayjsProvider>
|
||||
</UserProvider>
|
||||
</LazyMotion>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Dialog } from "@headlessui/react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { AnimatePresence, m } from "framer-motion";
|
||||
import * as React from "react";
|
||||
|
||||
import X from "@/components/icons/x.svg";
|
||||
|
@ -47,19 +47,19 @@ const Modal: React.VoidFunctionComponent<ModalProps> = ({
|
|||
if (overlayClosable) onCancel?.();
|
||||
}}
|
||||
>
|
||||
<motion.div
|
||||
<m.div
|
||||
transition={{ duration: 0.5 }}
|
||||
className="flex min-h-screen items-center justify-center"
|
||||
>
|
||||
<Dialog.Overlay
|
||||
as={motion.div}
|
||||
as={m.div}
|
||||
transition={{ duration: 0.5 }}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 z-0 bg-slate-900/25"
|
||||
/>
|
||||
<motion.div
|
||||
<m.div
|
||||
transition={{ duration: 0.1 }}
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
|
@ -116,8 +116,8 @@ const Modal: React.VoidFunctionComponent<ModalProps> = ({
|
|||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</m.div>
|
||||
</m.div>
|
||||
</Dialog>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { AnimatePresence, m } from "framer-motion";
|
||||
import { Trans, useTranslation } from "next-i18next";
|
||||
import * as React from "react";
|
||||
import { useMeasure } from "react-use";
|
||||
|
@ -191,7 +191,7 @@ const Poll: React.VoidFunctionComponent = () => {
|
|||
{shouldShowNewParticipantForm &&
|
||||
!poll.closed &&
|
||||
!editingParticipantId ? (
|
||||
<motion.div
|
||||
<m.div
|
||||
variants={{
|
||||
hidden: { height: 0, y: -50, opacity: 0 },
|
||||
visible: { height: "auto", y: 0, opacity: 1 },
|
||||
|
@ -211,7 +211,7 @@ const Poll: React.VoidFunctionComponent = () => {
|
|||
});
|
||||
}}
|
||||
/>
|
||||
</motion.div>
|
||||
</m.div>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
{participants.map((participant, i) => {
|
||||
|
@ -247,7 +247,7 @@ const Poll: React.VoidFunctionComponent = () => {
|
|||
</div>
|
||||
<AnimatePresence initial={false}>
|
||||
{shouldShowNewParticipantForm || editingParticipantId ? (
|
||||
<motion.div
|
||||
<m.div
|
||||
variants={{
|
||||
hidden: { height: 0, y: 30, opacity: 0 },
|
||||
visible: { height: "auto", y: 0, opacity: 1 },
|
||||
|
@ -281,7 +281,7 @@ const Poll: React.VoidFunctionComponent = () => {
|
|||
{shouldShowNewParticipantForm ? t("continue") : t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</motion.div>
|
||||
</m.div>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import clsx from "clsx";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { AnimatePresence, m } from "framer-motion";
|
||||
import React from "react";
|
||||
|
||||
import { usePollContext } from "./poll-context";
|
||||
|
@ -16,7 +16,7 @@ const ControlledScrollArea: React.VoidFunctionComponent<{
|
|||
style={{ width: availableSpace, maxWidth: availableSpace }}
|
||||
>
|
||||
<AnimatePresence initial={false}>
|
||||
<motion.div
|
||||
<m.div
|
||||
className="flex h-full"
|
||||
transition={{
|
||||
type: "spring",
|
||||
|
@ -26,7 +26,7 @@ const ControlledScrollArea: React.VoidFunctionComponent<{
|
|||
animate={{ x: scrollPosition * -1 }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
</m.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Listbox } from "@headlessui/react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { AnimatePresence, m } from "framer-motion";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import * as React from "react";
|
||||
import { FormProvider, useForm } from "react-hook-form";
|
||||
|
@ -131,7 +131,7 @@ const MobilePoll: React.VoidFunctionComponent = () => {
|
|||
<ChevronDown className="h-5 shrink-0" />
|
||||
</Listbox.Button>
|
||||
<Listbox.Options
|
||||
as={motion.div}
|
||||
as={m.div}
|
||||
transition={{
|
||||
duration: 0.1,
|
||||
}}
|
||||
|
@ -257,7 +257,7 @@ const MobilePoll: React.VoidFunctionComponent = () => {
|
|||
/>
|
||||
<AnimatePresence>
|
||||
{isEditing ? (
|
||||
<motion.div
|
||||
<m.div
|
||||
variants={{
|
||||
hidden: { opacity: 0, y: -100, height: 0 },
|
||||
visible: { opacity: 1, y: 0, height: "auto" },
|
||||
|
@ -281,7 +281,7 @@ const MobilePoll: React.VoidFunctionComponent = () => {
|
|||
{selectedParticipantId ? t("save") : t("continue")}
|
||||
</Button>
|
||||
</div>
|
||||
</motion.div>
|
||||
</m.div>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
</form>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Participant, VoteType } from "@prisma/client";
|
||||
import clsx from "clsx";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { AnimatePresence, m } from "framer-motion";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import * as React from "react";
|
||||
|
||||
|
@ -33,7 +33,7 @@ const CollapsibleContainer: React.VoidFunctionComponent<{
|
|||
return (
|
||||
<AnimatePresence initial={false}>
|
||||
{expanded ? (
|
||||
<motion.div
|
||||
<m.div
|
||||
variants={{
|
||||
collapsed: {
|
||||
width: 0,
|
||||
|
@ -50,7 +50,7 @@ const CollapsibleContainer: React.VoidFunctionComponent<{
|
|||
className={className}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
</m.div>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
);
|
||||
|
@ -61,14 +61,14 @@ const PopInOut: React.VoidFunctionComponent<{
|
|||
className?: string;
|
||||
}> = ({ children, className }) => {
|
||||
return (
|
||||
<motion.div
|
||||
<m.div
|
||||
initial={{ scale: 0 }}
|
||||
animate={{ scale: 1 }}
|
||||
exit={{ scale: 0 }}
|
||||
className={clsx(className)}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
</m.div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -83,7 +83,7 @@ const PollOptionVoteSummary: React.VoidFunctionComponent<{ optionId: string }> =
|
|||
participantsWhoVotedYes.length + participantsWhoVotedIfNeedBe.length ===
|
||||
0;
|
||||
return (
|
||||
<motion.div
|
||||
<m.div
|
||||
transition={{
|
||||
duration: 0.1,
|
||||
}}
|
||||
|
@ -145,7 +145,7 @@ const PollOptionVoteSummary: React.VoidFunctionComponent<{ optionId: string }> =
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
</m.div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -228,7 +228,7 @@ const PollOption: React.VoidFunctionComponent<PollOptionProps> = ({
|
|||
<div className="mr-3 shrink-0 grow">{children}</div>
|
||||
<AnimatePresence initial={false}>
|
||||
{editable ? null : (
|
||||
<motion.button
|
||||
<m.button
|
||||
exit={{ opacity: 0, x: -10 }}
|
||||
type="button"
|
||||
onTouchStart={(e) => e.stopPropagation()}
|
||||
|
@ -249,7 +249,7 @@ const PollOption: React.VoidFunctionComponent<PollOptionProps> = ({
|
|||
},
|
||||
)}
|
||||
/>
|
||||
</motion.button>
|
||||
</m.button>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<div className="mx-3">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { AnimatePresence, m } from "framer-motion";
|
||||
import * as React from "react";
|
||||
import { usePrevious } from "react-use";
|
||||
|
||||
|
@ -23,7 +23,7 @@ export const ScoreSummary: React.VoidFunctionComponent<PopularityScoreProps> =
|
|||
>
|
||||
<CheckCircle className="inline-block h-4 text-slate-300 transition-opacity" />
|
||||
<AnimatePresence initial={false} exitBeforeEnter={true}>
|
||||
<motion.span
|
||||
<m.span
|
||||
transition={{
|
||||
duration: 0.1,
|
||||
}}
|
||||
|
@ -40,7 +40,7 @@ export const ScoreSummary: React.VoidFunctionComponent<PopularityScoreProps> =
|
|||
className="relative"
|
||||
>
|
||||
{score}
|
||||
</motion.span>
|
||||
</m.span>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { motion } from "framer-motion";
|
||||
import { m } from "framer-motion";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import posthog from "posthog-js";
|
||||
import * as React from "react";
|
||||
|
@ -16,7 +16,7 @@ export interface UserDetailsProps {
|
|||
email?: string;
|
||||
}
|
||||
|
||||
const MotionButton = motion(Button);
|
||||
const MotionButton = m(Button);
|
||||
|
||||
export const UserDetails: React.VoidFunctionComponent<UserDetailsProps> = ({
|
||||
userId,
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
useRole,
|
||||
} from "@floating-ui/react-dom-interactions";
|
||||
import clsx from "clsx";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { AnimatePresence, m } from "framer-motion";
|
||||
import * as React from "react";
|
||||
|
||||
import { preventWidows } from "@/utils/prevent-widows";
|
||||
|
@ -104,7 +104,7 @@ const Tooltip: React.VoidFunctionComponent<TooltipProps> = ({
|
|||
<FloatingPortal>
|
||||
<AnimatePresence>
|
||||
{open ? (
|
||||
<motion.div
|
||||
<m.div
|
||||
className="z-30 rounded-md bg-slate-700 px-3 py-2 text-slate-200 shadow-md"
|
||||
initial="hidden"
|
||||
transition={{
|
||||
|
@ -140,7 +140,7 @@ const Tooltip: React.VoidFunctionComponent<TooltipProps> = ({
|
|||
}}
|
||||
/>
|
||||
{typeof content === "string" ? preventWidows(content) : content}
|
||||
</motion.div>
|
||||
</m.div>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
</FloatingPortal>
|
||||
|
|
|
@ -4,6 +4,7 @@ import "~/style.css";
|
|||
|
||||
import { Inter, Noto_Sans_Mono } from "@next/font/google";
|
||||
import { inject } from "@vercel/analytics";
|
||||
import { domAnimation, LazyMotion, m } from "framer-motion";
|
||||
import { NextPage } from "next";
|
||||
import { AppProps } from "next/app";
|
||||
import Head from "next/head";
|
||||
|
@ -15,7 +16,6 @@ import { Toaster } from "react-hot-toast";
|
|||
import Maintenance from "@/components/maintenance";
|
||||
|
||||
import { useCrispChat } from "../components/crisp-chat";
|
||||
import ModalProvider from "../components/modal/modal-provider";
|
||||
import { NextPageWithLayout } from "../types";
|
||||
import { absoluteUrl } from "../utils/absolute-url";
|
||||
import { UserSession } from "../utils/auth";
|
||||
|
@ -89,7 +89,17 @@ const MyApp: NextPage<AppPropsWithLayout> = ({ Component, pageProps }) => {
|
|||
--font-noto: ${noto.style.fontFamily};
|
||||
}
|
||||
`}</style>
|
||||
<ModalProvider>{getLayout(<Component {...pageProps} />)}</ModalProvider>
|
||||
<LazyMotion features={domAnimation}>
|
||||
{getLayout(
|
||||
<m.div
|
||||
initial={{ opacity: 0, y: -50 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: 50 }}
|
||||
>
|
||||
<Component {...pageProps} />
|
||||
</m.div>,
|
||||
)}
|
||||
</LazyMotion>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue