Skip to content

Commit

Permalink
feat: Navigation state provider and hook
Browse files Browse the repository at this point in the history
Temporary workaround for nodejs#6409
Get more info here - nodejs#6675

Signed-off-by: abizek <[email protected]>
  • Loading branch information
abizek committed May 4, 2024
1 parent 050f395 commit 1463cc6
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 3 deletions.
8 changes: 6 additions & 2 deletions components/Common/ProgressionSidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useTranslations } from 'next-intl';
import type { ComponentProps, FC } from 'react';
import { useRef } from 'react';

import ProgressionSidebarGroup from '@/components/Common/ProgressionSidebar/ProgressionSidebarGroup';
import WithRouterSelect from '@/components/withRouterSelect';
import { useClientContext } from '@/hooks/react-server';
import { useClientContext, useNavigationState } from '@/hooks';

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

Expand All @@ -14,6 +15,9 @@ type ProgressionSidebarProps = {
const ProgressionSidebar: FC<ProgressionSidebarProps> = ({ groups }) => {
const t = useTranslations();
const { pathname } = useClientContext();
const ref = useRef<HTMLElement>(null);

useNavigationState('progressionSidebar', ref);

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

return (
<nav className={styles.wrapper}>
<nav className={styles.wrapper} ref={ref}>
<WithRouterSelect
label={t('components.common.sidebar.title')}
values={selectItems}
Expand Down
2 changes: 2 additions & 0 deletions components/withProgressionSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

import type { RichTranslationValues } from 'next-intl';
import type { FC } from 'react';

Expand Down
1 change: 1 addition & 0 deletions hooks/react-client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { default as useClientContext } from './useClientContext';
export { default as useKeyboardCommands } from './useKeyboardCommands';
export { default as useClickOutside } from './useClickOutside';
export { default as useBottomScrollListener } from './useBottomScrollListener';
export { default as useNavigationState } from './useNavigationState';
40 changes: 40 additions & 0 deletions hooks/react-client/useNavigationState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use client';

import type { RefObject } from 'react';
import { useContext, useEffect } from 'react';

import { NavigationStateContext } from '@/providers/navigationStateProvider';
import { debounce } from '@/util/debounce';

const useNavigationState = <T extends HTMLElement>(
id: string,
ref: RefObject<T>,
debounceTime = 300
) => {
const navigationState = useContext(NavigationStateContext);

const handleScroll = debounce(() => {
if (ref.current) {
navigationState[id] = {
x: ref.current.scrollLeft,
y: ref.current.scrollTop,
};
}
}, debounceTime);

useEffect(() => {
const element = ref.current;
if (navigationState[id] && navigationState[id].y !== element?.scrollTop) {
element?.scroll({ top: navigationState[id].y, behavior: 'instant' });
}

element?.addEventListener('scroll', handleScroll, { passive: true });

return () => {
element?.removeEventListener('scroll', handleScroll);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
};

export default useNavigationState;
5 changes: 4 additions & 1 deletion layouts/Base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

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

import { NavigationStateProvider } from '@/providers/navigationStateProvider';
import { NotificationProvider } from '@/providers/notificationProvider';

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

const BaseLayout: FC<PropsWithChildren> = ({ children }) => (
<NotificationProvider viewportClassName="absolute bottom-0 right-0 list-none">
<div className={styles.baseLayout}>{children}</div>
<NavigationStateProvider>
<div className={styles.baseLayout}>{children}</div>
</NavigationStateProvider>
</NotificationProvider>
);

Expand Down
22 changes: 22 additions & 0 deletions providers/navigationStateProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use client';

import { createContext, useRef } from 'react';
import type { FC, PropsWithChildren } from 'react';

type NavigationStateContextType = Record<string, { x: number; y: number }>;

export const NavigationStateContext = createContext<NavigationStateContextType>(
{}
);

export const NavigationStateProvider: FC<PropsWithChildren> = ({
children,
}) => {
const navigationState = useRef<NavigationStateContextType>({});

return (
<NavigationStateContext.Provider value={navigationState.current}>
{children}
</NavigationStateContext.Provider>
);
};

0 comments on commit 1463cc6

Please sign in to comment.