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