{"version":3,"file":"Router.mjs","sources":["../../../../../../../web/src/adminPortal/Router.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo } from 'react';\n\nimport { noop } from './shared/utils/noop';\nimport { useBeforeUnloadNavigationBlock } from './utils/useNavigateAwayWarning';\n\nexport type RouterStateEntry<TScreenMap> = {\n  [K in keyof TScreenMap]: TScreenMap[K] extends never\n    ? { screen: K; params?: undefined }\n    : { screen: K; params: TScreenMap[K] };\n}[keyof TScreenMap];\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- permissive generic constraint\nexport type MappedPropsFromRouteMap<TRouteMap extends Record<string, React.ComponentType<any>>> = {\n  [K in keyof TRouteMap]: unknown extends React.ComponentProps<TRouteMap[K]>\n    ? never\n    : React.ComponentProps<TRouteMap[K]>;\n};\n\ninterface RouterState<TScreenMap> {\n  currentRoute: RouterStateEntry<TScreenMap>;\n  pendingRoute?: RouterStateEntry<TScreenMap> | undefined;\n  blockersActive: Set<EnableBlock>;\n}\n\ntype RouterContextState<TScreenMap> = Pick<RouterState<TScreenMap>, 'currentRoute'>;\n\ntype EnableBlock = boolean | (() => boolean);\n\nexport type UseBlockNavigation = (enableBlock: EnableBlock) => {\n  blocked: boolean;\n  allowNavigation: () => void;\n  cancelNavigation: () => void;\n};\n\nexport interface RouterController<TScreenMap> {\n  navigate: (destination: RouterStateEntry<TScreenMap>) => void;\n  useBlockNavigation: UseBlockNavigation;\n}\n\ntype NavigateAction<TScreenMap> = {\n  type: 'navigate';\n  payload: RouterStateEntry<TScreenMap>;\n};\n\ntype AddBlockerAction = {\n  type: 'addBlocker';\n  payload: { callback: EnableBlock };\n};\n\ntype RemoveBlockerAction = {\n  type: 'removeBlocker';\n  payload: { callback: EnableBlock };\n};\n\ntype ApproveNavigationAction = {\n  type: 'approveNavigation';\n};\n\ntype CancelNavigationAction = {\n  type: 'cancelNavigation';\n};\n\ntype RouterAction<TScreenMap> =\n  | NavigateAction<TScreenMap>\n  | AddBlockerAction\n  | RemoveBlockerAction\n  | ApproveNavigationAction\n  | CancelNavigationAction;\n\nconst routerReducer = <TScreenMap,>(\n  state: RouterState<TScreenMap>,\n  action: RouterAction<TScreenMap>,\n): RouterState<TScreenMap> => {\n  switch (action.type) {\n    case 'navigate':\n      if (state.blockersActive.size > 0) {\n        return { ...state, pendingRoute: action.payload };\n      }\n\n      return { ...state, currentRoute: action.payload, pendingRoute: undefined };\n    case 'addBlocker': {\n      const blockersActive = new Set(state.blockersActive);\n      blockersActive.add(action.payload.callback);\n      return { ...state, blockersActive };\n    }\n    case 'removeBlocker': {\n      const blockersActive = new Set(state.blockersActive);\n      blockersActive.delete(action.payload.callback);\n\n      if (blockersActive.size === 0 && state.pendingRoute) {\n        return { ...state, blockersActive, currentRoute: state.pendingRoute, pendingRoute: undefined };\n      }\n\n      return { ...state, blockersActive };\n    }\n    case 'approveNavigation':\n      if (!state.pendingRoute) {\n        return state;\n      }\n      return { ...state, currentRoute: state.pendingRoute, pendingRoute: undefined };\n    case 'cancelNavigation':\n      return { ...state, pendingRoute: undefined };\n    default:\n      return state;\n  }\n};\n\ntype RouteMap<TScreenMap> = {\n  [K in keyof TScreenMap]: React.ComponentType<TScreenMap[K]>;\n};\n\nexport const createRouter = <TScreenMap extends Record<string, object>>() => {\n  const initialState = {\n    blockersActive: new Set(),\n  } satisfies Omit<RouterState<TScreenMap>, 'currentRoute'>;\n\n  // TODO: consider throwing if not initialized\n  const StateContext = React.createContext<RouterContextState<TScreenMap>>({} as RouterContextState<TScreenMap>);\n\n  // TODO: consider throwing if not initialized\n  const ControllerContext = React.createContext<RouterController<TScreenMap>>({\n    navigate: noop,\n    useBlockNavigation: () => ({ blocked: false, allowNavigation: noop, cancelNavigation: noop }),\n  });\n\n  const useRouterState = () => {\n    return React.useContext(StateContext);\n  };\n\n  const useRouterController = () => {\n    return React.useContext(ControllerContext);\n  };\n\n  const Router = ({ routeMap }: { routeMap: RouteMap<TScreenMap> }) => {\n    const { currentRoute } = useRouterState();\n    const ChildComponent = routeMap[currentRoute.screen] as React.ComponentType<TScreenMap[keyof TScreenMap]>;\n    const childComponentProps = currentRoute.params as TScreenMap[keyof TScreenMap];\n\n    return ChildComponent ? <ChildComponent {...childComponentProps} /> : null;\n  };\n\n  const RouterProvider = ({\n    children,\n    initialRoute,\n  }: {\n    children: React.ReactNode;\n    initialRoute: RouterStateEntry<TScreenMap>;\n  }) => {\n    const [state, dispatch] = React.useReducer(routerReducer<TScreenMap>, {\n      ...initialState,\n      currentRoute: initialRoute,\n    });\n\n    const navigate = useCallback((destination: RouterStateEntry<TScreenMap>) => {\n      dispatch({ type: 'navigate', payload: destination });\n    }, []);\n\n    const routerState = useMemo(() => ({ currentRoute: state.currentRoute }), [state.currentRoute]);\n\n    const isNavigationBlocked = !!state.pendingRoute;\n    const useBlockNavigation = useCallback<UseBlockNavigation>(\n      (enableBlock: EnableBlock) => {\n        // eslint-disable-next-line react-hooks/rules-of-hooks -- hook definition inside callback\n        const callback = useMemo(\n          () => (typeof enableBlock === 'function' ? enableBlock : () => enableBlock),\n          [enableBlock],\n        );\n\n        // eslint-disable-next-line react-hooks/rules-of-hooks -- hook definition inside callback\n        useEffect(() => {\n          const shouldEnableBlock = callback();\n          if (shouldEnableBlock) {\n            dispatch({ type: 'addBlocker', payload: { callback } });\n            return () => {\n              dispatch({ type: 'removeBlocker', payload: { callback } });\n            };\n          }\n        }, [callback, enableBlock]);\n\n        return {\n          blocked: isNavigationBlocked,\n          allowNavigation: () => {\n            dispatch({ type: 'approveNavigation' });\n          },\n          cancelNavigation: () => {\n            dispatch({ type: 'cancelNavigation' });\n          },\n        };\n      },\n      [isNavigationBlocked],\n    );\n    useBeforeUnloadNavigationBlock(state.blockersActive.size > 0);\n\n    const controller = useMemo(() => {\n      return { navigate, useBlockNavigation };\n    }, [navigate, useBlockNavigation]);\n\n    return (\n      <ControllerContext.Provider value={controller}>\n        <StateContext.Provider value={routerState}>{children}</StateContext.Provider>\n      </ControllerContext.Provider>\n    );\n  };\n\n  return {\n    RouterProvider,\n    Router,\n    useRouterController,\n    useRouterState,\n  };\n};\n"],"names":["routerReducer","state","action","type","blockersActive","size","pendingRoute","payload","currentRoute","undefined","Set","add","callback","delete","createRouter","initialState","StateContext","React","createContext","ControllerContext","navigate","noop","useBlockNavigation","blocked","allowNavigation","cancelNavigation","useRouterState","useContext","useRouterController","Router","routeMap","ChildComponent","screen","childComponentProps","params","RouterProvider","children","initialRoute","dispatch","useReducer","useCallback","destination","routerState","useMemo","isNavigationBlocked","enableBlock","useEffect","shouldEnableBlock","useBeforeUnloadNavigationBlock","controller","Provider","value"],"mappings":"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"}