raviger
Version:
React routing with hooks
3 lines (2 loc) • 9.07 kB
JavaScript
import t,{createContext as e,useRef as n,useLayoutEffect as r,useState as a,useCallback as o,useContext as i,useMemo as s,forwardRef as u}from"react";const c=e(""),l=e(null);function RouterProvider({basePath:e="",path:n,children:r}){return t.createElement(c.Provider,{value:e},t.createElement(l.Provider,{value:null!=n?n:null},r))}let h=!0;try{h=void 0===window}catch{}const d=new Set;let g=!1,f=!1,P=[0,0];function shouldCancelNavigation(){return P=[window.scrollX,window.scrollY],g?f:Array.from(d).some((t=>{const e=t();return!!e&&(f=!window.confirm(e),g=!0,setTimeout((()=>{g=!1,f=!1}),0),f)}))}function usePath(t){const e=i(l),n=useBasePath();t=t||n;const[,r]=a(getFormattedPath(t));return useLocationChange(o((({path:t})=>r(t)),[]),{basePath:t,inheritBasePath:!t,onInitial:!0}),e||getFormattedPath(t)}function useBasePath(){return i(c)}function useFullPath(){const[t,e]=a(getCurrentPath());return useLocationChange(o((({path:t})=>e(t)),[]),{inheritBasePath:!1}),t||"/"}function useHash({stripHash:t=!0}={}){const[e,n]=a(window.location.hash),i=o((()=>{const t=window.location.hash;t!==e&&n(t)}),[n,e]);return r((()=>(window.addEventListener("hashchange",i,!1),()=>window.removeEventListener("hashchange",i))),[i]),useLocationChange(i),t?e.substring(1):e}function getCurrentPath(){return h?"/":window.location.pathname||"/"}function getCurrentHash(){if(h){const t="/",e=t.indexOf("#");return t.substring(e)}return window.location.hash}function useLocationChange(t,{inheritBasePath:e=!0,basePath:a="",isActive:i,onInitial:s=!1}={}){if(h)return;const u=useBasePath();e&&u&&(a=u);const c=n(t);r((()=>{c.current=t}));const l=o((()=>{(void 0===i||isPredicateActive(i))&&(shouldCancelNavigation()||c.current(getFormattedLocation(a)))}),[i,a]);r((()=>(window.addEventListener("popstate",l),()=>window.removeEventListener("popstate",l))),[l]),function useMountedLayout(t,e,{onInitial:a=!1}={}){const o=n(a);r((()=>{o.current?t():o.current=!0}),e)}((()=>{(void 0===i||isPredicateActive(i))&&c.current(getFormattedLocation(a))}),[a,i],{onInitial:s})}function useHistory(){const[t,e]=a(getRavigerHistory());return useLocationChange(o((()=>e(getRavigerHistory())),[e])),t}function getRavigerHistory(){return h?{scrollRestoration:"manual",state:null}:{scrollRestoration:window.history.scrollRestoration,state:window.history.state}}function getFormattedPath(t){const e=getCurrentPath(),n=t&&!function isPathInBase(t,e){return!!(t&&e&&e.toLowerCase().startsWith(t.toLowerCase()))}(t,e);return null===e||n?null:decodeURIComponent(t?e.replace(function basePathMatcher(t){return new RegExp("^"+t,"i")}(t),"")||"/":e)}function getFormattedLocation(t){const e=getFormattedPath(t);return{basePath:t,path:e,pathname:e,fullPath:getCurrentPath(),search:window.location.search,hash:getCurrentHash(),host:window.location.host,hostname:window.location.hostname,href:window.location.href,origin:window.location.origin}}function isPredicateActive(t){return function isFunction(t){return!!t&&"function"==typeof t}(t)?t():t}const w=[null,null];function useRoutes(e,{basePath:n="",routeProps:i={},overridePathParams:s=!0,matchTrailingSlash:u=!0}={}){const c=usePath(n)&&getFormattedPath(n);!function useRedirectDetection(t,e){const[,n]=a({}),i=o((()=>n({})),[]);r((()=>{e!==getFormattedPath(t)&&i()}),[i,t,e])}(n,usePath(n));const l=function useMatchRoute(t,e,{routeProps:n,overridePathParams:r,matchTrailingSlash:a}){e=trailingMatch(e,a);const o=Array.isArray(t)?t:Object.entries(t).reduce(((t,[e,n])=>(t.push({path:e,fn:n}),t)),[]),i=useMatchers(o.map((t=>t.path)));if(null===e)return null;const[s,u]=getMatchParams(e,i);if(!s)return null;const c=o.find((t=>t.path==s.path));return c?c.fn(r?{...u,...n}:{...n,...u}):null}(e,c,{routeProps:i,overridePathParams:s,matchTrailingSlash:u});return l&&null!==c?t.createElement(RouterProvider,{basePath:n,path:c},l):null}function usePathParams(t,e={}){const n=!Array.isArray(t),[r,a]=usePathOptions(t,e);if(null===r)return n?null:w;const[o,i]=getMatchParams(r,a);return o?n?i:[o.path,i]:n?null:w}function useMatch(t,e={}){var n;const[r,a]=usePathOptions(t,e),o=a.find((({regex:t})=>null==r?void 0:r.match(t)));return null!==(n=null==o?void 0:o.path)&&void 0!==n?n:null}function usePathOptions(t,{basePath:e,matchTrailingSlash:n=!0}){const r=useMatchers(Array.isArray(t)?t:[t]);return[trailingMatch(usePath(e),n),r]}function useMatchers(t){return s((()=>t.map(createRouteMatcher)),[(e=t,[...e].sort().join(":"))]);var e}function getMatchParams(t,e){let n=null;const r=e.find((({regex:e})=>(n=t.match(e),!!n)));if(!r||null===n)return w;const a=r.props.reduce(((t,e,r)=>(t[e]=n[r+1],t)),{});return[r,a]}const p=/:[a-zA-Z_]+/g;function createRouteMatcher(t){var e,n;return{path:t,regex:new RegExp(`${"*"===t.substr(0,1)?"":"^"}${(n=t,n.replace(/[-\\^$+?.()|[\]{}]/g,"\\$&")).replace(p,"([^/]+)").replace(/\*/g,"")}${"*"===t.substr(-1)?"":"$"}`,"i"),props:(null!==(e=t.match(p))&&void 0!==e?e:[]).map((t=>t.substr(1)))}}function trailingMatch(t,e){return null===t||e&&t&&"/"===t[t.length-1]&&t.length>1&&(t=t.substring(0,t.length-1)),t}let v="";function navigate(t,e){if("string"!=typeof t)throw new Error('"url" must be a string, was provided a(n) '+typeof t);if(Array.isArray(null==e?void 0:e.query))throw new Error('"query" a serializable object or URLSearchParams');if(shouldCancelNavigation())return;if((null==e?void 0:e.query)&&(t+="?"+new URLSearchParams(e.query).toString()),v=t,function isAbsolute(t){return/^(?:[a-z]+:)?\/\//i.test(t)}(t)&&!function isCurrentOrigin(t){return window.location.origin===new URL(t).origin}(t))return void window.location.assign(t);(null==e?void 0:e.replace)?window.history.replaceState(null==e?void 0:e.state,"",t):window.history.pushState(null==e?void 0:e.state,"",t);const n=new PopStateEvent("popstate");n.__tag="raviger:navigation",dispatchEvent(n)}function useNavigationPrompt(t=!0,e="Are you sure you want to leave this page?"){h||(r((()=>{const onPopStateNavigation=()=>{shouldCancelNavigation()&&function undoNavigation(t){window.history.pushState(null,null,t),setTimeout((()=>{window.scrollTo(...P)}),0)}(v)};return window.addEventListener("popstate",onPopStateNavigation),()=>window.removeEventListener("popstate",onPopStateNavigation)}),[]),r((()=>{const handler=n=>{if(t)return n?function cancelNavigation(t,e){return t.preventDefault(),t.returnValue=e,e}(n,e):e};return function addInterceptor(t){window.addEventListener("beforeunload",t),d.add(t)}(handler),()=>function removeInterceptor(t){window.removeEventListener("beforeunload",t),d.delete(t)}(handler)}),[t,e]))}function useNavigate(t=""){const e=useBasePath();return o(((n,r)=>{const a=t||e;navigate(n.startsWith("/")?a+n:n,r)}),[e,t])}function useQueryParams(t=parseQuery,e=serializeQuery){const[n,r]=a(getQueryString()),i=o(((r,{overwrite:a=!0,replace:o=!1}={})=>{let i=getCurrentPath();r=a?r:{...t(n),...r};const s=e(r).toString();s&&(i+="?"+s),a||(i+=getCurrentHash()),navigate(i,{replace:o})}),[n,t,e]);return useLocationChange(o((()=>r(getQueryString())),[])),[t(n),i]}function parseQuery(t){const e=new URLSearchParams(t);return Object.fromEntries(e.entries())}function serializeQuery(t){return new URLSearchParams(Object.entries(t).filter((([,t])=>null!==t))).toString()}function getQueryString(){if(h){const t="/",e=t.indexOf("?");return-1===e?"":t.substring(e+1)}return window.location.search}function Redirect({to:t,query:e,replace:n=!0,merge:r=!0,state:a}){return useRedirect(usePath(),t,{query:e,replace:n,merge:r,state:a}),null}function useRedirect(t,e,{query:n,replace:a=!0,merge:o=!0,state:i}={}){const s=usePath(),[u]=useQueryParams(),c=getCurrentHash();let l=e;const h=new URLSearchParams({...o?u:{},...n}).toString();h&&(l+="?"+h),o&&c&&c.length&&(l+=c),r((()=>{s===t&&navigate(l,{replace:a,state:i})}),[t,l,a,s,i])}const m=u((function Link({href:e,basePath:n,replace:r,query:a,state:i,...s},u){e=getLinkHref(e,n=useLinkBasePath(n));const{onClick:c,target:l}=s,h=o((t=>{try{c&&c(t)}catch(e){throw t.preventDefault(),e}(function shouldTrap(t,e){return!t.defaultPrevented&&0===t.button&&!(e||"_self"===e)&&!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)})(t,l)&&(t.preventDefault(),navigate(t.currentTarget.href,{replace:r,query:a,state:i}))}),[c,l]);return t.createElement("a",{...s,href:e,onClick:h,ref:u})}));const y=u((function ActiveLink({basePath:e,className:n,exactActiveClass:r,activeClass:a,...o},i){e=useLinkBasePath(e);const s=useFullPath();let{href:u}=o;return u=function absolutePathName(t){return t.startsWith("/")?t:new URL(t,document.baseURI).pathname}(getLinkHref(u,e)),r&&s===u&&(n=`${null!=n?n:""} ${r}`.trim()),a&&s.startsWith(u)&&(n=`${null!=n?n:""} ${a}`.trim()),t.createElement(m,{...o,basePath:e,className:n,ref:i})}));function useLinkBasePath(t){const e=useBasePath();return"/"===t?"":t||e}function getLinkHref(t,e=""){return t.startsWith("/")?e+t:t}export{y as ActiveLink,m as Link,Redirect,RouterProvider,navigate,useBasePath,useFullPath,useHash,useHistory,useLocationChange,useMatch,useNavigate,useNavigationPrompt,usePath,usePathParams,useQueryParams,useRedirect,useRoutes};
//# sourceMappingURL=module.js.map