@molecule/app-react-native 1.0.0
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 +115 -0
- package/README.md +2666 -0
- package/dist/hooks/useAppState.d.ts +27 -0
- package/dist/hooks/useAppState.d.ts.map +1 -0
- package/dist/hooks/useAppState.js +48 -0
- package/dist/hooks/useBackHandler.d.ts +28 -0
- package/dist/hooks/useBackHandler.d.ts.map +1 -0
- package/dist/hooks/useBackHandler.js +46 -0
- package/dist/hooks/useKeyboardHeight.d.ts +27 -0
- package/dist/hooks/useKeyboardHeight.d.ts.map +1 -0
- package/dist/hooks/useKeyboardHeight.js +55 -0
- package/dist/hooks/useSafeArea.d.ts +35 -0
- package/dist/hooks/useSafeArea.d.ts.map +1 -0
- package/dist/hooks/useSafeArea.js +36 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/types.d.ts +31 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/package.json +64 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native hook for app foreground/background state.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { UseAppStateResult } from '../types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Tracks whether the app is in the foreground, background, or inactive.
|
|
9
|
+
*
|
|
10
|
+
* Uses React Native's `AppState` API under the hood. Falls back to
|
|
11
|
+
* 'active' on platforms where AppState is unavailable.
|
|
12
|
+
*
|
|
13
|
+
* @returns Current app state and whether the app is active.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const { appState, isActive } = useAppState()
|
|
18
|
+
*
|
|
19
|
+
* useEffect(() => {
|
|
20
|
+
* if (isActive) {
|
|
21
|
+
* refreshData()
|
|
22
|
+
* }
|
|
23
|
+
* }, [isActive])
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function useAppState(): UseAppStateResult;
|
|
27
|
+
//# sourceMappingURL=useAppState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAppState.d.ts","sourceRoot":"","sources":["../../src/hooks/useAppState.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAIpD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,IAAI,iBAAiB,CAmC/C"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native hook for app foreground/background state.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
7
|
+
/**
|
|
8
|
+
* Tracks whether the app is in the foreground, background, or inactive.
|
|
9
|
+
*
|
|
10
|
+
* Uses React Native's `AppState` API under the hood. Falls back to
|
|
11
|
+
* 'active' on platforms where AppState is unavailable.
|
|
12
|
+
*
|
|
13
|
+
* @returns Current app state and whether the app is active.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const { appState, isActive } = useAppState()
|
|
18
|
+
*
|
|
19
|
+
* useEffect(() => {
|
|
20
|
+
* if (isActive) {
|
|
21
|
+
* refreshData()
|
|
22
|
+
* }
|
|
23
|
+
* }, [isActive])
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function useAppState() {
|
|
27
|
+
const [appState, setAppState] = useState('active');
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
let cleanup;
|
|
30
|
+
try {
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
32
|
+
const { AppState } = require('react-native');
|
|
33
|
+
setAppState(AppState.currentState || 'active');
|
|
34
|
+
const subscription = AppState.addEventListener('change', (nextState) => {
|
|
35
|
+
setAppState(nextState || 'active');
|
|
36
|
+
});
|
|
37
|
+
cleanup = () => subscription.remove();
|
|
38
|
+
}
|
|
39
|
+
catch (_error) {
|
|
40
|
+
// Not running in React Native — stay 'active'
|
|
41
|
+
}
|
|
42
|
+
return cleanup;
|
|
43
|
+
}, []);
|
|
44
|
+
return {
|
|
45
|
+
appState,
|
|
46
|
+
isActive: appState === 'active',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native hook for Android hardware back button.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { UseBackHandlerOptions } from '../types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Registers a handler for the Android hardware back button.
|
|
9
|
+
*
|
|
10
|
+
* Return `true` from the handler to prevent the default back behavior,
|
|
11
|
+
* or `false` to allow it.
|
|
12
|
+
*
|
|
13
|
+
* @param handler - Callback invoked when the hardware back button is pressed.
|
|
14
|
+
* @param options - Optional configuration.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* useBackHandler(() => {
|
|
19
|
+
* if (isModalOpen) {
|
|
20
|
+
* closeModal()
|
|
21
|
+
* return true // Prevent default back
|
|
22
|
+
* }
|
|
23
|
+
* return false // Allow default back
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function useBackHandler(handler: () => boolean, options?: UseBackHandlerOptions): void;
|
|
28
|
+
//# sourceMappingURL=useBackHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBackHandler.d.ts","sourceRoot":"","sources":["../../src/hooks/useBackHandler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAExD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,OAAO,EAAE,OAAO,GAAE,qBAA0B,GAAG,IAAI,CAyBhG"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native hook for Android hardware back button.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { useEffect } from 'react';
|
|
7
|
+
/**
|
|
8
|
+
* Registers a handler for the Android hardware back button.
|
|
9
|
+
*
|
|
10
|
+
* Return `true` from the handler to prevent the default back behavior,
|
|
11
|
+
* or `false` to allow it.
|
|
12
|
+
*
|
|
13
|
+
* @param handler - Callback invoked when the hardware back button is pressed.
|
|
14
|
+
* @param options - Optional configuration.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* useBackHandler(() => {
|
|
19
|
+
* if (isModalOpen) {
|
|
20
|
+
* closeModal()
|
|
21
|
+
* return true // Prevent default back
|
|
22
|
+
* }
|
|
23
|
+
* return false // Allow default back
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function useBackHandler(handler, options = {}) {
|
|
28
|
+
const { enabled = true } = options;
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!enabled)
|
|
31
|
+
return;
|
|
32
|
+
// Dynamic import to avoid crashes on non-RN platforms
|
|
33
|
+
let cleanup;
|
|
34
|
+
try {
|
|
35
|
+
// BackHandler is available from react-native
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
37
|
+
const { BackHandler } = require('react-native');
|
|
38
|
+
const subscription = BackHandler.addEventListener('hardwareBackPress', handler);
|
|
39
|
+
cleanup = () => subscription.remove();
|
|
40
|
+
}
|
|
41
|
+
catch (_error) {
|
|
42
|
+
// Not running in React Native — no-op
|
|
43
|
+
}
|
|
44
|
+
return cleanup;
|
|
45
|
+
}, [handler, enabled]);
|
|
46
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native hook for keyboard visibility and height.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { UseKeyboardHeightResult } from '../types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Tracks soft keyboard visibility and height.
|
|
9
|
+
*
|
|
10
|
+
* Uses React Native's `Keyboard` API. Falls back to hidden state
|
|
11
|
+
* on platforms where Keyboard is unavailable.
|
|
12
|
+
*
|
|
13
|
+
* @returns Current keyboard height and visibility state.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const { keyboardHeight, isKeyboardVisible } = useKeyboardHeight()
|
|
18
|
+
*
|
|
19
|
+
* return (
|
|
20
|
+
* <View style={{ paddingBottom: keyboardHeight }}>
|
|
21
|
+
* <TextInput />
|
|
22
|
+
* </View>
|
|
23
|
+
* )
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function useKeyboardHeight(): UseKeyboardHeightResult;
|
|
27
|
+
//# sourceMappingURL=useKeyboardHeight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useKeyboardHeight.d.ts","sourceRoot":"","sources":["../../src/hooks/useKeyboardHeight.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAE1D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,IAAI,uBAAuB,CA2C3D"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native hook for keyboard visibility and height.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
7
|
+
/**
|
|
8
|
+
* Tracks soft keyboard visibility and height.
|
|
9
|
+
*
|
|
10
|
+
* Uses React Native's `Keyboard` API. Falls back to hidden state
|
|
11
|
+
* on platforms where Keyboard is unavailable.
|
|
12
|
+
*
|
|
13
|
+
* @returns Current keyboard height and visibility state.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const { keyboardHeight, isKeyboardVisible } = useKeyboardHeight()
|
|
18
|
+
*
|
|
19
|
+
* return (
|
|
20
|
+
* <View style={{ paddingBottom: keyboardHeight }}>
|
|
21
|
+
* <TextInput />
|
|
22
|
+
* </View>
|
|
23
|
+
* )
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function useKeyboardHeight() {
|
|
27
|
+
const [keyboardHeight, setKeyboardHeight] = useState(0);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const cleanups = [];
|
|
30
|
+
try {
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
32
|
+
const { Keyboard, Platform } = require('react-native');
|
|
33
|
+
const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
|
|
34
|
+
const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
|
|
35
|
+
const showSub = Keyboard.addListener(showEvent, (e) => {
|
|
36
|
+
setKeyboardHeight(e.endCoordinates.height);
|
|
37
|
+
});
|
|
38
|
+
cleanups.push(() => showSub.remove());
|
|
39
|
+
const hideSub = Keyboard.addListener(hideEvent, () => {
|
|
40
|
+
setKeyboardHeight(0);
|
|
41
|
+
});
|
|
42
|
+
cleanups.push(() => hideSub.remove());
|
|
43
|
+
}
|
|
44
|
+
catch (_error) {
|
|
45
|
+
// Not running in React Native (e.g. web/SSR); the Keyboard API is
|
|
46
|
+
// unavailable and the hook falls back to the hidden-state default.
|
|
47
|
+
// Ignoring is safe because no keyboard subscriptions were registered.
|
|
48
|
+
}
|
|
49
|
+
return () => cleanups.forEach((fn) => fn());
|
|
50
|
+
}, []);
|
|
51
|
+
return {
|
|
52
|
+
keyboardHeight,
|
|
53
|
+
isKeyboardVisible: keyboardHeight > 0,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native hook for safe area insets.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Safe area inset values in points.
|
|
8
|
+
*/
|
|
9
|
+
export interface SafeAreaInsets {
|
|
10
|
+
top: number;
|
|
11
|
+
right: number;
|
|
12
|
+
bottom: number;
|
|
13
|
+
left: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Returns the current safe area insets from `react-native-safe-area-context`.
|
|
17
|
+
*
|
|
18
|
+
* Must be used within a `SafeAreaProvider`. Falls back to zero insets
|
|
19
|
+
* if the context is unavailable.
|
|
20
|
+
*
|
|
21
|
+
* @returns Safe area insets for the current device.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* const insets = useSafeArea()
|
|
26
|
+
*
|
|
27
|
+
* return (
|
|
28
|
+
* <View style={{ paddingTop: insets.top, paddingBottom: insets.bottom }}>
|
|
29
|
+
* <App />
|
|
30
|
+
* </View>
|
|
31
|
+
* )
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function useSafeArea(): SafeAreaInsets;
|
|
35
|
+
//# sourceMappingURL=useSafeArea.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSafeArea.d.ts","sourceRoot":"","sources":["../../src/hooks/useSafeArea.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;CACb;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,IAAI,cAAc,CAY5C"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native hook for safe area insets.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Returns the current safe area insets from `react-native-safe-area-context`.
|
|
8
|
+
*
|
|
9
|
+
* Must be used within a `SafeAreaProvider`. Falls back to zero insets
|
|
10
|
+
* if the context is unavailable.
|
|
11
|
+
*
|
|
12
|
+
* @returns Safe area insets for the current device.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const insets = useSafeArea()
|
|
17
|
+
*
|
|
18
|
+
* return (
|
|
19
|
+
* <View style={{ paddingTop: insets.top, paddingBottom: insets.bottom }}>
|
|
20
|
+
* <App />
|
|
21
|
+
* </View>
|
|
22
|
+
* )
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function useSafeArea() {
|
|
26
|
+
try {
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
28
|
+
const { useSafeAreaInsets } = require('react-native-safe-area-context');
|
|
29
|
+
return useSafeAreaInsets();
|
|
30
|
+
}
|
|
31
|
+
catch (_error) {
|
|
32
|
+
// Safe to ignore: react-native-safe-area-context is an optional peer dep;
|
|
33
|
+
// if it is absent or the provider is not mounted, zero insets are the documented fallback.
|
|
34
|
+
return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
35
|
+
}
|
|
36
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native framework bindings for molecule.dev.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports every hook and provider from `@molecule/app-react` (they are pure
|
|
5
|
+
* React — no DOM dependency) and adds RN-specific hooks: `useAppState`
|
|
6
|
+
* (foreground/background), `useBackHandler` (Android back button),
|
|
7
|
+
* `useKeyboardHeight`, and `useSafeArea`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { MoleculeProvider, useAuth, useAppState, useSafeArea } from '@molecule/app-react-native'
|
|
12
|
+
* import { SafeAreaProvider } from 'react-native-safe-area-context'
|
|
13
|
+
* import { View } from 'react-native'
|
|
14
|
+
* import { provider as stateProvider } from '@molecule/app-state-zustand'
|
|
15
|
+
* import { createJWTAuthClient } from '@molecule/app-auth'
|
|
16
|
+
*
|
|
17
|
+
* const authClient = createJWTAuthClient({ baseURL: 'https://api.example.com' })
|
|
18
|
+
*
|
|
19
|
+
* function Shell() {
|
|
20
|
+
* const { isAuthenticated } = useAuth()
|
|
21
|
+
* const { isActive } = useAppState()
|
|
22
|
+
* const insets = useSafeArea()
|
|
23
|
+
* return (
|
|
24
|
+
* <View style={{ paddingTop: insets.top }}>
|
|
25
|
+
* {isAuthenticated && isActive ? <View /> : null}
|
|
26
|
+
* </View>
|
|
27
|
+
* )
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* function App() {
|
|
31
|
+
* return (
|
|
32
|
+
* <SafeAreaProvider>
|
|
33
|
+
* <MoleculeProvider state={stateProvider} auth={authClient}>
|
|
34
|
+
* <Shell />
|
|
35
|
+
* </MoleculeProvider>
|
|
36
|
+
* </SafeAreaProvider>
|
|
37
|
+
* )
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @remarks
|
|
42
|
+
* - **The re-exported hooks keep `@molecule/app-react`'s contract**: each one throws unless its
|
|
43
|
+
* provider was passed to `MoleculeProvider` (or mounted individually). See the
|
|
44
|
+
* `@molecule/app-react` docs for the hook→provider map.
|
|
45
|
+
* - **`useSafeArea` needs `SafeAreaProvider`** (from `react-native-safe-area-context`) mounted at
|
|
46
|
+
* the root; without it — or without the library installed — it silently returns zero insets
|
|
47
|
+
* rather than throwing, so a notch-overlapped header means missing provider, not a bug in the hook.
|
|
48
|
+
* - `useBackHandler` only fires on Android; iOS has no hardware back button.
|
|
49
|
+
* - For components/styling pair this with `@molecule/app-ui-react-native`, whose ClassMap
|
|
50
|
+
* styling requires the NativeWind setup (`@molecule/app-ui-nativewind`) — see that package's docs.
|
|
51
|
+
*
|
|
52
|
+
* @module
|
|
53
|
+
*/
|
|
54
|
+
export * from '@molecule/app-react';
|
|
55
|
+
export { useAppState } from './hooks/useAppState.js';
|
|
56
|
+
export { useBackHandler } from './hooks/useBackHandler.js';
|
|
57
|
+
export { useKeyboardHeight } from './hooks/useKeyboardHeight.js';
|
|
58
|
+
export { useSafeArea } from './hooks/useSafeArea.js';
|
|
59
|
+
export type { UseAppStateResult, UseBackHandlerOptions, UseKeyboardHeightResult } from './types.js';
|
|
60
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AAGH,cAAc,qBAAqB,CAAA;AAGnC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAGpD,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native framework bindings for molecule.dev.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports every hook and provider from `@molecule/app-react` (they are pure
|
|
5
|
+
* React — no DOM dependency) and adds RN-specific hooks: `useAppState`
|
|
6
|
+
* (foreground/background), `useBackHandler` (Android back button),
|
|
7
|
+
* `useKeyboardHeight`, and `useSafeArea`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { MoleculeProvider, useAuth, useAppState, useSafeArea } from '@molecule/app-react-native'
|
|
12
|
+
* import { SafeAreaProvider } from 'react-native-safe-area-context'
|
|
13
|
+
* import { View } from 'react-native'
|
|
14
|
+
* import { provider as stateProvider } from '@molecule/app-state-zustand'
|
|
15
|
+
* import { createJWTAuthClient } from '@molecule/app-auth'
|
|
16
|
+
*
|
|
17
|
+
* const authClient = createJWTAuthClient({ baseURL: 'https://api.example.com' })
|
|
18
|
+
*
|
|
19
|
+
* function Shell() {
|
|
20
|
+
* const { isAuthenticated } = useAuth()
|
|
21
|
+
* const { isActive } = useAppState()
|
|
22
|
+
* const insets = useSafeArea()
|
|
23
|
+
* return (
|
|
24
|
+
* <View style={{ paddingTop: insets.top }}>
|
|
25
|
+
* {isAuthenticated && isActive ? <View /> : null}
|
|
26
|
+
* </View>
|
|
27
|
+
* )
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* function App() {
|
|
31
|
+
* return (
|
|
32
|
+
* <SafeAreaProvider>
|
|
33
|
+
* <MoleculeProvider state={stateProvider} auth={authClient}>
|
|
34
|
+
* <Shell />
|
|
35
|
+
* </MoleculeProvider>
|
|
36
|
+
* </SafeAreaProvider>
|
|
37
|
+
* )
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @remarks
|
|
42
|
+
* - **The re-exported hooks keep `@molecule/app-react`'s contract**: each one throws unless its
|
|
43
|
+
* provider was passed to `MoleculeProvider` (or mounted individually). See the
|
|
44
|
+
* `@molecule/app-react` docs for the hook→provider map.
|
|
45
|
+
* - **`useSafeArea` needs `SafeAreaProvider`** (from `react-native-safe-area-context`) mounted at
|
|
46
|
+
* the root; without it — or without the library installed — it silently returns zero insets
|
|
47
|
+
* rather than throwing, so a notch-overlapped header means missing provider, not a bug in the hook.
|
|
48
|
+
* - `useBackHandler` only fires on Android; iOS has no hardware back button.
|
|
49
|
+
* - For components/styling pair this with `@molecule/app-ui-react-native`, whose ClassMap
|
|
50
|
+
* styling requires the NativeWind setup (`@molecule/app-ui-nativewind`) — see that package's docs.
|
|
51
|
+
*
|
|
52
|
+
* @module
|
|
53
|
+
*/
|
|
54
|
+
// Re-export everything from @molecule/app-react — all hooks are RN-compatible
|
|
55
|
+
export * from '@molecule/app-react';
|
|
56
|
+
// RN-specific hooks
|
|
57
|
+
export { useAppState } from './hooks/useAppState.js';
|
|
58
|
+
export { useBackHandler } from './hooks/useBackHandler.js';
|
|
59
|
+
export { useKeyboardHeight } from './hooks/useKeyboardHeight.js';
|
|
60
|
+
export { useSafeArea } from './hooks/useSafeArea.js';
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native specific types for molecule.dev.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Options for the useBackHandler hook.
|
|
8
|
+
*/
|
|
9
|
+
export interface UseBackHandlerOptions {
|
|
10
|
+
/** Whether the handler is enabled. Defaults to true. */
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Result from the useAppState hook.
|
|
15
|
+
*/
|
|
16
|
+
export interface UseAppStateResult {
|
|
17
|
+
/** Current app state: 'active' | 'background' | 'inactive' */
|
|
18
|
+
appState: string;
|
|
19
|
+
/** Whether the app is in the foreground */
|
|
20
|
+
isActive: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Result from the useKeyboardHeight hook.
|
|
24
|
+
*/
|
|
25
|
+
export interface UseKeyboardHeightResult {
|
|
26
|
+
/** Current keyboard height in points (0 when hidden) */
|
|
27
|
+
keyboardHeight: number;
|
|
28
|
+
/** Whether the keyboard is currently visible */
|
|
29
|
+
isKeyboardVisible: boolean;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAA;IAChB,2CAA2C;IAC3C,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,wDAAwD;IACxD,cAAc,EAAE,MAAM,CAAA;IACtB,gDAAgD;IAChD,iBAAiB,EAAE,OAAO,CAAA;CAC3B"}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@molecule/app-react-native",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React Native framework bindings for molecule.dev",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"test": "vitest run",
|
|
11
|
+
"test:watch": "vitest"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"molecule",
|
|
25
|
+
"react-native",
|
|
26
|
+
"framework"
|
|
27
|
+
],
|
|
28
|
+
"license": "Apache-2.0",
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@molecule/app-react": "1.0.0",
|
|
31
|
+
"@types/node": "26.1.2",
|
|
32
|
+
"react": "19.2.8",
|
|
33
|
+
"typescript": "6.0.3",
|
|
34
|
+
"vitest": "4.1.10"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"@molecule/app-keyboard": "^1.0.0",
|
|
38
|
+
"@molecule/app-lifecycle": "^1.0.0",
|
|
39
|
+
"@molecule/app-react": "^1.0.0",
|
|
40
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
41
|
+
"react-native": ">=0.72.0",
|
|
42
|
+
"react-native-safe-area-context": ">=4.0.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependenciesMeta": {
|
|
45
|
+
"@molecule/app-react": {
|
|
46
|
+
"optional": true
|
|
47
|
+
},
|
|
48
|
+
"@molecule/app-lifecycle": {
|
|
49
|
+
"optional": true
|
|
50
|
+
},
|
|
51
|
+
"@molecule/app-keyboard": {
|
|
52
|
+
"optional": true
|
|
53
|
+
},
|
|
54
|
+
"react": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"react-native": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"react-native-safe-area-context": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|