Skip to content

Commit 57dd7d0

Browse files
committed
feat: added framer motions
1 parent 21c1695 commit 57dd7d0

File tree

4 files changed

+129
-56
lines changed

4 files changed

+129
-56
lines changed

package-lock.json

Lines changed: 38 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"preview": "vite preview"
1313
},
1414
"dependencies": {
15+
"framer-motion": "^11.15.0",
1516
"prismjs": "^1.29.0",
1617
"react": "^18.3.1",
1718
"react-dom": "^18.3.1",

src/components/SnippetList.tsx

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useState } from "react";
2+
import { motion, AnimatePresence } from "framer-motion";
23
import { SnippetType } from "../types";
34
import { useAppContext } from "../contexts/AppContext";
45
import { useSnippets } from "../hooks/useSnippets";
@@ -30,31 +31,53 @@ const SnippetList = () => {
3031

3132
return (
3233
<>
33-
<ul role="list" className="snippets">
34+
<motion.ul
35+
role="list"
36+
className="snippets"
37+
initial="hidden"
38+
animate="visible"
39+
variants={{
40+
visible: {
41+
transition: {
42+
staggerChildren: 0.1
43+
}
44+
}
45+
}}
46+
>
3447
{fetchedSnippets.map((snippet, idx) => (
35-
<li key={idx}>
36-
<button
48+
<motion.li
49+
key={idx}
50+
variants={{
51+
hidden: { opacity: 0, y: 20 },
52+
visible: { opacity: 1, y: 0 }
53+
}}
54+
>
55+
<motion.button
3756
className="snippet | flow"
3857
data-flow-space="sm"
3958
onClick={() => handleOpenModal(snippet)}
59+
whileHover={{ scale: 1.01 }}
60+
whileTap={{ scale: 0.98 }}
4061
>
4162
<div className="snippet__preview">
4263
<img src={language.icon} alt={language.lang} />
4364
</div>
4465

4566
<h3 className="snippet__title">{snippet.title}</h3>
46-
</button>
47-
</li>
67+
</motion.button>
68+
</motion.li>
4869
))}
49-
</ul>
50-
51-
{isModalOpen && snippet && (
52-
<SnippetModal
53-
snippet={snippet}
54-
handleCloseModal={handleCloseModal}
55-
language={language.lang}
56-
/>
57-
)}
70+
</motion.ul>
71+
72+
<AnimatePresence>
73+
{isModalOpen && snippet && (
74+
<SnippetModal
75+
snippet={snippet}
76+
handleCloseModal={handleCloseModal}
77+
language={language.lang}
78+
/>
79+
)}
80+
</AnimatePresence>
5881
</>
5982
);
6083
};

src/components/SnippetModal.tsx

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from "react";
22
import ReactDOM from "react-dom";
3+
import { motion, AnimatePresence } from "framer-motion";
34
import Button from "./Button";
45
import { CloseIcon } from "./Icons";
56
import CodePreview from "./CodePreview";
@@ -23,46 +24,58 @@ const SnippetModal: React.FC<Props> = ({
2324
useEscapeKey(handleCloseModal);
2425

2526
return ReactDOM.createPortal(
26-
<div
27-
className="modal-overlay"
28-
onClick={(e) => {
29-
if (e.target === e.currentTarget) {
30-
handleCloseModal();
31-
}
32-
}}
33-
>
34-
<div className="modal | flow" data-flow-space="lg">
35-
<div className="modal__header">
36-
<h2 className="section-title">{snippet.title}</h2>
37-
<Button isIcon={true} onClick={handleCloseModal}>
38-
<CloseIcon />
39-
</Button>
40-
</div>
41-
<CodePreview language={slugify(language)} code={snippet.code} />
42-
<p>
43-
<b>Description: </b>
44-
{snippet.description}
45-
</p>
46-
<p>
47-
Contributed by{" "}
48-
<a
49-
href={`https://github.com/${snippet.author}`}
50-
target="_blank"
51-
rel="noopener noreferrer"
52-
className="styled-link"
53-
>
54-
@{snippet.author}
55-
</a>
56-
</p>
57-
<ul role="list" className="modal__tags">
58-
{snippet.tags.map((tag) => (
59-
<li key={tag} className="modal__tag">
60-
{tag}
61-
</li>
62-
))}
63-
</ul>
64-
</div>
65-
</div>,
27+
<AnimatePresence>
28+
<motion.div
29+
className="modal-overlay"
30+
onClick={(e) => {
31+
if (e.target === e.currentTarget) {
32+
handleCloseModal();
33+
}
34+
}}
35+
initial={{ opacity: 0 }}
36+
animate={{ opacity: 1 }}
37+
exit={{ opacity: 0 }}
38+
>
39+
<motion.div
40+
className="modal | flow"
41+
data-flow-space="lg"
42+
initial={{ scale: 0.8, opacity: 0, y: 20 }}
43+
animate={{ scale: 1, opacity: 1, y: 0 }}
44+
exit={{ scale: 0.8, opacity: 0, y: 20 }}
45+
transition={{ type: "spring", duration: 0.5 }}
46+
>
47+
<div className="modal__header">
48+
<h2 className="section-title">{snippet.title}</h2>
49+
<Button isIcon={true} onClick={handleCloseModal}>
50+
<CloseIcon />
51+
</Button>
52+
</div>
53+
<CodePreview language={slugify(language)} code={snippet.code} />
54+
<p>
55+
<b>Description: </b>
56+
{snippet.description}
57+
</p>
58+
<p>
59+
Contributed by{" "}
60+
<a
61+
href={`https://github.com/${snippet.author}`}
62+
target="_blank"
63+
rel="noopener noreferrer"
64+
className="styled-link"
65+
>
66+
@{snippet.author}
67+
</a>
68+
</p>
69+
<ul role="list" className="modal__tags">
70+
{snippet.tags.map((tag) => (
71+
<li key={tag} className="modal__tag">
72+
{tag}
73+
</li>
74+
))}
75+
</ul>
76+
</motion.div>
77+
</motion.div>
78+
</AnimatePresence>,
6679
modalRoot
6780
);
6881
};

0 commit comments

Comments
 (0)