Skip to content

Commit 1463cc6

Browse files
committed
feat: Navigation state provider and hook
Temporary workaround for nodejs#6409 Get more info here - nodejs#6675 Signed-off-by: abizek <[email protected]>
1 parent 050f395 commit 1463cc6

File tree

6 files changed

+75
-3
lines changed

6 files changed

+75
-3
lines changed

components/Common/ProgressionSidebar/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { useTranslations } from 'next-intl';
22
import type { ComponentProps, FC } from 'react';
3+
import { useRef } from 'react';
34

45
import ProgressionSidebarGroup from '@/components/Common/ProgressionSidebar/ProgressionSidebarGroup';
56
import WithRouterSelect from '@/components/withRouterSelect';
6-
import { useClientContext } from '@/hooks/react-server';
7+
import { useClientContext, useNavigationState } from '@/hooks';
78

89
import styles from './index.module.css';
910

@@ -14,6 +15,9 @@ type ProgressionSidebarProps = {
1415
const ProgressionSidebar: FC<ProgressionSidebarProps> = ({ groups }) => {
1516
const t = useTranslations();
1617
const { pathname } = useClientContext();
18+
const ref = useRef<HTMLElement>(null);
19+
20+
useNavigationState('progressionSidebar', ref);
1721

1822
const selectItems = groups.map(({ items, groupName }) => ({
1923
label: groupName,
@@ -26,7 +30,7 @@ const ProgressionSidebar: FC<ProgressionSidebarProps> = ({ groups }) => {
2630
.find(item => pathname === item.value);
2731

2832
return (
29-
<nav className={styles.wrapper}>
33+
<nav className={styles.wrapper} ref={ref}>
3034
<WithRouterSelect
3135
label={t('components.common.sidebar.title')}
3236
values={selectItems}

components/withProgressionSidebar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import type { RichTranslationValues } from 'next-intl';
24
import type { FC } from 'react';
35

hooks/react-client/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export { default as useClientContext } from './useClientContext';
66
export { default as useKeyboardCommands } from './useKeyboardCommands';
77
export { default as useClickOutside } from './useClickOutside';
88
export { default as useBottomScrollListener } from './useBottomScrollListener';
9+
export { default as useNavigationState } from './useNavigationState';
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use client';
2+
3+
import type { RefObject } from 'react';
4+
import { useContext, useEffect } from 'react';
5+
6+
import { NavigationStateContext } from '@/providers/navigationStateProvider';
7+
import { debounce } from '@/util/debounce';
8+
9+
const useNavigationState = <T extends HTMLElement>(
10+
id: string,
11+
ref: RefObject<T>,
12+
debounceTime = 300
13+
) => {
14+
const navigationState = useContext(NavigationStateContext);
15+
16+
const handleScroll = debounce(() => {
17+
if (ref.current) {
18+
navigationState[id] = {
19+
x: ref.current.scrollLeft,
20+
y: ref.current.scrollTop,
21+
};
22+
}
23+
}, debounceTime);
24+
25+
useEffect(() => {
26+
const element = ref.current;
27+
if (navigationState[id] && navigationState[id].y !== element?.scrollTop) {
28+
element?.scroll({ top: navigationState[id].y, behavior: 'instant' });
29+
}
30+
31+
element?.addEventListener('scroll', handleScroll, { passive: true });
32+
33+
return () => {
34+
element?.removeEventListener('scroll', handleScroll);
35+
};
36+
// eslint-disable-next-line react-hooks/exhaustive-deps
37+
}, []);
38+
};
39+
40+
export default useNavigationState;

layouts/Base.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33
import type { FC, PropsWithChildren } from 'react';
44

5+
import { NavigationStateProvider } from '@/providers/navigationStateProvider';
56
import { NotificationProvider } from '@/providers/notificationProvider';
67

78
import styles from './layouts.module.css';
89

910
const BaseLayout: FC<PropsWithChildren> = ({ children }) => (
1011
<NotificationProvider viewportClassName="absolute bottom-0 right-0 list-none">
11-
<div className={styles.baseLayout}>{children}</div>
12+
<NavigationStateProvider>
13+
<div className={styles.baseLayout}>{children}</div>
14+
</NavigationStateProvider>
1215
</NotificationProvider>
1316
);
1417

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use client';
2+
3+
import { createContext, useRef } from 'react';
4+
import type { FC, PropsWithChildren } from 'react';
5+
6+
type NavigationStateContextType = Record<string, { x: number; y: number }>;
7+
8+
export const NavigationStateContext = createContext<NavigationStateContextType>(
9+
{}
10+
);
11+
12+
export const NavigationStateProvider: FC<PropsWithChildren> = ({
13+
children,
14+
}) => {
15+
const navigationState = useRef<NavigationStateContextType>({});
16+
17+
return (
18+
<NavigationStateContext.Provider value={navigationState.current}>
19+
{children}
20+
</NavigationStateContext.Provider>
21+
);
22+
};

0 commit comments

Comments
 (0)