{"version":3,"sources":["../src/store.tsx","../src/lib.tsx"],"sourcesContent":["\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\n\nexport type Store<T> = {\n  getValue(): T;\n  setValue(newValue: T): void;\n  subscribe(listener: () => void): () => void;\n};\n\nexport function createStore<T>(initialValue: T): Store<T> {\n  type Listener = () => void;\n  const listeners = new Set<Listener>();\n\n  let value = initialValue;\n\n  const notify = () => {\n    for (const listener of listeners) {\n      listener();\n    }\n  };\n\n  return {\n    getValue() {\n      return value;\n    },\n    setValue(newValue: T) {\n      if (newValue === value) {\n        return;\n      }\n      value = newValue;\n      notify();\n    },\n    subscribe(listener: Listener) {\n      listeners.add(listener);\n      return () => {\n        listeners.delete(listener);\n      };\n    },\n  };\n}\n\nexport function useStore<T>(store: Store<T>): T {\n  return useSyncExternalStore(store.subscribe, store.getValue, store.getValue);\n}\n","\"use client\";\n\nimport { createStore, useStore } from \"./store\";\nimport { usePathname, useRouter } from \"next/navigation\";\nimport {\n  Suspense,\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n  useTransition,\n} from \"react\";\n\n// General types -------------------------------------\n\nexport type SearchParams = Record<string, string | string[] | undefined>;\nexport type SetSearchParams = React.Dispatch<\n  React.SetStateAction<SearchParams>\n>;\n\n// contexts --------------------------------------------\n\nconst ResponsiveSearchParamsCtx = createContext<SearchParams | undefined>(\n  undefined\n);\n\nconst PageSearchParamsCtx = createContext<SearchParams>({});\n\nconst SetResponsiveSearchParamsCtx = createContext<SetSearchParams | undefined>(\n  undefined\n);\n\nconst IsRSCPendingCtx = createContext<boolean | undefined>(undefined);\n\n// stores ----------------------------------------------\n\nconst pendingSearchParamsStore = createStore<SearchParams | undefined>(\n  undefined\n);\n\n// Internals --------------------------------\n\nfunction stringifySearchParams(params: SearchParams) {\n  const keys = Object.keys(params).sort();\n  const urlSearchParams = new URLSearchParams(window.location.search);\n\n  for (const key of keys) {\n    const val = params[key];\n    if (val) {\n      if (Array.isArray(val)) {\n        for (const v of val) {\n          urlSearchParams.append(key, v);\n        }\n      } else {\n        urlSearchParams.set(key, val);\n      }\n    }\n  }\n\n  return urlSearchParams.toString();\n}\n\nfunction CacheRSC(props: {\n  searchParamsUsed: string[];\n  children: React.ReactNode;\n  cacheKey: string;\n  childrenCache: Map<string, React.ReactNode>;\n  suspendOnTransition: boolean;\n  isPending: boolean;\n}) {\n  const { childrenCache, isPending } = props;\n\n  if (isPending && props.suspendOnTransition) {\n    throw new Promise<void>((resolve) => {\n      const unsubscribe = pendingSearchParamsStore.subscribe(() => {\n        const val = pendingSearchParamsStore.getValue();\n        if (!val) {\n          resolve();\n          unsubscribe();\n        }\n      });\n    });\n  }\n\n  const cachedChildren = childrenCache.get(props.cacheKey);\n\n  if (cachedChildren) {\n    return cachedChildren;\n  }\n\n  if (!isPending) {\n    childrenCache.set(props.cacheKey, props.children);\n  }\n\n  return props.children;\n}\n\nfunction useCacheKey(searchParamsUsed: string[]) {\n  const responsiveSearchParams = useResponsiveSearchParams();\n\n  const cacheKey = useMemo(() => {\n    return searchParamsUsed\n      .filter((key) => responsiveSearchParams[key])\n      .map((key) => `${key}=${responsiveSearchParams[key]}`)\n      .join(\"&\");\n  }, [responsiveSearchParams, searchParamsUsed]);\n\n  return cacheKey;\n}\n\nfunction useIsSearchParamsPending(searchParamNames: string[]) {\n  const pendingSearchParams = useStore(pendingSearchParamsStore);\n  return useMemo(() => {\n    return (\n      !!pendingSearchParams &&\n      searchParamNames.some((key) => pendingSearchParams[key])\n    );\n  }, [pendingSearchParams, searchParamNames]);\n}\n\n// Components -----------------------------------------\n\nexport type ResponsiveSearchParamsProviderProps = {\n  children: React.ReactNode;\n  value: SearchParams;\n};\n\nexport function ResponsiveSearchParamsProvider(\n  props: ResponsiveSearchParamsProviderProps\n) {\n  const pathname = usePathname();\n  const router = useRouter();\n  const [isRoutePending, startRouteTransition] = useTransition();\n\n  const pageSearchParams = props.value;\n  const visitedSearchParamsRef = useRef(new Set<string>());\n\n  const [searchParamsOverride, setSearchParamsOverride] = useState<\n    SearchParams | undefined\n  >(undefined);\n\n  useEffect(() => {\n    if (!isRoutePending) {\n      pendingSearchParamsStore.setValue(undefined);\n    }\n  }, [isRoutePending]);\n\n  const responsiveSearchParams = useMemo(() => {\n    return {\n      ...pageSearchParams,\n      ...searchParamsOverride,\n    };\n  }, [searchParamsOverride, pageSearchParams]);\n\n  const setResponsiveSearchParams: SetSearchParams = useCallback(\n    (newSearchParamsDispatch) => {\n      const newSearchParams =\n        typeof newSearchParamsDispatch === \"function\"\n          ? newSearchParamsDispatch(responsiveSearchParams)\n          : newSearchParamsDispatch;\n\n      setSearchParamsOverride(newSearchParams);\n      const searchParamsString = stringifySearchParams(newSearchParams);\n\n      // if this search params was already visited\n      if (visitedSearchParamsRef.current.has(searchParamsString)) {\n        // update window without reloading\n        window.history.pushState({}, \"\", `${pathname}?${searchParamsString}`);\n        return;\n      }\n\n      // if this search params is new\n      visitedSearchParamsRef.current.add(searchParamsString);\n\n      // calculate pending search params by comparing new search params with current search params\n      const _pendingSearchParams: SearchParams = {};\n      for (const key in newSearchParams) {\n        if (\n          newSearchParams[key]?.toString() !==\n          responsiveSearchParams[key]?.toString()\n        ) {\n          _pendingSearchParams[key] = newSearchParams[key];\n        }\n      }\n\n      pendingSearchParamsStore.setValue(_pendingSearchParams);\n\n      startRouteTransition(() => {\n        router.replace(\n          `${pathname}${searchParamsString ? `?${searchParamsString}` : \"\"}`,\n          {\n            scroll: false,\n          }\n        );\n      });\n    },\n    [pathname, router, responsiveSearchParams]\n  );\n\n  return (\n    <ResponsiveSearchParamsCtx.Provider value={responsiveSearchParams}>\n      <PageSearchParamsCtx.Provider value={pageSearchParams}>\n        <SetResponsiveSearchParamsCtx.Provider\n          value={setResponsiveSearchParams}\n        >\n          {props.children}\n        </SetResponsiveSearchParamsCtx.Provider>\n      </PageSearchParamsCtx.Provider>\n    </ResponsiveSearchParamsCtx.Provider>\n  );\n}\n\nexport type ResponsiveSuspenseProps = {\n  searchParamsUsed: string[];\n  children: React.ReactNode;\n  fallback: React.ReactNode;\n  suspendOnTransition?: boolean;\n};\n\nexport function ResponsiveSuspense(props: ResponsiveSuspenseProps) {\n  const cacheKey = useCacheKey(props.searchParamsUsed);\n  const [childrenCache] = useState(() => new Map<string, React.ReactNode>());\n  const isPending = useIsSearchParamsPending(props.searchParamsUsed);\n\n  return (\n    <Suspense fallback={props.fallback}>\n      <IsRSCPendingCtx.Provider value={isPending}>\n        <CacheRSC\n          isPending={isPending}\n          suspendOnTransition={\n            props.suspendOnTransition === undefined\n              ? true\n              : props.suspendOnTransition\n          }\n          searchParamsUsed={props.searchParamsUsed}\n          cacheKey={cacheKey}\n          childrenCache={childrenCache}\n        >\n          {props.children}\n        </CacheRSC>\n      </IsRSCPendingCtx.Provider>\n    </Suspense>\n  );\n}\n\n// Hooks ----------------------------------------------\n\nexport function isRSCPending() {\n  const val = useContext(IsRSCPendingCtx);\n  if (val === undefined) {\n    throw new Error(\"isRSCPending must be used within a <ResponsiveSuspense>\");\n  }\n\n  return val;\n}\n\nexport function useResponsiveSearchParams() {\n  const val = useContext(ResponsiveSearchParamsCtx);\n  if (val === undefined) {\n    throw new Error(\n      \"useResponsiveSearchParams must be used within a <ResponsiveSearchParamsProvider>\"\n    );\n  }\n\n  return val;\n}\n\nexport function useSetResponsiveSearchParams() {\n  const val = useContext(SetResponsiveSearchParamsCtx);\n  if (val === undefined) {\n    throw new Error(\n      \"useSetResponsiveSearchParams must be used within a <ResponsiveSearchParamsProvider>\"\n    );\n  }\n  return val;\n}\n"],"mappings":"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","names":[]}