state-in-url 4.0.7 → 4.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -82,6 +82,8 @@ This library is a good alternative for NUQS.
82
82
  - 📘 **Typescript validation**: State is just an object, automatic static validation in IDE/tests according to Typescript definition
83
83
  - ✨ **Complex data**: Nested objects, dates and arrays, works same as JSON, but in URL
84
84
  - đŸ”ĩ **Server Side Rendering**: Can use it in Server Components, Next.js 14 and 15 are supported
85
+ - ☂ **Default values**: Giving you default values if parameter not in url
86
+ - ⌨ **Organized**: All possible values defined at start, protect you from getting non existing key
85
87
  - ⚙ **Well tested**: Unit tests and Playwright tests for Chrome/Firefox/Safari
86
88
  - ⚡ **Fast**: Minimal rerenders, less than [1ms](https://github.com/asmyshlyaev177/state-in-url/blob/87c8c7c995c5cd7d9e7aa039c30bfe64b24abe4b/packages/urlstate/encoder/encoder.test.ts#L185) to encode and decode big object
87
89
  - đŸĒļ **Lightweight**: Zero dependencies, library less than 2KB
@@ -1 +1 @@
1
- import{SYMBOLS as t}from"../constants/constants.mjs";import{typeOf as e}from"../utils.mjs";function n(n){if(u(n))return n;switch(e(n)){case"function":case"symbol":return"";case"date":return r=n,t.date+new Date(r).toISOString();case"undefined":return t.undefined;default:return JSON.stringify(n).replaceAll("'","%27").replaceAll('"',"'")}var r}function r(t,e){return a(t.replaceAll("'",'"').replaceAll("%27","'"),e)}const s=e=>{if(e!==t.undefined)return e?.startsWith?.(t.date)?new Date(e.slice(1)):c},c=Symbol("isError"),i=(t,e)=>{const n="string"==typeof e,r=n&&s(e);return r===c?e:n?r:e};function a(t,e){try{return JSON.parse(t,i)}catch{const n=s(t);return n!==c?n:e}}const o=new RegExp(`^(${t.undefined}|${t.date})`),u=t=>o.test(String(t));export{r as decode,s as decodePrimitive,n as encode,c as errorSym,a as parseJSON,i as reviver};
1
+ import{SYMBOLS as t}from"../constants/constants.mjs";import{typeOf as e}from"../utils.mjs";function n(n){if(u(n))return n;switch(e(n)){case"function":case"symbol":return"";case"date":return r=n,t.date+new Date(r).toISOString();case"undefined":return t.undefined;default:return JSON.stringify(n).replace(/'/g,"%27").replace(/"/g,"'")}var r}function r(t,e){return a(t.replace(/'/g,'"').replace(/%27/g,"'"),e)}const s=e=>{if(e!==t.undefined)return e?.startsWith?.(t.date)?new Date(e.slice(1)):c},c=Symbol("isError"),i=(t,e)=>{const n="string"==typeof e,r=n&&s(e);return r===c?e:n?r:e};function a(t,e){try{return JSON.parse(t,i)}catch{const n=s(t);return n!==c?n:e}}const o=new RegExp(`^(${t.undefined}|${t.date})`),u=t=>o.test(String(t));export{r as decode,s as decodePrimitive,n as encode,c as errorSym,a as parseJSON,i as reviver};
@@ -1,3 +1,4 @@
1
+ import { useRouter } from "next/navigation";
1
2
  import { type JSONCompatible } from "../../utils";
2
3
  /**
3
4
  * @deprecated .
@@ -17,9 +18,12 @@ export declare function useUrlState<T extends JSONCompatible>({ defaultState: T,
17
18
  * NextJS hook. Returns `urlState`, `setState`, and `setUrl` functions
18
19
  *
19
20
  * @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
20
- * @param {Object} params - Object with other parameters
21
- * @param {?SearchParams<T>} params.searchParams searchParams from Next server component
21
+ * @param {Object} params - Object with other parameters, including params from App router
22
+ * @param {boolean} params.replace replace URL of push, default `true`
22
23
  * @param {boolean} params.useHistory use window.history for navigation, default true, no _rsc requests https://github.com/vercel/next.js/discussions/59167
24
+ * @param {?SearchParams<T>} params.searchParams searchParams from Next server component
25
+ * @param {boolean} params.scroll reset scroll, default `false`
26
+
23
27
  *
24
28
  * * Example:
25
29
  * ```ts
@@ -29,9 +33,7 @@ export declare function useUrlState<T extends JSONCompatible>({ defaultState: T,
29
33
  * const { urlState, setState, setUrl } = useUrlState(form, { searchParams });
30
34
  *
31
35
  * setState({ name: 'test' });
32
- * // by default it's uses router.push with scroll: false
33
36
  * setUrl({ name: 'test' }, { replace: true, scroll: true });
34
- * // similar to React.useState
35
37
  * setUrl(curr => ({ ...curr, name: 'test' }), { replace: true, scroll: true });
36
38
  * ```
37
39
  *
@@ -40,8 +42,13 @@ export declare function useUrlState<T extends JSONCompatible>({ defaultState: T,
40
42
  export declare function useUrlState<T extends JSONCompatible>(defaultState: T, params?: Params): {
41
43
  urlState: T;
42
44
  setState: (value: Partial<T> | ((currState: T) => T)) => void;
43
- setUrl: (value?: Partial<T> | ((currState: T) => T)) => void;
45
+ setUrl: (value?: Partial<T> | ((currState: T) => T), options?: Options) => void;
44
46
  };
47
+ type Router = ReturnType<typeof useRouter>;
48
+ type RouterOptions = NonNullable<Parameters<Router["push"]>[1] | Parameters<Router["replace"]>[1]>;
49
+ interface Options extends RouterOptions {
50
+ replace?: boolean;
51
+ }
45
52
  interface OldParams<T> {
46
53
  defaultState: T;
47
54
  searchParams?: object;
@@ -1 +1 @@
1
- import{decodePrimitive as r,errorSym as t,decode as n}from"./encoder/encoder.mjs";function e(r,t){return{...t,...o(JSON.stringify(r),t)}}function o(n,e){try{return JSON.parse(n,c)}catch{const o=r(n);return o!==t?o:e}}const c=(r,t)=>{const e="string"==typeof t,o=e&&n(t?.replaceAll?.("'",'"'));return r&&e?o:t};export{e as parseSPObj};
1
+ import{decodePrimitive as r,errorSym as t,decode as n}from"./encoder/encoder.mjs";function e(r,t){return{...t,...o(JSON.stringify(r),t)}}function o(n,e){try{return JSON.parse(n,c)}catch{const o=r(n);return o!==t?o:e}}const c=(r,t)=>{const e="string"==typeof t,o=e&&n(t?.replace?.(/'/g,'"'));return r&&e?o:t};export{e as parseSPObj};
@@ -20,7 +20,9 @@ export declare function useUrlState<T extends JSONCompatible>({ defaultState: T,
20
20
  * @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
21
21
  * @param {Object} params - Object with other parameters
22
22
  * @param {NavigateOptions} params.NavigateOptions See type from `react-router-dom`
23
- * @param {boolean} params.useHistory use window.history for navigation
23
+ * @param {boolean} params.replace replace URL of push, default `true`
24
+ * @param {boolean} params.useHistory use window.history for navigation, default `false`
25
+ * @param {boolean} params.preventScrollReset keep scroll position, default `true`
24
26
  * * Example:
25
27
  * ```ts
26
28
  * export const form = { name: '', age: 0 };
@@ -37,7 +39,7 @@ export declare function useUrlState<T extends JSONCompatible>({ defaultState: T,
37
39
  export declare function useUrlState<T extends JSONCompatible>(defaultState: T, params?: Params): {
38
40
  urlState: T;
39
41
  setState: (value: Partial<T> | ((currState: T) => T)) => void;
40
- setUrl: (value?: Partial<T> | ((currState: T) => T)) => void;
42
+ setUrl: (value?: Partial<T> | ((currState: T) => T), options?: Params) => void;
41
43
  };
42
44
  type OldParams<T> = {
43
45
  defaultState: T;
@@ -1 +1 @@
1
- import t from"react";import{useInsertionEffect as e}from"../useInsertionEffect.mjs";import{useSharedState as n}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as o}from"../useUrlEncode/useUrlEncode.mjs";import{filterUnknownParamsClient as r}from"../utils.mjs";function s(s,c,i){const{parse:u,stringify:h}=o(s),{state:l,getState:m,setState:p}=n(s,(()=>i?.({parse:u})||s));e((()=>{const t=()=>{const t=u(r(s));p(t)};return window.addEventListener(a,t),()=>{window.removeEventListener(a,t)}}),[p]);const d=t.useRef([]),w=t.useCallback(((t,e)=>{const n="function"==typeof t?t(m()):t?{...m(),...t}:m(),o=h(n,function(t){const e=Object.keys(t),n=window.location.search,o=new URLSearchParams(n),r=new URLSearchParams;return o.forEach(((t,n)=>!e.includes(n)&&r.set(n,t))),r}(s)),r=`${window.location.pathname}${o.length?"?":""}${o}${window.location.hash}`;if(r===`${window.location.pathname}${window.location.search}${window.location.hash}`)return;let a;p(n);const{replace:i,...u}=e||{};d.current.push([i?"replace":"push",r,u]),1===d.current.length&&queueMicrotask((()=>{for(;d.current.length;){const t=d.current.shift();t&&t?.[1]!==a?.[1]&&(a=t)}const[t,e,n]=a||{};a=void 0,t&&c[t](e,n)}))}),[c,h,m]);return{updateState:p,updateUrl:w,state:l,getState:m}}const a="popstate";export{s as useUrlStateBase};
1
+ import t from"react";import{useInsertionEffect as e}from"../useInsertionEffect.mjs";import{useSharedState as n}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as o}from"../useUrlEncode/useUrlEncode.mjs";import{filterUnknownParamsClient as r}from"../utils.mjs";function s(s,c,i){const{parse:u,stringify:l}=o(s),{state:p,getState:d,setState:h}=n(s,(()=>i?.({parse:u})||s));e((()=>{const t=()=>{const t=u(r(s));h(t)};return window.addEventListener(a,t),()=>{window.removeEventListener(a,t)}}),[h]);const m=t.useRef([]),w=t.useCallback(((t,e)=>{const n="function"==typeof t?t(d()):t?{...d(),...t}:d(),o=l(n,function(t){const e=Object.keys(t),n=window.location.search,o=new URLSearchParams(n),r=new URLSearchParams;return o.forEach(((t,n)=>!e.includes(n)&&r.set(n,t))),r}(s)),r=`${window.location.pathname}${o.length?"?":""}${o}${window.location.hash}`;if(r===`${window.location.pathname}${window.location.search}${window.location.hash}`)return;let a;h(n);const i=e?.replace;delete e?.replace,m.current.push([i?"replace":"push",r,e]),1===m.current.length&&queueMicrotask((()=>{for(;m.current.length;){const t=m.current.shift();t&&t?.[1]!==a?.[1]&&(a=t)}const[t,e,n]=a||{};a=void 0,t&&c[t](e,n)}))}),[c,l,d]);return{updateState:h,updateUrl:w,state:p,getState:d}}const a="popstate";export{s as useUrlStateBase};
package/dist/utils.mjs CHANGED
@@ -1 +1 @@
1
- const t=t=>{const n=typeof t,e=null===t,r=Array.isArray(t),o=t instanceof Date;return(e?"null":o&&"date")||r&&"array"||!e&&!o&&!r&&"object"===n&&"object"||n},n=()=>"undefined"==typeof window,e=t=>new URLSearchParams("string"==typeof t?r(t):t?.toString?.()||""),r=t=>t.split("?")?.[1]||t||"",o=(t,n)=>JSON.stringify(t)===JSON.stringify(n);function i(t){const n=new URLSearchParams;return s(t,[...new URLSearchParams(window.location.search).entries()]).forEach((([t,e])=>n.set(t,e))),n.toString()}function c(t,n){return Object.fromEntries(s(t,Object.entries(n||{})))}function s(t,n){const e=Object.keys(t);return n.filter((([t])=>e.includes(t))).map((([t,n])=>[t.replaceAll("+"," "),n]))}function a(t,n){const e=Object.assign({},t);return Object.entries(t).forEach((([r])=>{const o=r,i=void 0!==n[o];e[o]=i?n[o]:t[o]})),e}const l={push:t=>{window&&window.history.pushState(null,"",t)},replace:t=>{window&&window.history.replaceState(null,"",t)}};export{a as assignValue,c as filterUnknownParams,i as filterUnknownParamsClient,e as getParams,o as isEqual,n as isSSR,l as routerHistory,t as typeOf};
1
+ const t=t=>{const n=typeof t,e=null===t,r=Array.isArray(t),o=t instanceof Date;return(e?"null":o&&"date")||r&&"array"||!e&&!o&&!r&&"object"===n&&"object"||n},n=()=>"undefined"==typeof window,e=t=>new URLSearchParams("string"==typeof t?r(t):t?.toString?.()||""),r=t=>t.split("?")?.[1]||t||"",o=(t,n)=>JSON.stringify(t)===JSON.stringify(n);function i(t){const n=new URLSearchParams;return s(t,[...new URLSearchParams(window.location.search).entries()]).forEach((([t,e])=>n.set(t,e))),n.toString()}function c(t,n){return Object.fromEntries(s(t,Object.entries(n||{})))}function s(t,n){const e=Object.keys(t);return n.filter((([t])=>e.includes(t))).map((([t,n])=>[t.replace(/\+/g," "),n]))}function a(t,n){const e=Object.assign({},t);return Object.entries(t).forEach((([r])=>{const o=r,i=void 0!==n[o];e[o]=i?n[o]:t[o]})),e}const u={push:t=>{window&&window.history.pushState(null,"",t)},replace:t=>{window&&window.history.replaceState(null,"",t)}};export{a as assignValue,c as filterUnknownParams,i as filterUnknownParamsClient,e as getParams,o as isEqual,n as isSSR,u as routerHistory,t as typeOf};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "state-in-url",
3
- "version": "4.0.7",
3
+ "version": "4.0.9",
4
4
  "description": "Easily share complex state objects between unrelated React components, preserve types and structure, with TS validation. Deep links and url state synchronization wthout any hasssle or boilerplate.",
5
5
  "homepage": "https://state-in-url.dev",
6
6
  "repository": {