ConceptPortal-public/rsconcept/frontend/src/app/Navigation/NavigationContext.tsx

120 lines
3.2 KiB
TypeScript
Raw Normal View History

'use client';
2023-09-05 00:23:53 +03:00
2025-03-05 00:10:02 +03:00
import { createContext, use, useEffect, useState } from 'react';
import { useNavigate } from 'react-router';
export interface NavigationProps {
path: string;
newTab?: boolean;
force?: boolean;
}
2023-12-28 14:04:44 +03:00
interface INavigationContext {
push: (props: NavigationProps) => void;
pushAsync: (props: NavigationProps) => void | Promise<void>;
replace: (props: Omit<NavigationProps, 'newTab'>) => void;
replaceAsync: (props: Omit<NavigationProps, 'newTab'>) => void | Promise<void>;
back: (force?: boolean) => void;
forward: (force?: boolean) => void;
2023-12-28 14:04:44 +03:00
canBack: () => boolean;
isBlocked: boolean;
setIsBlocked: (value: boolean) => void;
2023-09-05 00:23:53 +03:00
}
2025-03-05 00:10:02 +03:00
export const NavigationContext = createContext<INavigationContext | null>(null);
2023-09-05 00:23:53 +03:00
export const useConceptNavigation = () => {
2025-03-05 00:10:02 +03:00
const context = use(NavigationContext);
2023-09-05 00:23:53 +03:00
if (!context) {
throw new Error('useConceptNavigation has to be used within <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-12-28 14:04:44 +03:00
const [isBlocked, setIsBlocked] = useState(false);
function validate() {
2023-12-28 14:04:44 +03:00
return !isBlocked || confirm('Изменения не сохранены. Вы уверены что хотите совершить переход?');
}
2023-12-13 15:03:58 +03:00
function canBack() {
return !!window.history && window.history?.length !== 0;
}
function push(props: NavigationProps) {
if (props.newTab) {
window.open(`${props.path}`, '_blank');
} else if (props.force || validate()) {
setIsBlocked(false);
Promise.resolve(router(props.path, { viewTransition: true })).catch(console.error);
2023-09-05 00:23:53 +03:00
}
}
function pushAsync(props: NavigationProps): void | Promise<void> {
if (props.newTab) {
window.open(`${props.path}`, '_blank');
} else if (props.force || validate()) {
setIsBlocked(false);
return router(props.path, { viewTransition: true });
}
}
function replace(props: Omit<NavigationProps, 'newTab'>) {
if (props.force || validate()) {
setIsBlocked(false);
Promise.resolve(router(props.path, { replace: true, viewTransition: true })).catch(console.error);
}
}
2023-12-28 14:04:44 +03:00
function replaceAsync(props: Omit<NavigationProps, 'newTab'>): void | Promise<void> {
if (props.force || validate()) {
2023-12-13 15:03:58 +03:00
setIsBlocked(false);
return router(props.path, { replace: true, viewTransition: true });
2023-12-13 15:03:58 +03:00
}
}
function back(force?: boolean) {
if (force || validate()) {
Promise.resolve(router(-1)).catch(console.error);
2023-12-13 15:03:58 +03:00
setIsBlocked(false);
}
}
2023-09-05 00:23:53 +03:00
function forward(force?: boolean) {
if (force || validate()) {
Promise.resolve(router(1)).catch(console.error);
setIsBlocked(false);
}
}
2023-09-05 00:23:53 +03:00
return (
2024-12-12 21:58:19 +03:00
<NavigationContext
2023-12-28 14:04:44 +03:00
value={{
push,
pushAsync,
2023-12-28 14:04:44 +03:00
replace,
replaceAsync,
2023-12-28 14:04:44 +03:00
back,
forward,
canBack,
isBlocked,
setIsBlocked
}}
>
{children}
2024-12-12 21:58:19 +03:00
</NavigationContext>
2023-12-28 14:04:44 +03:00
);
};
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
}