feat: escape shortcut redirect to home page + page transition for all pages

This commit is contained in:
Sonny
2023-05-23 17:43:30 +02:00
parent 2908f4d4e6
commit c40bca2460
13 changed files with 76 additions and 68 deletions

View File

@@ -0,0 +1,25 @@
import { motion } from "framer-motion";
import { ReactNode } from "react";
export default function PageTransition({
className,
children,
}: {
className: string;
children: ReactNode;
}) {
return (
<motion.div
className={className}
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
transition={{
type: "spring",
stiffness: 260,
damping: 20,
}}
>
{children}
</motion.div>
);
}

View File

@@ -10,12 +10,12 @@ export default function SearchList({
items,
noItem,
cursor,
setCursor,
closeModal,
}: {
items: SearchItem[];
noItem?: ReactNode;
cursor: number;
setCursor: (cursor: number) => void;
closeModal: () => void;
}) {
const searchItemsGrouped = useMemo(
() => groupItemBy(items, "category.name"),
@@ -35,9 +35,8 @@ export default function SearchList({
{items.map((item) => (
<SearchListItem
item={item}
setCursor={setCursor}
selected={index === cursor}
index={index}
closeModal={closeModal}
key={item.id}
/>
))}

View File

@@ -1,65 +1,48 @@
import { motion } from "framer-motion";
import LinkTag from "next/link";
import { AiOutlineFolder } from "react-icons/ai";
import LinkFavicon from "components/Links/LinkFavicon";
import { SearchItem } from "types";
import { useEffect, useId, useRef, useState } from "react";
import { useEffect, useId, useRef } from "react";
import styles from "./search.module.scss";
export default function SearchListItem({
item,
index,
selected,
setCursor,
closeModal,
}: {
item: SearchItem;
index?: number;
selected: boolean;
setCursor: (cursor: number) => void;
closeModal: () => void;
}) {
const id = useId();
const ref = useRef<HTMLLIElement>(null);
const [isHover, setHover] = useState<boolean>(false);
const { name, type, url } = item;
useEffect(() => {
if (selected && !isHover) {
if (selected) {
ref.current?.scrollIntoView({ behavior: "smooth", block: "center" });
}
}, [isHover, selected]);
}, [selected]);
return (
<motion.li
className={styles["search-item"]}
initial={{ y: -15, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{
type: "spring",
stiffness: 260,
damping: 20,
delay: index * 0.025,
}}
ref={ref}
onMouseEnter={() => {
setCursor(index);
setHover(true);
}}
onMouseLeave={() => setHover(false)}
key={id}
>
<LinkTag href={url} target="_blank" rel="no-referrer">
<li className={styles["search-item"]} ref={ref} key={id}>
<LinkTag
href={url}
target="_blank"
rel="no-referrer"
onClick={closeModal}
>
{type === "link" ? (
<LinkFavicon url={item.url} noMargin size={24} />
) : (
<AiOutlineFolder size={24} />
)}
<span>{name}</span>
{selected && "selected"}
{selected && "[selected]"}
</LinkTag>
</motion.li>
</li>
);
}

View File

@@ -11,8 +11,8 @@ import SearchList from "./SearchList";
import * as Keys from "constants/keys";
import { GOOGLE_SEARCH_URL } from "constants/search-urls";
import { Category, SearchItem } from "types";
import { useLocalStorage } from "hooks/useLocalStorage";
import { Category, SearchItem } from "types";
import styles from "./search.module.scss";
@@ -134,7 +134,7 @@ export default function SearchModal({
items={itemsCompletion}
noItem={<LabelSearchWithGoogle />}
cursor={cursor}
setCursor={setCursor}
closeModal={close}
/>
)}
<button type="submit" disabled={!canSubmit} style={{ display: "none" }}>

View File

@@ -3,8 +3,10 @@ import { DefaultSeo } from "next-seo";
import { useRouter } from "next/router";
import nProgress from "nprogress";
import { useEffect } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import AuthRequired from "components/AuthRequired";
import * as Keys from "constants/keys";
import "nprogress/nprogress.css";
import "styles/globals.scss";
@@ -12,6 +14,11 @@ import "styles/globals.scss";
function MyApp({ Component, pageProps: { session, ...pageProps } }) {
const router = useRouter();
useHotkeys(Keys.CLOSE_SEARCH_KEY, () => router.push("/"), {
enabled: router.pathname !== "/",
enableOnFormTags: ["INPUT"],
});
useEffect(() => {
// Chargement pages
router.events.on("routeChangeStart", nProgress.start);

View File

@@ -3,11 +3,11 @@ import { useRouter } from "next/router";
import nProgress from "nprogress";
import { useMemo, useState } from "react";
import useAutoFocus from "hooks/useAutoFocus";
import FormLayout from "components/FormLayout";
import PageTransition from "components/PageTransition";
import TextBox from "components/TextBox";
import useAutoFocus from "hooks/useAutoFocus";
import { redirectWithoutClientCache } from "utils/client";
import { HandleAxiosError } from "utils/front";
@@ -48,7 +48,7 @@ function CreateCategory() {
};
return (
<>
<PageTransition className="page-category-create">
<FormLayout
title="Créer une catégorie"
errorMessage={error}
@@ -66,7 +66,7 @@ function CreateCategory() {
innerRef={autoFocusRef}
/>
</FormLayout>
</>
</PageTransition>
);
}

View File

@@ -4,10 +4,10 @@ import nProgress from "nprogress";
import { useMemo, useState } from "react";
import FormLayout from "components/FormLayout";
import PageTransition from "components/PageTransition";
import TextBox from "components/TextBox";
import useAutoFocus from "hooks/useAutoFocus";
import { Category } from "types";
import { prisma } from "utils/back";
import { BuildCategory, HandleAxiosError } from "utils/front";
@@ -51,7 +51,7 @@ function EditCategory({ category }: { category: Category }) {
};
return (
<>
<PageTransition className="page-category-edit">
<FormLayout
title="Modifier une catégorie"
errorMessage={error}
@@ -68,7 +68,7 @@ function EditCategory({ category }: { category: Category }) {
innerRef={autoFocusRef}
/>
</FormLayout>
</>
</PageTransition>
);
}

View File

@@ -5,6 +5,7 @@ import { useMemo, useState } from "react";
import Checkbox from "components/Checkbox";
import FormLayout from "components/FormLayout";
import PageTransition from "components/PageTransition";
import TextBox from "components/TextBox";
import { Category } from "types";
@@ -47,7 +48,7 @@ function RemoveCategory({ category }: { category: Category }) {
};
return (
<>
<PageTransition className="page-category-remove">
<FormLayout
title="Supprimer une catégorie"
categoryId={category.id.toString()}
@@ -72,7 +73,7 @@ function RemoveCategory({ category }: { category: Category }) {
onChangeCallback={(checked) => setConfirmDelete(checked)}
/>
</FormLayout>
</>
</PageTransition>
);
}

View File

@@ -1,15 +1,15 @@
import { AnimatePresence } from "framer-motion";
import { useRouter } from "next/router";
import { useCallback, useMemo, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import useModal from "hooks/useModal";
import Links from "components/Links/Links";
import PageTransition from "components/PageTransition";
import SearchModal from "components/SearchModal/SearchModal";
import SideMenu from "components/SideMenu/SideMenu";
import * as Keys from "constants/keys";
import { AnimatePresence, motion } from "framer-motion";
import useModal from "hooks/useModal";
import { Category, Link, SearchItem } from "types";
import { prisma } from "utils/back";
import { BuildCategory } from "utils/front";
@@ -157,16 +157,7 @@ function Home(props: HomeProps) {
);
return (
<motion.div
className="App"
initial={{ opacity: 0, scale: 0.85 }}
animate={{ opacity: 1, scale: 1 }}
transition={{
type: "spring",
stiffness: 260,
damping: 20,
}}
>
<PageTransition className="App">
<SideMenu
categories={categories}
favorites={favorites}
@@ -185,7 +176,7 @@ function Home(props: HomeProps) {
/>
)}
</AnimatePresence>
</motion.div>
</PageTransition>
);
}

View File

@@ -5,11 +5,11 @@ import { useMemo, useState } from "react";
import Checkbox from "components/Checkbox";
import FormLayout from "components/FormLayout";
import PageTransition from "components/PageTransition";
import Selector from "components/Selector";
import TextBox from "components/TextBox";
import useAutoFocus from "hooks/useAutoFocus";
import { Category, Link } from "types";
import { prisma } from "utils/back";
import { BuildCategory, HandleAxiosError, IsValidURL } from "utils/front";
@@ -61,7 +61,7 @@ function CreateLink({ categories }: { categories: Category[] }) {
};
return (
<>
<PageTransition className="page-link-create">
<FormLayout
title="Créer un lien"
categoryId={categoryIdQuery}
@@ -103,7 +103,7 @@ function CreateLink({ categories }: { categories: Category[] }) {
label="Favoris"
/>
</FormLayout>
</>
</PageTransition>
);
}

View File

@@ -5,11 +5,11 @@ import { useMemo, useState } from "react";
import Checkbox from "components/Checkbox";
import FormLayout from "components/FormLayout";
import PageTransition from "components/PageTransition";
import Selector from "components/Selector";
import TextBox from "components/TextBox";
import useAutoFocus from "hooks/useAutoFocus";
import { Category, Link } from "types";
import { prisma } from "utils/back";
import {
@@ -85,7 +85,7 @@ function EditLink({
};
return (
<>
<PageTransition className="page-link-edit">
<FormLayout
title="Modifier un lien"
errorMessage={error}
@@ -126,7 +126,7 @@ function EditLink({
label="Favoris"
/>
</FormLayout>
</>
</PageTransition>
);
}

View File

@@ -5,6 +5,7 @@ import { useMemo, useState } from "react";
import Checkbox from "components/Checkbox";
import FormLayout from "components/FormLayout";
import PageTransition from "components/PageTransition";
import TextBox from "components/TextBox";
import { Link } from "types";
@@ -42,7 +43,7 @@ function RemoveLink({ link }: { link: Link }) {
};
return (
<>
<PageTransition className="page-link-remove">
<FormLayout
title="Supprimer un lien"
categoryId={link.category.id.toString()}
@@ -86,7 +87,7 @@ function RemoveLink({ link }: { link: Link }) {
onChangeCallback={(checked) => setConfirmDelete(checked)}
/>
</FormLayout>
</>
</PageTransition>
);
}

View File

@@ -4,4 +4,5 @@ export function redirectWithoutClientCache(router: NextRouter, url: string) {
router.push(url, undefined, {
unstable_skipClientCache: true,
});
// FIXME: invalidate catch instead of weird hack
}