feat: add some animation

This commit is contained in:
Sonny
2023-05-17 01:53:07 +02:00
parent 15eae9a39a
commit 95c5ca78bc
9 changed files with 114 additions and 18 deletions

39
package-lock.json generated
View File

@@ -9,6 +9,7 @@
"@prisma/client": "^4.14.0", "@prisma/client": "^4.14.0",
"@svgr/webpack": "^8.0.1", "@svgr/webpack": "^8.0.1",
"axios": "^1.4.0", "axios": "^1.4.0",
"framer-motion": "^10.12.12",
"next": "^13.4.2", "next": "^13.4.2",
"next-auth": "^4.22.1", "next-auth": "^4.22.1",
"next-seo": "^6.0.0", "next-seo": "^6.0.0",
@@ -1763,6 +1764,21 @@
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz",
"integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ=="
}, },
"node_modules/@emotion/is-prop-valid": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
"optional": true,
"dependencies": {
"@emotion/memoize": "0.7.4"
}
},
"node_modules/@emotion/is-prop-valid/node_modules/@emotion/memoize": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
"optional": true
},
"node_modules/@emotion/memoize": { "node_modules/@emotion/memoize": {
"version": "0.8.0", "version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
@@ -4280,6 +4296,29 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/framer-motion": {
"version": "10.12.12",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.12.12.tgz",
"integrity": "sha512-DDCqp60U6hR7aUrXj/BXc/t0Sd/U4ep6w/NZQkw898K+u7s+Vv/P8yxq4WTNA86kU9QCsqOgn1Qhz2DpYK0Oag==",
"dependencies": {
"tslib": "^2.4.0"
},
"optionalDependencies": {
"@emotion/is-prop-valid": "^0.8.2"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",

View File

@@ -11,6 +11,7 @@
"@prisma/client": "^4.14.0", "@prisma/client": "^4.14.0",
"@svgr/webpack": "^8.0.1", "@svgr/webpack": "^8.0.1",
"axios": "^1.4.0", "axios": "^1.4.0",
"framer-motion": "^10.12.12",
"next": "^13.4.2", "next": "^13.4.2",
"next-auth": "^4.22.1", "next-auth": "^4.22.1",
"next-seo": "^6.0.0", "next-seo": "^6.0.0",

View File

@@ -1,4 +1,5 @@
import axios from "axios"; import axios from "axios";
import { motion } from "framer-motion";
import LinkTag from "next/link"; import LinkTag from "next/link";
import { AiFillStar } from "react-icons/ai"; import { AiFillStar } from "react-icons/ai";
@@ -14,9 +15,11 @@ import styles from "./links.module.scss";
export default function LinkItem({ export default function LinkItem({
link, link,
toggleFavorite, toggleFavorite,
index,
}: { }: {
link: Link; link: Link;
toggleFavorite: (linkId: Link["id"]) => void; toggleFavorite: (linkId: Link["id"]) => void;
index: number;
}) { }) {
const { id, name, url, favorite } = link; const { id, name, url, favorite } = link;
const onFavorite = () => { const onFavorite = () => {
@@ -33,7 +36,18 @@ export default function LinkItem({
}; };
return ( return (
<li className={styles["link"]} key={id}> <motion.li
className={styles["link"]}
key={id}
initial={{ x: -30, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{
type: "spring",
stiffness: 260,
damping: 20,
delay: index * 0.05,
}}
>
<LinkFavicon url={url} /> <LinkFavicon url={url} />
<LinkTag href={url} target={"_blank"} rel={"noreferrer"}> <LinkTag href={url} target={"_blank"} rel={"noreferrer"}>
<span className={styles["link-name"]}> <span className={styles["link-name"]}>
@@ -46,7 +60,7 @@ export default function LinkItem({
<EditItem type="link" id={id} /> <EditItem type="link" id={id} />
<RemoveItem type="link" id={id} /> <RemoveItem type="link" id={id} />
</div> </div>
</li> </motion.li>
); );
} }

View File

@@ -6,6 +6,7 @@ import EditItem from "components/QuickActions/EditItem";
import RemoveItem from "components/QuickActions/RemoveItem"; import RemoveItem from "components/QuickActions/RemoveItem";
import LinkItem from "./LinkItem"; import LinkItem from "./LinkItem";
import { AnimatePresence, motion } from "framer-motion";
import styles from "./links.module.scss"; import styles from "./links.module.scss";
export default function Links({ export default function Links({
@@ -40,16 +41,32 @@ export default function Links({
</span> </span>
</h2> </h2>
{links.length !== 0 ? ( {links.length !== 0 ? (
<ul className={styles["links"]} key={Math.random()}> <ul className={styles["links"]}>
{links.map((link, key) => ( {links.map((link, index) => (
<LinkItem key={key} link={link} toggleFavorite={toggleFavorite} /> <LinkItem
link={link}
toggleFavorite={toggleFavorite}
index={index}
key={link.id}
/>
))} ))}
</ul> </ul>
) : ( ) : (
<div className={styles["no-link"]}> <div className={styles["no-link"]}>
<p> <AnimatePresence>
<motion.p
key={Math.random()}
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
transition={{
type: "spring",
stiffness: 260,
damping: 20,
}}
>
Aucun lien pour <b>{name}</b> Aucun lien pour <b>{name}</b>
</p> </motion.p>
</AnimatePresence>
<LinkTag href={`/link/create?categoryId=${id}`}> <LinkTag href={`/link/create?categoryId=${id}`}>
Créer un lien Créer un lien
</LinkTag> </LinkTag>

View File

@@ -71,7 +71,6 @@
gap: 0.5em; gap: 0.5em;
padding: 3px; padding: 3px;
flex-direction: column; flex-direction: column;
animation: fadein 0.3s both; // bug on drag start
overflow-x: hidden; overflow-x: hidden;
overflow-y: scroll; overflow-y: scroll;
} }
@@ -89,7 +88,6 @@
outline: 3px solid transparent; outline: 3px solid transparent;
display: flex; display: flex;
align-items: center; align-items: center;
transition: 0.15s;
&:hover { &:hover {
border: 1px solid transparent; border: 1px solid transparent;

View File

@@ -22,12 +22,13 @@ export default function Categories({
<div className={styles["categories"]}> <div className={styles["categories"]}>
<h4>Catégories {linksCount}</h4> <h4>Catégories {linksCount}</h4>
<ul className={styles["items"]}> <ul className={styles["items"]}>
{categories.map((category, key) => ( {categories.map((category, index) => (
<CategoryItem <CategoryItem
category={category} category={category}
categoryActive={categoryActive} categoryActive={categoryActive}
handleSelectCategory={handleSelectCategory} handleSelectCategory={handleSelectCategory}
key={key} key={category.id}
index={index}
/> />
))} ))}
</ul> </ul>

View File

@@ -3,18 +3,21 @@ import { AiFillFolderOpen, AiOutlineFolder } from "react-icons/ai";
import { Category } from "types"; import { Category } from "types";
import { motion } from "framer-motion";
import styles from "./categories.module.scss"; import styles from "./categories.module.scss";
interface CategoryItemProps { interface CategoryItemProps {
category: Category; category: Category;
categoryActive: Category; categoryActive: Category;
handleSelectCategory: (category: Category) => void; handleSelectCategory: (category: Category) => void;
index: number;
} }
export default function CategoryItem({ export default function CategoryItem({
category, category,
categoryActive, categoryActive,
handleSelectCategory, handleSelectCategory,
index,
}: CategoryItemProps): JSX.Element { }: CategoryItemProps): JSX.Element {
const ref = useRef<HTMLLIElement>(); const ref = useRef<HTMLLIElement>();
const className = `${styles["item"]} ${ const className = `${styles["item"]} ${
@@ -29,11 +32,25 @@ export default function CategoryItem({
}, [category.id, categoryActive.id]); }, [category.id, categoryActive.id]);
return ( return (
<li <motion.li
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
transition={{
type: "spring",
stiffness: 260,
damping: 20,
delay: index * 0.025,
duration: 200,
}}
className={className} className={className}
ref={ref} ref={ref}
onClick={onClick} onClick={onClick}
style={{ display: "flex", alignItems: "center", gap: ".25em" }} style={{
display: "flex",
alignItems: "center",
gap: ".25em",
transition: "none",
}}
> >
{category.id === categoryActive.id ? ( {category.id === categoryActive.id ? (
<AiFillFolderOpen size={24} /> <AiFillFolderOpen size={24} />
@@ -45,6 +62,6 @@ export default function CategoryItem({
<span className={styles["name"]}>{category.name}</span> <span className={styles["name"]}>{category.name}</span>
<span className={styles["links-count"]}> {category.links.length}</span> <span className={styles["links-count"]}> {category.links.length}</span>
</div> </div>
</li> </motion.li>
); );
} }

View File

@@ -9,6 +9,7 @@ import SearchModal from "components/SearchModal/SearchModal";
import SideMenu from "components/SideMenu/SideMenu"; import SideMenu from "components/SideMenu/SideMenu";
import * as Keys from "constants/keys"; import * as Keys from "constants/keys";
import { motion } from "framer-motion";
import { Category, Link, SearchItem } from "types"; import { Category, Link, SearchItem } from "types";
import { prisma } from "utils/back"; import { prisma } from "utils/back";
import { BuildCategory } from "utils/front"; import { BuildCategory } from "utils/front";
@@ -155,7 +156,16 @@ function Home(props: HomeProps) {
); );
return ( return (
<div className="App"> <motion.div
className="App"
initial={{ opacity: 0, scale: 0.85 }}
animate={{ opacity: 1, scale: 1 }}
transition={{
type: "spring",
stiffness: 260,
damping: 20,
}}
>
<SideMenu <SideMenu
categories={categories} categories={categories}
favorites={favorites} favorites={favorites}
@@ -173,7 +183,7 @@ function Home(props: HomeProps) {
handleSelectCategory={handleSelectCategory} handleSelectCategory={handleSelectCategory}
/> />
)} )}
</div> </motion.div>
); );
} }

View File

@@ -34,7 +34,6 @@ body {
padding: 10px; padding: 10px;
display: flex; display: flex;
justify-content: center; justify-content: center;
animation: fadein 250ms both;
} }
a { a {