|
@ -9,6 +9,7 @@ import {H2} from 'components/MDX/Heading'; |
|
|
import {H4} from 'components/MDX/Heading'; |
|
|
import {H4} from 'components/MDX/Heading'; |
|
|
import {Challenge} from './Challenge'; |
|
|
import {Challenge} from './Challenge'; |
|
|
import {Navigation} from './Navigation'; |
|
|
import {Navigation} from './Navigation'; |
|
|
|
|
|
import {useRouter} from 'next/router'; |
|
|
|
|
|
|
|
|
interface ChallengesProps { |
|
|
interface ChallengesProps { |
|
|
children: React.ReactElement[]; |
|
|
children: React.ReactElement[]; |
|
@ -67,6 +68,11 @@ const parseChallengeContents = ( |
|
|
return contents; |
|
|
return contents; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
enum QueuedScroll { |
|
|
|
|
|
INIT = 'init', |
|
|
|
|
|
NEXT = 'next', |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
export function Challenges({ |
|
|
export function Challenges({ |
|
|
children, |
|
|
children, |
|
|
isRecipes, |
|
|
isRecipes, |
|
@ -76,19 +82,32 @@ export function Challenges({ |
|
|
const challenges = parseChallengeContents(children); |
|
|
const challenges = parseChallengeContents(children); |
|
|
const totalChallenges = challenges.length; |
|
|
const totalChallenges = challenges.length; |
|
|
const scrollAnchorRef = useRef<HTMLDivElement>(null); |
|
|
const scrollAnchorRef = useRef<HTMLDivElement>(null); |
|
|
const queuedScrollRef = useRef<boolean>(false); |
|
|
const queuedScrollRef = useRef<undefined | QueuedScroll>(QueuedScroll.INIT); |
|
|
const [activeIndex, setActiveIndex] = useState(0); |
|
|
const [activeIndex, setActiveIndex] = useState(0); |
|
|
const currentChallenge = challenges[activeIndex]; |
|
|
const currentChallenge = challenges[activeIndex]; |
|
|
|
|
|
const {asPath} = useRouter(); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
|
if (queuedScrollRef.current === true) { |
|
|
if (queuedScrollRef.current === QueuedScroll.INIT) { |
|
|
queuedScrollRef.current = false; |
|
|
const initIndex = challenges.findIndex( |
|
|
|
|
|
(challenge) => challenge.id === asPath.split('#')[1] |
|
|
|
|
|
); |
|
|
|
|
|
if (initIndex === -1) { |
|
|
|
|
|
queuedScrollRef.current = undefined; |
|
|
|
|
|
} else if (initIndex !== activeIndex) { |
|
|
|
|
|
setActiveIndex(initIndex); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (queuedScrollRef.current) { |
|
|
scrollAnchorRef.current!.scrollIntoView({ |
|
|
scrollAnchorRef.current!.scrollIntoView({ |
|
|
block: 'start', |
|
|
block: 'start', |
|
|
behavior: 'smooth', |
|
|
...(queuedScrollRef.current === QueuedScroll.NEXT && { |
|
|
|
|
|
behavior: 'smooth', |
|
|
|
|
|
}), |
|
|
}); |
|
|
}); |
|
|
|
|
|
queuedScrollRef.current = undefined; |
|
|
} |
|
|
} |
|
|
}); |
|
|
}, [activeIndex, asPath, challenges]); |
|
|
|
|
|
|
|
|
const handleChallengeChange = (index: number) => { |
|
|
const handleChallengeChange = (index: number) => { |
|
|
setActiveIndex(index); |
|
|
setActiveIndex(index); |
|
@ -129,7 +148,7 @@ export function Challenges({ |
|
|
hasNextChallenge={activeIndex < totalChallenges - 1} |
|
|
hasNextChallenge={activeIndex < totalChallenges - 1} |
|
|
handleClickNextChallenge={() => { |
|
|
handleClickNextChallenge={() => { |
|
|
setActiveIndex((i) => i + 1); |
|
|
setActiveIndex((i) => i + 1); |
|
|
queuedScrollRef.current = true; |
|
|
queuedScrollRef.current = QueuedScroll.NEXT; |
|
|
}} |
|
|
}} |
|
|
/> |
|
|
/> |
|
|
</div> |
|
|
</div> |
|
|