@daviapps/react-utils 0.0.3 → 0.0.4
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/form/index.ts +9 -8
- package/hooks/use-local-state.tsx +80 -0
- package/hooks/use-session-state.tsx +72 -0
- package/package.json +6 -1
- package/tsconfig.app.json +33 -0
- package/tsconfig.json +9 -23
- package/tsconfig.node.json +31 -0
package/form/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AxiosError } from "axios";
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import z, {
|
|
4
4
|
ZodArray,
|
|
5
5
|
ZodBoolean,
|
|
@@ -12,9 +12,11 @@ import z, {
|
|
|
12
12
|
ZodString,
|
|
13
13
|
} from "zod/v3";
|
|
14
14
|
|
|
15
|
+
import { type UseFormReturn } from "react-hook-form";
|
|
16
|
+
|
|
15
17
|
export function zodSchemaDefaults<S extends ZodObject<any>, T = z.infer<S>>(
|
|
16
18
|
schema: S,
|
|
17
|
-
overrides?: T
|
|
19
|
+
overrides?: T,
|
|
18
20
|
): any {
|
|
19
21
|
return (Object.entries(schema._def.shape()) as [keyof T, ZodSchema][]).reduce(
|
|
20
22
|
(acc, [key, value]) => {
|
|
@@ -23,7 +25,7 @@ export function zodSchemaDefaults<S extends ZodObject<any>, T = z.infer<S>>(
|
|
|
23
25
|
if (value instanceof ZodObject) {
|
|
24
26
|
acc[key] = zodSchemaDefaults(
|
|
25
27
|
value,
|
|
26
|
-
defaultValue
|
|
28
|
+
defaultValue,
|
|
27
29
|
) as unknown as T[keyof T];
|
|
28
30
|
} else if (
|
|
29
31
|
value instanceof ZodArray ||
|
|
@@ -55,7 +57,7 @@ export function zodSchemaDefaults<S extends ZodObject<any>, T = z.infer<S>>(
|
|
|
55
57
|
}
|
|
56
58
|
return acc;
|
|
57
59
|
},
|
|
58
|
-
{} as T
|
|
60
|
+
{} as T,
|
|
59
61
|
);
|
|
60
62
|
}
|
|
61
63
|
|
|
@@ -64,10 +66,9 @@ export interface ServerValidationErrorResponse {
|
|
|
64
66
|
fieldErrors: Map<string, Array<string>>;
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
export function zodServerResolver(
|
|
68
|
-
e
|
|
69
|
-
|
|
70
|
-
) {
|
|
69
|
+
export function zodServerResolver(e: Error, form: UseFormReturn<any>) {
|
|
70
|
+
if (!(e instanceof AxiosError)) return;
|
|
71
|
+
|
|
71
72
|
const data = e.response?.data as ServerValidationErrorResponse | undefined;
|
|
72
73
|
if (!data) return;
|
|
73
74
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useEffect,
|
|
5
|
+
useState,
|
|
6
|
+
type Dispatch,
|
|
7
|
+
type PropsWithChildren,
|
|
8
|
+
type SetStateAction,
|
|
9
|
+
} from "react";
|
|
10
|
+
|
|
11
|
+
export interface UseLocalStateProps<T> {
|
|
12
|
+
key: string;
|
|
13
|
+
initialState?: T | (() => T);
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useLocalState<T>({
|
|
18
|
+
key,
|
|
19
|
+
initialState,
|
|
20
|
+
enabled = true,
|
|
21
|
+
}: UseLocalStateProps<T>) {
|
|
22
|
+
const context = useContext(LocalStateContext);
|
|
23
|
+
if (!context) throw new Error("LocalStateProvider required");
|
|
24
|
+
|
|
25
|
+
const state = useState<T>(context.data[key] ?? (initialState as T));
|
|
26
|
+
const [value] = state;
|
|
27
|
+
|
|
28
|
+
// Initial value
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!enabled) return;
|
|
31
|
+
const value = context.data[key];
|
|
32
|
+
if (!value) return;
|
|
33
|
+
state[1](value);
|
|
34
|
+
}, [enabled]);
|
|
35
|
+
|
|
36
|
+
// Dispatch changes to context
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (!enabled) return;
|
|
39
|
+
context.setData((prev) => ({ ...prev, [key]: value }));
|
|
40
|
+
}, [value, enabled]);
|
|
41
|
+
|
|
42
|
+
return state;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type LocalStateContextProps<S> = {
|
|
46
|
+
data: S;
|
|
47
|
+
setData: Dispatch<SetStateAction<S>>;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const LocalStateContext = createContext<
|
|
51
|
+
LocalStateContextProps<{ [key: string]: any }> | undefined
|
|
52
|
+
>(undefined);
|
|
53
|
+
|
|
54
|
+
export interface LocalStateProviderProps extends PropsWithChildren {
|
|
55
|
+
storageKey?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function LocalStateProvider({
|
|
59
|
+
children,
|
|
60
|
+
storageKey = "local-state",
|
|
61
|
+
}: LocalStateProviderProps) {
|
|
62
|
+
const [data, setData] = useState(
|
|
63
|
+
JSON.parse(localStorage.getItem(storageKey) || "null") || {},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
localStorage.setItem(storageKey, JSON.stringify(data));
|
|
68
|
+
}, [data]);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<LocalStateContext.Provider
|
|
72
|
+
value={{
|
|
73
|
+
data,
|
|
74
|
+
setData,
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{children}
|
|
78
|
+
</LocalStateContext.Provider>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useEffect,
|
|
5
|
+
useState,
|
|
6
|
+
type Dispatch,
|
|
7
|
+
type PropsWithChildren,
|
|
8
|
+
type SetStateAction,
|
|
9
|
+
} from "react";
|
|
10
|
+
|
|
11
|
+
export interface UseSessionStateProps<T> {
|
|
12
|
+
key: string;
|
|
13
|
+
initialState?: T | (() => T);
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useSessionState<T>({
|
|
18
|
+
key,
|
|
19
|
+
initialState,
|
|
20
|
+
enabled = true,
|
|
21
|
+
}: UseSessionStateProps<T>) {
|
|
22
|
+
const context = useContext(SessionStateContext);
|
|
23
|
+
if (!context) throw new Error("SessionStateProvider required");
|
|
24
|
+
|
|
25
|
+
const state = useState<T>(context.data[key] ?? (initialState as T));
|
|
26
|
+
const [value] = state;
|
|
27
|
+
|
|
28
|
+
// Dispatch changes to context
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!enabled) return;
|
|
31
|
+
context.setData((prev) => ({ ...prev, [key]: value }));
|
|
32
|
+
}, [value, enabled]);
|
|
33
|
+
|
|
34
|
+
return state;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type SessionStateContextProps<S> = {
|
|
38
|
+
data: S;
|
|
39
|
+
setData: Dispatch<SetStateAction<S>>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const SessionStateContext = createContext<
|
|
43
|
+
SessionStateContextProps<{ [key: string]: any }> | undefined
|
|
44
|
+
>(undefined);
|
|
45
|
+
|
|
46
|
+
export interface SessionStateProviderProps extends PropsWithChildren {
|
|
47
|
+
storageKey?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function SessionStateProvider({
|
|
51
|
+
children,
|
|
52
|
+
storageKey = "session-state",
|
|
53
|
+
}: SessionStateProviderProps) {
|
|
54
|
+
const [data, setData] = useState(
|
|
55
|
+
JSON.parse(sessionStorage.getItem(storageKey) || "null") || {},
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
sessionStorage.setItem(storageKey, JSON.stringify(data));
|
|
60
|
+
}, [data]);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<SessionStateContext.Provider
|
|
64
|
+
value={{
|
|
65
|
+
data,
|
|
66
|
+
setData,
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
{children}
|
|
70
|
+
</SessionStateContext.Provider>
|
|
71
|
+
);
|
|
72
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@daviapps/react-utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"dependencies": {
|
|
6
|
+
"@trpc/client": "^11.7.1",
|
|
6
7
|
"axios": "^1.13.2",
|
|
8
|
+
"react": "^19.2.4",
|
|
7
9
|
"react-hook-form": "7.51.5",
|
|
8
10
|
"zod": "^4.1.12"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@types/react": "^19.2.13"
|
|
9
14
|
}
|
|
10
15
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"useDefineForClassFields": true,
|
|
6
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"types": ["vite/client"],
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
|
|
11
|
+
/* Bundler mode */
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"moduleDetection": "force",
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
"jsx": "react-jsx",
|
|
18
|
+
|
|
19
|
+
/* Linting */
|
|
20
|
+
"strict": true,
|
|
21
|
+
"noUnusedLocals": true,
|
|
22
|
+
"noUnusedParameters": true,
|
|
23
|
+
"erasableSyntaxOnly": false,
|
|
24
|
+
"noFallthroughCasesInSwitch": true,
|
|
25
|
+
"noUncheckedSideEffectImports": true,
|
|
26
|
+
|
|
27
|
+
"baseUrl": ".",
|
|
28
|
+
"paths": {
|
|
29
|
+
"@/*": ["./src/*"]
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"include": ["src", "_dep/layouts", "_dep/views"]
|
|
33
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,27 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
+
"files": [],
|
|
3
|
+
"references": [
|
|
4
|
+
{ "path": "./tsconfig.app.json" },
|
|
5
|
+
{ "path": "./tsconfig.node.json" }
|
|
6
|
+
],
|
|
2
7
|
"compilerOptions": {
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// "types": ["node"],
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
|
|
10
|
-
/* Bundler mode */
|
|
11
|
-
"moduleResolution": "bundler",
|
|
12
|
-
"allowImportingTsExtensions": true,
|
|
13
|
-
"verbatimModuleSyntax": true,
|
|
14
|
-
"moduleDetection": "force",
|
|
15
|
-
"noEmit": true,
|
|
16
|
-
|
|
17
|
-
/* Linting */
|
|
18
|
-
"strict": true,
|
|
19
|
-
"noUnusedLocals": true,
|
|
20
|
-
"noUnusedParameters": true,
|
|
21
|
-
"erasableSyntaxOnly": false,
|
|
22
|
-
"noFallthroughCasesInSwitch": true,
|
|
23
|
-
"noUncheckedSideEffectImports": true,
|
|
24
|
-
|
|
25
|
-
"baseUrl": "."
|
|
8
|
+
"baseUrl": ".",
|
|
9
|
+
"paths": {
|
|
10
|
+
"@/*": ["./src/*"]
|
|
11
|
+
}
|
|
26
12
|
}
|
|
27
13
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "ES2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"erasableSyntaxOnly": false,
|
|
22
|
+
"noFallthroughCasesInSwitch": true,
|
|
23
|
+
"noUncheckedSideEffectImports": true,
|
|
24
|
+
|
|
25
|
+
"baseUrl": ".",
|
|
26
|
+
"paths": {
|
|
27
|
+
"@/*": ["./src/*"]
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"include": ["vite.config.ts"]
|
|
31
|
+
}
|