@idealyst/navigation 1.3.13 → 1.3.15
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/navigation",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.15",
|
|
4
4
|
"description": "Cross-platform navigation library for React and React Native",
|
|
5
5
|
"readme": "README.md",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@idealyst/camera": "^1.2.30",
|
|
48
|
-
"@idealyst/components": "^1.3.
|
|
48
|
+
"@idealyst/components": "^1.3.15",
|
|
49
49
|
"@idealyst/microphone": "^1.2.30",
|
|
50
|
-
"@idealyst/theme": "^1.3.
|
|
50
|
+
"@idealyst/theme": "^1.3.15",
|
|
51
51
|
"@react-navigation/bottom-tabs": ">=7.0.0",
|
|
52
52
|
"@react-navigation/drawer": ">=7.0.0",
|
|
53
53
|
"@react-navigation/native": ">=7.0.0",
|
|
@@ -75,13 +75,13 @@
|
|
|
75
75
|
"@idealyst/blur": "^1.2.40",
|
|
76
76
|
"@idealyst/camera": "^1.2.30",
|
|
77
77
|
"@idealyst/charts": "^1.2.80",
|
|
78
|
-
"@idealyst/components": "^1.3.
|
|
78
|
+
"@idealyst/components": "^1.3.15",
|
|
79
79
|
"@idealyst/datagrid": "^1.2.30",
|
|
80
80
|
"@idealyst/datepicker": "^1.2.30",
|
|
81
81
|
"@idealyst/lottie": "^1.2.38",
|
|
82
|
-
"@idealyst/markdown": "^1.3.
|
|
82
|
+
"@idealyst/markdown": "^1.3.15",
|
|
83
83
|
"@idealyst/microphone": "^1.2.30",
|
|
84
|
-
"@idealyst/theme": "^1.3.
|
|
84
|
+
"@idealyst/theme": "^1.3.15",
|
|
85
85
|
"@types/react": "^19.1.8",
|
|
86
86
|
"@types/react-dom": "^19.1.6",
|
|
87
87
|
"react": "^19.1.0",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { useRoute, useNavigation } from '@react-navigation/native';
|
|
2
3
|
|
|
3
4
|
export interface UseNavigationStateOptions {
|
|
4
5
|
/**
|
|
@@ -10,25 +11,35 @@ export interface UseNavigationStateOptions {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
|
-
* Hook to access navigation state
|
|
14
|
-
* On native, state is
|
|
15
|
-
* Returns
|
|
14
|
+
* Hook to access and update navigation state.
|
|
15
|
+
* On native, state is stored as route params via React Navigation.
|
|
16
|
+
* Returns a tuple of [state, setState] for reading and updating params in-place.
|
|
16
17
|
*
|
|
17
18
|
* @example
|
|
18
19
|
* // Navigate with state
|
|
19
|
-
* navigate({ path: '/
|
|
20
|
+
* navigate({ path: '/search', state: { q: 'hello', sort: 'asc' } });
|
|
20
21
|
*
|
|
21
|
-
* //
|
|
22
|
-
* const
|
|
22
|
+
* // Read and update state in destination screen
|
|
23
|
+
* const [state, setState] = useNavigationState<{ q: string; sort: string }>();
|
|
24
|
+
* // state.q === 'hello', state.sort === 'asc'
|
|
25
|
+
*
|
|
26
|
+
* // Update params in-place (no navigation)
|
|
27
|
+
* setState({ sort: 'desc' });
|
|
23
28
|
*
|
|
24
29
|
* @example
|
|
25
30
|
* // The consume option is accepted but has no effect on native
|
|
26
|
-
* const
|
|
31
|
+
* const [state] = useNavigationState<{ autostart?: boolean }>({ consume: ['autostart'] });
|
|
27
32
|
*/
|
|
28
33
|
export function useNavigationState<T extends Record<string, unknown> = Record<string, unknown>>(
|
|
29
34
|
_options?: UseNavigationStateOptions
|
|
30
|
-
): T {
|
|
35
|
+
): [T, (updates: Partial<T>) => void] {
|
|
31
36
|
const route = useRoute();
|
|
32
|
-
const
|
|
33
|
-
|
|
37
|
+
const navigation = useNavigation();
|
|
38
|
+
const params = (route.params as T) || ({} as T);
|
|
39
|
+
|
|
40
|
+
const setState = useCallback((updates: Partial<T>) => {
|
|
41
|
+
navigation.setParams(updates as any);
|
|
42
|
+
}, [navigation]);
|
|
43
|
+
|
|
44
|
+
return [params, setState];
|
|
34
45
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
2
|
import { useSearchParams } from '../router';
|
|
3
3
|
|
|
4
4
|
export interface UseNavigationStateOptions {
|
|
@@ -16,22 +16,25 @@ export interface UseNavigationStateOptions {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Hook to access navigation state
|
|
20
|
-
* On web, state is
|
|
21
|
-
* Returns
|
|
19
|
+
* Hook to access and update navigation state.
|
|
20
|
+
* On web, state is stored as URL query parameters.
|
|
21
|
+
* Returns a tuple of [state, setState] for reading and updating params in-place.
|
|
22
22
|
*
|
|
23
23
|
* @example
|
|
24
24
|
* // Navigate with state
|
|
25
|
-
* navigate({ path: '/
|
|
25
|
+
* navigate({ path: '/search', state: { q: 'hello', sort: 'asc' } });
|
|
26
26
|
*
|
|
27
|
-
* //
|
|
28
|
-
* const
|
|
29
|
-
* //
|
|
27
|
+
* // Read and update state in destination screen
|
|
28
|
+
* const [state, setState] = useNavigationState<{ q: string; sort: string }>();
|
|
29
|
+
* // state.q === 'hello', state.sort === 'asc'
|
|
30
|
+
*
|
|
31
|
+
* // Update params in-place (no navigation, replaces history entry)
|
|
32
|
+
* setState({ sort: 'desc' });
|
|
30
33
|
*
|
|
31
34
|
* @example
|
|
32
|
-
* // Consume (remove)
|
|
33
|
-
* const
|
|
34
|
-
* // autostart = true, URL becomes: /recording (param removed)
|
|
35
|
+
* // Consume (remove) a parameter after reading
|
|
36
|
+
* const [state] = useNavigationState<{ autostart?: boolean }>({ consume: ['autostart'] });
|
|
37
|
+
* // state.autostart = true, URL becomes: /recording (param removed)
|
|
35
38
|
*/
|
|
36
39
|
function parseSearchParams(searchParams: URLSearchParams): Record<string, unknown> {
|
|
37
40
|
const state: Record<string, unknown> = {};
|
|
@@ -51,7 +54,7 @@ function parseSearchParams(searchParams: URLSearchParams): Record<string, unknow
|
|
|
51
54
|
|
|
52
55
|
export function useNavigationState<T extends Record<string, unknown> = Record<string, unknown>>(
|
|
53
56
|
options?: UseNavigationStateOptions
|
|
54
|
-
): T {
|
|
57
|
+
): [T, (updates: Partial<T>) => void] {
|
|
55
58
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
56
59
|
const consumedStateRef = useRef<Record<string, unknown> | null>(null);
|
|
57
60
|
const hasConsumed = useRef(false);
|
|
@@ -60,7 +63,6 @@ export function useNavigationState<T extends Record<string, unknown> = Record<st
|
|
|
60
63
|
if (options?.consume && options.consume.length > 0 && !hasConsumed.current) {
|
|
61
64
|
const keysToConsume = options.consume.filter(key => searchParams.has(key));
|
|
62
65
|
if (keysToConsume.length > 0 && consumedStateRef.current === null) {
|
|
63
|
-
// Capture the full state including values we're about to consume
|
|
64
66
|
consumedStateRef.current = parseSearchParams(searchParams);
|
|
65
67
|
}
|
|
66
68
|
}
|
|
@@ -78,10 +80,24 @@ export function useNavigationState<T extends Record<string, unknown> = Record<st
|
|
|
78
80
|
}
|
|
79
81
|
}, [options?.consume, searchParams, setSearchParams]);
|
|
80
82
|
|
|
83
|
+
const setState = useCallback((updates: Partial<T>) => {
|
|
84
|
+
setSearchParams((prev) => {
|
|
85
|
+
const newParams = new URLSearchParams(prev);
|
|
86
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
87
|
+
if (value === undefined || value === null) {
|
|
88
|
+
newParams.delete(key);
|
|
89
|
+
} else {
|
|
90
|
+
newParams.set(key, String(value));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return newParams;
|
|
94
|
+
}, { replace: true });
|
|
95
|
+
}, [setSearchParams]);
|
|
96
|
+
|
|
81
97
|
// Return captured state if we consumed params, otherwise parse current URL
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
98
|
+
const state = consumedStateRef.current !== null
|
|
99
|
+
? consumedStateRef.current as T
|
|
100
|
+
: parseSearchParams(searchParams) as T;
|
|
85
101
|
|
|
86
|
-
return
|
|
102
|
+
return [state, setState];
|
|
87
103
|
}
|