d606f8371c
Ports the weather check added to stroy-bot-main on 2026-10-16: daily station weather, report positions with weather per shift and the AI assessment of working conditions. Opens as ?page=weather&start=&end= instead of the #weathercheck hash, and hub dates survive the way back. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
59 lines
2.3 KiB
TypeScript
59 lines
2.3 KiB
TypeScript
import * as React from 'react';
|
|
import { buildSearch, IRoute, parseRoute } from './routes';
|
|
|
|
interface INavigateOptions {
|
|
/** Заменить текущую запись истории (replaceState), а не добавить новую. */
|
|
replace?: boolean;
|
|
}
|
|
|
|
interface IRouterValue {
|
|
route: IRoute;
|
|
/** Перейти в раздел: меняет адрес (history.pushState) и перерисовывает приложение. */
|
|
navigate: (route: IRoute, options?: INavigateOptions) => void;
|
|
/** Полный адрес раздела — для настоящих ссылок (Ctrl+клик, «открыть в новой вкладке»). */
|
|
buildHref: (route: IRoute) => string;
|
|
}
|
|
|
|
const RouterContext = React.createContext<IRouterValue | undefined>(undefined);
|
|
|
|
function readCurrentRoute(): IRoute {
|
|
return parseRoute(window.location.search);
|
|
}
|
|
|
|
function buildHref(route: IRoute): string {
|
|
return `${window.location.pathname}${buildSearch(route, window.location.search)}`;
|
|
}
|
|
|
|
export const RouterProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
const [route, setRoute] = React.useState<IRoute>(readCurrentRoute);
|
|
|
|
// Кнопки «назад/вперёд» браузера меняют адрес без нашего участия — перечитываем маршрут.
|
|
React.useEffect(() => {
|
|
const onPopState = (): void => setRoute(readCurrentRoute());
|
|
window.addEventListener('popstate', onPopState);
|
|
return () => window.removeEventListener('popstate', onPopState);
|
|
}, []);
|
|
|
|
const navigate = React.useCallback((next: IRoute, options: INavigateOptions = {}): void => {
|
|
if (options.replace) {
|
|
window.history.replaceState(undefined, '', buildHref(next));
|
|
} else {
|
|
window.history.pushState(undefined, '', buildHref(next));
|
|
}
|
|
setRoute(next);
|
|
window.scrollTo(0, 0);
|
|
}, []);
|
|
|
|
const value = React.useMemo<IRouterValue>(() => ({ route, navigate, buildHref }), [route, navigate]);
|
|
|
|
return <RouterContext.Provider value={value}>{children}</RouterContext.Provider>;
|
|
};
|
|
|
|
export function useRouter(): IRouterValue {
|
|
const router = React.useContext(RouterContext);
|
|
if (!router) {
|
|
throw new Error('useRouter: компонент отрисован вне RouterProvider');
|
|
}
|
|
return router;
|
|
}
|