@tanstack/solid-router
Version:
Modern and scalable routing for Solid applications
368 lines (367 loc) • 12.5 kB
JavaScript
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
const require_ClientOnly = require("./ClientOnly.cjs");
const require_useRouter = require("./useRouter.cjs");
const require_utils = require("./utils.cjs");
let _tanstack_router_core = require("@tanstack/router-core");
let _solidjs_web = require("@solidjs/web");
let solid_js = require("solid-js");
solid_js = require_runtime.__toESM(solid_js);
let _tanstack_router_core_isServer = require("@tanstack/router-core/isServer");
//#region src/link.tsx
var _tmpl$ = /* @__PURE__ */ (0, _solidjs_web.template)(`<svg><a>`), _tmpl$2 = /* @__PURE__ */ (0, _solidjs_web.template)(`<a>`);
function mergeRefs(...refs) {
return (el) => {
for (const ref of refs) if (typeof ref === "function") ref(el);
};
}
function splitProps(props, keys) {
return [props, solid_js.omit(props, ...keys)];
}
var timeoutMap = /* @__PURE__ */ new WeakMap();
function useLinkProps(options) {
const router = require_useRouter.useRouter();
const [isTransitioning, setIsTransitioning] = solid_js.createSignal(false);
const shouldHydrateHash = !_tanstack_router_core_isServer.isServer && !!router.options.ssr;
const hasHydrated = require_ClientOnly.useHydrated();
let hasRenderFetched = false;
const [local, rest] = splitProps(solid_js.merge({
activeProps: STATIC_ACTIVE_PROPS_GET,
inactiveProps: STATIC_INACTIVE_PROPS_GET
}, options), [
"activeProps",
"inactiveProps",
"activeOptions",
"to",
"preload",
"preloadDelay",
"hashScrollIntoView",
"replace",
"startTransition",
"resetScroll",
"viewTransition",
"target",
"disabled",
"style",
"class",
"onClick",
"onBlur",
"onFocus",
"onMouseEnter",
"onMouseLeave",
"onMouseOver",
"onMouseOut",
"onTouchStart",
"ignoreBlocker"
]);
const [_, propsSafeToSpread] = splitProps(rest, [
"params",
"search",
"hash",
"state",
"mask",
"reloadDocument",
"unsafeRelative"
]);
const currentLocation = solid_js.createMemo(() => router.stores.location.state, void 0, { equals: (prev, next) => prev?.href === next?.href });
const _options = () => options;
const next = solid_js.createMemo(() => {
const options = {
_fromLocation: currentLocation(),
..._options()
};
return solid_js.untrack(() => router.buildLocation(options));
});
const hrefOption = solid_js.createMemo(() => {
if (_options().disabled) return void 0;
const location = next().maskedLocation ?? next();
const publicHref = location.publicHref;
if (location.external) return {
href: publicHref,
external: true
};
return {
href: router.history.createHref(publicHref) || "/",
external: false
};
});
const externalLink = solid_js.createMemo(() => {
const _href = hrefOption();
if (_href?.external) {
if ((0, _tanstack_router_core.isDangerousProtocol)(_href.href, router.protocolAllowlist)) {
if (process.env.NODE_ENV !== "production") console.warn(`Blocked Link with dangerous protocol: ${_href.href}`);
return;
}
return _href.href;
}
const to = _options().to;
if (isSafeInternal(to)) return void 0;
if (typeof to !== "string" || to.indexOf(":") === -1) return void 0;
try {
new URL(to);
if ((0, _tanstack_router_core.isDangerousProtocol)(to, router.protocolAllowlist)) {
if (process.env.NODE_ENV !== "production") console.warn(`Blocked Link with dangerous protocol: ${to}`);
return;
}
return to;
} catch {}
});
const preload = solid_js.createMemo(() => {
if (_options().reloadDocument || externalLink()) return false;
return local.preload ?? router.options.defaultPreload;
});
const preloadDelay = () => local.preloadDelay ?? router.options.defaultPreloadDelay ?? 0;
const isActive = solid_js.createMemo(() => {
if (externalLink()) return false;
const activeOptions = local.activeOptions;
const current = currentLocation();
const nextLocation = next();
if (activeOptions?.exact) {
if (!(0, _tanstack_router_core.exactPathTest)(current.pathname, nextLocation.pathname, router.basepath)) return false;
} else {
const currentPath = (0, _tanstack_router_core.removeTrailingSlash)(current.pathname, router.basepath);
const nextPath = (0, _tanstack_router_core.removeTrailingSlash)(nextLocation.pathname, router.basepath);
if (!(currentPath.startsWith(nextPath) && (currentPath.length === nextPath.length || currentPath[nextPath.length] === "/"))) return false;
}
if (activeOptions?.includeSearch ?? true) {
if (!(0, _tanstack_router_core.deepEqual)(current.search, nextLocation.search, {
partial: !activeOptions?.exact,
ignoreUndefined: !activeOptions?.explicitUndefined
})) return false;
}
if (activeOptions?.includeHash) return (shouldHydrateHash && !hasHydrated() ? "" : current.hash) === nextLocation.hash;
return true;
});
const doPreload = () => router.preloadRoute({
..._options(),
_builtLocation: next()
}).catch((err) => {
console.warn(err);
console.warn(_tanstack_router_core.preloadWarning);
});
const preloadViewportIoCallback = (entry) => {
if (entry?.isIntersecting) doPreload();
};
const [ref, setRefSignal] = solid_js.createSignal(null);
const setRef = (el) => {
solid_js.runWithOwner(null, () => {
setRefSignal(el);
});
};
require_utils.useIntersectionObserver(ref, preloadViewportIoCallback, { rootMargin: "100px" }, { disabled: !!local.disabled || !(solid_js.untrack(preload) === "viewport") });
solid_js.createEffect(preload, (preloadValue) => {
if (hasRenderFetched) return;
if (!local.disabled && preloadValue === "render") {
solid_js.untrack(() => doPreload());
hasRenderFetched = true;
}
});
if (solid_js.untrack(externalLink)) {
const externalHref = solid_js.untrack(externalLink);
return solid_js.merge(propsSafeToSpread, { href: externalHref }, splitProps(local, [
"target",
"disabled",
"style",
"class",
"onClick",
"onBlur",
"onFocus",
"onMouseEnter",
"onMouseLeave",
"onMouseOut",
"onMouseOver",
"onTouchStart"
])[0]);
}
const handleClick = (e) => {
const elementTarget = e.currentTarget.getAttribute("target");
const effectiveTarget = local.target !== void 0 ? local.target : elementTarget;
if (!local.disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!effectiveTarget || effectiveTarget === "_self") && e.button === 0) {
e.preventDefault();
solid_js.runWithOwner(null, () => {
setIsTransitioning(true);
});
const unsub = router.subscribe("onResolved", () => {
unsub();
solid_js.runWithOwner(null, () => {
setIsTransitioning(false);
});
});
router.navigate({
..._options(),
replace: local.replace,
resetScroll: local.resetScroll,
hashScrollIntoView: local.hashScrollIntoView,
startTransition: local.startTransition,
viewTransition: local.viewTransition,
ignoreBlocker: local.ignoreBlocker
});
}
};
const enqueueIntentPreload = (e) => {
if (local.disabled || preload() !== "intent") return;
if (!preloadDelay()) {
doPreload();
return;
}
const eventTarget = e.currentTarget || e.target;
if (!eventTarget || timeoutMap.has(eventTarget)) return;
timeoutMap.set(eventTarget, setTimeout(() => {
timeoutMap.delete(eventTarget);
doPreload();
}, preloadDelay()));
};
const handleTouchStart = (_) => {
if (local.disabled || preload() !== "intent") return;
doPreload();
};
const handleLeave = (e) => {
if (local.disabled) return;
const eventTarget = e.currentTarget || e.target;
if (eventTarget) {
const id = timeoutMap.get(eventTarget);
clearTimeout(id);
timeoutMap.delete(eventTarget);
}
};
const simpleStyling = solid_js.createMemo(() => local.activeProps === STATIC_ACTIVE_PROPS_GET && local.inactiveProps === STATIC_INACTIVE_PROPS_GET && local.class === void 0 && local.style === void 0);
const onClick = createComposedHandler(() => local.onClick, handleClick);
const onBlur = createComposedHandler(() => local.onBlur, handleLeave);
const onFocus = createComposedHandler(() => local.onFocus, enqueueIntentPreload);
const onMouseEnter = createComposedHandler(() => local.onMouseEnter, enqueueIntentPreload);
const onMouseOver = createComposedHandler(() => local.onMouseOver, enqueueIntentPreload);
const onMouseLeave = createComposedHandler(() => local.onMouseLeave, handleLeave);
const onMouseOut = createComposedHandler(() => local.onMouseOut, handleLeave);
const onTouchStart = createComposedHandler(() => local.onTouchStart, handleTouchStart);
const resolvedProps = solid_js.createMemo(() => {
const active = isActive();
const base = {
href: hrefOption()?.href,
ref: mergeRefs(setRef, _options().ref),
onClick,
onBlur,
onFocus,
onMouseEnter,
onMouseOver,
onMouseLeave,
onMouseOut,
onTouchStart,
disabled: !!local.disabled,
target: local.target,
...local.disabled && STATIC_DISABLED_PROPS,
...isTransitioning() && STATIC_TRANSITIONING_ATTRIBUTES
};
if (simpleStyling()) return {
...base,
...active && STATIC_DEFAULT_ACTIVE_ATTRIBUTES
};
const activeProps = active ? (0, _tanstack_router_core.functionalUpdate)(local.activeProps, {}) ?? EMPTY_OBJECT : EMPTY_OBJECT;
const inactiveProps = active ? EMPTY_OBJECT : (0, _tanstack_router_core.functionalUpdate)(local.inactiveProps, {});
const style = {
...local.style,
...activeProps.style,
...inactiveProps.style
};
const className = [
local.class,
activeProps.class,
inactiveProps.class
].filter(Boolean).join(" ");
return {
...activeProps,
...inactiveProps,
...base,
...Object.keys(style).length ? { style } : void 0,
...className ? { class: className } : void 0,
...active && STATIC_ACTIVE_ATTRIBUTES
};
});
return solid_js.merge(propsSafeToSpread, resolvedProps);
}
var STATIC_ACTIVE_PROPS = { class: "active" };
var STATIC_ACTIVE_PROPS_GET = () => STATIC_ACTIVE_PROPS;
var EMPTY_OBJECT = {};
var STATIC_INACTIVE_PROPS_GET = () => 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"
};
var STATIC_TRANSITIONING_ATTRIBUTES = { "data-transitioning": "transitioning" };
/** 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, _solidjs_web.createComponent)(Link, (0, _solidjs_web.mergeProps)(props, { _asChild: Comp }));
}
var Link = (props) => {
const [local, rest] = splitProps(props, ["_asChild", "children"]);
const [_, linkProps] = splitProps(useLinkProps(rest), ["type"]);
const resolvedChildren = solid_js.children(() => local.children);
const children = () => {
const ch = resolvedChildren();
if (typeof ch === "function") return ch({
get isActive() {
return linkProps["data-status"] === "active";
},
get isTransitioning() {
return linkProps["data-transitioning"] === "transitioning";
}
});
return ch;
};
if (local._asChild === "svg") {
const [_, svgLinkProps] = splitProps(linkProps, ["class"]);
return (() => {
var _el$ = _tmpl$(), _el$2 = _el$.firstChild;
(0, _solidjs_web.spread)(_el$2, svgLinkProps, true);
(0, _solidjs_web.insert)(_el$2, children);
return _el$;
})();
}
if (!local._asChild) return (() => {
var _el$3 = _tmpl$2();
(0, _solidjs_web.spread)(_el$3, linkProps, true);
(0, _solidjs_web.insert)(_el$3, children);
return _el$3;
})();
return (0, _solidjs_web.createComponent)(_solidjs_web.Dynamic, (0, _solidjs_web.mergeProps)({ get component() {
return local._asChild;
} }, linkProps, { get children() {
return children();
} }));
};
function isCtrlEvent(e) {
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
}
function isSafeInternal(to) {
if (typeof to !== "string") return false;
const zero = to.charCodeAt(0);
if (zero === 47) return to.charCodeAt(1) !== 47;
return zero === 46;
}
var linkOptions = (options) => {
return options;
};
//#endregion
exports.Link = Link;
exports.createLink = createLink;
exports.linkOptions = linkOptions;
exports.useLinkProps = useLinkProps;
//# sourceMappingURL=link.cjs.map