@tanstack/solid-router
Version:
Modern and scalable routing for Solid applications
328 lines (327 loc) • 11.9 kB
JavaScript
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
const require_ClientOnly = require("./ClientOnly.cjs");
const require_useRouter = require("./useRouter.cjs");
const require_matchContext = require("./matchContext.cjs");
const require_utils = require("./utils.cjs");
let _tanstack_router_core = require("@tanstack/router-core");
let solid_js_web = require("solid-js/web");
let solid_js = require("solid-js");
solid_js = require_runtime.__toESM(solid_js, 1);
let _tanstack_router_core_isServer = require("@tanstack/router-core/isServer");
//#region src/link.tsx
var _tmpl$ = /*#__PURE__*/ (0, solid_js_web.template)(`<svg><a>`);
var _tmpl$2 = /*#__PURE__*/ (0, solid_js_web.template)(`<a>`);
function useLinkProps(options) {
return createLinkProps(options, LINK_OPTION_KEYS);
}
function createLinkProps(options, ownKeys) {
const router = require_useRouter.useRouter();
const [, propsSafeToSpread] = solid_js.splitProps(options, ownKeys);
const routeId = solid_js.useContext(require_matchContext.nearestMatchContext)[0]();
const currentLocation = _tanstack_router_core_isServer.isServer ?? router.isServer ? () => router.stores.location.get() : solid_js.createMemo((prev) => {
const location = router.stores.location.get();
return prev && (0, _tanstack_router_core._isRouteDeparting)(router, routeId, location) ? prev : location;
});
const getDest = () => {
const dest = {};
for (const key of NAVIGATION_KEYS) {
const value = dest[key] = options[key];
if (isStore(value) || key === "mask" && value && Object.values(value).some(isStore)) currentLocation();
}
return dest;
};
const dest = _tanstack_router_core_isServer.isServer ?? router.isServer ? getDest : solid_js.createMemo(getDest);
const hasHydrated = !(_tanstack_router_core_isServer.isServer ?? router.isServer) && router.options.ssr ? require_ClientOnly.useHydrated() : void 0;
const getLinkState = (prev) => {
const location = currentLocation();
const to = options.to;
const disabled = options.disabled;
const scheme = typeof to === "string" && (0, _tanstack_router_core.getUrlScheme)(to);
let href;
let external;
let active = false;
if (scheme) {
if (router.protocolAllowlist.has(scheme)) external = to;
else {
if (process.env.NODE_ENV !== "production") console.warn(`Blocked Link with dangerous protocol: ${to}`);
external = null;
}
} else {
const options_ = dest();
options_._fromLocation = location;
const next = solid_js.untrack(() => router.buildLocation(options_));
if (!disabled) {
const shown = next.maskedLocation ?? next;
const publicHref = shown.publicHref;
const shownHref = shown.external ? publicHref : router.history.createHref(publicHref) || "/";
if ((shown.external || shownHref !== publicHref) && (0, _tanstack_router_core.isDangerousProtocol)(shownHref, router.protocolAllowlist)) {
if (process.env.NODE_ENV !== "production") console.warn(`Blocked Link with dangerous protocol: ${shownHref}`);
} else href = shownHref;
}
external = !href && !disabled ? null : href && (0, _tanstack_router_core.getUrlScheme)(href) ? href : void 0;
if (external === void 0) {
const activeOptions = options.activeOptions;
const currentPath = (0, _tanstack_router_core.removeTrailingSlash)(location.pathname, router.basepath);
const nextPath = (0, _tanstack_router_core.removeTrailingSlash)(next.pathname, router.basepath);
active = (activeOptions?.exact ? currentPath === nextPath : currentPath.startsWith(nextPath) && (currentPath.length === nextPath.length || currentPath[nextPath.length] === "/")) && (!(activeOptions?.includeSearch ?? true) || (0, _tanstack_router_core.deepEqual)(location.search, next.search, !activeOptions?.exact, activeOptions?.explicitUndefined)) && (!activeOptions?.includeHash || (hasHydrated && !hasHydrated() ? "" : location.hash) === next.hash);
}
}
href = external === null ? void 0 : external || href;
return prev && prev[0] === href && prev[1] === external && prev[2] === active ? prev : [
href,
external,
active
];
};
const linkState = _tanstack_router_core_isServer.isServer ?? router.isServer ? getLinkState : solid_js.createMemo(getLinkState);
const resolveLinkStateProps = ([href, external, active], base) => {
const disabled = options.disabled || external === null;
base.href = href;
base.disabled = disabled;
base.target = options.target;
if (disabled) Object.assign(base, STATIC_DISABLED_PROPS);
if (options.activeProps === void 0 && options.inactiveProps === void 0 && options.class === void 0 && options.style === void 0) return active ? Object.assign(base, STATIC_DEFAULT_ACTIVE_ATTRIBUTES) : base;
const stateProps = (0, _tanstack_router_core.functionalUpdate)((active ? options.activeProps : options.inactiveProps) ?? (active ? STATIC_ACTIVE_PROPS : EMPTY_OBJECT), {}) ?? EMPTY_OBJECT;
const baseStyle = options.style;
const stateStyle = stateProps.style;
const style = baseStyle || stateStyle ? {
...baseStyle,
...stateStyle
} : void 0;
const baseClass = options.class;
const stateClass = stateProps.class;
const className = baseClass ? stateClass ? `${baseClass} ${stateClass}` : baseClass : stateClass;
return {
...base,
...stateProps,
href,
disabled,
target: base.target,
...style && (0, _tanstack_router_core.hasKeys)(style) && { style },
...className && { class: className },
...active && STATIC_ACTIVE_ATTRIBUTES
};
};
if (_tanstack_router_core_isServer.isServer ?? router.isServer) {
const props = resolveLinkStateProps(linkState(), {
onClick: options.onClick,
onBlur: options.onBlur,
onFocus: options.onFocus,
onMouseEnter: options.onMouseEnter,
onMouseLeave: options.onMouseLeave,
onMouseOut: options.onMouseOut,
onMouseOver: options.onMouseOver,
onTouchStart: options.onTouchStart,
ref: options.ref
});
for (const key of STATIC_EVENT_PROPS) if (props[key] === void 0) delete props[key];
return solid_js.mergeProps(propsSafeToSpread, props);
}
let hasRenderFetched = false;
const preload = () => !options.reloadDocument && linkState()[1] === void 0 && !options.disabled && (options.preload ?? router.options.defaultPreload);
const preloadDelay = () => options.preloadDelay ?? router.options.defaultPreloadDelay ?? 0;
const doPreload = () => router.preloadRoute(dest()).catch((err) => {
console.warn(err);
console.warn(_tanstack_router_core.preloadWarning);
});
const [ref, setRef] = solid_js.createSignal(null);
let preloadTimeout;
const cancelPreload = () => {
clearTimeout(preloadTimeout);
preloadTimeout = void 0;
};
const enqueuePreload = (e) => {
if (!e) {
cancelPreload();
return;
}
if (!(e.isIntersecting ?? preload() === "intent")) {
if (e.isIntersecting === false) cancelPreload();
return;
}
if (!preloadDelay()) {
doPreload();
return;
}
preloadTimeout ??= setTimeout(() => {
preloadTimeout = void 0;
doPreload();
}, preloadDelay());
};
require_utils.useIntersectionObserver(ref, enqueuePreload, preload);
solid_js.createEffect(() => {
if (hasRenderFetched) return;
if (preload() === "render") {
doPreload();
hasRenderFetched = true;
}
});
const handleClick = (e) => {
const elementTarget = e.currentTarget.getAttribute("target");
const effectiveTarget = options.target !== void 0 ? options.target : elementTarget;
if (!options.disabled && linkState()[1] === void 0 && !(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && !e.defaultPrevented && (!effectiveTarget || effectiveTarget === "_self") && e.button === 0) {
e.preventDefault();
cancelPreload();
router.navigate(dest());
}
};
const handleTouchStart = () => {
if (preload() !== "intent") return;
doPreload();
};
const handleLeave = () => {
if (preload() === "intent") cancelPreload();
};
const onClick = createComposedHandler(() => options.onClick, handleClick);
const onBlur = createComposedHandler(() => options.onBlur, handleLeave);
const onFocus = createComposedHandler(() => options.onFocus, enqueuePreload);
const onMouseEnter = createComposedHandler(() => options.onMouseEnter, enqueuePreload);
const onMouseOver = createComposedHandler(() => options.onMouseOver, enqueuePreload);
const onMouseLeave = createComposedHandler(() => options.onMouseLeave, handleLeave);
const onMouseOut = createComposedHandler(() => options.onMouseOut, handleLeave);
const onTouchStart = createComposedHandler(() => options.onTouchStart, handleTouchStart);
const linkRef = (el) => {
setRef(el);
const ref = resolved.ref ?? options.ref;
if (typeof ref === "function") ref(el);
};
let resolved;
const resolvedProps = () => resolved = resolveLinkStateProps(linkState(), {
onClick,
onBlur,
onFocus,
onMouseEnter,
onMouseOver,
onMouseLeave,
onMouseOut,
onTouchStart
});
return solid_js.mergeProps(propsSafeToSpread, resolvedProps, { ref: linkRef });
}
var isStore = (value) => !!value?.[solid_js.$PROXY];
var NAVIGATION_KEYS = [
"to",
"reloadDocument",
"replace",
"resetScroll",
"hashScrollIntoView",
"startTransition",
"viewTransition",
"ignoreBlocker",
"params",
"search",
"hash",
"state",
"mask",
"unsafeRelative",
"from",
"href"
];
var LINK_OPTION_KEYS = [
...NAVIGATION_KEYS,
"activeProps",
"inactiveProps",
"activeOptions",
"preload",
"preloadDelay",
"preloadIntentProximity",
"target",
"disabled",
"style",
"class",
"onClick",
"onBlur",
"onFocus",
"onMouseEnter",
"onMouseLeave",
"onMouseOver",
"onMouseOut",
"onTouchStart"
];
var LINK_ELEMENT_KEYS = [
...LINK_OPTION_KEYS,
"_asChild",
"children",
"type"
];
var STATIC_EVENT_PROPS = [
"onClick",
"onBlur",
"onFocus",
"onMouseEnter",
"onMouseLeave",
"onMouseOut",
"onMouseOver",
"onTouchStart"
];
var STATIC_ACTIVE_PROPS = { class: "active" };
var EMPTY_OBJECT = {};
var STATIC_DEFAULT_ACTIVE_ATTRIBUTES = {
class: "active",
"data-status": "active",
"aria-current": "page"
};
var STATIC_DISABLED_PROPS = {
role: "link",
"aria-disabled": true
};
var STATIC_ACTIVE_ATTRIBUTES = {
"data-status": "active",
"aria-current": "page"
};
/** Call a JSX.EventHandlerUnion with the event. */
function callHandler(event, handler) {
if (typeof handler === "function") handler(event);
else handler[0](handler[1], event);
return event.defaultPrevented;
}
function createComposedHandler(getHandler, fallback) {
return (event) => {
const handler = getHandler();
if (!handler || !callHandler(event, handler)) fallback(event);
};
}
function createLink(Comp) {
return (props) => (0, solid_js_web.createComponent)(Link, (0, solid_js_web.mergeProps)(props, { _asChild: Comp }));
}
var Link = (props) => {
const local = props;
const linkProps = createLinkProps(props, LINK_ELEMENT_KEYS);
const children = () => {
const ch = local.children;
if (typeof ch === "function") return ch({ get isActive() {
return linkProps["data-status"] === "active";
} });
return ch;
};
if (local._asChild === "svg") {
const [_, svgLinkProps] = solid_js.splitProps(linkProps, ["class"]);
return (() => {
var _el$ = _tmpl$(), _el$2 = _el$.firstChild;
(0, solid_js_web.spread)(_el$2, svgLinkProps, false, true);
(0, solid_js_web.insert)(_el$2, children);
return _el$;
})();
}
if (!local._asChild) return (() => {
var _el$3 = _tmpl$2();
(0, solid_js_web.spread)(_el$3, linkProps, false, true);
(0, solid_js_web.insert)(_el$3, children);
return _el$3;
})();
const memo = solid_js.createMemo(children);
return (0, solid_js_web.createComponent)(solid_js_web.Dynamic, (0, solid_js_web.mergeProps)({ get component() {
return local._asChild;
} }, linkProps, { get children() {
return memo();
} }));
};
var linkOptions = (options) => {
return options;
};
//#endregion
exports.Link = Link;
exports.createLink = createLink;
exports.linkOptions = linkOptions;
exports.useLinkProps = useLinkProps;
//# sourceMappingURL=link.cjs.map