ConceptPortal-public/rsconcept/frontend/src/context/NavigationContext.tsx

119 lines
3.0 KiB
TypeScript
Raw Normal View History

'use client';
2023-09-05 00:23:53 +03:00
import { createContext, useCallback, useContext, useEffect, useState } from 'react';
2024-11-25 19:53:20 +03:00
import { useLocation, useNavigate } from 'react-router';
import { globals } from '@/utils/constants';
import { contextOutsideScope } from '@/utils/labels';
2023-09-05 00:23:53 +03:00
2023-12-28 14:04:44 +03:00
interface INavigationContext {
push: (path: string, newTab?: boolean) => void;
2023-12-28 14:04:44 +03:00
replace: (path: string) => void;
back: () => void;
forward: () => void;
canBack: () => boolean;
isBlocked: boolean;
setIsBlocked: (value: boolean) => void;
2023-09-05 00:23:53 +03:00
}
2023-12-26 14:23:51 +03:00
const NavigationContext = createContext<INavigationContext | null>(null);
2023-09-05 00:23:53 +03:00
export const useConceptNavigation = () => {
2023-12-26 14:23:51 +03:00
const context = useContext(NavigationContext);
2023-09-05 00:23:53 +03:00
if (!context) {
throw new Error(contextOutsideScope('useConceptNavigation', 'NavigationState'));
2023-09-05 00:23:53 +03:00
}
return context;
2023-12-28 14:04:44 +03:00
};
2023-09-05 00:23:53 +03:00
2024-09-19 17:49:25 +03:00
export const NavigationState = ({ children }: React.PropsWithChildren) => {
const router = useNavigate();
2023-09-05 00:23:53 +03:00
const { pathname } = useLocation();
2023-12-28 14:04:44 +03:00
const [isBlocked, setIsBlocked] = useState(false);
2023-12-28 14:04:44 +03:00
const validate = useCallback(() => {
return !isBlocked || confirm('Изменения не сохранены. Вы уверены что хотите совершить переход?');
2023-12-13 15:03:58 +03:00
}, [isBlocked]);
2023-12-28 14:04:44 +03:00
const canBack = useCallback(() => !!window.history && window.history?.length !== 0, []);
2023-12-28 14:04:44 +03:00
const scrollTop = useCallback(() => {
2023-09-05 00:23:53 +03:00
window.scrollTo(0, 0);
const mainScroll = document.getElementById(globals.main_scroll);
2023-09-05 00:23:53 +03:00
if (mainScroll) {
2023-12-28 14:04:44 +03:00
mainScroll.scroll(0, 0);
2023-09-05 00:23:53 +03:00
}
}, []);
2023-09-05 00:23:53 +03:00
const push = useCallback(
(path: string, newTab?: boolean) => {
if (newTab) {
window.open(`${path}`, '_blank');
return;
}
2023-12-28 14:04:44 +03:00
if (validate()) {
scrollTop();
2024-11-25 19:53:20 +03:00
Promise.resolve(router(path)).catch(console.log);
2023-12-28 14:04:44 +03:00
setIsBlocked(false);
}
},
[router, validate, scrollTop]
);
2023-09-05 00:23:53 +03:00
const replace = useCallback(
2023-12-28 14:04:44 +03:00
(path: string) => {
if (validate()) {
scrollTop();
2024-11-25 19:53:20 +03:00
Promise.resolve(router(path, { replace: true })).catch(console.log);
2023-12-28 14:04:44 +03:00
setIsBlocked(false);
}
},
[router, validate, scrollTop]
);
const back = useCallback(() => {
2023-12-13 15:03:58 +03:00
if (validate()) {
scrollTop();
2024-11-25 19:53:20 +03:00
Promise.resolve(router(-1)).catch(console.log);
2023-12-13 15:03:58 +03:00
setIsBlocked(false);
}
}, [router, validate, scrollTop]);
2023-12-28 14:04:44 +03:00
const forward = useCallback(() => {
2023-12-13 15:03:58 +03:00
if (validate()) {
scrollTop();
2024-11-25 19:53:20 +03:00
Promise.resolve(router(1)).catch(console.log);
2023-12-13 15:03:58 +03:00
setIsBlocked(false);
}
}, [router, validate, scrollTop]);
2023-09-05 00:23:53 +03:00
useEffect(() => {
scrollTop();
}, [pathname, scrollTop]);
2023-09-05 00:23:53 +03:00
return (
2023-12-28 14:04:44 +03:00
<NavigationContext.Provider
value={{
push,
replace,
back,
forward,
canBack,
isBlocked,
setIsBlocked
}}
>
{children}
</NavigationContext.Provider>
);
};
2023-12-13 15:03:58 +03:00
export function useBlockNavigation(isBlocked: boolean) {
const router = useConceptNavigation();
2023-12-28 14:04:44 +03:00
useEffect(() => {
2023-12-13 15:03:58 +03:00
router.setIsBlocked(isBlocked);
return () => router.setIsBlocked(false);
}, [router, isBlocked]);
2023-12-28 14:04:44 +03:00
}