@tanstack/solid-router
Version:
Modern and scalable routing for Solid applications
1 lines • 12 kB
Source Map (JSON)
{"version":3,"file":"useBlocker.cjs","names":["{children()}"],"sources":["../../src/useBlocker.tsx"],"sourcesContent":["import * as Solid from 'solid-js'\nimport { useRouter } from './useRouter'\nimport type {\n BlockerFnArgs,\n HistoryAction,\n HistoryLocation,\n} from '@tanstack/history'\nimport type { SolidNode } from './route'\nimport type {\n AnyRoute,\n AnyRouter,\n ParseRoute,\n RegisteredRouter,\n} from '@tanstack/router-core'\n\ntype ShouldBlockFnLocation<\n out TRouteId,\n out TFullPath,\n out TAllParams,\n out TFullSearchSchema,\n> = {\n routeId: TRouteId\n fullPath: TFullPath\n pathname: string\n params: TAllParams\n search: TFullSearchSchema\n}\n\ntype AnyShouldBlockFnLocation = ShouldBlockFnLocation<any, any, any, any>\ntype MakeShouldBlockFnLocationUnion<\n TRouter extends AnyRouter = RegisteredRouter,\n TRoute extends AnyRoute = ParseRoute<TRouter['routeTree']>,\n> = TRoute extends any\n ? ShouldBlockFnLocation<\n TRoute['id'],\n TRoute['fullPath'],\n TRoute['types']['allParams'],\n TRoute['types']['fullSearchSchema']\n >\n : never\n\ntype BlockerResolver<TRouter extends AnyRouter = RegisteredRouter> =\n | {\n status: 'blocked'\n current: MakeShouldBlockFnLocationUnion<TRouter>\n next: MakeShouldBlockFnLocationUnion<TRouter>\n action: HistoryAction\n proceed: () => void\n reset: () => void\n }\n | {\n status: 'idle'\n current: undefined\n next: undefined\n action: undefined\n proceed: undefined\n reset: undefined\n }\n\ntype ShouldBlockFnArgs<TRouter extends AnyRouter = RegisteredRouter> = {\n current: MakeShouldBlockFnLocationUnion<TRouter>\n next: MakeShouldBlockFnLocationUnion<TRouter>\n action: HistoryAction\n}\n\nexport type ShouldBlockFn<TRouter extends AnyRouter = RegisteredRouter> = (\n args: ShouldBlockFnArgs<TRouter>,\n) => boolean | Promise<boolean>\nexport type UseBlockerOpts<\n TRouter extends AnyRouter = RegisteredRouter,\n TWithResolver extends boolean = boolean,\n> = {\n shouldBlockFn: ShouldBlockFn<TRouter>\n enableBeforeUnload?: boolean | (() => boolean)\n disabled?: boolean\n withResolver?: TWithResolver\n}\n\ntype LegacyBlockerFn = () => Promise<any> | any\ntype LegacyBlockerOpts = {\n blockerFn?: LegacyBlockerFn\n condition?: boolean | any\n}\n\nfunction _resolveBlockerOpts(\n opts?: UseBlockerOpts | LegacyBlockerOpts | LegacyBlockerFn,\n condition?: boolean | any,\n): UseBlockerOpts {\n if (opts === undefined) {\n return {\n shouldBlockFn: () => true,\n withResolver: false,\n }\n }\n\n if ('shouldBlockFn' in opts) {\n return opts\n }\n\n if (typeof opts === 'function') {\n const shouldBlock = Boolean(condition ?? true)\n\n const _customBlockerFn = async () => {\n if (shouldBlock) return await opts()\n return false\n }\n\n return {\n shouldBlockFn: _customBlockerFn,\n enableBeforeUnload: shouldBlock,\n withResolver: false,\n }\n }\n\n const shouldBlock = Solid.createMemo(() => Boolean(opts.condition ?? true))\n\n const _customBlockerFn = async () => {\n if (shouldBlock() && opts.blockerFn !== undefined) {\n return await opts.blockerFn()\n }\n return shouldBlock()\n }\n\n return {\n get shouldBlockFn() {\n return _customBlockerFn\n },\n get enableBeforeUnload() {\n return shouldBlock()\n },\n get withResolver() {\n return opts.blockerFn === undefined\n },\n }\n}\n\nexport function useBlocker<\n TRouter extends AnyRouter = RegisteredRouter,\n TWithResolver extends boolean = false,\n>(\n opts: UseBlockerOpts<TRouter, TWithResolver>,\n): TWithResolver extends true ? Solid.Accessor<BlockerResolver<TRouter>> : void\n\n/**\n * @deprecated Use the shouldBlockFn property instead\n */\nexport function useBlocker(\n blockerFnOrOpts?: LegacyBlockerOpts,\n): Solid.Accessor<BlockerResolver>\n\n/**\n * @deprecated Use the UseBlockerOpts object syntax instead\n */\nexport function useBlocker(\n blockerFn?: LegacyBlockerFn,\n condition?: boolean | any,\n): Solid.Accessor<BlockerResolver>\n\nexport function useBlocker(\n opts?: UseBlockerOpts | LegacyBlockerOpts | LegacyBlockerFn,\n condition?: boolean | any,\n): Solid.Accessor<BlockerResolver> | void {\n const props = Solid.merge(\n {\n enableBeforeUnload: true,\n disabled: false,\n withResolver: false,\n },\n _resolveBlockerOpts(opts, condition),\n )\n\n const router = useRouter()\n\n const [resolver, setResolver] = Solid.createSignal<BlockerResolver>({\n status: 'idle',\n current: undefined,\n next: undefined,\n action: undefined,\n proceed: undefined,\n reset: undefined,\n })\n\n let disposeBlock: (() => void) | undefined\n\n Solid.createEffect(\n () => props.disabled,\n (disabled) => {\n // Dispose previous blocker registration when re-running\n disposeBlock?.()\n disposeBlock = undefined\n\n if (disabled) {\n return\n }\n\n const blockerFnComposed = async (blockerFnArgs: BlockerFnArgs) => {\n function getLocation(\n location: HistoryLocation,\n ): AnyShouldBlockFnLocation {\n const parsedLocation = router.parseLocation(location)\n const [, rawParams, foundRoute] = router.getMatchedRoutes(\n parsedLocation.pathname,\n )\n if (foundRoute === undefined) {\n return {\n routeId: '__notFound__',\n fullPath: parsedLocation.pathname,\n pathname: parsedLocation.pathname,\n params: rawParams,\n search: parsedLocation.search,\n }\n }\n return {\n routeId: foundRoute.id,\n fullPath: foundRoute.fullPath,\n pathname: parsedLocation.pathname,\n params: rawParams,\n search: parsedLocation.search,\n }\n }\n\n const current = getLocation(blockerFnArgs.currentLocation)\n const next = getLocation(blockerFnArgs.nextLocation)\n\n if (\n current.routeId === '__notFound__' &&\n next.routeId !== '__notFound__'\n ) {\n return false\n }\n\n const shouldBlock = await props.shouldBlockFn({\n action: blockerFnArgs.action,\n current,\n next,\n })\n if (!props.withResolver) {\n return shouldBlock\n }\n\n if (!shouldBlock) {\n return false\n }\n\n const promise = new Promise<boolean>((resolve) => {\n setResolver({\n status: 'blocked',\n current,\n next,\n action: blockerFnArgs.action,\n proceed: () => resolve(false),\n reset: () => resolve(true),\n })\n })\n\n const canNavigateAsync = await promise\n setResolver({\n status: 'idle',\n current: undefined,\n next: undefined,\n action: undefined,\n proceed: undefined,\n reset: undefined,\n })\n\n return canNavigateAsync\n }\n\n disposeBlock = router.history.block({\n blockerFn: blockerFnComposed,\n enableBeforeUnload: props.enableBeforeUnload,\n })\n },\n )\n\n // Clean up on unmount\n Solid.onSettled(() => {\n return () => {\n disposeBlock?.()\n disposeBlock = undefined\n }\n })\n\n return resolver\n}\n\nconst _resolvePromptBlockerArgs = (\n props: PromptProps | LegacyPromptProps,\n): UseBlockerOpts => {\n if ('shouldBlockFn' in props) {\n return props\n }\n\n const shouldBlock = Solid.createMemo(() => Boolean(props.condition ?? true))\n\n const _customBlockerFn = async () => {\n if (shouldBlock() && props.blockerFn !== undefined) {\n return await props.blockerFn()\n }\n return shouldBlock\n }\n\n return {\n shouldBlockFn: _customBlockerFn,\n get enableBeforeUnload() {\n return shouldBlock()\n },\n get withResolver() {\n return props.blockerFn === undefined\n },\n }\n}\n\ninterface BlockComponent {\n <\n TRouter extends AnyRouter = RegisteredRouter,\n TWithResolver extends boolean = boolean,\n >(\n opts: PromptProps<TRouter, TWithResolver>,\n ): SolidNode\n /**\n * @deprecated Use the UseBlockerOpts property instead\n */\n (opts: LegacyPromptProps): SolidNode\n}\n\nexport const Block: BlockComponent = function Block(\n opts: PromptProps | LegacyPromptProps,\n): SolidNode {\n const propsWithChildren = {\n get children() {\n return opts.children\n },\n }\n const args = _resolvePromptBlockerArgs(opts)\n\n const resolver = useBlocker(args)\n const children = Solid.createMemo(() => {\n const child = propsWithChildren.children\n if (resolver && typeof child === 'function') return child(resolver())\n return child\n })\n\n return <>{children()}</>\n}\n\ntype LegacyPromptProps = {\n blockerFn?: LegacyBlockerFn\n condition?: boolean | any\n children?: SolidNode | ((params: BlockerResolver) => SolidNode)\n}\n\ntype PromptProps<\n TRouter extends AnyRouter = RegisteredRouter,\n TWithResolver extends boolean = boolean,\n TParams = TWithResolver extends true ? BlockerResolver<TRouter> : void,\n> = UseBlockerOpts<TRouter, TWithResolver> & {\n children?: SolidNode | ((params: TParams) => SolidNode)\n}\n"],"mappings":"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"}