@granite-js/react-native 0.0.1
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 +66 -0
- 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/Granite.d.ts +60 -0
- package/dist/app/index.d.ts +2 -0
- package/dist/app/registerPage.d.ts +20 -0
- package/dist/async-bridges.d.ts +2 -0
- package/dist/constant-bridges.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 +17 -0
- package/dist/initial-props/InitialProps.d.ts +127 -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 +1 -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 +53 -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/types/global.d.ts +18 -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/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 +92 -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/Granite.tsx +130 -0
- package/src/app/index.ts +2 -0
- package/src/app/registerPage.ts +29 -0
- package/src/async-bridges.ts +2 -0
- package/src/constant-bridges.ts +1 -0
- package/src/dev-entrypoint/index.tsx +21 -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 +19 -0
- package/src/initial-props/InitialProps.ts +144 -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 +1 -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 +111 -0
- package/src/scroll-view-inertial-background/ScrollViewInertialBackground.tsx +99 -0
- package/src/scroll-view-inertial-background/index.ts +1 -0
- package/src/types/global.ts +31 -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/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,41 @@
|
|
|
1
|
+
import { PropsWithChildren, ReactElement } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* @name VisibilityChangedProvider
|
|
4
|
+
* @kind function
|
|
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.
|
|
8
|
+
*
|
|
9
|
+
* @param {ReactNode | undefined} children - Child components that check screen visibility.
|
|
10
|
+
* @param {boolean} isVisible - A boolean value indicating whether the screen is visible.
|
|
11
|
+
* @returns {ReactElement} - A React Provider component wrapped with `VisibilityChangedContext.Provider`.
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* export function VisibilityProvider({ isVisible, children }: Props) {
|
|
15
|
+
* return (
|
|
16
|
+
* <VisibilityChangedProvider isVisible={isVisible}>
|
|
17
|
+
* {children}
|
|
18
|
+
* </VisibilityChangedProvider>
|
|
19
|
+
* );
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function VisibilityChangedProvider({ children, isVisible, }: PropsWithChildren<{
|
|
24
|
+
isVisible: boolean;
|
|
25
|
+
}>): ReactElement;
|
|
26
|
+
/**
|
|
27
|
+
* @name useVisibilityChanged
|
|
28
|
+
* @category Hooks
|
|
29
|
+
* @kind function
|
|
30
|
+
* @description
|
|
31
|
+
* A Hook that returns whether a React Native screen is visible.
|
|
32
|
+
* @returns {boolean} - Returns whether the screen is visible.
|
|
33
|
+
* @throws {Error} Throws an error when not used within a `VisibilityChangedProvider`.
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const isVisible = useVisibilityChanged();
|
|
37
|
+
* console.log(isVisible);
|
|
38
|
+
* // true or false
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function useVisibilityChanged(): boolean;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @name usePrevious
|
|
3
|
+
* @category Hooks
|
|
4
|
+
* @kind function
|
|
5
|
+
* @description
|
|
6
|
+
* A Hook that returns the previous value.
|
|
7
|
+
* @param {T} value - The value to return the previous value of.
|
|
8
|
+
* @returns {T} - Returns the previous value.
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const isVisible = useVisibility();
|
|
12
|
+
* const prevValue = usePrevious<boolean>(isVisible) ?? false;
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare function usePrevious<T>(value: T): T;
|
package/jest.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dist/jest';
|
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@granite-js/react-native",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "The Granite Framework",
|
|
5
|
+
"bin": {
|
|
6
|
+
"granite": "./bin/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"typecheck": "tsc --noEmit",
|
|
10
|
+
"test": "vitest --no-watch",
|
|
11
|
+
"lint": "eslint .",
|
|
12
|
+
"build": "tsup && tsc -p tsconfig.build.json",
|
|
13
|
+
"prepack": "yarn build"
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./src/index.ts"
|
|
21
|
+
},
|
|
22
|
+
"./async-bridges": {
|
|
23
|
+
"types": "./src/async-bridges.ts",
|
|
24
|
+
"default": "./src/async-bridges.ts"
|
|
25
|
+
},
|
|
26
|
+
"./constant-bridges": {
|
|
27
|
+
"types": "./src/constant-bridges.ts",
|
|
28
|
+
"default": "./src/constant-bridges.ts"
|
|
29
|
+
},
|
|
30
|
+
"./jest": {
|
|
31
|
+
"types": "./dist/jest/index.d.ts",
|
|
32
|
+
"default": "./dist/jest/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./cli": {
|
|
35
|
+
"types": "./cli.d.ts",
|
|
36
|
+
"default": "./cli.js"
|
|
37
|
+
},
|
|
38
|
+
"./config": {
|
|
39
|
+
"types": "./config.d.ts",
|
|
40
|
+
"default": "./config.js"
|
|
41
|
+
},
|
|
42
|
+
"./types": {
|
|
43
|
+
"types": "./dist/types/global.d.ts"
|
|
44
|
+
},
|
|
45
|
+
"./package.json": "./package.json"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"src",
|
|
49
|
+
"dist",
|
|
50
|
+
"bin",
|
|
51
|
+
"config.js",
|
|
52
|
+
"cli.js",
|
|
53
|
+
"*.d.ts"
|
|
54
|
+
],
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@babel/core": "^7.24.9",
|
|
57
|
+
"@babel/preset-env": "^7.24.8",
|
|
58
|
+
"@babel/preset-typescript": "^7.24.7",
|
|
59
|
+
"@granite-js/native": "0.0.1",
|
|
60
|
+
"@testing-library/dom": "^10.4.0",
|
|
61
|
+
"@testing-library/react": "^16.1.0",
|
|
62
|
+
"@types/babel__core": "^7",
|
|
63
|
+
"@types/babel__preset-env": "^7",
|
|
64
|
+
"@types/node": "^22.10.2",
|
|
65
|
+
"@types/react": "18.3.3",
|
|
66
|
+
"@types/react-dom": "^18",
|
|
67
|
+
"esbuild": "^0.25.4",
|
|
68
|
+
"eslint": "^9.7.0",
|
|
69
|
+
"jsdom": "^25.0.1",
|
|
70
|
+
"react": "18.2.0",
|
|
71
|
+
"react-dom": "18.2.0",
|
|
72
|
+
"react-native": "0.72.6",
|
|
73
|
+
"tsup": "^8.5.0",
|
|
74
|
+
"typescript": "5.8.3",
|
|
75
|
+
"vitest": "^2.1.8"
|
|
76
|
+
},
|
|
77
|
+
"peerDependencies": {
|
|
78
|
+
"@granite-js/native": "*",
|
|
79
|
+
"@types/react": "*",
|
|
80
|
+
"react": "*",
|
|
81
|
+
"react-native": "*"
|
|
82
|
+
},
|
|
83
|
+
"dependencies": {
|
|
84
|
+
"@granite-js/cli": "0.0.1",
|
|
85
|
+
"@granite-js/image": "0.0.1",
|
|
86
|
+
"@granite-js/jest": "0.0.1",
|
|
87
|
+
"@granite-js/mpack": "0.0.1",
|
|
88
|
+
"@granite-js/style-utils": "0.0.1",
|
|
89
|
+
"es-toolkit": "^1.34.1",
|
|
90
|
+
"react-native-url-polyfill": "1.3.0"
|
|
91
|
+
}
|
|
92
|
+
}
|
package/presets.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dist/presets';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { VisibilityProvider } from '../../visibility';
|
|
3
|
+
import { Props } from '.';
|
|
4
|
+
|
|
5
|
+
type IOSInitialProps = Props & {
|
|
6
|
+
isVisible: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function App({ children, ...props }: IOSInitialProps) {
|
|
10
|
+
const { isVisible } = useMemo(() => props, [props]);
|
|
11
|
+
|
|
12
|
+
return <VisibilityProvider isVisible={isVisible}>{children}</VisibilityProvider>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { SafeAreaProvider } from '@granite-js/native/react-native-safe-area-context';
|
|
2
|
+
import { ComponentType, PropsWithChildren } from 'react';
|
|
3
|
+
import { App } from './App';
|
|
4
|
+
import { registerPage } from './registerPage';
|
|
5
|
+
import type { InitialProps } from '../initial-props';
|
|
6
|
+
import { Router, type RouterProps, type RequireContext } from '../router';
|
|
7
|
+
import { BackEventProvider, useBackEventState } from '../use-back-event';
|
|
8
|
+
|
|
9
|
+
export interface GraniteProps {
|
|
10
|
+
/**
|
|
11
|
+
* @description
|
|
12
|
+
* The name of the app.
|
|
13
|
+
*/
|
|
14
|
+
appName: string;
|
|
15
|
+
/**
|
|
16
|
+
* @description
|
|
17
|
+
* The context of the app.
|
|
18
|
+
*
|
|
19
|
+
* @TODO Hide context
|
|
20
|
+
*/
|
|
21
|
+
context: RequireContext;
|
|
22
|
+
/**
|
|
23
|
+
* @description
|
|
24
|
+
* Configuration object to be passed to the router.
|
|
25
|
+
*/
|
|
26
|
+
router?: RouterProps;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
interface AppRootProps extends GraniteProps {
|
|
33
|
+
container: ComponentType<PropsWithChildren<InitialProps>>;
|
|
34
|
+
initialProps: InitialProps;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const scheme = global.__granite.app.scheme;
|
|
38
|
+
|
|
39
|
+
function AppRoot({ appName, context, container, initialProps, router }: AppRootProps) {
|
|
40
|
+
const backEventState = useBackEventState();
|
|
41
|
+
const baseScheme = `${scheme}://${appName}`;
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<App {...initialProps}>
|
|
45
|
+
<SafeAreaProvider>
|
|
46
|
+
<BackEventProvider backEvent={backEventState}>
|
|
47
|
+
<Router
|
|
48
|
+
context={context}
|
|
49
|
+
initialProps={initialProps}
|
|
50
|
+
container={container}
|
|
51
|
+
canGoBack={!backEventState.hasBackEvent}
|
|
52
|
+
onBack={backEventState.onBack}
|
|
53
|
+
prefix={baseScheme}
|
|
54
|
+
{...router}
|
|
55
|
+
/>
|
|
56
|
+
</BackEventProvider>
|
|
57
|
+
</SafeAreaProvider>
|
|
58
|
+
</App>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const createApp = () => {
|
|
63
|
+
let _appName: string | null = null;
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
registerApp(
|
|
67
|
+
AppContainer: ComponentType<PropsWithChildren<InitialProps>>,
|
|
68
|
+
{ appName, context, router }: GraniteProps
|
|
69
|
+
): (initialProps: InitialProps) => JSX.Element {
|
|
70
|
+
function Root(initialProps: InitialProps) {
|
|
71
|
+
return (
|
|
72
|
+
<AppRoot
|
|
73
|
+
container={AppContainer}
|
|
74
|
+
initialProps={initialProps}
|
|
75
|
+
appName={appName}
|
|
76
|
+
context={context}
|
|
77
|
+
router={router}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
registerPage(Root);
|
|
83
|
+
_appName = appName;
|
|
84
|
+
return Root;
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
get appName(): string {
|
|
88
|
+
if (_appName === null) {
|
|
89
|
+
throw new Error('Granite.appName can only be used after registerApp has been called.');
|
|
90
|
+
}
|
|
91
|
+
return _appName;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @public
|
|
98
|
+
* @category Core
|
|
99
|
+
* @name Granite
|
|
100
|
+
* @description
|
|
101
|
+
*
|
|
102
|
+
* @property {RegisterService} registerApp - This function sets up the basic environment for your service and helps you start service development quickly without needing complex configuration. By just passing `appName`, you can immediately use various features such as file-based routing, query parameter handling, and back navigation control.
|
|
103
|
+
*
|
|
104
|
+
* The features provided by the `Granite.registerApp` function are as follows:
|
|
105
|
+
* - Routing: URLs are automatically mapped according to file paths. It works similarly to Next.js's file-based routing. For example, the `/my-service/pages/index.ts` file can be accessed at `scheme://my-service`, and the `/my-service/pages/home.ts` file can be accessed at `scheme://my-service/home`.
|
|
106
|
+
* - Query Parameters: You can easily use query parameters received through URL schemes. For example, you can receive a `referrer` parameter and log it.
|
|
107
|
+
* - Back Navigation Control: You can control back navigation events. For example, when a user presses back on a screen, you can show a dialog or close the screen.
|
|
108
|
+
* - Screen Visibility: You can determine whether a screen is visible or hidden from the user. For example, you can use this value to handle specific actions when a user leaves for the home screen.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
*
|
|
112
|
+
* ### Example of creating with the `Granite` component
|
|
113
|
+
*
|
|
114
|
+
* ```tsx
|
|
115
|
+
* import { PropsWithChildren } from 'react';
|
|
116
|
+
* import { Granite, InitialProps } from '@granite-js/react-native';
|
|
117
|
+
* import { context } from '../require.context';
|
|
118
|
+
*
|
|
119
|
+
* function AppContainer({ children }: PropsWithChildren<InitialProps>) {
|
|
120
|
+
* return <>{children}</>;
|
|
121
|
+
* }
|
|
122
|
+
*
|
|
123
|
+
* export default Granite.registerApp(AppContainer, {
|
|
124
|
+
* appName: 'my-app',
|
|
125
|
+
* context,
|
|
126
|
+
* });
|
|
127
|
+
*
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export const Granite = createApp();
|
package/src/app/index.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
|
|
3
|
+
const globalThis = new Function('return this')();
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @kind function
|
|
7
|
+
* @name registerPage
|
|
8
|
+
* @description
|
|
9
|
+
* Function to register a page component.
|
|
10
|
+
*
|
|
11
|
+
* @param {ComponentType<any>} Page - The page component to register
|
|
12
|
+
* @returns {ComponentType<any>} Returns the registered page component
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { registerPage } from './path/to/module';
|
|
17
|
+
*
|
|
18
|
+
* const MyPage: React.FC = () => <div>My Page</div>;
|
|
19
|
+
*
|
|
20
|
+
* registerPage(MyPage);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export function registerPage(Page: ComponentType<any>): ComponentType<any> {
|
|
24
|
+
if (globalThis.__SPLIT_CHUNK_ENABLED__) {
|
|
25
|
+
globalThis.Page = Page;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return Page;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './native-modules/natives/getSchemeUri';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import type { ComponentType } from 'react';
|
|
4
|
+
import { AppRegistry } from 'react-native';
|
|
5
|
+
import { setup } from '../rn-polyfills';
|
|
6
|
+
|
|
7
|
+
setup();
|
|
8
|
+
|
|
9
|
+
let registered = false;
|
|
10
|
+
|
|
11
|
+
export function register(Component: ComponentType<any>) {
|
|
12
|
+
if (registered) {
|
|
13
|
+
throw new Error('둘 이상의 Page를 register할 수 없습니다. entrypoint에서 1회에 한해 호출해주세요.');
|
|
14
|
+
}
|
|
15
|
+
registered = true;
|
|
16
|
+
|
|
17
|
+
const component = (props: any) => <Component {...props} />;
|
|
18
|
+
|
|
19
|
+
AppRegistry.registerComponent('Page', () => component);
|
|
20
|
+
AppRegistry.registerComponent('shared', () => component);
|
|
21
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
export abstract class GraniteEventDefinition<O = undefined, R = void> {
|
|
2
|
+
// Event name (literal type recommended)
|
|
3
|
+
abstract name: string;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Method to implement event logic.
|
|
7
|
+
* @param options - Event options (undefined if not used)
|
|
8
|
+
* @param onEvent - Callback called when the event is triggered.
|
|
9
|
+
* @param onError - Callback called on error (error type: unknown)
|
|
10
|
+
*/
|
|
11
|
+
abstract listener(options: O, onEvent: (response: R) => void, onError: (error: unknown) => void): void;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Additional logic to execute when removing the event listener.
|
|
15
|
+
* If needed in the plugin, override this method and it will be called
|
|
16
|
+
* when the listener is removed.
|
|
17
|
+
*/
|
|
18
|
+
abstract remove(): void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* GraniteEvent class registers event definition instances passed to the constructor.
|
|
23
|
+
* The addEventListener method only allows registered event names, and the types of passed
|
|
24
|
+
* options/callbacks are automatically inferred.
|
|
25
|
+
*
|
|
26
|
+
* Each subscription is uniquely identified by (event name + options).
|
|
27
|
+
*/
|
|
28
|
+
export class GraniteEvent<Defs extends GraniteEventDefinition<unknown, unknown>> {
|
|
29
|
+
private definitions = new Map<Defs['name'], Defs>();
|
|
30
|
+
private subscriptionGroups = new Map<
|
|
31
|
+
string,
|
|
32
|
+
{
|
|
33
|
+
eventName: Defs['name'];
|
|
34
|
+
options: Extract<Defs, { name: Defs['name'] }> extends GraniteEventDefinition<infer O, any> ? O : never;
|
|
35
|
+
callbacks: Array<{
|
|
36
|
+
onEvent: (response: any) => void;
|
|
37
|
+
onError?: (error: unknown) => void;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
>();
|
|
41
|
+
|
|
42
|
+
constructor(defs: Defs[]) {
|
|
43
|
+
for (const def of defs) {
|
|
44
|
+
this.definitions.set(def.name, def);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
addEventListener<E extends Defs['name']>(
|
|
49
|
+
eventName: E,
|
|
50
|
+
listener: Extract<Defs, { name: E }> extends GraniteEventDefinition<infer O, infer R>
|
|
51
|
+
? [O] extends [undefined]
|
|
52
|
+
? { onEvent: (response: R) => void; onError?: (error: unknown) => void }
|
|
53
|
+
: { options: O; onEvent: (response: R) => void; onError?: (error: unknown) => void }
|
|
54
|
+
: never
|
|
55
|
+
): () => void {
|
|
56
|
+
const def = this.definitions.get(eventName);
|
|
57
|
+
if (!def) {
|
|
58
|
+
throw new Error(`Event "${String(eventName)}" is not registered.`);
|
|
59
|
+
}
|
|
60
|
+
const opts = (listener as any).options;
|
|
61
|
+
const key = this._getKey(String(eventName), opts);
|
|
62
|
+
let group = this.subscriptionGroups.get(key);
|
|
63
|
+
|
|
64
|
+
if (!group) {
|
|
65
|
+
group = {
|
|
66
|
+
eventName,
|
|
67
|
+
options: opts,
|
|
68
|
+
callbacks: [],
|
|
69
|
+
};
|
|
70
|
+
this.subscriptionGroups.set(key, group);
|
|
71
|
+
|
|
72
|
+
const aggregatedOnEvent = (response: any) => {
|
|
73
|
+
for (const cb of group!.callbacks) {
|
|
74
|
+
cb.onEvent(response);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const aggregatedOnError = (error: unknown) => {
|
|
78
|
+
for (const cb of group!.callbacks) {
|
|
79
|
+
if (cb.onError) {
|
|
80
|
+
cb.onError(error);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
def.listener(group.options, aggregatedOnEvent, aggregatedOnError);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
group.callbacks.push({ onEvent: listener.onEvent, onError: listener.onError });
|
|
89
|
+
|
|
90
|
+
return () => {
|
|
91
|
+
const grp = this.subscriptionGroups.get(key);
|
|
92
|
+
if (!grp) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
grp.callbacks = grp.callbacks.filter((cb) => cb.onEvent !== listener.onEvent);
|
|
96
|
+
if (grp.callbacks.length === 0) {
|
|
97
|
+
def.remove();
|
|
98
|
+
this.subscriptionGroups.delete(key);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
emit<E extends Defs['name']>(
|
|
104
|
+
eventName: E,
|
|
105
|
+
data: Extract<Defs, { name: E }> extends GraniteEventDefinition<unknown, infer R> ? R : never
|
|
106
|
+
) {
|
|
107
|
+
const def = this.definitions.get(eventName);
|
|
108
|
+
if (!def) {
|
|
109
|
+
throw new Error(`Event "${String(eventName)}" is not registered.`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const group of this.subscriptionGroups.values()) {
|
|
113
|
+
if (group.eventName === eventName) {
|
|
114
|
+
for (const callback of group.callbacks) {
|
|
115
|
+
try {
|
|
116
|
+
callback.onEvent(data);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (callback.onError) {
|
|
119
|
+
callback.onError(error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private _getKey(eventName: string, options: unknown): string {
|
|
128
|
+
return `${eventName}:${options ? JSON.stringify(options) : ''}`;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { GraniteEvent, GraniteEventDefinition } from './abstract';
|
|
3
|
+
import { BackEventControls, useBackEvent } from '../use-back-event/useBackEvent';
|
|
4
|
+
|
|
5
|
+
class BackEvent extends GraniteEventDefinition<void, void> {
|
|
6
|
+
name = 'backEvent' as const;
|
|
7
|
+
|
|
8
|
+
ref = {
|
|
9
|
+
remove: () => {},
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
constructor(private backEventControls: BackEventControls) {
|
|
13
|
+
super();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
remove() {
|
|
17
|
+
this.ref.remove();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
listener(_: void, onEvent: (response: void) => void): void {
|
|
21
|
+
this.backEventControls.addEventListener(onEvent);
|
|
22
|
+
this.ref.remove = () => this.backEventControls.removeEventListener(onEvent);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function useGraniteEvent() {
|
|
27
|
+
const backEvent = useBackEvent();
|
|
28
|
+
|
|
29
|
+
const event = useMemo(() => {
|
|
30
|
+
return new GraniteEvent([new BackEvent(backEvent)]);
|
|
31
|
+
}, [backEvent]);
|
|
32
|
+
|
|
33
|
+
return event;
|
|
34
|
+
}
|