@tanstack/solid-router
Version:
Modern and scalable routing for Solid applications
163 lines • 5.55 kB
JSX
import * as Solid from 'solid-js';
import { useRouter } from './useRouter';
function _resolveBlockerOpts(opts, condition) {
if (opts === undefined) {
return {
shouldBlockFn: () => true,
withResolver: false,
};
}
if ('shouldBlockFn' in opts) {
return opts;
}
if (typeof opts === 'function') {
const shouldBlock = Boolean(condition ?? true);
const _customBlockerFn = async () => {
if (shouldBlock)
return await opts();
return false;
};
return {
shouldBlockFn: _customBlockerFn,
enableBeforeUnload: shouldBlock,
withResolver: false,
};
}
const shouldBlock = Solid.createMemo(() => Boolean(opts.condition ?? true));
const _customBlockerFn = async () => {
if (shouldBlock() && opts.blockerFn !== undefined) {
return await opts.blockerFn();
}
return shouldBlock();
};
return {
get shouldBlockFn() {
return _customBlockerFn;
},
get enableBeforeUnload() {
return shouldBlock();
},
get withResolver() {
return opts.blockerFn === undefined;
},
};
}
export function useBlocker(opts, condition) {
const props = Solid.mergeProps({
enableBeforeUnload: true,
disabled: false,
withResolver: false,
}, _resolveBlockerOpts(opts, condition));
const router = useRouter();
const [resolver, setResolver] = Solid.createSignal({
status: 'idle',
current: undefined,
next: undefined,
action: undefined,
proceed: undefined,
reset: undefined,
});
Solid.createEffect(() => {
const blockerFnComposed = async (blockerFnArgs) => {
function getLocation(location) {
const parsedLocation = router.parseLocation(location);
const matchedRoutes = router.getMatchedRoutes(parsedLocation.pathname);
if (matchedRoutes.foundRoute === undefined) {
return {
routeId: '__notFound__',
fullPath: parsedLocation.pathname,
pathname: parsedLocation.pathname,
params: matchedRoutes.routeParams,
search: parsedLocation.search,
};
}
return {
routeId: matchedRoutes.foundRoute.id,
fullPath: matchedRoutes.foundRoute.fullPath,
pathname: parsedLocation.pathname,
params: matchedRoutes.routeParams,
search: parsedLocation.search,
};
}
const current = getLocation(blockerFnArgs.currentLocation);
const next = getLocation(blockerFnArgs.nextLocation);
if (current.routeId === '__notFound__' &&
next.routeId !== '__notFound__') {
return false;
}
const shouldBlock = await props.shouldBlockFn({
action: blockerFnArgs.action,
current,
next,
});
if (!props.withResolver) {
return shouldBlock;
}
if (!shouldBlock) {
return false;
}
const promise = new Promise((resolve) => {
setResolver({
status: 'blocked',
current,
next,
action: blockerFnArgs.action,
proceed: () => resolve(false),
reset: () => resolve(true),
});
});
const canNavigateAsync = await promise;
setResolver({
status: 'idle',
current: undefined,
next: undefined,
action: undefined,
proceed: undefined,
reset: undefined,
});
return canNavigateAsync;
};
const disposeBlock = props.disabled
? undefined
: router.history.block({
blockerFn: blockerFnComposed,
enableBeforeUnload: props.enableBeforeUnload,
});
Solid.onCleanup(() => disposeBlock?.());
});
return resolver;
}
const _resolvePromptBlockerArgs = (props) => {
if ('shouldBlockFn' in props) {
return props;
}
const shouldBlock = Solid.createMemo(() => Boolean(props.condition ?? true));
const _customBlockerFn = async () => {
if (shouldBlock() && props.blockerFn !== undefined) {
return await props.blockerFn();
}
return shouldBlock;
};
return {
shouldBlockFn: _customBlockerFn,
get enableBeforeUnload() {
return shouldBlock();
},
get withResolver() {
return props.blockerFn === undefined;
},
};
};
export const Block = function Block(opts) {
const [propsWithChildren, rest] = Solid.splitProps(opts, ['children']);
const args = _resolvePromptBlockerArgs(rest);
const resolver = useBlocker(args);
const children = Solid.createMemo(() => {
const child = propsWithChildren.children;
if (resolver && typeof child === 'function')
return child(resolver());
return child;
});
return <>{children()}</>;
};
//# sourceMappingURL=useBlocker.jsx.map