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",
"@svgr/webpack": "^8.0.1",
"axios": "^1.4.0",
"framer-motion": "^10.12.12",
"next": "^13.4.2",
"next-auth": "^4.22.1",
"next-seo": "^6.0.0",
@@ -1763,6 +1764,21 @@
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz",
"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": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
@@ -4280,6 +4296,29 @@
"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": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",

View File

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

View File

@@ -1,4 +1,5 @@
import axios from "axios";
import { motion } from "framer-motion";
import LinkTag from "next/link";
import { AiFillStar } from "react-icons/ai";
@@ -14,9 +15,11 @@ import styles from "./links.module.scss";
export default function LinkItem({
link,
toggleFavorite,
index,
}: {
link: Link;
toggleFavorite: (linkId: Link["id"]) => void;
index: number;
}) {
const { id, name, url, favorite } = link;
const onFavorite = () => {
@@ -33,7 +36,18 @@ export default function LinkItem({
};
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} />
<LinkTag href={url} target={"_blank"} rel={"noreferrer"}>
<span className={styles["link-name"]}>
@@ -46,7 +60,7 @@ export default function LinkItem({
<EditItem type="link" id={id} />
<RemoveItem type="link" id={id} />
</div>
</li>
</motion.li>
);
}

View File

@@ -6,6 +6,7 @@ import EditItem from "components/QuickActions/EditItem";
import RemoveItem from "components/QuickActions/RemoveItem";
import LinkItem from "./LinkItem";
import { AnimatePresence, motion } from "framer-motion";
import styles from "./links.module.scss";
export default function Links({
@@ -40,16 +41,32 @@ export default function Links({
</span>
</h2>
{links.length !== 0 ? (
<ul className={styles["links"]} key={Math.random()}>
{links.map((link, key) => (
<LinkItem key={key} link={link} toggleFavorite={toggleFavorite} />
<ul className={styles["links"]}>
{links.map((link, index) => (
<LinkItem
link={link}
toggleFavorite={toggleFavorite}
index={index}
key={link.id}
/>
))}
</ul>
) : (
<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>
</p>
</motion.p>
</AnimatePresence>
<LinkTag href={`/link/create?categoryId=${id}`}>
Créer un lien
</LinkTag>

View File

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

View File

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

View File

@@ -3,18 +3,21 @@ import { AiFillFolderOpen, AiOutlineFolder } from "react-icons/ai";
import { Category } from "types";
import { motion } from "framer-motion";
import styles from "./categories.module.scss";
interface CategoryItemProps {
category: Category;
categoryActive: Category;
handleSelectCategory: (category: Category) => void;
index: number;
}
export default function CategoryItem({
category,
categoryActive,
handleSelectCategory,
index,
}: CategoryItemProps): JSX.Element {
const ref = useRef<HTMLLIElement>();
const className = `${styles["item"]} ${
@@ -29,11 +32,25 @@ export default function CategoryItem({
}, [category.id, categoryActive.id]);
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}
ref={ref}
onClick={onClick}
style={{ display: "flex", alignItems: "center", gap: ".25em" }}
style={{
display: "flex",
alignItems: "center",
gap: ".25em",
transition: "none",
}}
>
{category.id === categoryActive.id ? (
<AiFillFolderOpen size={24} />
@@ -45,6 +62,6 @@ export default function CategoryItem({
<span className={styles["name"]}>{category.name}</span>
<span className={styles["links-count"]}> {category.links.length}</span>
</div>
</li>
</motion.li>
);
}

View File

@@ -9,6 +9,7 @@ import SearchModal from "components/SearchModal/SearchModal";
import SideMenu from "components/SideMenu/SideMenu";
import * as Keys from "constants/keys";
import { motion } from "framer-motion";
import { Category, Link, SearchItem } from "types";
import { prisma } from "utils/back";
import { BuildCategory } from "utils/front";
@@ -155,7 +156,16 @@ function Home(props: HomeProps) {
);
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
categories={categories}
favorites={favorites}
@@ -173,7 +183,7 @@ function Home(props: HomeProps) {
handleSelectCategory={handleSelectCategory}
/>
)}
</div>
</motion.div>
);
}

View File

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