@granite-js/react-native 0.0.0-dev-20250725013859
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 +202 -0
- package/README.md +24 -0
- package/bin/cli.js +3 -0
- package/cli.d.ts +1 -0
- package/cli.js +4 -0
- package/config.d.ts +2 -0
- package/config.js +5 -0
- package/dist/app/App/index.android.d.ts +2 -0
- package/dist/app/App/index.ios.d.ts +6 -0
- package/dist/app/AppRoot.d.ts +1 -0
- package/dist/app/Granite.d.ts +61 -0
- package/dist/app/HostAppRoot.d.ts +1 -0
- package/dist/app/index.d.ts +2 -0
- package/dist/async-bridges.d.ts +2 -0
- package/dist/blur/BlurView.d.ts +78 -0
- package/dist/blur/ReactNativeBlurModule.d.ts +6 -0
- package/dist/blur/constants.d.ts +1 -0
- package/dist/blur/index.d.ts +1 -0
- package/dist/constant-bridges.d.ts +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/dev-entrypoint/index.d.ts +2 -0
- package/dist/event/abstract.d.ts +42 -0
- package/dist/event/index.d.ts +2 -0
- package/dist/event/useGraniteEvent.d.ts +14 -0
- package/dist/impression-area/ImpressionArea.d.ts +231 -0
- package/dist/impression-area/index.d.ts +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/initial-props/InitialProps.d.ts +92 -0
- package/dist/initial-props/index.d.ts +1 -0
- package/dist/intersection-observer/IOContext.d.ts +10 -0
- package/dist/intersection-observer/IOFlatList.d.ts +55 -0
- package/dist/intersection-observer/IOManager.d.ts +24 -0
- package/dist/intersection-observer/IOScrollView.d.ts +59 -0
- package/dist/intersection-observer/InView.d.ts +107 -0
- package/dist/intersection-observer/IntersectionObserver.d.ts +67 -0
- package/dist/intersection-observer/index.d.ts +8 -0
- package/dist/intersection-observer/withIO.d.ts +20 -0
- package/dist/jest/index.d.ts +1 -0
- package/dist/jest/index.js +32 -0
- package/dist/keyboard/KeyboardAboveView.d.ts +40 -0
- package/dist/keyboard/index.d.ts +2 -0
- package/dist/keyboard/useKeyboardAnimatedHeight.d.ts +20 -0
- package/dist/native-event-emitter/eventEmitters/index.d.ts +2 -0
- package/dist/native-event-emitter/eventEmitters/types.d.ts +4 -0
- package/dist/native-event-emitter/eventEmitters/visibilityChanged.d.ts +10 -0
- package/dist/native-event-emitter/index.d.ts +1 -0
- package/dist/native-event-emitter/nativeEventEmitter.d.ts +15 -0
- package/dist/native-modules/core/GraniteCoreModule.d.ts +8 -0
- package/dist/native-modules/index.d.ts +3 -0
- package/dist/native-modules/natives/GraniteModule.d.ts +7 -0
- package/dist/native-modules/natives/closeView.d.ts +21 -0
- package/dist/native-modules/natives/getSchemeUri.d.ts +23 -0
- package/dist/native-modules/natives/index.d.ts +3 -0
- package/dist/native-modules/natives/openURL.d.ts +36 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/useWaitForReturnNavigator.d.ts +39 -0
- package/dist/rn-polyfills/index.d.ts +1 -0
- package/dist/rn-polyfills/symbol-asynciterator/index.d.ts +9 -0
- package/dist/rn-polyfills/url/index.d.ts +1 -0
- package/dist/router/Router.d.ts +59 -0
- package/dist/router/components/BackButton.d.ts +7 -0
- package/dist/router/components/CanGoBackGuard.d.ts +6 -0
- package/dist/router/components/RouterBackButton.d.ts +9 -0
- package/dist/router/components/StackNavigator.d.ts +54 -0
- package/dist/router/constants.d.ts +2 -0
- package/dist/router/createRoute.d.ts +39 -0
- package/dist/router/createRoute.test-d.d.ts +9 -0
- package/dist/router/hooks/useInitialRouteName.d.ts +1 -0
- package/dist/router/hooks/useIsInitialScreen.d.ts +1 -0
- package/dist/router/hooks/useRouterControls.d.ts +11 -0
- package/dist/router/index.d.ts +3 -0
- package/dist/router/types/RequireContext.d.ts +7 -0
- package/dist/router/types/RouteScreen.d.ts +16 -0
- package/dist/router/types/Screen.d.ts +23 -0
- package/dist/router/types/index.d.ts +3 -0
- package/dist/router/types/screen-option.d.ts +4 -0
- package/dist/router/utils/createParentRouteScreenMap.d.ts +8 -0
- package/dist/router/utils/defaultParserParams.d.ts +9 -0
- package/dist/router/utils/index.d.ts +2 -0
- package/dist/router/utils/matchers.d.ts +2 -0
- package/dist/router/utils/mergeParentLayoutScreen.d.ts +18 -0
- package/dist/router/utils/path.d.ts +53 -0
- package/dist/router/utils/screen.d.ts +37 -0
- package/dist/scroll-view-inertial-background/ScrollViewInertialBackground.d.ts +49 -0
- package/dist/scroll-view-inertial-background/index.d.ts +1 -0
- package/dist/status-bar/StatusBar.android.d.ts +3 -0
- package/dist/status-bar/StatusBar.ios.d.ts +3 -0
- package/dist/status-bar/index.d.ts +2 -0
- package/dist/status-bar/types.d.ts +20 -0
- package/dist/status-bar/utils.d.ts +3 -0
- package/dist/types/global.d.ts +14 -0
- package/dist/use-back-event/index.d.ts +1 -0
- package/dist/use-back-event/useBackEvent.d.ts +135 -0
- package/dist/utils/noop.d.ts +1 -0
- package/dist/utils/usePreservedCallback.d.ts +1 -0
- package/dist/video/Video.d.ts +67 -0
- package/dist/video/index.d.ts +1 -0
- package/dist/video/instance.d.ts +9 -0
- package/dist/visibility/VisibilityProvider.d.ts +27 -0
- package/dist/visibility/index.d.ts +6 -0
- package/dist/visibility/react-navigation/index.d.ts +2 -0
- package/dist/visibility/react-navigation/useIsFocusedSafely.d.ts +20 -0
- package/dist/visibility/react-navigation/useNavigationSafely.d.ts +19 -0
- package/dist/visibility/useIsAppForeground.d.ts +39 -0
- package/dist/visibility/useVisibility.d.ts +35 -0
- package/dist/visibility/useVisibilityChange.d.ts +51 -0
- package/dist/visibility/useVisibilityChanged.d.ts +41 -0
- package/dist/visibility/utils/usePrevious.d.ts +15 -0
- package/jest.d.ts +1 -0
- package/package.json +94 -0
- package/presets.d.ts +1 -0
- package/src/app/App/index.android.tsx +6 -0
- package/src/app/App/index.d.ts +6 -0
- package/src/app/App/index.ios.tsx +13 -0
- package/src/app/AppRoot.tsx +39 -0
- package/src/app/Granite.tsx +128 -0
- package/src/app/HostAppRoot.tsx +19 -0
- package/src/app/index.ts +2 -0
- package/src/async-bridges.ts +2 -0
- package/src/blur/BlurView.tsx +103 -0
- package/src/blur/ReactNativeBlurModule.ts +19 -0
- package/src/blur/constants.ts +3 -0
- package/src/blur/index.ts +1 -0
- package/src/constant-bridges.ts +1 -0
- package/src/constants.ts +1 -0
- package/src/dev-entrypoint/index.tsx +17 -0
- package/src/event/abstract.ts +130 -0
- package/src/event/index.ts +2 -0
- package/src/event/useGraniteEvent.ts +34 -0
- package/src/impression-area/ImpressionArea.tsx +341 -0
- package/src/impression-area/index.ts +1 -0
- package/src/index.ts +24 -0
- package/src/initial-props/InitialProps.ts +95 -0
- package/src/initial-props/index.ts +1 -0
- package/src/intersection-observer/IOContext.ts +16 -0
- package/src/intersection-observer/IOFlatList.ts +72 -0
- package/src/intersection-observer/IOManager.ts +73 -0
- package/src/intersection-observer/IOScrollView.ts +69 -0
- package/src/intersection-observer/InView.tsx +205 -0
- package/src/intersection-observer/IntersectionObserver.ts +212 -0
- package/src/intersection-observer/index.ts +24 -0
- package/src/intersection-observer/withIO.tsx +151 -0
- package/src/jest/index.ts +1 -0
- package/src/keyboard/KeyboardAboveView.tsx +62 -0
- package/src/keyboard/index.ts +2 -0
- package/src/keyboard/useKeyboardAnimatedHeight.tsx +81 -0
- package/src/native-event-emitter/eventEmitters/index.ts +3 -0
- package/src/native-event-emitter/eventEmitters/types.ts +4 -0
- package/src/native-event-emitter/eventEmitters/visibilityChanged.ts +11 -0
- package/src/native-event-emitter/index.ts +1 -0
- package/src/native-event-emitter/nativeEventEmitter.ts +18 -0
- package/src/native-modules/core/GraniteCoreModule.ts +9 -0
- package/src/native-modules/index.ts +3 -0
- package/src/native-modules/natives/GraniteModule.ts +8 -0
- package/src/native-modules/natives/closeView.ts +25 -0
- package/src/native-modules/natives/getSchemeUri.ts +27 -0
- package/src/native-modules/natives/index.ts +3 -0
- package/src/native-modules/natives/openURL.ts +40 -0
- package/src/react/index.ts +1 -0
- package/src/react/useWaitForReturnNavigator.ts +75 -0
- package/src/rn-polyfills/index.ts +7 -0
- package/src/rn-polyfills/symbol-asynciterator/index.ts +15 -0
- package/src/rn-polyfills/url/index.ts +1 -0
- package/src/router/Router.tsx +164 -0
- package/src/router/components/BackButton.tsx +58 -0
- package/src/router/components/CanGoBackGuard.tsx +31 -0
- package/src/router/components/RouterBackButton.tsx +32 -0
- package/src/router/components/StackNavigator.tsx +12 -0
- package/src/router/constants.ts +3 -0
- package/src/router/createRoute.test-d.ts +52 -0
- package/src/router/createRoute.ts +161 -0
- package/src/router/hooks/useInitialRouteName.tsx +22 -0
- package/src/router/hooks/useIsInitialScreen.ts +7 -0
- package/src/router/hooks/useRouterControls.tsx +72 -0
- package/src/router/index.ts +3 -0
- package/src/router/types/RequireContext.ts +7 -0
- package/src/router/types/RouteScreen.ts +17 -0
- package/src/router/types/Screen.tsx +24 -0
- package/src/router/types/index.ts +3 -0
- package/src/router/types/screen-option.ts +23 -0
- package/src/router/utils/createParentRouteScreenMap.spec.ts +166 -0
- package/src/router/utils/createParentRouteScreenMap.ts +136 -0
- package/src/router/utils/defaultParserParams.spec.ts +46 -0
- package/src/router/utils/defaultParserParams.ts +19 -0
- package/src/router/utils/index.ts +2 -0
- package/src/router/utils/matchers.ts +5 -0
- package/src/router/utils/mergeParentLayoutScreen.spec.tsx +112 -0
- package/src/router/utils/mergeParentLayoutScreen.tsx +43 -0
- package/src/router/utils/path.spec.ts +135 -0
- package/src/router/utils/path.ts +105 -0
- package/src/router/utils/screen.tsx +95 -0
- package/src/scroll-view-inertial-background/ScrollViewInertialBackground.tsx +99 -0
- package/src/scroll-view-inertial-background/index.ts +1 -0
- package/src/status-bar/StatusBar.android.tsx +36 -0
- package/src/status-bar/StatusBar.d.ts +4 -0
- package/src/status-bar/StatusBar.ios.tsx +34 -0
- package/src/status-bar/index.ts +2 -0
- package/src/status-bar/types.ts +21 -0
- package/src/status-bar/utils.ts +20 -0
- package/src/types/global.ts +21 -0
- package/src/use-back-event/index.ts +1 -0
- package/src/use-back-event/useBackEvent.tsx +260 -0
- package/src/utils/noop.ts +1 -0
- package/src/utils/usePreservedCallback.ts +16 -0
- package/src/video/Video.tsx +104 -0
- package/src/video/index.ts +1 -0
- package/src/video/instance.tsx +28 -0
- package/src/visibility/VisibilityProvider.tsx +36 -0
- package/src/visibility/index.ts +6 -0
- package/src/visibility/react-navigation/index.ts +2 -0
- package/src/visibility/react-navigation/useIsFocusedSafely.tsx +58 -0
- package/src/visibility/react-navigation/useNavigationSafely.tsx +30 -0
- package/src/visibility/useIsAppForeground.tsx +73 -0
- package/src/visibility/useVisibility.tsx +54 -0
- package/src/visibility/useVisibilityChange.ts +69 -0
- package/src/visibility/useVisibilityChanged.tsx +69 -0
- package/src/visibility/utils/usePrevious.tsx +24 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { type ComponentProps, PureComponent, RefObject, createRef } from 'react';
|
|
2
|
+
import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, ScrollView, findNodeHandle } from 'react-native';
|
|
3
|
+
import IOContext, { IOContextValue } from './IOContext';
|
|
4
|
+
import IOManager from './IOManager';
|
|
5
|
+
import { Root, RootMargin } from './IntersectionObserver';
|
|
6
|
+
|
|
7
|
+
export interface IOComponentProps {
|
|
8
|
+
rootMargin?: RootMargin;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @category Functions
|
|
13
|
+
* @kind function
|
|
14
|
+
* @name withIO
|
|
15
|
+
* @description
|
|
16
|
+
* A Higher-Order Component (HoC) that wraps a component with `IOContext` to enable Intersection Observer functionality.
|
|
17
|
+
*
|
|
18
|
+
* @argument
|
|
19
|
+
* @param {React.ComponentType} [BaseComponent] - Callback function that is called when the component is mounted.
|
|
20
|
+
* @param {string[]} [methods] - List of event handler names from BaseComponent to be bound.
|
|
21
|
+
* @returns {React.ComponentType} - Returns a wrapped component that can use Intersection Observer functionality.
|
|
22
|
+
*/
|
|
23
|
+
function withIO<
|
|
24
|
+
CompProps extends Pick<
|
|
25
|
+
ComponentProps<typeof ScrollView>,
|
|
26
|
+
'horizontal' | 'scrollEventThrottle' | 'onContentSizeChange' | 'onLayout' | 'onScroll'
|
|
27
|
+
>,
|
|
28
|
+
>(BaseComponent: React.ComponentType<CompProps>, methods: string[]) {
|
|
29
|
+
type ScrollableComponentProps = CompProps & IOComponentProps;
|
|
30
|
+
const IOScrollableComponent = class extends PureComponent<ScrollableComponentProps> {
|
|
31
|
+
protected node: any;
|
|
32
|
+
protected scroller: RefObject<any>;
|
|
33
|
+
protected root: Root;
|
|
34
|
+
protected manager: IOManager;
|
|
35
|
+
protected contextValue: IOContextValue;
|
|
36
|
+
|
|
37
|
+
constructor(props: ScrollableComponentProps) {
|
|
38
|
+
super(props);
|
|
39
|
+
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
41
|
+
const self = this;
|
|
42
|
+
this.scroller = createRef();
|
|
43
|
+
this.root = {
|
|
44
|
+
get node() {
|
|
45
|
+
return self.node;
|
|
46
|
+
},
|
|
47
|
+
get horizontal() {
|
|
48
|
+
return Boolean(self.props.horizontal);
|
|
49
|
+
},
|
|
50
|
+
current: {
|
|
51
|
+
contentInset: {
|
|
52
|
+
top: 0,
|
|
53
|
+
right: 0,
|
|
54
|
+
bottom: 0,
|
|
55
|
+
left: 0,
|
|
56
|
+
},
|
|
57
|
+
contentOffset: {
|
|
58
|
+
x: 0,
|
|
59
|
+
y: 0,
|
|
60
|
+
},
|
|
61
|
+
contentSize: {
|
|
62
|
+
width: 0,
|
|
63
|
+
height: 0,
|
|
64
|
+
},
|
|
65
|
+
layoutMeasurement: {
|
|
66
|
+
width: 0,
|
|
67
|
+
height: 0,
|
|
68
|
+
},
|
|
69
|
+
zoomScale: 1,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
const manager = new IOManager({
|
|
73
|
+
root: this.root,
|
|
74
|
+
get rootMargin() {
|
|
75
|
+
return self.props.rootMargin;
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
this.manager = manager;
|
|
79
|
+
this.contextValue = {
|
|
80
|
+
manager,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
componentDidMount() {
|
|
85
|
+
this.node = findNodeHandle(this.scroller.current);
|
|
86
|
+
methods.forEach((method) => {
|
|
87
|
+
(this as any)[method] = (...args: any) => {
|
|
88
|
+
this.scroller.current?.[method]?.(...args);
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
protected handleContentSizeChange = (width: number, height: number) => {
|
|
94
|
+
const { contentSize } = this.root.current;
|
|
95
|
+
if (width !== contentSize.width || height !== contentSize.height) {
|
|
96
|
+
this.root.current.contentSize = { width, height };
|
|
97
|
+
if (width > 0 && height > 0 && this.root.onLayout) {
|
|
98
|
+
this.root.onLayout();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const { onContentSizeChange } = this.props;
|
|
102
|
+
if (onContentSizeChange) {
|
|
103
|
+
onContentSizeChange(width, height);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
protected handleLayout = (event: LayoutChangeEvent) => {
|
|
108
|
+
const {
|
|
109
|
+
nativeEvent: { layout },
|
|
110
|
+
} = event;
|
|
111
|
+
const { layoutMeasurement } = this.root.current;
|
|
112
|
+
if (layoutMeasurement.width !== layout.width || layoutMeasurement.height !== layout.height) {
|
|
113
|
+
this.root.current.layoutMeasurement = layout;
|
|
114
|
+
}
|
|
115
|
+
const { onLayout } = this.props;
|
|
116
|
+
if (onLayout) {
|
|
117
|
+
onLayout(event);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
protected handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
122
|
+
this.root.current = event.nativeEvent;
|
|
123
|
+
if (this.root.onScroll) {
|
|
124
|
+
this.root.onScroll(this.root.current);
|
|
125
|
+
}
|
|
126
|
+
const { onScroll } = this.props;
|
|
127
|
+
if (onScroll) {
|
|
128
|
+
onScroll(event);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
render() {
|
|
133
|
+
return (
|
|
134
|
+
<IOContext.Provider value={this.contextValue}>
|
|
135
|
+
<BaseComponent
|
|
136
|
+
scrollEventThrottle={16}
|
|
137
|
+
{...this.props}
|
|
138
|
+
ref={this.scroller}
|
|
139
|
+
onContentSizeChange={this.handleContentSizeChange}
|
|
140
|
+
onLayout={this.handleLayout}
|
|
141
|
+
onScroll={this.handleScroll}
|
|
142
|
+
/>
|
|
143
|
+
</IOContext.Provider>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return IOScrollableComponent as typeof BaseComponent;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export default withIO;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { setup, config } from '@granite-js/jest';
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ComponentProps, ReactElement } from 'react';
|
|
2
|
+
import { Animated, View } from 'react-native';
|
|
3
|
+
import { useKeyboardAnimatedHeight } from './useKeyboardAnimatedHeight';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @public
|
|
7
|
+
* @category UI
|
|
8
|
+
* @name KeyboardAboveView
|
|
9
|
+
* @kind function
|
|
10
|
+
* @description
|
|
11
|
+
* A component that automatically lifts child components above the keyboard when it appears on the screen.
|
|
12
|
+
* It's useful when you want to keep elements like a "Send" button fixed above the keyboard during text input.
|
|
13
|
+
*
|
|
14
|
+
* @param {StyleProp<ViewStyle>} [props.style] - Additional styles can be applied. For example, you can set background color or size.
|
|
15
|
+
* @param {ReactNode} [props.children] - Components to be displayed above the keyboard when it appears. For example, you can include buttons, text input fields, etc.
|
|
16
|
+
* @returns {ReactElement} - Returns an [`Animated.View`](https://reactnative.dev/docs/animated#createanimatedcomponent) that is adjusted above the keyboard when it appears.
|
|
17
|
+
* @example
|
|
18
|
+
*
|
|
19
|
+
* ### Lifting elements above the keyboard
|
|
20
|
+
*
|
|
21
|
+
* ```tsx
|
|
22
|
+
* import { ScrollView, TextInput, View, Text } from 'react-native';
|
|
23
|
+
* import { KeyboardAboveView } from '@granite-js/react-native';
|
|
24
|
+
*
|
|
25
|
+
* export function KeyboardAboveViewExample() {
|
|
26
|
+
* return (
|
|
27
|
+
* <>
|
|
28
|
+
* <ScrollView>
|
|
29
|
+
* <TextInput placeholder="placeholder" />
|
|
30
|
+
* </ScrollView>
|
|
31
|
+
*
|
|
32
|
+
* <KeyboardAboveView>
|
|
33
|
+
* <View style={{ width: '100%', height: 50, backgroundColor: 'yellow' }}>
|
|
34
|
+
* <Text>Above the keyboard</Text>
|
|
35
|
+
* </View>
|
|
36
|
+
* </KeyboardAboveView>
|
|
37
|
+
* </>
|
|
38
|
+
* );
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function KeyboardAboveView({ style, children, ...props }: ComponentProps<typeof View>): ReactElement {
|
|
43
|
+
const keyboardHeight = useKeyboardAnimatedHeight();
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Animated.View
|
|
47
|
+
style={[
|
|
48
|
+
style,
|
|
49
|
+
{
|
|
50
|
+
transform: [
|
|
51
|
+
{
|
|
52
|
+
translateY: Animated.subtract(0, keyboardHeight),
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
]}
|
|
57
|
+
{...props}
|
|
58
|
+
>
|
|
59
|
+
{children}
|
|
60
|
+
</Animated.View>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { Animated, Keyboard, Platform } from 'react-native';
|
|
3
|
+
|
|
4
|
+
function getInitialKeyboardHeight() {
|
|
5
|
+
if (Platform.OS !== 'ios') {
|
|
6
|
+
return 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Branch handling for React Native 0.68.0 version where `metrics()` does not exist
|
|
11
|
+
*/
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
if (typeof Keyboard?.metrics === 'function') {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
return Keyboard.metrics()?.height ?? 0;
|
|
18
|
+
} else {
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @category Hooks
|
|
25
|
+
* @name useKeyboardAnimatedHeight
|
|
26
|
+
* @description
|
|
27
|
+
* A Hook that returns an animatable value (`Animated.Value`) representing the keyboard height changes when the keyboard appears or disappears. You can smoothly animate UI elements according to the keyboard height as it rises or falls.
|
|
28
|
+
*
|
|
29
|
+
* This Hook is primarily used on iOS. On Android, it does not detect keyboard height changes and always returns an `Animated.Value` with an initial value of `0`. In other words, animations are not applied in the Android environment.
|
|
30
|
+
*
|
|
31
|
+
* @returns {Animated.Value} - An animation value representing the keyboard height.
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const keyboardHeight = useKeyboardAnimatedHeight();
|
|
35
|
+
*
|
|
36
|
+
* <Animated.View style={{ marginBottom: keyboardHeight }}>
|
|
37
|
+
* {children}
|
|
38
|
+
* </Animated.View>
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function useKeyboardAnimatedHeight(): Animated.Value {
|
|
42
|
+
const keyboardHeight = useRef(new Animated.Value(getInitialKeyboardHeight())).current;
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (Platform.OS === 'ios') {
|
|
46
|
+
const willShowSubscription = Keyboard.addListener('keyboardWillShow', (event) => {
|
|
47
|
+
const height = event.endCoordinates.height;
|
|
48
|
+
|
|
49
|
+
Animated.spring(keyboardHeight, {
|
|
50
|
+
toValue: height,
|
|
51
|
+
useNativeDriver: true,
|
|
52
|
+
...spring.quick,
|
|
53
|
+
}).start();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const willHideSubscription = Keyboard.addListener('keyboardWillHide', () => {
|
|
57
|
+
Animated.spring(keyboardHeight, {
|
|
58
|
+
toValue: 0,
|
|
59
|
+
useNativeDriver: true,
|
|
60
|
+
...spring.quick,
|
|
61
|
+
}).start();
|
|
62
|
+
});
|
|
63
|
+
return () => {
|
|
64
|
+
willShowSubscription.remove();
|
|
65
|
+
willHideSubscription.remove();
|
|
66
|
+
};
|
|
67
|
+
} else {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}, [keyboardHeight]);
|
|
71
|
+
|
|
72
|
+
return keyboardHeight;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const spring = {
|
|
76
|
+
quick: {
|
|
77
|
+
stiffness: 800,
|
|
78
|
+
damping: 55,
|
|
79
|
+
mass: 1,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { EventEmitterSchema } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @name VisibilityChangedEventEmitter
|
|
5
|
+
* @kind typedef
|
|
6
|
+
* @platform iOS
|
|
7
|
+
* @description
|
|
8
|
+
* Emits an event when the visibility state of the app changes.
|
|
9
|
+
* Only available on iOS.
|
|
10
|
+
*/
|
|
11
|
+
export type VisibilityChangedEventEmitter = EventEmitterSchema<'visibilityChanged', [boolean]>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './nativeEventEmitter';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { EmitterSubscription, NativeEventEmitter } from 'react-native';
|
|
2
|
+
import { EventEmitters } from './eventEmitters';
|
|
3
|
+
import { GraniteCoreModule } from '../native-modules';
|
|
4
|
+
import { EventEmitterSchema } from './eventEmitters/types';
|
|
5
|
+
|
|
6
|
+
type MapOf<T> = T extends EventEmitterSchema<infer K, any> ? { [key in K]: T } : never;
|
|
7
|
+
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
8
|
+
type EventEmittersMap = UnionToIntersection<MapOf<EventEmitters>>;
|
|
9
|
+
type EventKeys = keyof EventEmittersMap;
|
|
10
|
+
type ParamOf<K extends EventKeys> = EventEmittersMap[K]['params'];
|
|
11
|
+
interface EventEmitter {
|
|
12
|
+
addListener<Event extends EventKeys>(
|
|
13
|
+
event: Event,
|
|
14
|
+
callback: (...params: ParamOf<Event>) => void
|
|
15
|
+
): EmitterSubscription;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const nativeEventEmitter = new NativeEventEmitter(GraniteCoreModule) as unknown as EventEmitter;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TurboModule, TurboModuleRegistry } from 'react-native';
|
|
2
|
+
|
|
3
|
+
interface GraniteCoreModule extends TurboModule {
|
|
4
|
+
addListener: (eventType: string) => void;
|
|
5
|
+
removeListeners: (count: number) => void;
|
|
6
|
+
importLazy: () => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const GraniteCoreModule = TurboModuleRegistry.getEnforcing<GraniteCoreModule>('GraniteCoreModule');
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { TurboModule, TurboModuleRegistry } from 'react-native';
|
|
2
|
+
|
|
3
|
+
interface GraniteModuleSpec extends TurboModule {
|
|
4
|
+
closeView: () => void;
|
|
5
|
+
schemeUri: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const GraniteModule = TurboModuleRegistry.getEnforcing<GraniteModuleSpec>('GraniteModule');
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { GraniteModule } from './GraniteModule';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
* @category Screen Control
|
|
6
|
+
* @kind function
|
|
7
|
+
* @name closeView
|
|
8
|
+
* @description Function that closes the current screen. It can be used when you want to exit a service by pressing a "Close" button.
|
|
9
|
+
* @returns {Promise<void>}
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ### Close screen with close button
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import { Button } from 'react-native';
|
|
16
|
+
* import { closeView } from '@granite-js/react-native';
|
|
17
|
+
*
|
|
18
|
+
* function CloseButton() {
|
|
19
|
+
* return <Button title="Close" onPress={closeView} />;
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export async function closeView() {
|
|
24
|
+
return GraniteModule.closeView();
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { GraniteModule } from './GraniteModule';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
* @name getSchemeUri
|
|
6
|
+
* @category Environment Check
|
|
7
|
+
* @kind function
|
|
8
|
+
* @description Returns the scheme value when first entering the screen. URI changes due to page navigation are not reflected.
|
|
9
|
+
* @returns {string} Returns the scheme value when first entering the screen.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ### Get initial scheme value
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import { getSchemeUri } from '@granite-js/react-native';
|
|
16
|
+
* import { Text } from 'react-native';
|
|
17
|
+
*
|
|
18
|
+
* function MyPage() {
|
|
19
|
+
* const schemeUri = getSchemeUri();
|
|
20
|
+
*
|
|
21
|
+
* return <Text>Initial scheme value: {schemeUri}</Text>
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function getSchemeUri() {
|
|
26
|
+
return GraniteModule.schemeUri;
|
|
27
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Linking } from 'react-native';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
* @kind function
|
|
6
|
+
* @category Screen Navigation
|
|
7
|
+
*
|
|
8
|
+
* @name openURL
|
|
9
|
+
* @signature
|
|
10
|
+
* ```typescript
|
|
11
|
+
* function openURL(url: string): Promise<any>;
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* @description
|
|
15
|
+
* Opens the specified URL in the device's default browser or related app.
|
|
16
|
+
* This function uses the [`Linking.openURL`](https://reactnative.dev/docs/0.72/linking#openurl) method from `react-native` to open the URL.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} url URL address to open
|
|
19
|
+
* @returns {Promise<any>} Promise that resolves when the URL is successfully opened
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
*
|
|
23
|
+
* ### Open external URL
|
|
24
|
+
*
|
|
25
|
+
* ```tsx
|
|
26
|
+
* import { openURL } from '@granite-js/react-native';
|
|
27
|
+
* import { Button } from 'react-native';
|
|
28
|
+
*
|
|
29
|
+
* function Page() {
|
|
30
|
+
* const handlePress = () => {
|
|
31
|
+
* openURL('https://google.com');
|
|
32
|
+
* };
|
|
33
|
+
*
|
|
34
|
+
* return <Button title="Open Google Website" onPress={handlePress} />;
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export function openURL(url: string): Promise<any> {
|
|
39
|
+
return Linking.openURL(url);
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useWaitForReturnNavigator } from './useWaitForReturnNavigator';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useNavigation } from '@granite-js/native/@react-navigation/native';
|
|
2
|
+
import { NativeStackNavigationProp } from '@granite-js/native/@react-navigation/native-stack';
|
|
3
|
+
import { useCallback, useRef } from 'react';
|
|
4
|
+
import { useVisibilityChange, type VisibilityState } from '../visibility';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @public
|
|
8
|
+
* @category Screen Control
|
|
9
|
+
* @name useWaitForReturnNavigator
|
|
10
|
+
* @description
|
|
11
|
+
* A Hook that helps execute the next code synchronously when returning from a screen transition.
|
|
12
|
+
* Screen navigation uses [@react-navigation/native `useNavigation`'s `navigate`](https://reactnavigation.org/docs/6.x/navigation-prop#navigate).
|
|
13
|
+
*
|
|
14
|
+
* For example, it can be used when you want to log that a user has navigated to another screen and returned.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ### Example of code execution when returning from screen navigation
|
|
18
|
+
*
|
|
19
|
+
* When the **"Navigate"** button is pressed, it navigates to another screen, and logs are created when returning.
|
|
20
|
+
*
|
|
21
|
+
* ```tsx
|
|
22
|
+
* import { Button } from 'react-native';
|
|
23
|
+
* import { useWaitForReturnNavigator } from '@granite-js/react-native';
|
|
24
|
+
*
|
|
25
|
+
* export function UseWaitForReturnNavigator() {
|
|
26
|
+
* const navigate = useWaitForReturnNavigator();
|
|
27
|
+
*
|
|
28
|
+
* return (
|
|
29
|
+
* <>
|
|
30
|
+
* <Button
|
|
31
|
+
* title="Navigate"
|
|
32
|
+
* onPress={async () => {
|
|
33
|
+
* console.log(1);
|
|
34
|
+
* await navigate('/examples/use-visibility');
|
|
35
|
+
* // This code executes when returning to the screen
|
|
36
|
+
* console.log(2);
|
|
37
|
+
* }}
|
|
38
|
+
* />
|
|
39
|
+
* </>
|
|
40
|
+
* );
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
export function useWaitForReturnNavigator<T extends Record<string, object | undefined>>() {
|
|
46
|
+
const callbacks = useRef<Array<() => void>>([]).current;
|
|
47
|
+
const navigation = useNavigation<NativeStackNavigationProp<T>>();
|
|
48
|
+
|
|
49
|
+
const startNavigating = useCallback(
|
|
50
|
+
<RouteName extends keyof T>(route: RouteName, params?: T[RouteName]): Promise<void> => {
|
|
51
|
+
return new Promise<void>((resolve) => {
|
|
52
|
+
callbacks.push(resolve);
|
|
53
|
+
navigation.navigate(route as any, params as any);
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
[callbacks, navigation]
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const handleVisibilityChange = useCallback(
|
|
60
|
+
(state: VisibilityState) => {
|
|
61
|
+
if (state === 'visible' && callbacks.length > 0) {
|
|
62
|
+
for (const callback of callbacks) {
|
|
63
|
+
callback();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
callbacks.splice(0, callbacks.length);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
[callbacks]
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
useVisibilityChange(handleVisibilityChange);
|
|
73
|
+
|
|
74
|
+
return startNavigating;
|
|
75
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polyfill for @swc/helpers build compatibility
|
|
3
|
+
*
|
|
4
|
+
* @see https://github.com/swc-project/swc/blob/v1.4.15/packages/helpers/esm/_async_iterator.js#L3
|
|
5
|
+
*
|
|
6
|
+
* - babel: No runtime issues after build as there is a fallback for `Symbol.asyncIterator`
|
|
7
|
+
* - swc: No fallback for `Symbol.asyncIterator`, so it needs to be defined in advance
|
|
8
|
+
*/
|
|
9
|
+
export function setup() {
|
|
10
|
+
if (typeof Symbol !== 'undefined' && !Symbol.asyncIterator) {
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
12
|
+
// @ts-ignore
|
|
13
|
+
Symbol.asyncIterator = Symbol.for('@@asyncIterator');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { setupURLPolyfill as setup } from 'react-native-url-polyfill';
|