@granite-js/react-native 0.1.31 → 0.1.33
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/CHANGELOG.md +32 -0
- package/dist/router/createRoute.d.ts +4 -4
- package/dist/visibility/VisibilityProvider.d.ts +2 -1
- package/dist/visibility/useVisibilityChanged.d.ts +1 -2
- package/package.json +14 -14
- package/src/router/createRoute.test-d.ts +100 -2
- package/src/router/createRoute.ts +9 -4
- package/src/visibility/VisibilityProvider.tsx +17 -3
- package/src/visibility/useVisibilityChanged.tsx +3 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @granite-js/react-native
|
|
2
2
|
|
|
3
|
+
## 0.1.33
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fdf55a6: fix(router): type infer incorrect undefined
|
|
8
|
+
- Updated dependencies [fdf55a6]
|
|
9
|
+
- @granite-js/native@0.1.33
|
|
10
|
+
- @granite-js/image@0.1.33
|
|
11
|
+
- @granite-js/jest@0.1.33
|
|
12
|
+
- @granite-js/lottie@0.1.33
|
|
13
|
+
- @granite-js/style-utils@0.1.33
|
|
14
|
+
- @granite-js/cli@0.1.33
|
|
15
|
+
- @granite-js/mpack@0.1.33
|
|
16
|
+
- @granite-js/plugin-core@0.1.33
|
|
17
|
+
|
|
18
|
+
## 0.1.32
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- 74b2223: fix(react-native): VisibilityChangedProvider only provider
|
|
23
|
+
- 7572713: bump version up babel
|
|
24
|
+
- Updated dependencies [74b2223]
|
|
25
|
+
- Updated dependencies [7572713]
|
|
26
|
+
- @granite-js/native@0.1.32
|
|
27
|
+
- @granite-js/plugin-core@0.1.32
|
|
28
|
+
- @granite-js/style-utils@0.1.32
|
|
29
|
+
- @granite-js/image@0.1.32
|
|
30
|
+
- @granite-js/mpack@0.1.32
|
|
31
|
+
- @granite-js/cli@0.1.32
|
|
32
|
+
- @granite-js/jest@0.1.32
|
|
33
|
+
- @granite-js/lottie@0.1.32
|
|
34
|
+
|
|
3
35
|
## 0.1.31
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
|
@@ -48,13 +48,13 @@ export declare function createRoute<TSchema extends StandardSchemaV1<any, any>>(
|
|
|
48
48
|
_path: keyof RegisterScreenInput;
|
|
49
49
|
useNavigation: typeof useNavigation;
|
|
50
50
|
useParams: () => InferOutput<TSchema>;
|
|
51
|
-
_inputType
|
|
52
|
-
_outputType
|
|
51
|
+
_inputType: InferInput<TSchema>;
|
|
52
|
+
_outputType: InferOutput<TSchema>;
|
|
53
53
|
};
|
|
54
54
|
export declare function createRoute<T extends Readonly<object | undefined>>(path: keyof RegisterScreenInput, options: RouteOptions<T>): {
|
|
55
55
|
_path: keyof RegisterScreenInput;
|
|
56
56
|
useNavigation: typeof useNavigation;
|
|
57
57
|
useParams: () => T;
|
|
58
|
-
_inputType
|
|
59
|
-
_outputType
|
|
58
|
+
_inputType: T;
|
|
59
|
+
_outputType: T;
|
|
60
60
|
};
|
|
@@ -7,8 +7,9 @@ interface Props {
|
|
|
7
7
|
* @name VisibilityProvider
|
|
8
8
|
* @description
|
|
9
9
|
* A Provider that manages whether a ReactNative view is currently in the foreground state.
|
|
10
|
+
* It subscribes to the app's `visibilityChanged` event to detect and manage screen visibility.
|
|
10
11
|
* @param {boolean} isVisible - Whether the app is in the foreground state.
|
|
11
|
-
* @param {ReactNode | undefined} children - Child components that observe `AppState
|
|
12
|
+
* @param {ReactNode | undefined} children - Child components that observe `visibilityChanged` and `AppState` event.
|
|
12
13
|
* @returns {ReactElement} - A React Provider component wrapped with `VisibilityChangedProvider`.
|
|
13
14
|
* @example
|
|
14
15
|
* ```typescript
|
|
@@ -3,8 +3,7 @@ import { PropsWithChildren, ReactElement } from 'react';
|
|
|
3
3
|
* @name VisibilityChangedProvider
|
|
4
4
|
* @kind function
|
|
5
5
|
* @description
|
|
6
|
-
* A Provider that manages whether a React Native screen is visible.
|
|
7
|
-
* It subscribes to the app's `visibilityChanged` event to detect and manage screen visibility.
|
|
6
|
+
* A Provider that manages whether a React Native screen is visible provider.
|
|
8
7
|
*
|
|
9
8
|
* @param {ReactNode | undefined} children - Child components that check screen visibility.
|
|
10
9
|
* @param {boolean} isVisible - A boolean value indicating whether the screen is visible.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@granite-js/react-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
4
4
|
"description": "The Granite Framework",
|
|
5
5
|
"bin": {
|
|
6
6
|
"granite": "./bin/cli.js"
|
|
@@ -88,10 +88,10 @@
|
|
|
88
88
|
"*.d.ts"
|
|
89
89
|
],
|
|
90
90
|
"devDependencies": {
|
|
91
|
-
"@babel/core": "
|
|
92
|
-
"@babel/preset-env": "
|
|
93
|
-
"@babel/preset-typescript": "
|
|
94
|
-
"@granite-js/native": "0.1.
|
|
91
|
+
"@babel/core": "7.28.5",
|
|
92
|
+
"@babel/preset-env": "7.28.5",
|
|
93
|
+
"@babel/preset-typescript": "7.28.5",
|
|
94
|
+
"@granite-js/native": "0.1.33",
|
|
95
95
|
"@testing-library/dom": "^10.4.0",
|
|
96
96
|
"@testing-library/react": "^16.1.0",
|
|
97
97
|
"@types/babel__core": "^7",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"@types/node": "^22.10.2",
|
|
100
100
|
"@types/react": "18.3.3",
|
|
101
101
|
"@types/react-dom": "^18",
|
|
102
|
-
"@vitest/coverage-v8": "^
|
|
102
|
+
"@vitest/coverage-v8": "^4.0.12",
|
|
103
103
|
"esbuild": "0.25.8",
|
|
104
104
|
"eslint": "^9.7.0",
|
|
105
105
|
"jsdom": "^25.0.1",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"react-native": "0.72.6",
|
|
109
109
|
"tsup": "^8.5.0",
|
|
110
110
|
"typescript": "5.8.3",
|
|
111
|
-
"vitest": "^
|
|
111
|
+
"vitest": "^4.0.12",
|
|
112
112
|
"zod": "^4.1.12"
|
|
113
113
|
},
|
|
114
114
|
"peerDependencies": {
|
|
@@ -118,13 +118,13 @@
|
|
|
118
118
|
"react-native": "*"
|
|
119
119
|
},
|
|
120
120
|
"dependencies": {
|
|
121
|
-
"@granite-js/cli": "0.1.
|
|
122
|
-
"@granite-js/image": "0.1.
|
|
123
|
-
"@granite-js/jest": "0.1.
|
|
124
|
-
"@granite-js/lottie": "0.1.
|
|
125
|
-
"@granite-js/mpack": "0.1.
|
|
126
|
-
"@granite-js/plugin-core": "0.1.
|
|
127
|
-
"@granite-js/style-utils": "0.1.
|
|
121
|
+
"@granite-js/cli": "0.1.33",
|
|
122
|
+
"@granite-js/image": "0.1.33",
|
|
123
|
+
"@granite-js/jest": "0.1.33",
|
|
124
|
+
"@granite-js/lottie": "0.1.33",
|
|
125
|
+
"@granite-js/mpack": "0.1.33",
|
|
126
|
+
"@granite-js/plugin-core": "0.1.33",
|
|
127
|
+
"@granite-js/style-utils": "0.1.33",
|
|
128
128
|
"@standard-schema/spec": "^1.0.0",
|
|
129
129
|
"es-toolkit": "^1.39.8",
|
|
130
130
|
"react-native-url-polyfill": "3.0.0"
|
|
@@ -77,6 +77,30 @@ describe('createRoute', () => {
|
|
|
77
77
|
// @ts-expect-error Type error should occur since 'from' and 'strict: false' are conflicting options
|
|
78
78
|
assertType(useParams({ from: '/test', strict: false }));
|
|
79
79
|
});
|
|
80
|
+
|
|
81
|
+
it('should infer _inputType and _outputType without undefined for function pattern', () => {
|
|
82
|
+
type InputType = (typeof Route)['_inputType'];
|
|
83
|
+
type OutputType = (typeof Route)['_outputType'];
|
|
84
|
+
|
|
85
|
+
// _inputType should be inferred as the exact type, not a union with undefined
|
|
86
|
+
assertType<{
|
|
87
|
+
id: string;
|
|
88
|
+
name: string;
|
|
89
|
+
}>({} as InputType);
|
|
90
|
+
|
|
91
|
+
// _outputType should be inferred as the exact type, not a union with undefined
|
|
92
|
+
assertType<{
|
|
93
|
+
id: string;
|
|
94
|
+
name: string;
|
|
95
|
+
}>({} as OutputType);
|
|
96
|
+
|
|
97
|
+
// Verify that undefined is not part of the union
|
|
98
|
+
type InputHasUndefined = undefined extends InputType ? true : false;
|
|
99
|
+
type OutputHasUndefined = undefined extends OutputType ? true : false;
|
|
100
|
+
|
|
101
|
+
assertType<false>({} as InputHasUndefined);
|
|
102
|
+
assertType<false>({} as OutputHasUndefined);
|
|
103
|
+
});
|
|
80
104
|
});
|
|
81
105
|
|
|
82
106
|
describe('createRoute with StandardSchema', () => {
|
|
@@ -95,7 +119,56 @@ describe('createRoute with StandardSchema', () => {
|
|
|
95
119
|
}>(RouteWithSchema.useParams());
|
|
96
120
|
});
|
|
97
121
|
|
|
122
|
+
it('should infer _inputType and _outputType without undefined', () => {
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
124
|
+
const RouteWithSchema = createRoute('/test-schema', {
|
|
125
|
+
component: () => null,
|
|
126
|
+
validateParams: z.object({
|
|
127
|
+
id: z.string(),
|
|
128
|
+
count: z.number(),
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
type InputType = (typeof RouteWithSchema)['_inputType'];
|
|
133
|
+
type OutputType = (typeof RouteWithSchema)['_outputType'];
|
|
134
|
+
type UseParamsReturnType = ReturnType<(typeof RouteWithSchema)['useParams']>;
|
|
135
|
+
|
|
136
|
+
// _inputType should be inferred as the exact type, not a union with undefined
|
|
137
|
+
assertType<{
|
|
138
|
+
id: string;
|
|
139
|
+
count: number;
|
|
140
|
+
}>({} as InputType);
|
|
141
|
+
|
|
142
|
+
// _outputType should be inferred as the exact type, not a union with undefined
|
|
143
|
+
assertType<{
|
|
144
|
+
id: string;
|
|
145
|
+
count: number;
|
|
146
|
+
}>({} as OutputType);
|
|
147
|
+
|
|
148
|
+
// useParams should return the exact type, not a union with undefined
|
|
149
|
+
assertType<{
|
|
150
|
+
id: string;
|
|
151
|
+
count: number;
|
|
152
|
+
}>({} as UseParamsReturnType);
|
|
153
|
+
|
|
154
|
+
// Verify that undefined is not part of the union for all types
|
|
155
|
+
type InputHasUndefined = undefined extends InputType ? true : false;
|
|
156
|
+
type OutputHasUndefined = undefined extends OutputType ? true : false;
|
|
157
|
+
type UseParamsHasUndefined = undefined extends UseParamsReturnType ? true : false;
|
|
158
|
+
|
|
159
|
+
assertType<false>({} as InputHasUndefined);
|
|
160
|
+
assertType<false>({} as OutputHasUndefined);
|
|
161
|
+
assertType<false>({} as UseParamsHasUndefined);
|
|
162
|
+
|
|
163
|
+
// useNavigation should accept input type for navigate parameters
|
|
164
|
+
const navigation = useNavigation();
|
|
165
|
+
|
|
166
|
+
// Should accept exact input type
|
|
167
|
+
navigation.navigate('/test-schema', { id: 'test', count: 123 });
|
|
168
|
+
});
|
|
169
|
+
|
|
98
170
|
it('should infer output type from transformation', () => {
|
|
171
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
99
172
|
const RouteWithTransform = createRoute('/test-transform', {
|
|
100
173
|
component: () => null,
|
|
101
174
|
validateParams: z.object({
|
|
@@ -103,10 +176,35 @@ describe('createRoute with StandardSchema', () => {
|
|
|
103
176
|
}),
|
|
104
177
|
});
|
|
105
178
|
|
|
106
|
-
|
|
179
|
+
type InputType = (typeof RouteWithTransform)['_inputType'];
|
|
180
|
+
type OutputType = (typeof RouteWithTransform)['_outputType'];
|
|
181
|
+
type UseParamsReturnType = ReturnType<(typeof RouteWithTransform)['useParams']>;
|
|
182
|
+
|
|
183
|
+
// _inputType should be string (before transformation)
|
|
184
|
+
assertType<{
|
|
185
|
+
id: string;
|
|
186
|
+
}>({} as InputType);
|
|
187
|
+
|
|
188
|
+
// _outputType should be number (after transformation)
|
|
189
|
+
assertType<{
|
|
190
|
+
id: number;
|
|
191
|
+
}>({} as OutputType);
|
|
192
|
+
|
|
193
|
+
// useParams should return output type (number)
|
|
107
194
|
assertType<{
|
|
108
195
|
id: number;
|
|
109
|
-
}>(
|
|
196
|
+
}>({} as UseParamsReturnType);
|
|
197
|
+
|
|
198
|
+
// useNavigation should accept input type (string)
|
|
199
|
+
const navigation = useNavigation();
|
|
200
|
+
navigation.navigate('/test-transform', { id: 'test-id' });
|
|
201
|
+
|
|
202
|
+
// Verify that input and output types are different
|
|
203
|
+
type InputIsString = InputType extends { id: string } ? true : false;
|
|
204
|
+
type OutputIsNumber = OutputType extends { id: number } ? true : false;
|
|
205
|
+
|
|
206
|
+
assertType<true>({} as InputIsString);
|
|
207
|
+
assertType<true>({} as OutputIsNumber);
|
|
110
208
|
});
|
|
111
209
|
|
|
112
210
|
it('should work with useParams hook', () => {
|
|
@@ -175,8 +175,8 @@ export function createRoute<TSchema extends StandardSchemaV1<any, any>>(
|
|
|
175
175
|
_path: keyof RegisterScreenInput;
|
|
176
176
|
useNavigation: typeof useNavigation;
|
|
177
177
|
useParams: () => InferOutput<TSchema>;
|
|
178
|
-
_inputType
|
|
179
|
-
_outputType
|
|
178
|
+
_inputType: InferInput<TSchema>;
|
|
179
|
+
_outputType: InferOutput<TSchema>;
|
|
180
180
|
};
|
|
181
181
|
|
|
182
182
|
// Overload 2: Function pattern
|
|
@@ -187,8 +187,8 @@ export function createRoute<T extends Readonly<object | undefined>>(
|
|
|
187
187
|
_path: keyof RegisterScreenInput;
|
|
188
188
|
useNavigation: typeof useNavigation;
|
|
189
189
|
useParams: () => T;
|
|
190
|
-
_inputType
|
|
191
|
-
_outputType
|
|
190
|
+
_inputType: T;
|
|
191
|
+
_outputType: T;
|
|
192
192
|
};
|
|
193
193
|
|
|
194
194
|
// Implementation
|
|
@@ -205,5 +205,10 @@ export function createRoute(path: keyof RegisterScreenInput, options: RouteOptio
|
|
|
205
205
|
_path,
|
|
206
206
|
useNavigation,
|
|
207
207
|
useParams: () => useParams({ from: _path as keyof RegisterScreen, strict: true }),
|
|
208
|
+
|
|
209
|
+
// These properties are only used for type inference in the generated router files
|
|
210
|
+
// The actual values are never accessed at runtime
|
|
211
|
+
_inputType: {},
|
|
212
|
+
_outputType: {},
|
|
208
213
|
};
|
|
209
214
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { ReactElement, ReactNode } from 'react';
|
|
1
|
+
import { ReactElement, ReactNode, useEffect, useState } from 'react';
|
|
2
2
|
import { AppStateProvider } from './useIsAppForeground';
|
|
3
3
|
import { VisibilityChangedProvider } from './useVisibilityChanged';
|
|
4
|
+
import { nativeEventEmitter } from '../native-event-emitter/nativeEventEmitter';
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
7
|
isVisible: boolean;
|
|
@@ -11,8 +12,9 @@ interface Props {
|
|
|
11
12
|
* @name VisibilityProvider
|
|
12
13
|
* @description
|
|
13
14
|
* A Provider that manages whether a ReactNative view is currently in the foreground state.
|
|
15
|
+
* It subscribes to the app's `visibilityChanged` event to detect and manage screen visibility.
|
|
14
16
|
* @param {boolean} isVisible - Whether the app is in the foreground state.
|
|
15
|
-
* @param {ReactNode | undefined} children - Child components that observe `AppState
|
|
17
|
+
* @param {ReactNode | undefined} children - Child components that observe `visibilityChanged` and `AppState` event.
|
|
16
18
|
* @returns {ReactElement} - A React Provider component wrapped with `VisibilityChangedProvider`.
|
|
17
19
|
* @example
|
|
18
20
|
* ```typescript
|
|
@@ -28,8 +30,20 @@ interface Props {
|
|
|
28
30
|
* ```
|
|
29
31
|
*/
|
|
30
32
|
export function VisibilityProvider({ isVisible, children }: Props): ReactElement {
|
|
33
|
+
const [visible, setVisible] = useState(isVisible);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const subscription = nativeEventEmitter.addListener('visibilityChanged', (nextVisible) => {
|
|
37
|
+
setVisible(nextVisible);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return () => {
|
|
41
|
+
subscription.remove();
|
|
42
|
+
};
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
31
45
|
return (
|
|
32
|
-
<VisibilityChangedProvider isVisible={
|
|
46
|
+
<VisibilityChangedProvider isVisible={visible}>
|
|
33
47
|
<AppStateProvider>{children}</AppStateProvider>
|
|
34
48
|
</VisibilityChangedProvider>
|
|
35
49
|
);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { createContext, PropsWithChildren, ReactElement, useContext
|
|
2
|
-
import { nativeEventEmitter } from '../native-event-emitter';
|
|
1
|
+
import { createContext, PropsWithChildren, ReactElement, useContext } from 'react';
|
|
3
2
|
|
|
4
3
|
const VisibilityChangedContext = createContext<boolean | undefined>(undefined);
|
|
5
4
|
|
|
@@ -7,8 +6,7 @@ const VisibilityChangedContext = createContext<boolean | undefined>(undefined);
|
|
|
7
6
|
* @name VisibilityChangedProvider
|
|
8
7
|
* @kind function
|
|
9
8
|
* @description
|
|
10
|
-
* A Provider that manages whether a React Native screen is visible.
|
|
11
|
-
* It subscribes to the app's `visibilityChanged` event to detect and manage screen visibility.
|
|
9
|
+
* A Provider that manages whether a React Native screen is visible provider.
|
|
12
10
|
*
|
|
13
11
|
* @param {ReactNode | undefined} children - Child components that check screen visibility.
|
|
14
12
|
* @param {boolean} isVisible - A boolean value indicating whether the screen is visible.
|
|
@@ -28,19 +26,7 @@ export function VisibilityChangedProvider({
|
|
|
28
26
|
children,
|
|
29
27
|
isVisible,
|
|
30
28
|
}: PropsWithChildren<{ isVisible: boolean }>): ReactElement {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
const subscription = nativeEventEmitter.addListener('visibilityChanged', (nextVisible) => {
|
|
35
|
-
setVisible(nextVisible);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
return () => {
|
|
39
|
-
subscription.remove();
|
|
40
|
-
};
|
|
41
|
-
}, []);
|
|
42
|
-
|
|
43
|
-
return <VisibilityChangedContext.Provider value={visible}>{children}</VisibilityChangedContext.Provider>;
|
|
29
|
+
return <VisibilityChangedContext.Provider value={isVisible}>{children}</VisibilityChangedContext.Provider>;
|
|
44
30
|
}
|
|
45
31
|
|
|
46
32
|
/**
|