UNPKG

@tanstack/react-router

Version:

Modern and scalable routing for React applications

355 lines (354 loc) • 10.6 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const jsxRuntime = require("react/jsx-runtime"); const React = require("react"); const reactDom = require("react-dom"); const routerCore = require("@tanstack/router-core"); const useRouterState = require("./useRouterState.cjs"); const useRouter = require("./useRouter.cjs"); const utils = require("./utils.cjs"); function _interopNamespaceDefault(e) { const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } }); if (e) { for (const k in e) { if (k !== "default") { const d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: () => e[k] }); } } } n.default = e; return Object.freeze(n); } const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React); function useLinkProps(options, forwardedRef) { const router = useRouter.useRouter(); const [isTransitioning, setIsTransitioning] = React__namespace.useState(false); const hasRenderFetched = React__namespace.useRef(false); const innerRef = utils.useForwardedRef(forwardedRef); const { // custom props activeProps, inactiveProps, activeOptions, to, preload: userPreload, preloadDelay: userPreloadDelay, hashScrollIntoView, replace, startTransition, resetScroll, viewTransition, // element props children, target, disabled, style, className, onClick, onFocus, onMouseEnter, onMouseLeave, onTouchStart, ignoreBlocker, // prevent these from being returned params: _params, search: _search, hash: _hash, state: _state, mask: _mask, reloadDocument: _reloadDocument, unsafeRelative: _unsafeRelative, from: _from, _fromLocation, ...propsSafeToSpread } = options; const currentSearch = useRouterState.useRouterState({ select: (s) => s.location.search, structuralSharing: true }); const from = options.from; const _options = React__namespace.useMemo( () => { return { ...options, from }; }, // eslint-disable-next-line react-hooks/exhaustive-deps [ router, currentSearch, from, options._fromLocation, options.hash, options.to, options.search, options.params, options.state, options.mask, options.unsafeRelative ] ); const next = React__namespace.useMemo( () => router.buildLocation({ ..._options }), [router, _options] ); const hrefOption = React__namespace.useMemo(() => { if (disabled) { return void 0; } let href = next.maskedLocation ? next.maskedLocation.url : next.url; let external = false; if (router.origin) { if (href.startsWith(router.origin)) { href = href.replace(router.origin, "") || "/"; } else { external = true; } } return { href, external }; }, [disabled, next.maskedLocation, next.url, router.origin]); const externalLink = React__namespace.useMemo(() => { if (hrefOption?.external) { return hrefOption.href; } try { new URL(to); return to; } catch { } return void 0; }, [to, hrefOption]); const preload = options.reloadDocument || externalLink ? false : userPreload ?? router.options.defaultPreload; const preloadDelay = userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0; const isActive = useRouterState.useRouterState({ select: (s) => { if (externalLink) return false; if (activeOptions?.exact) { const testExact = routerCore.exactPathTest( s.location.pathname, next.pathname, router.basepath ); if (!testExact) { return false; } } else { const currentPathSplit = routerCore.removeTrailingSlash( s.location.pathname, router.basepath ); const nextPathSplit = routerCore.removeTrailingSlash( next.pathname, router.basepath ); const pathIsFuzzyEqual = currentPathSplit.startsWith(nextPathSplit) && (currentPathSplit.length === nextPathSplit.length || currentPathSplit[nextPathSplit.length] === "/"); if (!pathIsFuzzyEqual) { return false; } } if (activeOptions?.includeSearch ?? true) { const searchTest = routerCore.deepEqual(s.location.search, next.search, { partial: !activeOptions?.exact, ignoreUndefined: !activeOptions?.explicitUndefined }); if (!searchTest) { return false; } } if (activeOptions?.includeHash) { return s.location.hash === next.hash; } return true; } }); const doPreload = React__namespace.useCallback(() => { router.preloadRoute({ ..._options }).catch((err) => { console.warn(err); console.warn(routerCore.preloadWarning); }); }, [router, _options]); const preloadViewportIoCallback = React__namespace.useCallback( (entry) => { if (entry?.isIntersecting) { doPreload(); } }, [doPreload] ); utils.useIntersectionObserver( innerRef, preloadViewportIoCallback, intersectionObserverOptions, { disabled: !!disabled || !(preload === "viewport") } ); React__namespace.useEffect(() => { if (hasRenderFetched.current) { return; } if (!disabled && preload === "render") { doPreload(); hasRenderFetched.current = true; } }, [disabled, doPreload, preload]); const handleClick = (e) => { const elementTarget = e.currentTarget.target; const effectiveTarget = target !== void 0 ? target : elementTarget; if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!effectiveTarget || effectiveTarget === "_self") && e.button === 0) { e.preventDefault(); reactDom.flushSync(() => { setIsTransitioning(true); }); const unsub = router.subscribe("onResolved", () => { unsub(); setIsTransitioning(false); }); router.navigate({ ..._options, replace, resetScroll, hashScrollIntoView, startTransition, viewTransition, ignoreBlocker }); } }; if (externalLink) { return { ...propsSafeToSpread, ref: innerRef, href: externalLink, ...children && { children }, ...target && { target }, ...disabled && { disabled }, ...style && { style }, ...className && { className }, ...onClick && { onClick }, ...onFocus && { onFocus }, ...onMouseEnter && { onMouseEnter }, ...onMouseLeave && { onMouseLeave }, ...onTouchStart && { onTouchStart } }; } const handleFocus = (_) => { if (disabled) return; if (preload) { doPreload(); } }; const handleTouchStart = handleFocus; const handleEnter = (e) => { if (disabled || !preload) return; if (!preloadDelay) { doPreload(); } else { const eventTarget = e.target; if (timeoutMap.has(eventTarget)) { return; } const id = setTimeout(() => { timeoutMap.delete(eventTarget); doPreload(); }, preloadDelay); timeoutMap.set(eventTarget, id); } }; const handleLeave = (e) => { if (disabled || !preload || !preloadDelay) return; const eventTarget = e.target; const id = timeoutMap.get(eventTarget); if (id) { clearTimeout(id); timeoutMap.delete(eventTarget); } }; const resolvedActiveProps = isActive ? routerCore.functionalUpdate(activeProps, {}) ?? STATIC_ACTIVE_OBJECT : STATIC_EMPTY_OBJECT; const resolvedInactiveProps = isActive ? STATIC_EMPTY_OBJECT : routerCore.functionalUpdate(inactiveProps, {}) ?? STATIC_EMPTY_OBJECT; const resolvedClassName = [ className, resolvedActiveProps.className, resolvedInactiveProps.className ].filter(Boolean).join(" "); const resolvedStyle = (style || resolvedActiveProps.style || resolvedInactiveProps.style) && { ...style, ...resolvedActiveProps.style, ...resolvedInactiveProps.style }; return { ...propsSafeToSpread, ...resolvedActiveProps, ...resolvedInactiveProps, href: hrefOption?.href, ref: innerRef, onClick: composeHandlers([onClick, handleClick]), onFocus: composeHandlers([onFocus, handleFocus]), onMouseEnter: composeHandlers([onMouseEnter, handleEnter]), onMouseLeave: composeHandlers([onMouseLeave, handleLeave]), onTouchStart: composeHandlers([onTouchStart, handleTouchStart]), disabled: !!disabled, target, ...resolvedStyle && { style: resolvedStyle }, ...resolvedClassName && { className: resolvedClassName }, ...disabled && STATIC_DISABLED_PROPS, ...isActive && STATIC_ACTIVE_PROPS, ...isTransitioning && STATIC_TRANSITIONING_PROPS }; } const STATIC_EMPTY_OBJECT = {}; const STATIC_ACTIVE_OBJECT = { className: "active" }; const STATIC_DISABLED_PROPS = { role: "link", "aria-disabled": true }; const STATIC_ACTIVE_PROPS = { "data-status": "active", "aria-current": "page" }; const STATIC_TRANSITIONING_PROPS = { "data-transitioning": "transitioning" }; const timeoutMap = /* @__PURE__ */ new WeakMap(); const intersectionObserverOptions = { rootMargin: "100px" }; const composeHandlers = (handlers) => (e) => { for (const handler of handlers) { if (!handler) continue; if (e.defaultPrevented) return; handler(e); } }; function createLink(Comp) { return React__namespace.forwardRef(function CreatedLink(props, ref) { return /* @__PURE__ */ jsxRuntime.jsx(Link, { ...props, _asChild: Comp, ref }); }); } const Link = React__namespace.forwardRef( (props, ref) => { const { _asChild, ...rest } = props; const { type: _type, ref: innerRef, ...linkProps } = useLinkProps(rest, ref); const children = typeof rest.children === "function" ? rest.children({ isActive: linkProps["data-status"] === "active" }) : rest.children; if (_asChild === void 0) { delete linkProps.disabled; } return React__namespace.createElement( _asChild ? _asChild : "a", { ...linkProps, ref: innerRef }, children ); } ); function isCtrlEvent(e) { return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey); } const linkOptions = (options) => { return options; }; exports.Link = Link; exports.createLink = createLink; exports.linkOptions = linkOptions; exports.useLinkProps = useLinkProps; //# sourceMappingURL=link.cjs.map