UNPKG

nuqs

Version:

Type-safe search params state manager for React - Like useState, but stored in the URL query string

110 lines (107 loc) 3.68 kB
import { createAdapterProvider, renderQueryString, debug } from './chunk-5WWTJYGR.js'; import { useRouter } from 'next/compat/router.js'; import { useMemo, useCallback } from 'react'; function isPagesRouter() { return typeof window.next?.router?.state?.asPath === "string"; } function useNuqsNextPagesRouterAdapter() { const router = useRouter(); const searchParams = useMemo(() => { const searchParams2 = new URLSearchParams(); if (router === null) { return searchParams2; } for (const [key, value] of Object.entries(router.query)) { if (typeof value === "string") { searchParams2.set(key, value); } else if (Array.isArray(value)) { for (const v of value) { searchParams2.append(key, v); } } } return searchParams2; }, [JSON.stringify(router?.query)]); const updateUrl = useCallback((search, options) => { const nextRouter = window.next?.router; const urlParams = extractDynamicUrlParams( nextRouter.pathname, nextRouter.query ); const asPath = getAsPathPathname(nextRouter.asPath) + renderQueryString(search) + location.hash; debug("[nuqs queue (pages)] Updating url: %s", asPath); const method = options.history === "push" ? nextRouter.push : nextRouter.replace; method.call( nextRouter, // This is what makes the URL work (mapping dynamic segments placeholders // in pathname to their values in query, plus search params in query too). { pathname: nextRouter.pathname, query: { // Note: we put search params first so that one that conflicts // with dynamic params will be overwritten. ...urlSearchParamsToObject(search), ...urlParams } // For some reason we don't need to pass the hash here, // it's preserved when passed as part of the asPath. }, // This is what makes the URL pretty (resolved dynamic segments // and nuqs-formatted search params). asPath, // And these are the options that are passed to the router. { scroll: options.scroll, shallow: options.shallow } ); }, []); return { searchParams, updateUrl }; } createAdapterProvider(useNuqsNextPagesRouterAdapter); function getAsPathPathname(asPath) { return asPath.replace(/#.*$/, "").replace(/\?.*$/, ""); } function urlSearchParamsToObject(search) { const out = {}; for (const key of search.keys()) { const values = search.getAll(key); if (values.length === 1) { out[key] = values[0]; } else if (values.length > 1) { out[key] = values; } } return out; } function extractDynamicUrlParams(pathname, values) { const paramNames = /* @__PURE__ */ new Set(); const dynamicRegex = /\[([^\]]+)\]/g; const catchAllRegex = /\[\.{3}([^\]]+)\]$/; const optionalCatchAllRegex = /\[\[\.{3}([^\]]+)\]\]$/; let match; while ((match = dynamicRegex.exec(pathname)) !== null) { const paramName = match[1]; if (paramName) { paramNames.add(paramName); } } const dynamicValues = Object.fromEntries( Object.entries(values).filter(([key]) => paramNames.has(key)) ); const matchCatchAll = catchAllRegex.exec(pathname); if (matchCatchAll && matchCatchAll[1]) { const key = matchCatchAll[1]; dynamicValues[key] = values[key] ?? []; } const matchOptionalCatchAll = optionalCatchAllRegex.exec(pathname); if (matchOptionalCatchAll && matchOptionalCatchAll[1]) { const key = matchOptionalCatchAll[1]; dynamicValues[key] = values[key] ?? []; } return dynamicValues; } export { isPagesRouter, useNuqsNextPagesRouterAdapter };