UNPKG

alinea

Version:
162 lines (160 loc) 4.34 kB
import { useAtomValue, useSetAtom } from "../../chunks/chunk-TOJF2G3X.js"; import { atom } from "../../chunks/chunk-WJ67RR7S.js"; import "../../chunks/chunk-NZLE2WMY.js"; // src/dashboard/atoms/RouterAtoms.tsx import { createContext, useContext, useEffect, useState } from "react"; import { locationAtom, matchAtoms } from "./LocationAtoms.js"; import { Fragment, jsx } from "react/jsx-runtime"; var Route = class { constructor(data) { this.data = data; } get component() { return this.data.component; } }; var Router = class { constructor(data) { this.data = data; this.matchers = this.data.routes.map((route) => ({ route, matcher: matchAtoms({ route: route.data.path }) })); } matchers; matchingRoute = atom((get) => { for (const { route, matcher } of this.matchers) { const params = get(matcher); if (params) return { route, params }; } }); matchingRouteWithData = atom(async (get) => { const match = get(this.matchingRoute); if (!match) return void 0; const { loader } = match.route.data; const data = loader ? await get(loader(match.params)) : {}; return { route: match.route, data, params: match.params }; }); currentRoute = atom(void 0); prevLocation = atom(void 0); blockers = atom(/* @__PURE__ */ new Set()); expected; cancelled = atom(false); match = atom( (get, { setSelf }) => { const current = get(this.currentRoute); const next = get(this.matchingRouteWithData); this.expected = next.then((value) => { setSelf(this.expected, value); return value; }); return current ?? this.expected; }, (get, set, forPromise, value) => { const cancelled = get(this.cancelled); if (cancelled) { set(this.cancelled, false); return; } const blockers = get(this.blockers); if (forPromise !== this.expected) return; const currentLocation = get(locationAtom); const prevLocation = get(this.prevLocation); const confirm = () => { for (const block of blockers) set(block, void 0); set(this.currentRoute, value); set(this.prevLocation, currentLocation); }; const cancel = () => { set(this.cancelled, true); for (const block of blockers) set(block, void 0); if (prevLocation) { const returnTo = prevLocation.pathname + prevLocation.search; set(locationAtom, returnTo); } }; if (blockers.size) { for (const block of blockers) set(block, { nextRoute: value, confirm, cancel }); } else { confirm(); } } ); }; var RouterContext = createContext(void 0); function RouterProvider({ children, router }) { return /* @__PURE__ */ jsx(RouterContext.Provider, { value: router, children }); } function useRouter() { const router = useContext(RouterContext); if (!router) throw new Error("No router context found"); return router; } function useRouteBlocker(message, when) { const router = useRouter(); const [blockingAtom] = useState(() => atom(void 0)); const setBlockers = useSetAtom(router.blockers); useEffect(() => { if (!when) return; setBlockers((blockers) => new Set(blockers).add(blockingAtom)); window.onbeforeunload = () => true; return () => { window.onbeforeunload = null; setBlockers((blockers) => { const res = new Set(blockers); res.delete(blockingAtom); return res; }); }; }, [message, when]); const block = useAtomValue(blockingAtom); return { isBlocking: Boolean(block), ...block }; } function useRouteMatch() { const router = useRouter(); return useAtomValue(router.match); } function useRouteParams() { const match = useRouteMatch(); return match?.params ?? {}; } function useRouteRender() { const match = useRouteMatch(); return match ? /* @__PURE__ */ jsx(match.route.component, { ...match.data }) : null; } function RouteView({ fallback }) { const view = useRouteRender(); return /* @__PURE__ */ jsx(Fragment, { children: view ?? fallback ?? null }); } export { Route, RouteView, Router, RouterProvider, useRouteBlocker, useRouteMatch, useRouteParams, useRouteRender, useRouter };