UNPKG

react-magic-search-params

Version:

Type-safe React hook to manage URL query/search params with React Router useSearchParams.

2 lines (1 loc) 10.6 kB
import{useSearchParams as Ke}from"react-router-dom";import{useMemo as A,useEffect as ae,useRef as ne,useCallback as S}from"react";var Me=({mandatory:d={},optional:M={},defaultParams:y={},arraySerialization:P="csv",forceParams:w={},omitParamsByValues:se=[],coerceParams:j={},codecs:I={},protectedParams:U={},historyMode:q="push",resetOnChange:oe={},paginationStrategy:l,unknownParamsPolicy:C="drop"})=>{let[i,z]=Ke(),v=ne({}),V=ne({}),_=S((e,r)=>{z(e,{replace:(r??q)==="replace"})},[z,q]),m=A(()=>({...d,...M}),[d,M]),H=A(()=>Array.from(Object.keys(m)),[m]),T=S(e=>Object.prototype.hasOwnProperty.call(m,e),[m]),ce=e=>!Array.isArray(m[e])||P==="csv"?i.get(e):P==="repeat"?i.getAll(e):i.getAll(`${e}[]`),$=S(()=>{if(C==="drop")return[];let e=[];for(let[r,a]of i.entries()){let t=r.endsWith("[]")?r.replace("[]",""):r;T(t)||e.push([r,a])}return e},[C,i,T]),D=S(e=>{if(C==="drop")return e;let r=new URLSearchParams(e.toString()),a=$();for(let[t,n]of a)r.append(t,n);return r},[C,$]),W=(e,r)=>Array.isArray(e)&&Array.isArray(r)?e.length!==r.length?!1:e.every((a,t)=>a===r[t]):e===r,E=A(()=>Object.keys(m).filter(e=>Array.isArray(m[e])),[m]),ie=(e,r)=>{let a={...e};return E.length===0||E.forEach(t=>{let n=[];switch(P){case"csv":{n=(i.get(t)||"").split(",").map(o=>o.trim()).filter(Boolean);break}case"repeat":{let s=i.getAll(t);n=s.length>0?s:[];break}case"brackets":{let s=i.getAll(`${t}[]`);n=s.length>0?s:[];break}default:n=(i.get(t)??"").split(",").map(o=>o.trim()).filter(Boolean)}if(r[t]!==void 0){let s=r[t],o=[];typeof s=="string"?o=n.includes(s)?n.filter(u=>u!==s):[...n,s]:Array.isArray(s)?o=Array.from(new Set([...s.map(String)])):o=n,a[t]=o}}),a},x=globalThis.Buffer,ue=e=>{if(x)return x.from(e,"utf8").toString("base64");if(typeof btoa<"u"&&typeof TextEncoder<"u"){let r=new TextEncoder().encode(e),a="";for(let t of r)a+=String.fromCharCode(t);return btoa(a)}throw new Error("Base64 encoding is not supported in this environment")},me=e=>{if(x)return x.from(e,"base64").toString("utf8");if(typeof atob<"u"&&typeof TextDecoder<"u"){let r=atob(e),a=Uint8Array.from(r,t=>t.charCodeAt(0));return new TextDecoder().decode(a)}throw new Error("Base64 decoding is not supported in this environment")},Q=e=>ue(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,""),fe=e=>{let r=e.replace(/-/g,"+").replace(/_/g,"/"),a=r.padEnd(r.length+(4-r.length%4)%4,"=");return me(a)},Y=e=>e==null?e:Array.isArray(e)?e.map(r=>Q(String(r))):Q(String(e)),G=e=>{let r=a=>{if(a==="")return"";try{return fe(a)}catch{return a}};return e==null?"":Array.isArray(e)?e.map(a=>r(a)):r(e)},J=A(()=>{let e={...I};for(let r of Object.keys(U)){let a=U[r];if(a){if(a===!0){e[r]={parse:t=>G(t),serialize:t=>Y(t)};continue}e[r]={parse:a.parse??(t=>G(t)),serialize:a.serialize??(t=>Y(t))}}}return e},[I,U]),O=e=>{let r=new URLSearchParams,a=Object.keys(e);for(let t of a){let n=J[t];if(n?.serialize){let s=n.serialize(e[t],{key:t});if(s==null)continue;if(Array.isArray(s))if(Array.isArray(m[t]))if(P==="csv")r.set(t,s.join(","));else if(P==="repeat")for(let o of s)r.append(t,String(o));else for(let o of s)r.append(`${t}[]`,String(o));else s.length>0&&r.set(t,String(s[0]));else r.set(t,String(s));continue}if(Array.isArray(m[t])){let s=e[t];switch(P){case"csv":{r.set(t,s.join(","));break}case"repeat":{for(let o of s)r.append(t,String(o));break}case"brackets":{for(let o of s)r.append(`${t}[]`,String(o));break}default:r.set(t,s.join(","))}}else r.set(t,String(e[t]))}return r},p=(e,r)=>Object.prototype.hasOwnProperty.call(d,e)?d[e]:Object.prototype.hasOwnProperty.call(y,e)?y[e]:r,X=e=>Object.prototype.hasOwnProperty.call(M,e)?M[e]==="":!1,B=(e,r)=>{let a=Array.isArray(r)?r[0]:r,t=a==null?"":String(a).trim().toLowerCase();if(t==="true")return!0;if(t==="false")return!1;if(X(e))return"";let n=p(e,!1);return typeof n=="boolean"?n:!1},Z=({paramsForced:e,compareParams:r})=>Object.entries(e).every(([t,n])=>r[t]===n),F=e=>{let r=ce(e),a=J[e];if(a?.parse)return a.parse(r,{key:e,searchParams:i});let t=j[e];if(t==="number"){let n=Number.parseInt(String(r??""),10);if(Number.isNaN(n)){let s=p(e,0);return typeof s=="number"?s:0}return n}if(t==="boolean")return B(e,r);if(t==="array")return P==="csv"?String(r??"").split(",").filter(Boolean):Array.isArray(r)?r:r?[r]:[];if(t==="string")return Array.isArray(r)?r[0]??"":r??"";if(typeof m[e]=="number"){let n=Number.parseInt(String(r??""),10);if(Number.isNaN(n)){let s=p(e,0);return typeof s=="number"?s:0}return n}return typeof m[e]=="boolean"?B(e,r):Array.isArray(m[e])?P==="csv"?String(r??"").split(",").filter(Boolean):Array.isArray(r)?r:r?[r]:[]:Array.isArray(r)?r[0]??"":r??""},ee=(e,r)=>{let a=j[e];if(!a)return r;if(a==="boolean")return B(e,r);if(a==="number"){if(typeof r=="number"&&Number.isFinite(r))return r;let t=Number.parseInt(String(r??""),10);if(Number.isNaN(t)){let n=p(e,0);return typeof n=="number"?n:0}return t}return a==="array"?Array.isArray(r)?r:r==null||r===""?[]:[String(r)]:a==="string"?Array.isArray(r)?r[0]??"":r==null?"":String(r):r},re=(e,r)=>{if(Array.isArray(m[e])){if(P==="brackets"){let n=i.getAll(`${e}[]`),s=O({[e]:n}).toString();return decodeURIComponent(s)}if(P==="csv"){let n=i.getAll(e),s=O({[e]:n}).toString();return decodeURIComponent(s)}let t=i.getAll(e);return O({[e]:t}).toString()}return r[e]},Pe=e=>{let r={};for(let[a,t]of e.entries())if(a.endsWith("[]")){let n=a.replace("[]","");r[n]?r[n].push(t):r[n]=[t]}else r[a]?Array.isArray(r[a])?r[a].push(t):r[a]=[r[a],t]:r[a]=t;return r},le=e=>Object.entries(e).reduce((r,[a,t])=>(t===""||t==null||(r[a]=t),r),{}),R=A(()=>P==="brackets"?Pe(i):Object.fromEntries(i.entries()),[i,P]);function h({convert:e=!0,forRequest:r=!1}={}){let a=(e===!0?H:Object.keys(R)).reduce((t,n)=>{if(Object.prototype.hasOwnProperty.call(m,n)){let s=P==="brackets"?n.replace("[]",""):n;if(e===!0){let o=Object.prototype.hasOwnProperty.call(d,s),u=Object.prototype.hasOwnProperty.call(R,s),c=Object.prototype.hasOwnProperty.call(M,s)&&j[s]==="boolean"&&X(s);if(!o&&!u&&!c)return t;let g=F(s);t[s]=ee(s,g)}else t[s]=re(n,R)}return t},{});return e===!0&&r===!0?le(a):a}let b=(e,r)=>{let a=String(e);return r?.convert!==!1?ee(a,F(a)):re(a,R)},de=e=>e==null||typeof e!="object"?!1:Object.prototype.hasOwnProperty.call(e,"newParams")||Object.prototype.hasOwnProperty.call(e,"keepParams")||Object.prototype.hasOwnProperty.call(e,"historyMode"),pe=e=>{let r=h({convert:!0});if(typeof e=="function"){let a=e(r);return de(a)?{newParams:typeof a.newParams=="function"?a.newParams(r):a.newParams??{},keepParams:a.keepParams??{},historyMode:a.historyMode}:{newParams:a,keepParams:{},historyMode:void 0}}return e?{newParams:typeof e.newParams=="function"?e.newParams(r):e.newParams??{},keepParams:e.keepParams??{},historyMode:e.historyMode}:{newParams:{},keepParams:{},historyMode:void 0}},ye=({currentParams:e,newParams:r,keepParams:a})=>{let t={...r},n={...a};for(let[s,o]of Object.entries(oe))if(!(!o||o.length===0||!Object.prototype.hasOwnProperty.call(t,s)||W(t[s],e[s]))){for(let c of o)if(!Object.prototype.hasOwnProperty.call(w,c)){if(Object.prototype.hasOwnProperty.call(d,c)){t[c]=d[c],delete n[c];continue}if(Object.prototype.hasOwnProperty.call(y,c)){t[c]=y[c],delete n[c];continue}delete t[c],n[c]=!1}}return{newParams:t,keepParams:n}},ge=(e,r)=>{let a=h(),t=Object.entries(e).filter(([o])=>!Array.isArray(m[o])),n=Object.assign({...a,...Object.fromEntries(t)},w),s=Object.keys(n).reduce((o,u)=>{if(Object.prototype.hasOwnProperty.call(r,u)&&r[u]===!1)return o;let c=n[u],g=typeof c=="string"&&se.includes(c);return c!=null&&c!==""&&!g&&(o[u]=c),o},{});return{...d,...s}},we=e=>H.reduce((a,t)=>(Object.prototype.hasOwnProperty.call(e,t)&&(a[t]=e[t]),a),{}),Oe=()=>{let e=E.length>0,r=h({convert:e});return Object.keys(r).reduce((t,n)=>(Object.prototype.hasOwnProperty.call(d,n)&&(t[n]=r[n]),t),{})},te=({keepMandatoryParams:e=!0,historyMode:r}={})=>{let a=O({...d,...e&&{...Oe()},...w}),t=D(a);_(t,r)},f=e=>{let r=pe(e),a=h({convert:!0}),t=ye({currentParams:a,newParams:r.newParams,keepParams:r.keepParams}),n=t.newParams,s=t.keepParams;if(Object.keys(n).length===0&&Object.keys(s).length===0){te({historyMode:r.historyMode});return}let o=ge(n,s),u=ie(o,n),c=we(u),g=O(c),L=D(g);_(L,r.historyMode)},K=l?.mode==="page"?l:void 0,k=l?.mode==="offset"?l:void 0,N=l?.mode==="cursor"?l:void 0,be={mode:l?.mode??"page",next:e=>{let r=l?.mode??"page";if(r==="cursor"){let s=N?.cursorKey??"cursor";typeof e=="string"&&f({newParams:{[s]:e}});return}if(r==="offset"){let s=k?.offsetKey??"offset",o=k?.limitKey??"limit",u=Number(b(s,{convert:!0})??0),c=Number(b(o,{convert:!0})??p(String(o),10)),g=Number.isFinite(u)?u:0,L=Number.isFinite(c)&&c>0?c:10;f({newParams:{[s]:g+L}});return}let a=K?K.pageKey??"page":"page",t=Number(b(a,{convert:!0})??p(String(a),1)),n=Number.isFinite(t)&&t>0?t:1;f({newParams:{[a]:n+1}})},prev:()=>{let e=l?.mode??"page";if(e==="cursor"){let n=N?.cursorKey??"cursor";f({keepParams:{[n]:!1}});return}if(e==="offset"){let n=k?.offsetKey??"offset",s=k?.limitKey??"limit",o=Number(b(n,{convert:!0})??0),u=Number(b(s,{convert:!0})??p(String(s),10)),c=Number.isFinite(o)?o:0,g=Number.isFinite(u)&&u>0?u:10;f({newParams:{[n]:Math.max(0,c-g)}});return}let r=K?K.pageKey??"page":"page",a=Number(b(r,{convert:!0})??p(String(r),1)),t=Number.isFinite(a)&&a>0?a:1;f({newParams:{[r]:Math.max(1,t-1)}})},reset:()=>{let e=l?.mode??"page";if(e==="cursor"){let t=N?.cursorKey??"cursor";f({keepParams:{[t]:!1}});return}if(e==="offset"){let t=k?.offsetKey??"offset",n=Number(p(String(t),0));f({newParams:{[t]:Number.isFinite(n)?n:0}});return}let r=K?K.pageKey??"page":"page",a=Number(p(String(r),1));f({newParams:{[r]:Number.isFinite(a)&&a>0?a:1}})},setCursor:e=>{let a=(l?.mode??"page")==="cursor"?N?.cursorKey??"cursor":"cursor";if(e==null||e===""){f({keepParams:{[a]:!1}});return}f({newParams:{[a]:e}})}},he=S((e,r)=>{let a=String(e);return v.current[a]=r,()=>{delete v.current[a],delete V.current[a]}},[]);return ae(()=>{for(let[e,r]of Object.entries(v.current)){let a=T(e)?F(e):i.get(e),t=V.current[e]??null;if(!W(a,t))for(let n of r)n({key:e,previousValue:t,currentValue:a});V.current[e]=a}},[R,T,i]),ae(()=>{let e=Object.keys(y),r=Object.keys(w);if(e.length===0&&r.length===0)return;function a(){let t=O(y).toString(),n=h(),s=O(n).toString();if(!Z({paramsForced:w,compareParams:n})){f({newParams:{...y,...w},historyMode:"replace"});return}let u=Z({paramsForced:w,compareParams:y});if(e.length>0&&u){if(t===s)return;f({newParams:y,historyMode:"replace"})}}a()},[]),{searchParams:i,updateParams:f,clearParams:te,getParams:h,getParam:b,onChange:he,pagination:be}};export{Me as useMagicSearchParams};