@aigamo/route-sphere 0.0.1-alpha.66
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/LICENSE +22 -0
- package/README.md +84 -0
- package/dist/components/IStateAccessor.d.ts +7 -0
- package/dist/components/IStateCodec.d.ts +7 -0
- package/dist/components/IStateHandlerOptions.d.ts +13 -0
- package/dist/components/ScrollToTop.d.ts +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/useLocalStorageState.d.ts +2 -0
- package/dist/components/useStateHandler.d.ts +4 -0
- package/dist/helpers/includesAny.d.ts +1 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/index.cjs.js +2 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.es.js +62 -0
- package/dist/index.es.js.map +1 -0
- package/dist/react-router/index.d.ts +1 -0
- package/dist/react-router/useLocationState.d.ts +3 -0
- package/dist/react-router.cjs.js +2 -0
- package/dist/react-router.cjs.js.map +1 -0
- package/dist/react-router.d.ts +2 -0
- package/dist/react-router.es.js +57 -0
- package/dist/react-router.es.js.map +1 -0
- package/dist/stores/IStateStore.d.ts +10 -0
- package/dist/stores/StateChangeEvent.d.ts +6 -0
- package/dist/stores/StateRestoreEvent.d.ts +3 -0
- package/dist/stores/StateSaveEvent.d.ts +3 -0
- package/dist/stores/index.d.ts +4 -0
- package/dist/tanstack-router/index.d.ts +1 -0
- package/dist/tanstack-router/useLocationState.d.ts +3 -0
- package/dist/tanstack-router.cjs.js +2 -0
- package/dist/tanstack-router.cjs.js.map +1 -0
- package/dist/tanstack-router.d.ts +2 -0
- package/dist/tanstack-router.es.js +59 -0
- package/dist/tanstack-router.es.js.map +1 -0
- package/dist/useStateHandler-Dbkejxjm.js +41 -0
- package/dist/useStateHandler-Dbkejxjm.js.map +1 -0
- package/dist/useStateHandler-Ff_CnvN8.cjs +2 -0
- package/dist/useStateHandler-Ff_CnvN8.cjs.map +1 -0
- package/package.json +111 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 VocaDB Devgroup
|
|
4
|
+
Copyright (c) 2023 Aigamo
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Route Sphere
|
|
2
|
+
|
|
3
|
+
Sync query parameters with a MobX store and React Router.
|
|
4
|
+
|
|
5
|
+
This was originally developed in [VocaDB/vocadb#965](https://github.com/VocaDB/vocadb/issues/965) as a part of VocaDB.
|
|
6
|
+
|
|
7
|
+
NOTE: This is an independent fork of VocaDB/route-sphere.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- [React](https://github.com/facebook/react)
|
|
12
|
+
- [React Router](https://github.com/remix-run/react-router) (v6)
|
|
13
|
+
- [MobX](https://github.com/mobxjs/mobx)
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
`yarn add @aigamo/route-sphere` or `npm i @aigamo/route-sphere`
|
|
18
|
+
|
|
19
|
+
## How it works
|
|
20
|
+
|
|
21
|
+
There are three custom hooks, depending on the use case: `useStoreWithUpdateResults`, `useStoreWithPagination` and `useStoreWithRouteParams`. These hooks are defined as below:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const useStoreWithUpdateResults = <TRouteParams>(store: StoreWithUpdateResults<TRouteParams>): void;
|
|
25
|
+
const useStoreWithPagination = <TRouteParams>(store: StoreWithPagination<TRouteParams>): void;
|
|
26
|
+
const useStoreWithRouteParams = <TRouteParams>(store: StoreWithRouteParams<TRouteParams>): void;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## useStoreWithUpdateResults hook
|
|
30
|
+
|
|
31
|
+
The `useStoreWithUpdateResults` updates search results whenever the `routeParams` property changes. The `StoreWithUpdateResult` interface is defined as below:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
interface StoreWithUpdateResults<TRouteParams>
|
|
35
|
+
extends StoreWithRouteParams<TRouteParams> {
|
|
36
|
+
readonly clearResultsByQueryKeys: (keyof TRouteParams)[];
|
|
37
|
+
/** Called when search results should be cleared. */
|
|
38
|
+
onClearResults?: () => void;
|
|
39
|
+
updateResults(clearResults: boolean): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The `useStoreWithUpdateResults` hook determines if search results should be cleared by comparing the current and previous values. If that's the case, the `onClearResults` callback is called.
|
|
44
|
+
|
|
45
|
+
## useStoreWithPagination hook
|
|
46
|
+
|
|
47
|
+
The `useStoreWithPagination` hook is a helper hook that is composed of the `useStoreWithUpdateResults` and `useStoreWithRouteParams` hooks. The `StoreWithPagination` interface is defined as below:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
interface StoreWithPagination<TRouteParams>
|
|
51
|
+
extends StoreWithUpdateResults<TRouteParams> {
|
|
52
|
+
/** Called when search results should be cleared. */
|
|
53
|
+
onClearResults(): void;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## useStoreWithRouteParams hook
|
|
58
|
+
|
|
59
|
+
The `useStoreWithRouteParams` hook updates a store that implements the `StoreWithRouteParams` interface when a route changes, and vice versa. The `StoreWithRouteParams` interface is defined as below:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
interface StoreWithRouteParams<TRouteParams> {
|
|
63
|
+
routeParams: TRouteParams;
|
|
64
|
+
validateRouteParams(data: any): data is TRouteParams;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The `validateRouteParams` function validates route params and should return `true` if and only if the passed data is valid. Validation happens every time [`location`](https://github.com/ycanardeau/route-sphere/blob/fd53c1324df12e9bbc3c1495136ce97bb9da0377/src/components/useStoreWithRouteParams.tsx#L25) (not URL) changes, which means, when the page is first loaded, when the back/forward buttons on the browser are clicked, and when the page is navigated to a new location programmatically by using the `useNavigate` hook. Note that the `<Link>` component uses the `useNavigate` hook internally.
|
|
69
|
+
|
|
70
|
+
The `routeParams` property gets and sets the state of the store.
|
|
71
|
+
|
|
72
|
+
> Reactions should be independent: Does your code rely on some other reaction having to run first? If that is the case, you probably either violated the first rule, or the new reaction you are about to create should be merged into the one it is depending upon. MobX does not guarantee the order in which reactions will be run.
|
|
73
|
+
|
|
74
|
+
Source: [Running side effects with reactions · MobX](https://mobx.js.org/reactions.html)
|
|
75
|
+
|
|
76
|
+
## How to generate schema
|
|
77
|
+
|
|
78
|
+
We use JSON schema to validate route params. Use `typescript-json-schema project/directory/tsconfig.json TYPE` to generate schema from a TypeScript type. For more information, see [YousefED/typescript-json-schema](https://github.com/YousefED/typescript-json-schema).
|
|
79
|
+
|
|
80
|
+
## References
|
|
81
|
+
|
|
82
|
+
- [Feat/163 update URL on search page by ycanardeau · Pull Request #965 · VocaDB/vocadb](https://github.com/VocaDB/vocadb/pull/965)
|
|
83
|
+
- [The complete guide to publishing a React package to npm - LogRocket Blog](https://blog.logrocket.com/the-complete-guide-to-publishing-a-react-package-to-npm/)
|
|
84
|
+
- [How to publish packages to npm (the way the industry does things) | Zell Liew](https://zellwk.com/blog/publish-to-npm/)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { StateChangeEvent } from '../stores/StateChangeEvent';
|
|
2
|
+
import { StateRestoreEvent } from '../stores/StateRestoreEvent';
|
|
3
|
+
import { StateSaveEvent } from '../stores/StateSaveEvent';
|
|
4
|
+
export interface IRestoreStateOptions<TState> {
|
|
5
|
+
onStateRestore?: (event: StateRestoreEvent<TState>) => void;
|
|
6
|
+
}
|
|
7
|
+
export interface IHandleStateChangeOptions<TState> {
|
|
8
|
+
onStateChange?: (event: StateChangeEvent<TState>) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface ISaveStateOptions<TState> {
|
|
11
|
+
onStateSave?: (event: StateSaveEvent<TState>) => void;
|
|
12
|
+
}
|
|
13
|
+
export type IStateHandlerOptions<TState> = IRestoreStateOptions<TState> & IHandleStateChangeOptions<TState> & ISaveStateOptions<TState>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ScrollToTop: () => null;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { IStateAccessor } from './IStateAccessor';
|
|
2
|
+
import { IStateCodec } from './IStateCodec';
|
|
3
|
+
import { IStateHandlerOptions } from './IStateHandlerOptions';
|
|
4
|
+
export declare const useStateHandler: <TState>(codec: IStateCodec<TState>, validator: (state: unknown) => state is TState, accessor: IStateAccessor<TState>, options: IStateHandlerOptions<TState>) => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const includesAny: <T>(array: T[], values: T[]) => boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './includesAny';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("react"),n=require("react-router-dom"),c=require("./useStateHandler-Ff_CnvN8.cjs"),l=()=>{const e=n.useLocation();return a.useLayoutEffect(()=>{window.scrollTo(0,0)},[e]),null},u=e=>a.useCallback(()=>{try{return JSON.parse(window.localStorage.getItem(e)??JSON.stringify({}))}catch(t){return console.error(t),{}}},[e]),S=e=>a.useCallback(t=>{window.localStorage.setItem(e,JSON.stringify(t))},[e]),i=e=>{const t=u(e),o=S(e);return a.useMemo(()=>({deserialize:t,serialize:o}),[t,o])},d=(e,t,o,r)=>{const s=i(e);c.useStateHandler(s,t,o,r)},g=e=>a.useCallback(()=>e.state,[e]),L=e=>a.useCallback(t=>{e.state=t},[e]),m=e=>{const t=g(e),o=L(e);return a.useMemo(()=>({get:t,set:o}),[t,o])},C=(e,t)=>{const o=m(t),r=a.useMemo(()=>({onStateRestore:t.onStateRestore,onStateChange:t.onStateChange,onStateSave:t.onStateSave}),[t]);d(e,t.validateState,o,r)},b=(e,t)=>t.some(o=>e.includes(o));exports.useStateHandler=c.useStateHandler;exports.ScrollToTop=l;exports.includesAny=b;exports.useLocalStorageState=C;
|
|
2
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/components/ScrollToTop.tsx","../src/components/useLocalStorageState.tsx","../src/helpers/includesAny.ts"],"sourcesContent":["import { useLayoutEffect } from 'react';\nimport { useLocation } from 'react-router-dom';\n\nexport const ScrollToTop = (): null => {\n\tconst location = useLocation();\n\n\tuseLayoutEffect(() => {\n\t\twindow.scrollTo(0, 0);\n\t}, [location]);\n\n\treturn null;\n};\n","import type { IStateAccessor } from '@/components/IStateAccessor';\nimport type { IStateCodec } from '@/components/IStateCodec';\nimport type { IStateHandlerOptions } from '@/components/IStateHandlerOptions';\nimport { useStateHandler } from '@/components/useStateHandler';\nimport type { IStateStore } from '@/stores/IStateStore';\nimport { useCallback, useMemo } from 'react';\n\nconst useLocalStorageStateDeserializer = (key: string): (() => unknown) => {\n\treturn useCallback((): unknown => {\n\t\ttry {\n\t\t\treturn JSON.parse(\n\t\t\t\twindow.localStorage.getItem(key) ?? JSON.stringify({}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconsole.error(error);\n\t\t\treturn {};\n\t\t}\n\t}, [key]);\n};\n\nconst useLocalStorageStateSerializer = <TState,>(\n\tkey: string,\n): ((state: TState) => void) => {\n\treturn useCallback(\n\t\t(state: TState): void => {\n\t\t\twindow.localStorage.setItem(key, JSON.stringify(state));\n\t\t},\n\t\t[key],\n\t);\n};\n\nconst useLocalStorageStateCodec = <TState,>(\n\tkey: string,\n): IStateCodec<TState> => {\n\tconst deserializer = useLocalStorageStateDeserializer(key);\n\tconst serializer = useLocalStorageStateSerializer(key);\n\tconst codec = useMemo(\n\t\t(): IStateCodec<TState> => ({\n\t\t\tdeserialize: deserializer,\n\t\t\tserialize: serializer,\n\t\t}),\n\t\t[deserializer, serializer],\n\t);\n\treturn codec;\n};\n\nconst useLocalStorageStateHandler = <TState,>(\n\tkey: string,\n\tvalidator: (state: unknown) => state is TState,\n\taccessor: IStateAccessor<TState>,\n\toptions: IStateHandlerOptions<TState>,\n): void => {\n\tconst codec = useLocalStorageStateCodec(key);\n\tuseStateHandler(codec, validator, accessor, options);\n};\n\nconst useLocalStorageStateGetter = <TState,>(\n\tstore: IStateStore<TState>,\n): (() => TState) => {\n\treturn useCallback((): TState => store.state, [store]);\n};\n\nconst useLocalStorageStateSetter = <TState,>(\n\tstore: IStateStore<TState>,\n): ((state: TState) => void) => {\n\treturn useCallback(\n\t\t(state: TState): void => {\n\t\t\tstore.state = state;\n\t\t},\n\t\t[store],\n\t);\n};\n\nconst useLocalStorageStateAccessor = <TState,>(\n\tstore: IStateStore<TState>,\n): IStateAccessor<TState> => {\n\tconst getter = useLocalStorageStateGetter(store);\n\tconst setter = useLocalStorageStateSetter(store);\n\tconst accessor = useMemo(\n\t\t(): IStateAccessor<TState> => ({ get: getter, set: setter }),\n\t\t[getter, setter],\n\t);\n\treturn accessor;\n};\n\nexport const useLocalStorageState = <TState,>(\n\tkey: string,\n\tstore: IStateStore<TState>,\n): void => {\n\tconst accessor = useLocalStorageStateAccessor(store);\n\tconst options = useMemo(\n\t\t() => ({\n\t\t\tonStateRestore: store.onStateRestore,\n\t\t\tonStateChange: store.onStateChange,\n\t\t\tonStateSave: store.onStateSave,\n\t\t}),\n\t\t[store],\n\t);\n\tuseLocalStorageStateHandler(key, store.validateState, accessor, options);\n};\n","export const includesAny = <T>(array: T[], values: T[]): boolean => {\n\treturn values.some((value) => array.includes(value));\n};\n"],"names":["ScrollToTop","location","useLocation","useLayoutEffect","useLocalStorageStateDeserializer","key","useCallback","error","useLocalStorageStateSerializer","state","useLocalStorageStateCodec","deserializer","serializer","useMemo","useLocalStorageStateHandler","validator","accessor","options","codec","useStateHandler","useLocalStorageStateGetter","store","useLocalStorageStateSetter","useLocalStorageStateAccessor","getter","setter","useLocalStorageState","includesAny","array","values","value"],"mappings":"mLAGaA,EAAc,IAAY,CACtC,MAAMC,EAAWC,EAAAA,YAAA,EAEjBC,OAAAA,EAAAA,gBAAgB,IAAM,CACrB,OAAO,SAAS,EAAG,CAAC,CACrB,EAAG,CAACF,CAAQ,CAAC,EAEN,IACR,ECJMG,EAAoCC,GAClCC,EAAAA,YAAY,IAAe,CACjC,GAAI,CACH,OAAO,KAAK,MACX,OAAO,aAAa,QAAQD,CAAG,GAAK,KAAK,UAAU,CAAA,CAAE,CAAA,CAEvD,OAASE,EAAO,CACf,eAAQ,MAAMA,CAAK,EACZ,CAAA,CACR,CACD,EAAG,CAACF,CAAG,CAAC,EAGHG,EACLH,GAEOC,EAAAA,YACLG,GAAwB,CACxB,OAAO,aAAa,QAAQJ,EAAK,KAAK,UAAUI,CAAK,CAAC,CACvD,EACA,CAACJ,CAAG,CAAA,EAIAK,EACLL,GACyB,CACzB,MAAMM,EAAeP,EAAiCC,CAAG,EACnDO,EAAaJ,EAA+BH,CAAG,EAQrD,OAPcQ,EAAAA,QACb,KAA4B,CAC3B,YAAaF,EACb,UAAWC,CAAA,GAEZ,CAACD,EAAcC,CAAU,CAAA,CAG3B,EAEME,EAA8B,CACnCT,EACAU,EACAC,EACAC,IACU,CACV,MAAMC,EAAQR,EAA0BL,CAAG,EAC3Cc,EAAAA,gBAAgBD,EAAOH,EAAWC,EAAUC,CAAO,CACpD,EAEMG,EACLC,GAEOf,EAAAA,YAAY,IAAce,EAAM,MAAO,CAACA,CAAK,CAAC,EAGhDC,EACLD,GAEOf,EAAAA,YACLG,GAAwB,CACxBY,EAAM,MAAQZ,CACf,EACA,CAACY,CAAK,CAAA,EAIFE,EACLF,GAC4B,CAC5B,MAAMG,EAASJ,EAA2BC,CAAK,EACzCI,EAASH,EAA2BD,CAAK,EAK/C,OAJiBR,EAAAA,QAChB,KAA+B,CAAE,IAAKW,EAAQ,IAAKC,CAAA,GACnD,CAACD,EAAQC,CAAM,CAAA,CAGjB,EAEaC,EAAuB,CACnCrB,EACAgB,IACU,CACV,MAAML,EAAWO,EAA6BF,CAAK,EAC7CJ,EAAUJ,EAAAA,QACf,KAAO,CACN,eAAgBQ,EAAM,eACtB,cAAeA,EAAM,cACrB,YAAaA,EAAM,WAAA,GAEpB,CAACA,CAAK,CAAA,EAEPP,EAA4BT,EAAKgB,EAAM,cAAeL,EAAUC,CAAO,CACxE,ECnGaU,EAAc,CAAIC,EAAYC,IACnCA,EAAO,KAAMC,GAAUF,EAAM,SAASE,CAAK,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.es.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useLayoutEffect as n, useMemo as c, useCallback as r } from "react";
|
|
2
|
+
import { useLocation as S } from "react-router-dom";
|
|
3
|
+
import { u as l } from "./useStateHandler-Dbkejxjm.js";
|
|
4
|
+
const C = () => {
|
|
5
|
+
const e = S();
|
|
6
|
+
return n(() => {
|
|
7
|
+
window.scrollTo(0, 0);
|
|
8
|
+
}, [e]), null;
|
|
9
|
+
}, i = (e) => r(() => {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(
|
|
12
|
+
window.localStorage.getItem(e) ?? JSON.stringify({})
|
|
13
|
+
);
|
|
14
|
+
} catch (t) {
|
|
15
|
+
return console.error(t), {};
|
|
16
|
+
}
|
|
17
|
+
}, [e]), u = (e) => r(
|
|
18
|
+
(t) => {
|
|
19
|
+
window.localStorage.setItem(e, JSON.stringify(t));
|
|
20
|
+
},
|
|
21
|
+
[e]
|
|
22
|
+
), g = (e) => {
|
|
23
|
+
const t = i(e), o = u(e);
|
|
24
|
+
return c(
|
|
25
|
+
() => ({
|
|
26
|
+
deserialize: t,
|
|
27
|
+
serialize: o
|
|
28
|
+
}),
|
|
29
|
+
[t, o]
|
|
30
|
+
);
|
|
31
|
+
}, d = (e, t, o, a) => {
|
|
32
|
+
const s = g(e);
|
|
33
|
+
l(s, t, o, a);
|
|
34
|
+
}, m = (e) => r(() => e.state, [e]), L = (e) => r(
|
|
35
|
+
(t) => {
|
|
36
|
+
e.state = t;
|
|
37
|
+
},
|
|
38
|
+
[e]
|
|
39
|
+
), f = (e) => {
|
|
40
|
+
const t = m(e), o = L(e);
|
|
41
|
+
return c(
|
|
42
|
+
() => ({ get: t, set: o }),
|
|
43
|
+
[t, o]
|
|
44
|
+
);
|
|
45
|
+
}, h = (e, t) => {
|
|
46
|
+
const o = f(t), a = c(
|
|
47
|
+
() => ({
|
|
48
|
+
onStateRestore: t.onStateRestore,
|
|
49
|
+
onStateChange: t.onStateChange,
|
|
50
|
+
onStateSave: t.onStateSave
|
|
51
|
+
}),
|
|
52
|
+
[t]
|
|
53
|
+
);
|
|
54
|
+
d(e, t.validateState, o, a);
|
|
55
|
+
}, v = (e, t) => t.some((o) => e.includes(o));
|
|
56
|
+
export {
|
|
57
|
+
C as ScrollToTop,
|
|
58
|
+
v as includesAny,
|
|
59
|
+
h as useLocalStorageState,
|
|
60
|
+
l as useStateHandler
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=index.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/components/ScrollToTop.tsx","../src/components/useLocalStorageState.tsx","../src/helpers/includesAny.ts"],"sourcesContent":["import { useLayoutEffect } from 'react';\nimport { useLocation } from 'react-router-dom';\n\nexport const ScrollToTop = (): null => {\n\tconst location = useLocation();\n\n\tuseLayoutEffect(() => {\n\t\twindow.scrollTo(0, 0);\n\t}, [location]);\n\n\treturn null;\n};\n","import type { IStateAccessor } from '@/components/IStateAccessor';\nimport type { IStateCodec } from '@/components/IStateCodec';\nimport type { IStateHandlerOptions } from '@/components/IStateHandlerOptions';\nimport { useStateHandler } from '@/components/useStateHandler';\nimport type { IStateStore } from '@/stores/IStateStore';\nimport { useCallback, useMemo } from 'react';\n\nconst useLocalStorageStateDeserializer = (key: string): (() => unknown) => {\n\treturn useCallback((): unknown => {\n\t\ttry {\n\t\t\treturn JSON.parse(\n\t\t\t\twindow.localStorage.getItem(key) ?? JSON.stringify({}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconsole.error(error);\n\t\t\treturn {};\n\t\t}\n\t}, [key]);\n};\n\nconst useLocalStorageStateSerializer = <TState,>(\n\tkey: string,\n): ((state: TState) => void) => {\n\treturn useCallback(\n\t\t(state: TState): void => {\n\t\t\twindow.localStorage.setItem(key, JSON.stringify(state));\n\t\t},\n\t\t[key],\n\t);\n};\n\nconst useLocalStorageStateCodec = <TState,>(\n\tkey: string,\n): IStateCodec<TState> => {\n\tconst deserializer = useLocalStorageStateDeserializer(key);\n\tconst serializer = useLocalStorageStateSerializer(key);\n\tconst codec = useMemo(\n\t\t(): IStateCodec<TState> => ({\n\t\t\tdeserialize: deserializer,\n\t\t\tserialize: serializer,\n\t\t}),\n\t\t[deserializer, serializer],\n\t);\n\treturn codec;\n};\n\nconst useLocalStorageStateHandler = <TState,>(\n\tkey: string,\n\tvalidator: (state: unknown) => state is TState,\n\taccessor: IStateAccessor<TState>,\n\toptions: IStateHandlerOptions<TState>,\n): void => {\n\tconst codec = useLocalStorageStateCodec(key);\n\tuseStateHandler(codec, validator, accessor, options);\n};\n\nconst useLocalStorageStateGetter = <TState,>(\n\tstore: IStateStore<TState>,\n): (() => TState) => {\n\treturn useCallback((): TState => store.state, [store]);\n};\n\nconst useLocalStorageStateSetter = <TState,>(\n\tstore: IStateStore<TState>,\n): ((state: TState) => void) => {\n\treturn useCallback(\n\t\t(state: TState): void => {\n\t\t\tstore.state = state;\n\t\t},\n\t\t[store],\n\t);\n};\n\nconst useLocalStorageStateAccessor = <TState,>(\n\tstore: IStateStore<TState>,\n): IStateAccessor<TState> => {\n\tconst getter = useLocalStorageStateGetter(store);\n\tconst setter = useLocalStorageStateSetter(store);\n\tconst accessor = useMemo(\n\t\t(): IStateAccessor<TState> => ({ get: getter, set: setter }),\n\t\t[getter, setter],\n\t);\n\treturn accessor;\n};\n\nexport const useLocalStorageState = <TState,>(\n\tkey: string,\n\tstore: IStateStore<TState>,\n): void => {\n\tconst accessor = useLocalStorageStateAccessor(store);\n\tconst options = useMemo(\n\t\t() => ({\n\t\t\tonStateRestore: store.onStateRestore,\n\t\t\tonStateChange: store.onStateChange,\n\t\t\tonStateSave: store.onStateSave,\n\t\t}),\n\t\t[store],\n\t);\n\tuseLocalStorageStateHandler(key, store.validateState, accessor, options);\n};\n","export const includesAny = <T>(array: T[], values: T[]): boolean => {\n\treturn values.some((value) => array.includes(value));\n};\n"],"names":["ScrollToTop","location","useLocation","useLayoutEffect","useLocalStorageStateDeserializer","key","useCallback","error","useLocalStorageStateSerializer","state","useLocalStorageStateCodec","deserializer","serializer","useMemo","useLocalStorageStateHandler","validator","accessor","options","codec","useStateHandler","useLocalStorageStateGetter","store","useLocalStorageStateSetter","useLocalStorageStateAccessor","getter","setter","useLocalStorageState","includesAny","array","values","value"],"mappings":";;;AAGO,MAAMA,IAAc,MAAY;AACtC,QAAMC,IAAWC,EAAA;AAEjB,SAAAC,EAAgB,MAAM;AACrB,WAAO,SAAS,GAAG,CAAC;AAAA,EACrB,GAAG,CAACF,CAAQ,CAAC,GAEN;AACR,GCJMG,IAAmC,CAACC,MAClCC,EAAY,MAAe;AACjC,MAAI;AACH,WAAO,KAAK;AAAA,MACX,OAAO,aAAa,QAAQD,CAAG,KAAK,KAAK,UAAU,CAAA,CAAE;AAAA,IAAA;AAAA,EAEvD,SAASE,GAAO;AACf,mBAAQ,MAAMA,CAAK,GACZ,CAAA;AAAA,EACR;AACD,GAAG,CAACF,CAAG,CAAC,GAGHG,IAAiC,CACtCH,MAEOC;AAAA,EACN,CAACG,MAAwB;AACxB,WAAO,aAAa,QAAQJ,GAAK,KAAK,UAAUI,CAAK,CAAC;AAAA,EACvD;AAAA,EACA,CAACJ,CAAG;AAAA,GAIAK,IAA4B,CACjCL,MACyB;AACzB,QAAMM,IAAeP,EAAiCC,CAAG,GACnDO,IAAaJ,EAA+BH,CAAG;AAQrD,SAPcQ;AAAA,IACb,OAA4B;AAAA,MAC3B,aAAaF;AAAA,MACb,WAAWC;AAAA,IAAA;AAAA,IAEZ,CAACD,GAAcC,CAAU;AAAA,EAAA;AAG3B,GAEME,IAA8B,CACnCT,GACAU,GACAC,GACAC,MACU;AACV,QAAMC,IAAQR,EAA0BL,CAAG;AAC3C,EAAAc,EAAgBD,GAAOH,GAAWC,GAAUC,CAAO;AACpD,GAEMG,IAA6B,CAClCC,MAEOf,EAAY,MAAce,EAAM,OAAO,CAACA,CAAK,CAAC,GAGhDC,IAA6B,CAClCD,MAEOf;AAAA,EACN,CAACG,MAAwB;AACxB,IAAAY,EAAM,QAAQZ;AAAA,EACf;AAAA,EACA,CAACY,CAAK;AAAA,GAIFE,IAA+B,CACpCF,MAC4B;AAC5B,QAAMG,IAASJ,EAA2BC,CAAK,GACzCI,IAASH,EAA2BD,CAAK;AAK/C,SAJiBR;AAAA,IAChB,OAA+B,EAAE,KAAKW,GAAQ,KAAKC,EAAA;AAAA,IACnD,CAACD,GAAQC,CAAM;AAAA,EAAA;AAGjB,GAEaC,IAAuB,CACnCrB,GACAgB,MACU;AACV,QAAML,IAAWO,EAA6BF,CAAK,GAC7CJ,IAAUJ;AAAA,IACf,OAAO;AAAA,MACN,gBAAgBQ,EAAM;AAAA,MACtB,eAAeA,EAAM;AAAA,MACrB,aAAaA,EAAM;AAAA,IAAA;AAAA,IAEpB,CAACA,CAAK;AAAA,EAAA;AAEP,EAAAP,EAA4BT,GAAKgB,EAAM,eAAeL,GAAUC,CAAO;AACxE,GCnGaU,IAAc,CAAIC,GAAYC,MACnCA,EAAO,KAAK,CAACC,MAAUF,EAAM,SAASE,CAAK,CAAC;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useLocationState';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./useStateHandler-Ff_CnvN8.cjs"),c=require("qs"),o=require("react"),n=require("react-router-dom"),i=()=>{const e=n.useLocation();return o.useCallback(()=>c.parse(e.search.slice(1)),[e])},u=()=>{const e=n.useNavigate();return o.useCallback(async t=>{const a=`?${c.stringify(t)}`;await e(a)},[e])},l=()=>{const e=i(),t=u();return o.useMemo(()=>({deserialize:e,serialize:t}),[e,t])},S=(e,t,a)=>{const s=l();r.useStateHandler(s,e,t,a)},d=e=>o.useCallback(()=>e.state,[e]),L=e=>o.useCallback(t=>{e.state=t},[e]),g=e=>{const t=d(e),a=L(e);return o.useMemo(()=>({get:t,set:a}),[t,a])},C=e=>{const t=g(e),a=o.useMemo(()=>({onStateRestore:e.onStateRestore,onStateChange:e.onStateChange,onStateSave:e.onStateSave}),[e]);S(e.validateState,t,a)};exports.useLocationState=C;
|
|
2
|
+
//# sourceMappingURL=react-router.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-router.cjs.js","sources":["../src/react-router/useLocationState.tsx"],"sourcesContent":["import type { IStateAccessor } from '@/components/IStateAccessor';\nimport type { IStateCodec } from '@/components/IStateCodec';\nimport type { IStateHandlerOptions } from '@/components/IStateHandlerOptions';\nimport { useStateHandler } from '@/components/useStateHandler';\nimport type { IStateStore } from '@/stores/IStateStore';\nimport { type ParsedQs, parse, stringify } from 'qs';\nimport { useCallback, useMemo } from 'react';\nimport { useLocation, useNavigate } from 'react-router-dom';\n\nconst useLocationStateDeserializer = (): (() => ParsedQs) => {\n\tconst location = useLocation();\n\n\t// Pass `location` as deps instead of `location.search`.\n\treturn useCallback(\n\t\t(): ParsedQs => parse(location.search.slice(1)),\n\t\t[location],\n\t);\n};\n\nconst useLocationStateSerializer = <TState,>(): ((state: TState) => void) => {\n\tconst navigate = useNavigate();\n\n\treturn useCallback(\n\t\tasync (state: TState): Promise<void> => {\n\t\t\tconst newUrl = `?${stringify(state)}`;\n\t\t\tawait navigate(newUrl);\n\t\t},\n\t\t[navigate],\n\t);\n};\n\nconst useLocationStateCodec = <TState,>(): IStateCodec<TState> => {\n\tconst deserializer = useLocationStateDeserializer();\n\tconst serializer = useLocationStateSerializer();\n\tconst codec = useMemo(\n\t\t(): IStateCodec<TState> => ({\n\t\t\tdeserialize: deserializer,\n\t\t\tserialize: serializer,\n\t\t}),\n\t\t[deserializer, serializer],\n\t);\n\treturn codec;\n};\n\n/** Updates a store that implements the {@link LocationStateStore} interface when a route changes, and vice versa. */\nconst useLocationStateHandler = <TState,>(\n\tvalidator: (state: unknown) => state is TState,\n\taccessor: IStateAccessor<TState>,\n\toptions: IStateHandlerOptions<TState>,\n): void => {\n\tconst codec = useLocationStateCodec();\n\tuseStateHandler(codec, validator, accessor, options);\n};\n\nconst useLocationStateGetter = <TState,>(\n\tstore: IStateStore<TState>,\n): (() => TState) => {\n\treturn useCallback((): TState => store.state, [store]);\n};\n\nconst useLocationStateSetter = <TState,>(\n\tstore: IStateStore<TState>,\n): ((state: TState) => void) => {\n\treturn useCallback(\n\t\t(state: TState): void => {\n\t\t\tstore.state = state;\n\t\t},\n\t\t[store],\n\t);\n};\n\nconst useLocationStateAccessor = <TState,>(\n\tstore: IStateStore<TState>,\n): IStateAccessor<TState> => {\n\tconst getter = useLocationStateGetter(store);\n\tconst setter = useLocationStateSetter(store);\n\tconst accessor = useMemo(\n\t\t(): IStateAccessor<TState> => ({ get: getter, set: setter }),\n\t\t[getter, setter],\n\t);\n\treturn accessor;\n};\n\n/** Updates a store that implements the {@link LocationStateStore} interface when a route changes, and vice versa. */\nexport const useLocationState = <TState,>(store: IStateStore<TState>): void => {\n\tconst accessor = useLocationStateAccessor(store);\n\tconst options = useMemo(\n\t\t() => ({\n\t\t\tonStateRestore: store.onStateRestore,\n\t\t\tonStateChange: store.onStateChange,\n\t\t\tonStateSave: store.onStateSave,\n\t\t}),\n\t\t[store],\n\t);\n\tuseLocationStateHandler(store.validateState, accessor, options);\n};\n"],"names":["useLocationStateDeserializer","location","useLocation","useCallback","parse","useLocationStateSerializer","navigate","useNavigate","state","newUrl","stringify","useLocationStateCodec","deserializer","serializer","useMemo","useLocationStateHandler","validator","accessor","options","codec","useStateHandler","useLocationStateGetter","store","useLocationStateSetter","useLocationStateAccessor","getter","setter","useLocationState"],"mappings":"mMASMA,EAA+B,IAAwB,CAC5D,MAAMC,EAAWC,EAAAA,YAAA,EAGjB,OAAOC,EAAAA,YACN,IAAgBC,EAAAA,MAAMH,EAAS,OAAO,MAAM,CAAC,CAAC,EAC9C,CAACA,CAAQ,CAAA,CAEX,EAEMI,EAA6B,IAA0C,CAC5E,MAAMC,EAAWC,EAAAA,YAAA,EAEjB,OAAOJ,EAAAA,YACN,MAAOK,GAAiC,CACvC,MAAMC,EAAS,IAAIC,EAAAA,UAAUF,CAAK,CAAC,GACnC,MAAMF,EAASG,CAAM,CACtB,EACA,CAACH,CAAQ,CAAA,CAEX,EAEMK,EAAwB,IAAoC,CACjE,MAAMC,EAAeZ,EAAA,EACfa,EAAaR,EAAA,EAQnB,OAPcS,EAAAA,QACb,KAA4B,CAC3B,YAAaF,EACb,UAAWC,CAAA,GAEZ,CAACD,EAAcC,CAAU,CAAA,CAG3B,EAGME,EAA0B,CAC/BC,EACAC,EACAC,IACU,CACV,MAAMC,EAAQR,EAAA,EACdS,EAAAA,gBAAgBD,EAAOH,EAAWC,EAAUC,CAAO,CACpD,EAEMG,EACLC,GAEOnB,EAAAA,YAAY,IAAcmB,EAAM,MAAO,CAACA,CAAK,CAAC,EAGhDC,EACLD,GAEOnB,EAAAA,YACLK,GAAwB,CACxBc,EAAM,MAAQd,CACf,EACA,CAACc,CAAK,CAAA,EAIFE,EACLF,GAC4B,CAC5B,MAAMG,EAASJ,EAAuBC,CAAK,EACrCI,EAASH,EAAuBD,CAAK,EAK3C,OAJiBR,EAAAA,QAChB,KAA+B,CAAE,IAAKW,EAAQ,IAAKC,CAAA,GACnD,CAACD,EAAQC,CAAM,CAAA,CAGjB,EAGaC,EAA6BL,GAAqC,CAC9E,MAAML,EAAWO,EAAyBF,CAAK,EACzCJ,EAAUJ,EAAAA,QACf,KAAO,CACN,eAAgBQ,EAAM,eACtB,cAAeA,EAAM,cACrB,YAAaA,EAAM,WAAA,GAEpB,CAACA,CAAK,CAAA,EAEPP,EAAwBO,EAAM,cAAeL,EAAUC,CAAO,CAC/D"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { u as c } from "./useStateHandler-Dbkejxjm.js";
|
|
2
|
+
import { parse as r, stringify as i } from "qs";
|
|
3
|
+
import { useMemo as n, useCallback as a } from "react";
|
|
4
|
+
import { useLocation as u, useNavigate as S } from "react-router-dom";
|
|
5
|
+
const l = () => {
|
|
6
|
+
const t = u();
|
|
7
|
+
return a(
|
|
8
|
+
() => r(t.search.slice(1)),
|
|
9
|
+
[t]
|
|
10
|
+
);
|
|
11
|
+
}, d = () => {
|
|
12
|
+
const t = S();
|
|
13
|
+
return a(
|
|
14
|
+
async (e) => {
|
|
15
|
+
const o = `?${i(e)}`;
|
|
16
|
+
await t(o);
|
|
17
|
+
},
|
|
18
|
+
[t]
|
|
19
|
+
);
|
|
20
|
+
}, m = () => {
|
|
21
|
+
const t = l(), e = d();
|
|
22
|
+
return n(
|
|
23
|
+
() => ({
|
|
24
|
+
deserialize: t,
|
|
25
|
+
serialize: e
|
|
26
|
+
}),
|
|
27
|
+
[t, e]
|
|
28
|
+
);
|
|
29
|
+
}, L = (t, e, o) => {
|
|
30
|
+
const s = m();
|
|
31
|
+
c(s, t, e, o);
|
|
32
|
+
}, g = (t) => a(() => t.state, [t]), p = (t) => a(
|
|
33
|
+
(e) => {
|
|
34
|
+
t.state = e;
|
|
35
|
+
},
|
|
36
|
+
[t]
|
|
37
|
+
), z = (t) => {
|
|
38
|
+
const e = g(t), o = p(t);
|
|
39
|
+
return n(
|
|
40
|
+
() => ({ get: e, set: o }),
|
|
41
|
+
[e, o]
|
|
42
|
+
);
|
|
43
|
+
}, w = (t) => {
|
|
44
|
+
const e = z(t), o = n(
|
|
45
|
+
() => ({
|
|
46
|
+
onStateRestore: t.onStateRestore,
|
|
47
|
+
onStateChange: t.onStateChange,
|
|
48
|
+
onStateSave: t.onStateSave
|
|
49
|
+
}),
|
|
50
|
+
[t]
|
|
51
|
+
);
|
|
52
|
+
L(t.validateState, e, o);
|
|
53
|
+
};
|
|
54
|
+
export {
|
|
55
|
+
w as useLocationState
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=react-router.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-router.es.js","sources":["../src/react-router/useLocationState.tsx"],"sourcesContent":["import type { IStateAccessor } from '@/components/IStateAccessor';\nimport type { IStateCodec } from '@/components/IStateCodec';\nimport type { IStateHandlerOptions } from '@/components/IStateHandlerOptions';\nimport { useStateHandler } from '@/components/useStateHandler';\nimport type { IStateStore } from '@/stores/IStateStore';\nimport { type ParsedQs, parse, stringify } from 'qs';\nimport { useCallback, useMemo } from 'react';\nimport { useLocation, useNavigate } from 'react-router-dom';\n\nconst useLocationStateDeserializer = (): (() => ParsedQs) => {\n\tconst location = useLocation();\n\n\t// Pass `location` as deps instead of `location.search`.\n\treturn useCallback(\n\t\t(): ParsedQs => parse(location.search.slice(1)),\n\t\t[location],\n\t);\n};\n\nconst useLocationStateSerializer = <TState,>(): ((state: TState) => void) => {\n\tconst navigate = useNavigate();\n\n\treturn useCallback(\n\t\tasync (state: TState): Promise<void> => {\n\t\t\tconst newUrl = `?${stringify(state)}`;\n\t\t\tawait navigate(newUrl);\n\t\t},\n\t\t[navigate],\n\t);\n};\n\nconst useLocationStateCodec = <TState,>(): IStateCodec<TState> => {\n\tconst deserializer = useLocationStateDeserializer();\n\tconst serializer = useLocationStateSerializer();\n\tconst codec = useMemo(\n\t\t(): IStateCodec<TState> => ({\n\t\t\tdeserialize: deserializer,\n\t\t\tserialize: serializer,\n\t\t}),\n\t\t[deserializer, serializer],\n\t);\n\treturn codec;\n};\n\n/** Updates a store that implements the {@link LocationStateStore} interface when a route changes, and vice versa. */\nconst useLocationStateHandler = <TState,>(\n\tvalidator: (state: unknown) => state is TState,\n\taccessor: IStateAccessor<TState>,\n\toptions: IStateHandlerOptions<TState>,\n): void => {\n\tconst codec = useLocationStateCodec();\n\tuseStateHandler(codec, validator, accessor, options);\n};\n\nconst useLocationStateGetter = <TState,>(\n\tstore: IStateStore<TState>,\n): (() => TState) => {\n\treturn useCallback((): TState => store.state, [store]);\n};\n\nconst useLocationStateSetter = <TState,>(\n\tstore: IStateStore<TState>,\n): ((state: TState) => void) => {\n\treturn useCallback(\n\t\t(state: TState): void => {\n\t\t\tstore.state = state;\n\t\t},\n\t\t[store],\n\t);\n};\n\nconst useLocationStateAccessor = <TState,>(\n\tstore: IStateStore<TState>,\n): IStateAccessor<TState> => {\n\tconst getter = useLocationStateGetter(store);\n\tconst setter = useLocationStateSetter(store);\n\tconst accessor = useMemo(\n\t\t(): IStateAccessor<TState> => ({ get: getter, set: setter }),\n\t\t[getter, setter],\n\t);\n\treturn accessor;\n};\n\n/** Updates a store that implements the {@link LocationStateStore} interface when a route changes, and vice versa. */\nexport const useLocationState = <TState,>(store: IStateStore<TState>): void => {\n\tconst accessor = useLocationStateAccessor(store);\n\tconst options = useMemo(\n\t\t() => ({\n\t\t\tonStateRestore: store.onStateRestore,\n\t\t\tonStateChange: store.onStateChange,\n\t\t\tonStateSave: store.onStateSave,\n\t\t}),\n\t\t[store],\n\t);\n\tuseLocationStateHandler(store.validateState, accessor, options);\n};\n"],"names":["useLocationStateDeserializer","location","useLocation","useCallback","parse","useLocationStateSerializer","navigate","useNavigate","state","newUrl","stringify","useLocationStateCodec","deserializer","serializer","useMemo","useLocationStateHandler","validator","accessor","options","codec","useStateHandler","useLocationStateGetter","store","useLocationStateSetter","useLocationStateAccessor","getter","setter","useLocationState"],"mappings":";;;;AASA,MAAMA,IAA+B,MAAwB;AAC5D,QAAMC,IAAWC,EAAA;AAGjB,SAAOC;AAAA,IACN,MAAgBC,EAAMH,EAAS,OAAO,MAAM,CAAC,CAAC;AAAA,IAC9C,CAACA,CAAQ;AAAA,EAAA;AAEX,GAEMI,IAA6B,MAA0C;AAC5E,QAAMC,IAAWC,EAAA;AAEjB,SAAOJ;AAAA,IACN,OAAOK,MAAiC;AACvC,YAAMC,IAAS,IAAIC,EAAUF,CAAK,CAAC;AACnC,YAAMF,EAASG,CAAM;AAAA,IACtB;AAAA,IACA,CAACH,CAAQ;AAAA,EAAA;AAEX,GAEMK,IAAwB,MAAoC;AACjE,QAAMC,IAAeZ,EAAA,GACfa,IAAaR,EAAA;AAQnB,SAPcS;AAAA,IACb,OAA4B;AAAA,MAC3B,aAAaF;AAAA,MACb,WAAWC;AAAA,IAAA;AAAA,IAEZ,CAACD,GAAcC,CAAU;AAAA,EAAA;AAG3B,GAGME,IAA0B,CAC/BC,GACAC,GACAC,MACU;AACV,QAAMC,IAAQR,EAAA;AACd,EAAAS,EAAgBD,GAAOH,GAAWC,GAAUC,CAAO;AACpD,GAEMG,IAAyB,CAC9BC,MAEOnB,EAAY,MAAcmB,EAAM,OAAO,CAACA,CAAK,CAAC,GAGhDC,IAAyB,CAC9BD,MAEOnB;AAAA,EACN,CAACK,MAAwB;AACxB,IAAAc,EAAM,QAAQd;AAAA,EACf;AAAA,EACA,CAACc,CAAK;AAAA,GAIFE,IAA2B,CAChCF,MAC4B;AAC5B,QAAMG,IAASJ,EAAuBC,CAAK,GACrCI,IAASH,EAAuBD,CAAK;AAK3C,SAJiBR;AAAA,IAChB,OAA+B,EAAE,KAAKW,GAAQ,KAAKC,EAAA;AAAA,IACnD,CAACD,GAAQC,CAAM;AAAA,EAAA;AAGjB,GAGaC,IAAmB,CAAUL,MAAqC;AAC9E,QAAML,IAAWO,EAAyBF,CAAK,GACzCJ,IAAUJ;AAAA,IACf,OAAO;AAAA,MACN,gBAAgBQ,EAAM;AAAA,MACtB,eAAeA,EAAM;AAAA,MACrB,aAAaA,EAAM;AAAA,IAAA;AAAA,IAEpB,CAACA,CAAK;AAAA,EAAA;AAEP,EAAAP,EAAwBO,EAAM,eAAeL,GAAUC,CAAO;AAC/D;"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { StateChangeEvent } from './StateChangeEvent';
|
|
2
|
+
import { StateRestoreEvent } from './StateRestoreEvent';
|
|
3
|
+
import { StateSaveEvent } from './StateSaveEvent';
|
|
4
|
+
export interface IStateStore<TState> {
|
|
5
|
+
state: TState;
|
|
6
|
+
validateState(state: unknown): state is TState;
|
|
7
|
+
onStateRestore?(event: StateRestoreEvent<TState>): void;
|
|
8
|
+
onStateChange?(event: StateChangeEvent<TState>): void;
|
|
9
|
+
onStateSave?(event: StateSaveEvent<TState>): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useLocationState';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("./useStateHandler-Ff_CnvN8.cjs"),c=require("@tanstack/react-router"),r=require("qs"),o=require("react"),i=()=>{const e=c.useLocation();return o.useCallback(()=>r.parse(e.searchStr.slice(1)),[e])},u=()=>{const e=c.useNavigate();return o.useCallback(async t=>{await e({search:t})},[e])},S=()=>{const e=i(),t=u();return o.useMemo(()=>({deserialize:e,serialize:t}),[e,t])},l=(e,t,a)=>{const s=S();n.useStateHandler(s,e,t,a)},d=e=>o.useCallback(()=>e.state,[e]),L=e=>o.useCallback(t=>{e.state=t},[e]),g=e=>{const t=d(e),a=L(e);return o.useMemo(()=>({get:t,set:a}),[t,a])},C=e=>{const t=g(e),a=o.useMemo(()=>({onStateRestore:e.onStateRestore,onStateChange:e.onStateChange,onStateSave:e.onStateSave}),[e]);l(e.validateState,t,a)};exports.useLocationState=C;
|
|
2
|
+
//# sourceMappingURL=tanstack-router.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tanstack-router.cjs.js","sources":["../src/tanstack-router/useLocationState.tsx"],"sourcesContent":["import type { IStateAccessor } from '@/components/IStateAccessor';\nimport type { IStateCodec } from '@/components/IStateCodec';\nimport type { IStateHandlerOptions } from '@/components/IStateHandlerOptions';\nimport { useStateHandler } from '@/components/useStateHandler';\nimport type { IStateStore } from '@/stores/IStateStore';\nimport { useLocation, useNavigate } from '@tanstack/react-router';\nimport { type ParsedQs, parse } from 'qs';\nimport { useCallback, useMemo } from 'react';\n\nconst useLocationStateDeserializer = (): (() => ParsedQs) => {\n\tconst location = useLocation();\n\n\t// Pass `location` as deps instead of `location.search`.\n\treturn useCallback(\n\t\t(): ParsedQs => parse(location.searchStr.slice(1)),\n\t\t[location],\n\t);\n};\n\nconst useLocationStateSerializer = <TState,>(): ((state: TState) => void) => {\n\tconst navigate = useNavigate();\n\n\treturn useCallback(\n\t\tasync (state: TState): Promise<void> => {\n\t\t\tawait navigate({ search: state as any /* FIXME */ });\n\t\t},\n\t\t[navigate],\n\t);\n};\n\nconst useLocationStateCodec = <TState,>(): IStateCodec<TState> => {\n\tconst deserializer = useLocationStateDeserializer();\n\tconst serializer = useLocationStateSerializer();\n\tconst codec = useMemo(\n\t\t(): IStateCodec<TState> => ({\n\t\t\tdeserialize: deserializer,\n\t\t\tserialize: serializer,\n\t\t}),\n\t\t[deserializer, serializer],\n\t);\n\treturn codec;\n};\n\n/** Updates a store that implements the {@link LocationStateStore} interface when a route changes, and vice versa. */\nconst useLocationStateHandler = <TState,>(\n\tvalidator: (state: unknown) => state is TState,\n\taccessor: IStateAccessor<TState>,\n\toptions: IStateHandlerOptions<TState>,\n): void => {\n\tconst codec = useLocationStateCodec();\n\tuseStateHandler(codec, validator, accessor, options);\n};\n\nconst useLocationStateGetter = <TState,>(\n\tstore: IStateStore<TState>,\n): (() => TState) => {\n\treturn useCallback(() => store.state, [store]);\n};\n\nconst useLocationStateSetter = <TState,>(\n\tstore: IStateStore<TState>,\n): ((state: TState) => void) => {\n\treturn useCallback(\n\t\t(state: TState) => {\n\t\t\tstore.state = state;\n\t\t},\n\t\t[store],\n\t);\n};\n\nconst useLocationStateAccessor = <TState,>(\n\tstore: IStateStore<TState>,\n): IStateAccessor<TState> => {\n\tconst getter = useLocationStateGetter(store);\n\tconst setter = useLocationStateSetter(store);\n\tconst accessor = useMemo(\n\t\t(): IStateAccessor<TState> => ({ get: getter, set: setter }),\n\t\t[getter, setter],\n\t);\n\treturn accessor;\n};\n\n/** Updates a store that implements the {@link LocationStateStore} interface when a route changes, and vice versa. */\nexport const useLocationState = <TState,>(store: IStateStore<TState>): void => {\n\tconst accessor = useLocationStateAccessor(store);\n\tconst options = useMemo(\n\t\t() => ({\n\t\t\tonStateRestore: store.onStateRestore,\n\t\t\tonStateChange: store.onStateChange,\n\t\t\tonStateSave: store.onStateSave,\n\t\t}),\n\t\t[store],\n\t);\n\tuseLocationStateHandler(store.validateState, accessor, options);\n};\n"],"names":["useLocationStateDeserializer","location","useLocation","useCallback","parse","useLocationStateSerializer","navigate","useNavigate","state","useLocationStateCodec","deserializer","serializer","useMemo","useLocationStateHandler","validator","accessor","options","codec","useStateHandler","useLocationStateGetter","store","useLocationStateSetter","useLocationStateAccessor","getter","setter","useLocationState"],"mappings":"yMASMA,EAA+B,IAAwB,CAC5D,MAAMC,EAAWC,EAAAA,YAAA,EAGjB,OAAOC,EAAAA,YACN,IAAgBC,EAAAA,MAAMH,EAAS,UAAU,MAAM,CAAC,CAAC,EACjD,CAACA,CAAQ,CAAA,CAEX,EAEMI,EAA6B,IAA0C,CAC5E,MAAMC,EAAWC,EAAAA,YAAA,EAEjB,OAAOJ,EAAAA,YACN,MAAOK,GAAiC,CACvC,MAAMF,EAAS,CAAE,OAAQE,CAAA,CAA0B,CACpD,EACA,CAACF,CAAQ,CAAA,CAEX,EAEMG,EAAwB,IAAoC,CACjE,MAAMC,EAAeV,EAAA,EACfW,EAAaN,EAAA,EAQnB,OAPcO,EAAAA,QACb,KAA4B,CAC3B,YAAaF,EACb,UAAWC,CAAA,GAEZ,CAACD,EAAcC,CAAU,CAAA,CAG3B,EAGME,EAA0B,CAC/BC,EACAC,EACAC,IACU,CACV,MAAMC,EAAQR,EAAA,EACdS,EAAAA,gBAAgBD,EAAOH,EAAWC,EAAUC,CAAO,CACpD,EAEMG,EACLC,GAEOjB,EAAAA,YAAY,IAAMiB,EAAM,MAAO,CAACA,CAAK,CAAC,EAGxCC,EACLD,GAEOjB,EAAAA,YACLK,GAAkB,CAClBY,EAAM,MAAQZ,CACf,EACA,CAACY,CAAK,CAAA,EAIFE,EACLF,GAC4B,CAC5B,MAAMG,EAASJ,EAAuBC,CAAK,EACrCI,EAASH,EAAuBD,CAAK,EAK3C,OAJiBR,EAAAA,QAChB,KAA+B,CAAE,IAAKW,EAAQ,IAAKC,CAAA,GACnD,CAACD,EAAQC,CAAM,CAAA,CAGjB,EAGaC,EAA6BL,GAAqC,CAC9E,MAAML,EAAWO,EAAyBF,CAAK,EACzCJ,EAAUJ,EAAAA,QACf,KAAO,CACN,eAAgBQ,EAAM,eACtB,cAAeA,EAAM,cACrB,YAAaA,EAAM,WAAA,GAEpB,CAACA,CAAK,CAAA,EAEPP,EAAwBO,EAAM,cAAeL,EAAUC,CAAO,CAC/D"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { u as n } from "./useStateHandler-Dbkejxjm.js";
|
|
2
|
+
import { useLocation as r, useNavigate as i } from "@tanstack/react-router";
|
|
3
|
+
import { parse as S } from "qs";
|
|
4
|
+
import { useMemo as s, useCallback as a } from "react";
|
|
5
|
+
const u = () => {
|
|
6
|
+
const t = r();
|
|
7
|
+
return a(
|
|
8
|
+
() => S(t.searchStr.slice(1)),
|
|
9
|
+
[t]
|
|
10
|
+
);
|
|
11
|
+
}, l = () => {
|
|
12
|
+
const t = i();
|
|
13
|
+
return a(
|
|
14
|
+
async (e) => {
|
|
15
|
+
await t({
|
|
16
|
+
search: e
|
|
17
|
+
/* FIXME */
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
[t]
|
|
21
|
+
);
|
|
22
|
+
}, d = () => {
|
|
23
|
+
const t = u(), e = l();
|
|
24
|
+
return s(
|
|
25
|
+
() => ({
|
|
26
|
+
deserialize: t,
|
|
27
|
+
serialize: e
|
|
28
|
+
}),
|
|
29
|
+
[t, e]
|
|
30
|
+
);
|
|
31
|
+
}, m = (t, e, o) => {
|
|
32
|
+
const c = d();
|
|
33
|
+
n(c, t, e, o);
|
|
34
|
+
}, L = (t) => a(() => t.state, [t]), p = (t) => a(
|
|
35
|
+
(e) => {
|
|
36
|
+
t.state = e;
|
|
37
|
+
},
|
|
38
|
+
[t]
|
|
39
|
+
), g = (t) => {
|
|
40
|
+
const e = L(t), o = p(t);
|
|
41
|
+
return s(
|
|
42
|
+
() => ({ get: e, set: o }),
|
|
43
|
+
[e, o]
|
|
44
|
+
);
|
|
45
|
+
}, C = (t) => {
|
|
46
|
+
const e = g(t), o = s(
|
|
47
|
+
() => ({
|
|
48
|
+
onStateRestore: t.onStateRestore,
|
|
49
|
+
onStateChange: t.onStateChange,
|
|
50
|
+
onStateSave: t.onStateSave
|
|
51
|
+
}),
|
|
52
|
+
[t]
|
|
53
|
+
);
|
|
54
|
+
m(t.validateState, e, o);
|
|
55
|
+
};
|
|
56
|
+
export {
|
|
57
|
+
C as useLocationState
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=tanstack-router.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tanstack-router.es.js","sources":["../src/tanstack-router/useLocationState.tsx"],"sourcesContent":["import type { IStateAccessor } from '@/components/IStateAccessor';\nimport type { IStateCodec } from '@/components/IStateCodec';\nimport type { IStateHandlerOptions } from '@/components/IStateHandlerOptions';\nimport { useStateHandler } from '@/components/useStateHandler';\nimport type { IStateStore } from '@/stores/IStateStore';\nimport { useLocation, useNavigate } from '@tanstack/react-router';\nimport { type ParsedQs, parse } from 'qs';\nimport { useCallback, useMemo } from 'react';\n\nconst useLocationStateDeserializer = (): (() => ParsedQs) => {\n\tconst location = useLocation();\n\n\t// Pass `location` as deps instead of `location.search`.\n\treturn useCallback(\n\t\t(): ParsedQs => parse(location.searchStr.slice(1)),\n\t\t[location],\n\t);\n};\n\nconst useLocationStateSerializer = <TState,>(): ((state: TState) => void) => {\n\tconst navigate = useNavigate();\n\n\treturn useCallback(\n\t\tasync (state: TState): Promise<void> => {\n\t\t\tawait navigate({ search: state as any /* FIXME */ });\n\t\t},\n\t\t[navigate],\n\t);\n};\n\nconst useLocationStateCodec = <TState,>(): IStateCodec<TState> => {\n\tconst deserializer = useLocationStateDeserializer();\n\tconst serializer = useLocationStateSerializer();\n\tconst codec = useMemo(\n\t\t(): IStateCodec<TState> => ({\n\t\t\tdeserialize: deserializer,\n\t\t\tserialize: serializer,\n\t\t}),\n\t\t[deserializer, serializer],\n\t);\n\treturn codec;\n};\n\n/** Updates a store that implements the {@link LocationStateStore} interface when a route changes, and vice versa. */\nconst useLocationStateHandler = <TState,>(\n\tvalidator: (state: unknown) => state is TState,\n\taccessor: IStateAccessor<TState>,\n\toptions: IStateHandlerOptions<TState>,\n): void => {\n\tconst codec = useLocationStateCodec();\n\tuseStateHandler(codec, validator, accessor, options);\n};\n\nconst useLocationStateGetter = <TState,>(\n\tstore: IStateStore<TState>,\n): (() => TState) => {\n\treturn useCallback(() => store.state, [store]);\n};\n\nconst useLocationStateSetter = <TState,>(\n\tstore: IStateStore<TState>,\n): ((state: TState) => void) => {\n\treturn useCallback(\n\t\t(state: TState) => {\n\t\t\tstore.state = state;\n\t\t},\n\t\t[store],\n\t);\n};\n\nconst useLocationStateAccessor = <TState,>(\n\tstore: IStateStore<TState>,\n): IStateAccessor<TState> => {\n\tconst getter = useLocationStateGetter(store);\n\tconst setter = useLocationStateSetter(store);\n\tconst accessor = useMemo(\n\t\t(): IStateAccessor<TState> => ({ get: getter, set: setter }),\n\t\t[getter, setter],\n\t);\n\treturn accessor;\n};\n\n/** Updates a store that implements the {@link LocationStateStore} interface when a route changes, and vice versa. */\nexport const useLocationState = <TState,>(store: IStateStore<TState>): void => {\n\tconst accessor = useLocationStateAccessor(store);\n\tconst options = useMemo(\n\t\t() => ({\n\t\t\tonStateRestore: store.onStateRestore,\n\t\t\tonStateChange: store.onStateChange,\n\t\t\tonStateSave: store.onStateSave,\n\t\t}),\n\t\t[store],\n\t);\n\tuseLocationStateHandler(store.validateState, accessor, options);\n};\n"],"names":["useLocationStateDeserializer","location","useLocation","useCallback","parse","useLocationStateSerializer","navigate","useNavigate","state","useLocationStateCodec","deserializer","serializer","useMemo","useLocationStateHandler","validator","accessor","options","codec","useStateHandler","useLocationStateGetter","store","useLocationStateSetter","useLocationStateAccessor","getter","setter","useLocationState"],"mappings":";;;;AASA,MAAMA,IAA+B,MAAwB;AAC5D,QAAMC,IAAWC,EAAA;AAGjB,SAAOC;AAAA,IACN,MAAgBC,EAAMH,EAAS,UAAU,MAAM,CAAC,CAAC;AAAA,IACjD,CAACA,CAAQ;AAAA,EAAA;AAEX,GAEMI,IAA6B,MAA0C;AAC5E,QAAMC,IAAWC,EAAA;AAEjB,SAAOJ;AAAA,IACN,OAAOK,MAAiC;AACvC,YAAMF,EAAS;AAAA,QAAE,QAAQE;AAAA;AAAA,MAAA,CAA0B;AAAA,IACpD;AAAA,IACA,CAACF,CAAQ;AAAA,EAAA;AAEX,GAEMG,IAAwB,MAAoC;AACjE,QAAMC,IAAeV,EAAA,GACfW,IAAaN,EAAA;AAQnB,SAPcO;AAAA,IACb,OAA4B;AAAA,MAC3B,aAAaF;AAAA,MACb,WAAWC;AAAA,IAAA;AAAA,IAEZ,CAACD,GAAcC,CAAU;AAAA,EAAA;AAG3B,GAGME,IAA0B,CAC/BC,GACAC,GACAC,MACU;AACV,QAAMC,IAAQR,EAAA;AACd,EAAAS,EAAgBD,GAAOH,GAAWC,GAAUC,CAAO;AACpD,GAEMG,IAAyB,CAC9BC,MAEOjB,EAAY,MAAMiB,EAAM,OAAO,CAACA,CAAK,CAAC,GAGxCC,IAAyB,CAC9BD,MAEOjB;AAAA,EACN,CAACK,MAAkB;AAClB,IAAAY,EAAM,QAAQZ;AAAA,EACf;AAAA,EACA,CAACY,CAAK;AAAA,GAIFE,IAA2B,CAChCF,MAC4B;AAC5B,QAAMG,IAASJ,EAAuBC,CAAK,GACrCI,IAASH,EAAuBD,CAAK;AAK3C,SAJiBR;AAAA,IAChB,OAA+B,EAAE,KAAKW,GAAQ,KAAKC,EAAA;AAAA,IACnD,CAACD,GAAQC,CAAM;AAAA,EAAA;AAGjB,GAGaC,IAAmB,CAAUL,MAAqC;AAC9E,QAAML,IAAWO,EAAyBF,CAAK,GACzCJ,IAAUJ;AAAA,IACf,OAAO;AAAA,MACN,gBAAgBQ,EAAM;AAAA,MACtB,eAAeA,EAAM;AAAA,MACrB,aAAaA,EAAM;AAAA,IAAA;AAAA,IAEpB,CAACA,CAAK;AAAA,EAAA;AAEP,EAAAP,EAAwBO,EAAM,eAAeL,GAAUC,CAAO;AAC/D;"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { omitBy as h, isEqual as S } from "lodash-es";
|
|
2
|
+
import { reaction as l } from "mobx";
|
|
3
|
+
import { useRef as C, useEffect as a, useCallback as g } from "react";
|
|
4
|
+
const i = (e, u, r, t, n) => {
|
|
5
|
+
a(() => {
|
|
6
|
+
const s = u.deserialize();
|
|
7
|
+
r(s) && (e.current = !0, t.set(s), e.current = !1, n.onStateRestore?.({ state: s }));
|
|
8
|
+
}, [u, r, e, t, n]);
|
|
9
|
+
}, k = (e, u, r) => {
|
|
10
|
+
const t = g(
|
|
11
|
+
(n, s) => {
|
|
12
|
+
if (!r.onStateChange) return;
|
|
13
|
+
const f = h(
|
|
14
|
+
n,
|
|
15
|
+
(m, o) => S(s[o], m)
|
|
16
|
+
), c = Object.keys(f);
|
|
17
|
+
console.assert(c.length > 0), r.onStateChange({
|
|
18
|
+
state: n,
|
|
19
|
+
previousState: s,
|
|
20
|
+
keys: c,
|
|
21
|
+
popState: e.current
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
[e, r]
|
|
25
|
+
);
|
|
26
|
+
a(() => l(u.get, t), [u, t]), a(() => {
|
|
27
|
+
const n = u.get(), s = {};
|
|
28
|
+
e.current = !0, t(n, s), e.current = !1;
|
|
29
|
+
}, [u, e, t]);
|
|
30
|
+
}, y = (e, u, r, t) => {
|
|
31
|
+
a(() => l(u.get, (n) => {
|
|
32
|
+
e.current || (r.serialize(n), t.onStateSave?.({ state: n }));
|
|
33
|
+
}), [u, e, r, t]);
|
|
34
|
+
}, H = (e, u, r, t) => {
|
|
35
|
+
const n = C(!1);
|
|
36
|
+
i(n, e, u, r, t), k(n, r, t), y(n, r, e, t);
|
|
37
|
+
};
|
|
38
|
+
export {
|
|
39
|
+
H as u
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=useStateHandler-Dbkejxjm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStateHandler-Dbkejxjm.js","sources":["../src/components/useStateHandler.tsx"],"sourcesContent":["import type {\n\tIStateAccessor,\n\tIStateGetter,\n\tIStateSetter,\n} from '@/components/IStateAccessor';\nimport type {\n\tIStateCodec,\n\tIStateDeserializer,\n\tIStateSerializer,\n} from '@/components/IStateCodec';\nimport type {\n\tIHandleStateChangeOptions,\n\tIRestoreStateOptions,\n\tISaveStateOptions,\n\tIStateHandlerOptions,\n} from '@/components/IStateHandlerOptions';\nimport { isEqual, omitBy } from 'lodash-es';\nimport { reaction } from 'mobx';\nimport { type MutableRefObject, useCallback, useEffect, useRef } from 'react';\n\nconst useRestoreState = <TState,>(\n\tpopStateRef: MutableRefObject<boolean>,\n\tdeserializer: IStateDeserializer /*<TState>*/,\n\tvalidator: (state: unknown) => state is TState,\n\tsetter: IStateSetter<TState>,\n\toptions: IRestoreStateOptions<TState>,\n): void => {\n\tuseEffect(() => {\n\t\tconst state = deserializer.deserialize();\n\n\t\tif (validator(state)) {\n\t\t\tpopStateRef.current = true;\n\n\t\t\tsetter.set(state);\n\n\t\t\tpopStateRef.current = false;\n\n\t\t\toptions.onStateRestore?.({ state: state });\n\t\t}\n\t}, [deserializer, validator, popStateRef, setter, options]);\n};\n\nconst useHandleStateChange = <TState extends Partial<TState>>(\n\tpopStateRef: MutableRefObject<boolean>,\n\tgetter: IStateGetter<TState>,\n\toptions: IHandleStateChangeOptions<TState>,\n): void => {\n\tconst handleStateChange = useCallback(\n\t\t(state: TState, previousState: TState): void => {\n\t\t\tif (!options.onStateChange) return;\n\n\t\t\t// Compare the current and previous values.\n\t\t\tconst diff = omitBy(state, (v, k) =>\n\t\t\t\tisEqual(previousState[k as keyof typeof previousState], v),\n\t\t\t);\n\n\t\t\t// Assuming that the current value is `{ filter: 'Miku', page: 3939, searchType: 'Artist' }`, and the previous one is `{ filter: 'Miku', page: 1 }`,\n\t\t\t// then the diff will be `{ page: 3939, searchType: 'Artist' }`, which results in `['page', 'searchType']`.\n\t\t\tconst keys = Object.keys(diff) as (keyof TState)[];\n\t\t\tconsole.assert(keys.length > 0);\n\n\t\t\toptions.onStateChange({\n\t\t\t\tstate: state,\n\t\t\t\tpreviousState: previousState,\n\t\t\t\tkeys: keys,\n\t\t\t\tpopState: popStateRef.current,\n\t\t\t});\n\t\t},\n\t\t[popStateRef, options],\n\t);\n\n\tuseEffect(() => {\n\t\t// Returns the disposer.\n\t\treturn reaction(getter.get, handleStateChange);\n\t}, [getter, handleStateChange]);\n\n\t// This is called when the page is first loaded.\n\tuseEffect(() => {\n\t\tconst state = getter.get();\n\t\tconst previousState = {} as TState;\n\n\t\tpopStateRef.current = true;\n\n\t\thandleStateChange(state, previousState);\n\n\t\tpopStateRef.current = false;\n\t}, [getter, popStateRef, handleStateChange]);\n};\n\nconst useSaveState = <TState,>(\n\tpopStateRef: MutableRefObject<boolean>,\n\tgetter: IStateGetter<TState>,\n\tserializer: IStateSerializer<TState>,\n\toptions: ISaveStateOptions<TState>,\n): void => {\n\tuseEffect(() => {\n\t\t// Returns the disposer.\n\t\treturn reaction(getter.get, (state) => {\n\t\t\tif (popStateRef.current) return;\n\n\t\t\tserializer.serialize(state);\n\n\t\t\toptions.onStateSave?.({ state: state });\n\t\t});\n\t}, [getter, popStateRef, serializer, options]);\n};\n\nexport const useStateHandler = <TState,>(\n\tcodec: IStateCodec<TState>,\n\tvalidator: (state: unknown) => state is TState,\n\taccessor: IStateAccessor<TState>,\n\toptions: IStateHandlerOptions<TState>,\n): void => {\n\t// Whether currently processing popstate. This is to prevent adding the previous state to history.\n\tconst popStateRef = useRef(false);\n\n\tuseRestoreState(popStateRef, codec, validator, accessor, options);\n\n\t// This must be called before `useSaveState`, so that state can be changed in the `onStateChange` callback.\n\tuseHandleStateChange(popStateRef, accessor, options);\n\n\tuseSaveState(popStateRef, accessor, codec, options);\n};\n"],"names":["useRestoreState","popStateRef","deserializer","validator","setter","options","useEffect","state","useHandleStateChange","getter","handleStateChange","useCallback","previousState","diff","omitBy","v","k","isEqual","keys","reaction","useSaveState","serializer","useStateHandler","codec","accessor","useRef"],"mappings":";;;AAoBA,MAAMA,IAAkB,CACvBC,GACAC,GACAC,GACAC,GACAC,MACU;AACV,EAAAC,EAAU,MAAM;AACf,UAAMC,IAAQL,EAAa,YAAA;AAE3B,IAAIC,EAAUI,CAAK,MAClBN,EAAY,UAAU,IAEtBG,EAAO,IAAIG,CAAK,GAEhBN,EAAY,UAAU,IAEtBI,EAAQ,iBAAiB,EAAE,OAAAE,GAAc;AAAA,EAE3C,GAAG,CAACL,GAAcC,GAAWF,GAAaG,GAAQC,CAAO,CAAC;AAC3D,GAEMG,IAAuB,CAC5BP,GACAQ,GACAJ,MACU;AACV,QAAMK,IAAoBC;AAAA,IACzB,CAACJ,GAAeK,MAAgC;AAC/C,UAAI,CAACP,EAAQ,cAAe;AAG5B,YAAMQ,IAAOC;AAAA,QAAOP;AAAA,QAAO,CAACQ,GAAGC,MAC9BC,EAAQL,EAAcI,CAA+B,GAAGD,CAAC;AAAA,MAAA,GAKpDG,IAAO,OAAO,KAAKL,CAAI;AAC7B,cAAQ,OAAOK,EAAK,SAAS,CAAC,GAE9Bb,EAAQ,cAAc;AAAA,QACrB,OAAAE;AAAA,QACA,eAAAK;AAAA,QACA,MAAAM;AAAA,QACA,UAAUjB,EAAY;AAAA,MAAA,CACtB;AAAA,IACF;AAAA,IACA,CAACA,GAAaI,CAAO;AAAA,EAAA;AAGtB,EAAAC,EAAU,MAEFa,EAASV,EAAO,KAAKC,CAAiB,GAC3C,CAACD,GAAQC,CAAiB,CAAC,GAG9BJ,EAAU,MAAM;AACf,UAAMC,IAAQE,EAAO,IAAA,GACfG,IAAgB,CAAA;AAEtB,IAAAX,EAAY,UAAU,IAEtBS,EAAkBH,GAAOK,CAAa,GAEtCX,EAAY,UAAU;AAAA,EACvB,GAAG,CAACQ,GAAQR,GAAaS,CAAiB,CAAC;AAC5C,GAEMU,IAAe,CACpBnB,GACAQ,GACAY,GACAhB,MACU;AACV,EAAAC,EAAU,MAEFa,EAASV,EAAO,KAAK,CAACF,MAAU;AACtC,IAAIN,EAAY,YAEhBoB,EAAW,UAAUd,CAAK,GAE1BF,EAAQ,cAAc,EAAE,OAAAE,GAAc;AAAA,EACvC,CAAC,GACC,CAACE,GAAQR,GAAaoB,GAAYhB,CAAO,CAAC;AAC9C,GAEaiB,IAAkB,CAC9BC,GACApB,GACAqB,GACAnB,MACU;AAEV,QAAMJ,IAAcwB,EAAO,EAAK;AAEhC,EAAAzB,EAAgBC,GAAasB,GAAOpB,GAAWqB,GAAUnB,CAAO,GAGhEG,EAAqBP,GAAauB,GAAUnB,CAAO,GAEnDe,EAAanB,GAAauB,GAAUD,GAAOlB,CAAO;AACnD;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";const l=require("lodash-es"),f=require("mobx"),c=require("react"),E=(e,u,s,t,n)=>{c.useEffect(()=>{const r=u.deserialize();s(r)&&(e.current=!0,t.set(r),e.current=!1,n.onStateRestore?.({state:r}))},[u,s,e,t,n])},i=(e,u,s)=>{const t=c.useCallback((n,r)=>{if(!s.onStateChange)return;const h=l.omitBy(n,(S,o)=>l.isEqual(r[o],S)),a=Object.keys(h);console.assert(a.length>0),s.onStateChange({state:n,previousState:r,keys:a,popState:e.current})},[e,s]);c.useEffect(()=>f.reaction(u.get,t),[u,t]),c.useEffect(()=>{const n=u.get(),r={};e.current=!0,t(n,r),e.current=!1},[u,e,t])},C=(e,u,s,t)=>{c.useEffect(()=>f.reaction(u.get,n=>{e.current||(s.serialize(n),t.onStateSave?.({state:n}))}),[u,e,s,t])},d=(e,u,s,t)=>{const n=c.useRef(!1);E(n,e,u,s,t),i(n,s,t),C(n,s,e,t)};exports.useStateHandler=d;
|
|
2
|
+
//# sourceMappingURL=useStateHandler-Ff_CnvN8.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStateHandler-Ff_CnvN8.cjs","sources":["../src/components/useStateHandler.tsx"],"sourcesContent":["import type {\n\tIStateAccessor,\n\tIStateGetter,\n\tIStateSetter,\n} from '@/components/IStateAccessor';\nimport type {\n\tIStateCodec,\n\tIStateDeserializer,\n\tIStateSerializer,\n} from '@/components/IStateCodec';\nimport type {\n\tIHandleStateChangeOptions,\n\tIRestoreStateOptions,\n\tISaveStateOptions,\n\tIStateHandlerOptions,\n} from '@/components/IStateHandlerOptions';\nimport { isEqual, omitBy } from 'lodash-es';\nimport { reaction } from 'mobx';\nimport { type MutableRefObject, useCallback, useEffect, useRef } from 'react';\n\nconst useRestoreState = <TState,>(\n\tpopStateRef: MutableRefObject<boolean>,\n\tdeserializer: IStateDeserializer /*<TState>*/,\n\tvalidator: (state: unknown) => state is TState,\n\tsetter: IStateSetter<TState>,\n\toptions: IRestoreStateOptions<TState>,\n): void => {\n\tuseEffect(() => {\n\t\tconst state = deserializer.deserialize();\n\n\t\tif (validator(state)) {\n\t\t\tpopStateRef.current = true;\n\n\t\t\tsetter.set(state);\n\n\t\t\tpopStateRef.current = false;\n\n\t\t\toptions.onStateRestore?.({ state: state });\n\t\t}\n\t}, [deserializer, validator, popStateRef, setter, options]);\n};\n\nconst useHandleStateChange = <TState extends Partial<TState>>(\n\tpopStateRef: MutableRefObject<boolean>,\n\tgetter: IStateGetter<TState>,\n\toptions: IHandleStateChangeOptions<TState>,\n): void => {\n\tconst handleStateChange = useCallback(\n\t\t(state: TState, previousState: TState): void => {\n\t\t\tif (!options.onStateChange) return;\n\n\t\t\t// Compare the current and previous values.\n\t\t\tconst diff = omitBy(state, (v, k) =>\n\t\t\t\tisEqual(previousState[k as keyof typeof previousState], v),\n\t\t\t);\n\n\t\t\t// Assuming that the current value is `{ filter: 'Miku', page: 3939, searchType: 'Artist' }`, and the previous one is `{ filter: 'Miku', page: 1 }`,\n\t\t\t// then the diff will be `{ page: 3939, searchType: 'Artist' }`, which results in `['page', 'searchType']`.\n\t\t\tconst keys = Object.keys(diff) as (keyof TState)[];\n\t\t\tconsole.assert(keys.length > 0);\n\n\t\t\toptions.onStateChange({\n\t\t\t\tstate: state,\n\t\t\t\tpreviousState: previousState,\n\t\t\t\tkeys: keys,\n\t\t\t\tpopState: popStateRef.current,\n\t\t\t});\n\t\t},\n\t\t[popStateRef, options],\n\t);\n\n\tuseEffect(() => {\n\t\t// Returns the disposer.\n\t\treturn reaction(getter.get, handleStateChange);\n\t}, [getter, handleStateChange]);\n\n\t// This is called when the page is first loaded.\n\tuseEffect(() => {\n\t\tconst state = getter.get();\n\t\tconst previousState = {} as TState;\n\n\t\tpopStateRef.current = true;\n\n\t\thandleStateChange(state, previousState);\n\n\t\tpopStateRef.current = false;\n\t}, [getter, popStateRef, handleStateChange]);\n};\n\nconst useSaveState = <TState,>(\n\tpopStateRef: MutableRefObject<boolean>,\n\tgetter: IStateGetter<TState>,\n\tserializer: IStateSerializer<TState>,\n\toptions: ISaveStateOptions<TState>,\n): void => {\n\tuseEffect(() => {\n\t\t// Returns the disposer.\n\t\treturn reaction(getter.get, (state) => {\n\t\t\tif (popStateRef.current) return;\n\n\t\t\tserializer.serialize(state);\n\n\t\t\toptions.onStateSave?.({ state: state });\n\t\t});\n\t}, [getter, popStateRef, serializer, options]);\n};\n\nexport const useStateHandler = <TState,>(\n\tcodec: IStateCodec<TState>,\n\tvalidator: (state: unknown) => state is TState,\n\taccessor: IStateAccessor<TState>,\n\toptions: IStateHandlerOptions<TState>,\n): void => {\n\t// Whether currently processing popstate. This is to prevent adding the previous state to history.\n\tconst popStateRef = useRef(false);\n\n\tuseRestoreState(popStateRef, codec, validator, accessor, options);\n\n\t// This must be called before `useSaveState`, so that state can be changed in the `onStateChange` callback.\n\tuseHandleStateChange(popStateRef, accessor, options);\n\n\tuseSaveState(popStateRef, accessor, codec, options);\n};\n"],"names":["useRestoreState","popStateRef","deserializer","validator","setter","options","useEffect","state","useHandleStateChange","getter","handleStateChange","useCallback","previousState","diff","omitBy","v","k","isEqual","keys","reaction","useSaveState","serializer","useStateHandler","codec","accessor","useRef"],"mappings":"+EAoBMA,EAAkB,CACvBC,EACAC,EACAC,EACAC,EACAC,IACU,CACVC,EAAAA,UAAU,IAAM,CACf,MAAMC,EAAQL,EAAa,YAAA,EAEvBC,EAAUI,CAAK,IAClBN,EAAY,QAAU,GAEtBG,EAAO,IAAIG,CAAK,EAEhBN,EAAY,QAAU,GAEtBI,EAAQ,iBAAiB,CAAE,MAAAE,EAAc,EAE3C,EAAG,CAACL,EAAcC,EAAWF,EAAaG,EAAQC,CAAO,CAAC,CAC3D,EAEMG,EAAuB,CAC5BP,EACAQ,EACAJ,IACU,CACV,MAAMK,EAAoBC,EAAAA,YACzB,CAACJ,EAAeK,IAAgC,CAC/C,GAAI,CAACP,EAAQ,cAAe,OAG5B,MAAMQ,EAAOC,EAAAA,OAAOP,EAAO,CAACQ,EAAGC,IAC9BC,EAAAA,QAAQL,EAAcI,CAA+B,EAAGD,CAAC,CAAA,EAKpDG,EAAO,OAAO,KAAKL,CAAI,EAC7B,QAAQ,OAAOK,EAAK,OAAS,CAAC,EAE9Bb,EAAQ,cAAc,CACrB,MAAAE,EACA,cAAAK,EACA,KAAAM,EACA,SAAUjB,EAAY,OAAA,CACtB,CACF,EACA,CAACA,EAAaI,CAAO,CAAA,EAGtBC,EAAAA,UAAU,IAEFa,WAASV,EAAO,IAAKC,CAAiB,EAC3C,CAACD,EAAQC,CAAiB,CAAC,EAG9BJ,EAAAA,UAAU,IAAM,CACf,MAAMC,EAAQE,EAAO,IAAA,EACfG,EAAgB,CAAA,EAEtBX,EAAY,QAAU,GAEtBS,EAAkBH,EAAOK,CAAa,EAEtCX,EAAY,QAAU,EACvB,EAAG,CAACQ,EAAQR,EAAaS,CAAiB,CAAC,CAC5C,EAEMU,EAAe,CACpBnB,EACAQ,EACAY,EACAhB,IACU,CACVC,EAAAA,UAAU,IAEFa,WAASV,EAAO,IAAMF,GAAU,CAClCN,EAAY,UAEhBoB,EAAW,UAAUd,CAAK,EAE1BF,EAAQ,cAAc,CAAE,MAAAE,EAAc,EACvC,CAAC,EACC,CAACE,EAAQR,EAAaoB,EAAYhB,CAAO,CAAC,CAC9C,EAEaiB,EAAkB,CAC9BC,EACApB,EACAqB,EACAnB,IACU,CAEV,MAAMJ,EAAcwB,EAAAA,OAAO,EAAK,EAEhCzB,EAAgBC,EAAasB,EAAOpB,EAAWqB,EAAUnB,CAAO,EAGhEG,EAAqBP,EAAauB,EAAUnB,CAAO,EAEnDe,EAAanB,EAAauB,EAAUD,EAAOlB,CAAO,CACnD"}
|
package/package.json
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aigamo/route-sphere",
|
|
3
|
+
"version": "0.0.1-alpha.66",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"devDependencies": {
|
|
6
|
+
"@eslint/compat": "^2.0.0",
|
|
7
|
+
"@eslint/eslintrc": "^3.3.3",
|
|
8
|
+
"@tanstack/react-router": "^1.144.0",
|
|
9
|
+
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
10
|
+
"@types/lodash-es": "^4.17.12",
|
|
11
|
+
"@types/node": "^25.0.3",
|
|
12
|
+
"@types/qs": "^6.14.0",
|
|
13
|
+
"@types/react": "^18.3.27",
|
|
14
|
+
"@types/react-dom": "^18.3.7",
|
|
15
|
+
"@typescript-eslint/eslint-plugin": "^8.51.0",
|
|
16
|
+
"@typescript-eslint/parser": "^8.51.0",
|
|
17
|
+
"@typescript/native-preview": "7.0.0-dev.20260217.1",
|
|
18
|
+
"@vitejs/plugin-react": "^5.1.4",
|
|
19
|
+
"eslint": "^9.39.2",
|
|
20
|
+
"eslint-config-prettier": "^10.1.8",
|
|
21
|
+
"eslint-config-react-app": "^7.0.1",
|
|
22
|
+
"eslint-plugin-flowtype": "^8.0.3",
|
|
23
|
+
"eslint-plugin-import": "^2.32.0",
|
|
24
|
+
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
25
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
26
|
+
"eslint-plugin-react": "^7.37.5",
|
|
27
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
28
|
+
"lodash-es": "^4.17.23",
|
|
29
|
+
"mobx": "^6.15.0",
|
|
30
|
+
"mobx-react-lite": "^4.1.1",
|
|
31
|
+
"prettier": "^3.7.4",
|
|
32
|
+
"qs": "^6.14.2",
|
|
33
|
+
"react": "^18.3.1",
|
|
34
|
+
"react-dom": "^18.3.1",
|
|
35
|
+
"react-router-dom": "^7.13.1",
|
|
36
|
+
"rimraf": "^6.1.2",
|
|
37
|
+
"vite": "^7.3.1",
|
|
38
|
+
"vite-plugin-dts": "^4.5.4",
|
|
39
|
+
"vite-plugin-externalize-deps": "^0.10.0",
|
|
40
|
+
"vitest": "^4.0.18"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@tanstack/react-router": "^1.144.0",
|
|
44
|
+
"lodash-es": "^4.17.23",
|
|
45
|
+
"mobx": "^6.15.0",
|
|
46
|
+
"mobx-react-lite": "^3.0.0 || ^4.0.0",
|
|
47
|
+
"qs": "^6.14.2",
|
|
48
|
+
"react": "^17 || ^18",
|
|
49
|
+
"react-dom": "^17 || ^18",
|
|
50
|
+
"react-router-dom": "^7.13.1"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"dist"
|
|
54
|
+
],
|
|
55
|
+
"main": "./dist/index.cjs.js",
|
|
56
|
+
"module": "./dist/index.es.js",
|
|
57
|
+
"types": "./dist/index.d.ts",
|
|
58
|
+
"exports": {
|
|
59
|
+
".": {
|
|
60
|
+
"import": {
|
|
61
|
+
"types": "./dist/index.d.ts",
|
|
62
|
+
"default": "./dist/index.es.js"
|
|
63
|
+
},
|
|
64
|
+
"require": {
|
|
65
|
+
"types": "./dist/index.d.ts",
|
|
66
|
+
"default": "./dist/index.cjs.js"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"./react-router": {
|
|
70
|
+
"import": {
|
|
71
|
+
"types": "./dist/react-router.d.ts",
|
|
72
|
+
"default": "./dist/react-router.es.js"
|
|
73
|
+
},
|
|
74
|
+
"require": {
|
|
75
|
+
"types": "./dist/react-router.d.ts",
|
|
76
|
+
"default": "./dist/react-router.cjs.js"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"./tanstack-router": {
|
|
80
|
+
"import": {
|
|
81
|
+
"types": "./dist/tanstack-router.d.ts",
|
|
82
|
+
"default": "./dist/tanstack-router.es.js"
|
|
83
|
+
},
|
|
84
|
+
"require": {
|
|
85
|
+
"types": "./dist/tanstack-router.d.ts",
|
|
86
|
+
"default": "./dist/tanstack-router.cjs.js"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"description": "Sync query parameters with a MobX store and React Router.",
|
|
91
|
+
"repository": {
|
|
92
|
+
"type": "git",
|
|
93
|
+
"url": "git+https://github.com/ycanardeau/hydrangean-diva.git"
|
|
94
|
+
},
|
|
95
|
+
"keywords": [
|
|
96
|
+
"react",
|
|
97
|
+
"react-router",
|
|
98
|
+
"mobx"
|
|
99
|
+
],
|
|
100
|
+
"author": "Aigamo <51428094+ycanardeau@users.noreply.github.com>",
|
|
101
|
+
"license": "MIT",
|
|
102
|
+
"bugs": {
|
|
103
|
+
"url": "https://github.com/ycanardeau/route-sphere/issues"
|
|
104
|
+
},
|
|
105
|
+
"scripts": {
|
|
106
|
+
"clean": "rimraf ./dist",
|
|
107
|
+
"build": "pnpm clean && tsgo -b && vite build",
|
|
108
|
+
"build:watch": "pnpm build --watch",
|
|
109
|
+
"lint": "eslint ."
|
|
110
|
+
}
|
|
111
|
+
}
|