Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f4cb272

Browse files
Merge branch 'main' into main
2 parents 438de62 + 6db694d commit f4cb272

File tree

5 files changed

+118
-64
lines changed

5 files changed

+118
-64
lines changed

‎package-lock.json‎

Lines changed: 32 additions & 3 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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"snippets:consolidate": "node ./utils/consolidateSnippets.js"
1717
},
1818
"dependencies": {
19-
"framer-motion": "^11.15.0",
19+
"motion": "^11.15.0",
2020
"prismjs": "^1.29.0",
2121
"react": "^18.3.1",
2222
"react-dom": "^18.3.1",

‎src/components/SnippetList.tsx‎

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { motion, AnimatePresence} from "framer-motion";
1+
import { motion, AnimatePresence,useReducedMotion} from "motion/react";
22
import { useState } from "react";
33

44
import { useAppContext } from "@contexts/AppContext";
@@ -13,6 +13,8 @@ const SnippetList = () => {
1313
const { fetchedSnippets } = useSnippets();
1414
const [isModalOpen, setIsModalOpen] = useState(false);
1515

16+
const shouldReduceMotion = useReducedMotion();
17+
1618
if (!fetchedSnippets)
1719
return (
1820
<div>
@@ -34,41 +36,49 @@ const SnippetList = () => {
3436
<>
3537
<motion.ul role="list" className="snippets">
3638
<AnimatePresence mode="popLayout">
37-
{fetchedSnippets.map((snippet, idx) => (
38-
<motion.li
39-
key={idx}
40-
initial={{opacity: 0,y: 20}}
41-
animate={{
42-
opacity: 1,
43-
y: 0,
44-
transition: {
45-
delay: idx*0.05,
46-
duration: 0.2,
47-
},
48-
}}
49-
exit={{
50-
opacity: 0,
51-
y: -20,
52-
transition: {
53-
delay: (fetchedSnippets.length-1-idx)*0.01,
54-
duration: 0.09,
55-
},
56-
}}
57-
>
58-
<motion.button
59-
className="snippet | flow"
60-
data-flow-space="sm"
61-
onClick={()=>handleOpenModal(snippet)}
62-
whileHover={{scale: 1.01}}
63-
whileTap={{scale: 0.98}}
39+
{fetchedSnippets.map((snippet, idx) => {
40+
constuniqueId=`${language.name}-${snippet.title}`;
41+
return(
42+
<motion.li
43+
key={uniqueId}
44+
layoutId={uniqueId}
45+
initial={{opacity: 0,y: 20}}
46+
animate={{
47+
opacity: 1,
48+
y: 0,
49+
transition: {
50+
delay: shouldReduceMotion ? 0 : 0.09+idx*0.05,
51+
duration: shouldReduceMotion ? 0 : 0.2,
52+
},
53+
}}
54+
exit={{
55+
opacity: 0,
56+
y: -20,
57+
transition: {
58+
delay: idx*0.01,
59+
duration: shouldReduceMotion ? 0 : 0.09,
60+
},
61+
}}
62+
transition={{
63+
ease: [0,0.75,0.25,1],
64+
duration: shouldReduceMotion ? 0 : 0.25,
65+
}}
6466
>
65-
<div className="snippet__preview">
66-
<img src={language.icon} alt={language.name} />
67-
</div>
68-
<h3 className="snippet__title">{snippet.title}</h3>
69-
</motion.button>
70-
</motion.li>
71-
))}
67+
<motion.button
68+
className="snippet | flow"
69+
data-flow-space="sm"
70+
onClick={() => handleOpenModal(snippet)}
71+
whileHover={{ scale: 1.01 }}
72+
whileTap={{ scale: 0.98 }}
73+
>
74+
<div className="snippet__preview">
75+
<img src={language.icon} alt={language.name} />
76+
</div>
77+
<h3 className="snippet__title">{snippet.title}</h3>
78+
</motion.button>
79+
</motion.li>
80+
);
81+
})}
7282
</AnimatePresence>
7383
</motion.ul>
7484

‎src/components/SnippetModal.tsx‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { motion} from "framer-motion";
1+
import { motion,useReducedMotion} from "motion/react";
22
import React from "react";
33
import ReactDOM from "react-dom";
44

@@ -23,6 +23,8 @@ const SnippetModal: React.FC<Props> = ({
2323
}) => {
2424
const modalRoot = document.getElementById("modal-root");
2525

26+
const shouldReduceMotion = useReducedMotion();
27+
2628
useEscapeKey(handleCloseModal);
2729

2830
if (!modalRoot) {
@@ -41,16 +43,17 @@ const SnippetModal: React.FC<Props> = ({
4143
initial={{ opacity: 0 }}
4244
animate={{ opacity: 1 }}
4345
exit={{ opacity: 0 }}
44-
transition={{ duration: 0.2 }}
46+
transition={{ duration: shouldReduceMotion ? 0 : 0.2 }}
4547
>
4648
<motion.div
4749
key="modal-content"
4850
className="modal | flow"
4951
data-flow-space="lg"
50-
initial={{ scale: 0.8, opacity: 0, y: 20 }}
51-
animate={{ scale: 1, opacity: 1, y: 0 }}
52-
exit={{ scale: 0.8, opacity: 0, y: 20 }}
53-
transition={{ type: "spring", duration: 0.5 }}
52+
layoutId={`${language}-${snippet.title}`}
53+
transition={{
54+
ease: [0, 0.75, 0.25, 1],
55+
duration: shouldReduceMotion ? 0 : 0.3,
56+
}}
5457
>
5558
<div className="modal__header">
5659
<h2 className="section-title">{snippet.title}</h2>

‎src/hooks/useKeyboardNavigation.ts‎

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ interface UseKeyboardNavigationProps {
99
onClose: () => void;
1010
}
1111

12+
const keyboardEventKeys = {
13+
arrowDown: "ArrowDown",
14+
arrowUp: "ArrowUp",
15+
enter: "Enter",
16+
escape: "Escape",
17+
} as const;
18+
19+
type KeyboardEventKeys =
20+
(typeof keyboardEventKeys)[keyof typeof keyboardEventKeys];
21+
1222
export const useKeyboardNavigation = ({
1323
items,
1424
isOpen,
@@ -20,25 +30,27 @@ export const useKeyboardNavigation = ({
2030
const handleKeyDown = (event: React.KeyboardEvent) => {
2131
if (!isOpen) return;
2232

23-
switch (event.key) {
24-
case "ArrowDown":
25-
event.preventDefault();
26-
setFocusedIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0));
27-
break;
28-
case "ArrowUp":
29-
event.preventDefault();
30-
setFocusedIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1));
31-
break;
32-
case "Enter":
33-
event.preventDefault();
34-
if (focusedIndex >= 0) {
35-
onSelect(items[focusedIndex]);
36-
}
37-
break;
38-
case "Escape":
39-
event.preventDefault();
40-
onClose();
41-
break;
33+
const key = event.key as KeyboardEventKeys;
34+
35+
if (Object.values(keyboardEventKeys).includes(key)) {
36+
event.preventDefault();
37+
38+
switch (key) {
39+
case "ArrowDown":
40+
setFocusedIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0));
41+
break;
42+
case "ArrowUp":
43+
setFocusedIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1));
44+
break;
45+
case "Enter":
46+
if (focusedIndex >= 0) {
47+
onSelect(items[focusedIndex]);
48+
}
49+
break;
50+
case "Escape":
51+
onClose();
52+
break;
53+
}
4254
}
4355
};
4456

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /