@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,135 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
excludeDynamicNamePattern,
|
|
4
|
+
excludeFileExtension,
|
|
5
|
+
excludeRelativePath,
|
|
6
|
+
getFileNameFromPath,
|
|
7
|
+
getRoutePath,
|
|
8
|
+
} from './path';
|
|
9
|
+
|
|
10
|
+
describe('excludeFileExtension', () => {
|
|
11
|
+
it('"./index.ts" is changed to "./index"', () => {
|
|
12
|
+
expect(excludeFileExtension('./index.ts')).toEqual('./index');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('"./index.js" is changed to "./index"', () => {
|
|
16
|
+
expect(excludeFileExtension('./index.js')).toEqual('./index');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('"./list/detail.tsx" is changed to "./list/detail"', () => {
|
|
20
|
+
expect(excludeFileExtension('./list/detail.tsx')).toEqual('./list/detail');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('"./list/detail.jsx" is changed to "./list/detail"', () => {
|
|
24
|
+
expect(excludeFileExtension('./list/detail.jsx')).toEqual('./list/detail');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('"./detail.foo" remains as "./detail.foo"', () => {
|
|
28
|
+
expect(excludeFileExtension('./detail.foo')).toEqual('./detail.foo');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('excludeRelativePath', () => {
|
|
33
|
+
it('"./index.tsx" is changed to "index.tsx"', () => {
|
|
34
|
+
expect(excludeRelativePath('./index.tsx')).toEqual('index.tsx');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('"./../index.tsx" is changed to "index.tsx"', () => {
|
|
38
|
+
expect(excludeRelativePath('./../index.tsx')).toEqual('index.tsx');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('"../../index.tsx" is changed to "index.tsx"', () => {
|
|
42
|
+
expect(excludeRelativePath('../../index.tsx')).toEqual('index.tsx');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('excludeDynamicNamePattern', () => {
|
|
47
|
+
it('"[id]" is changed to "id"', () => {
|
|
48
|
+
expect(excludeDynamicNamePattern('[id]')).toEqual('id');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('"[id]/[name]" is changed to "id/name"', () => {
|
|
52
|
+
expect(excludeDynamicNamePattern('[id]/[name]')).toEqual('id/name');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('getRoutePath', () => {
|
|
57
|
+
describe('posix', () => {
|
|
58
|
+
it('"/index.tsx" is converted to "/"', () => {
|
|
59
|
+
expect(getRoutePath('/index.tsx')).toEqual('/');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('"/index.ts" is converted to "/"', () => {
|
|
63
|
+
expect(getRoutePath('/index.tsx')).toEqual('/');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('"/list/index.tsx" is converted to "/list"', () => {
|
|
67
|
+
expect(getRoutePath('/list/index.tsx')).toEqual('/list');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('"/list/detail.tsx" is converted to "/list"', () => {
|
|
71
|
+
expect(getRoutePath('/list/detail.tsx')).toEqual('/list/detail');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('"/list/[id].tsx" is converted to "/:id"', () => {
|
|
75
|
+
expect(getRoutePath('/list/[id].tsx')).toEqual('/list/:id');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('"/list/[id].js" is converted to "/:id"', () => {
|
|
79
|
+
expect(getRoutePath('/list/[id].js')).toEqual('/list/:id');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('window', () => {
|
|
84
|
+
it('"\\index.tsx" is converted to "/"', () => {
|
|
85
|
+
expect(getRoutePath('\\index.tsx')).toEqual('/');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('"\\index.ts" is converted to "/"', () => {
|
|
89
|
+
expect(getRoutePath('\\index.tsx')).toEqual('/');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('"\\list\\index.tsx" is converted to "/list"', () => {
|
|
93
|
+
expect(getRoutePath('\\list\\index.tsx')).toEqual('/list');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('"\\list\\detail.tsx" is converted to "/list"', () => {
|
|
97
|
+
expect(getRoutePath('\\list\\detail.tsx')).toEqual('/list/detail');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('"\\list\\[id].tsx" is converted to "/:id"', () => {
|
|
101
|
+
expect(getRoutePath('\\list\\[id].tsx')).toEqual('/list/:id');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('"\\list\\[id].js" is converted to "/:id"', () => {
|
|
105
|
+
expect(getRoutePath('\\list\\[id].js')).toEqual('/list/:id');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('getFileNameFromPath', () => {
|
|
111
|
+
it('extracts filename from file path', () => {
|
|
112
|
+
const result = getFileNameFromPath('/path/to/file.txt');
|
|
113
|
+
expect(result).toBe('file.txt');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('returns the filename when path is just a filename', () => {
|
|
117
|
+
const result = getFileNameFromPath('file.txt');
|
|
118
|
+
expect(result).toBe('file.txt');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('returns empty string when path is empty', () => {
|
|
122
|
+
const result = getFileNameFromPath('');
|
|
123
|
+
expect(result).toBe('');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('returns empty string when path ends with slash', () => {
|
|
127
|
+
const result = getFileNameFromPath('/path/to/directory/');
|
|
128
|
+
expect(result).toBe('');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('extracts filename without extension using withExtension option', () => {
|
|
132
|
+
const result = getFileNameFromPath('/path/to/file.txt', { withExtension: false });
|
|
133
|
+
expect(result).toBe('file');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { matchDynamicName } from './matchers';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Converts Windows-style paths to Posix-based paths.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* toPosixBasedPath(".\\index.tsx") // "./index.tsx"
|
|
9
|
+
* toPosixBasedPath("..\\..\\index.tsx") // "../../index.tsx"
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
function toPosixBasedPath(filePath: string): string {
|
|
13
|
+
return filePath.replace(/\\/g, '/');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @name excludeFileExtension
|
|
18
|
+
* @description Removes file extension.
|
|
19
|
+
*/
|
|
20
|
+
export function excludeFileExtension(name: string): string {
|
|
21
|
+
return name.replace(/\.(tsx|jsx|ts|js)$/g, '');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @name excludeRelativePath
|
|
26
|
+
* @description Removes relative path from path.
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* excludeRelativePath("./index.tsx") // "index.tsx"
|
|
30
|
+
* excludeRelativePath("./list/detail.tsx") // "list/detail.tsx"
|
|
31
|
+
* excludeRelativePath("../../index.tsx") // "index.tsx"
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function excludeRelativePath(filePath: string): string {
|
|
35
|
+
return filePath.replace(/^(?:\.\.?\/)+/g, '');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @name excludeDynamicNamePattern
|
|
40
|
+
* @description Removes `[` `]` from dynamic route pattern.
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* excludeDynamicNamePattern("[id]") // "id"
|
|
44
|
+
* excludeDynamicNamePattern("[id]/[name]") // "id/name"
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function excludeDynamicNamePattern(filePath: string): string {
|
|
48
|
+
return filePath.replace(/\[|\]/g, '');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @name getRoutePath
|
|
53
|
+
* @description Converts to route path.
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* getRoutePath('./index.tsx') // "/"
|
|
57
|
+
* getRoutePath('./list/index.tsx') // "/list"
|
|
58
|
+
* getRoutePath('./list/detail.tsx') // "/list/detail"
|
|
59
|
+
* getRoutePath('./list/[id].js') // "/list/:id"
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export function getRoutePath(filePath: string): string {
|
|
63
|
+
const posixBasedPath = toPosixBasedPath(filePath);
|
|
64
|
+
const normalPath = excludeRelativePath(excludeFileExtension(posixBasedPath));
|
|
65
|
+
|
|
66
|
+
const routePath = normalPath
|
|
67
|
+
.split('/')
|
|
68
|
+
.map((segment) => {
|
|
69
|
+
if (segment === 'index') {
|
|
70
|
+
return '';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (matchDynamicName(segment)) {
|
|
74
|
+
return `:${excludeDynamicNamePattern(segment)}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return segment;
|
|
78
|
+
})
|
|
79
|
+
.filter((segment) => segment.length > 0)
|
|
80
|
+
.join('/');
|
|
81
|
+
|
|
82
|
+
return '/' + routePath;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @name getFileNameFromPath
|
|
87
|
+
* @description Extracts filename from file path.
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* getFileNameFromPath('/path/to/file.txt') // "file.txt"
|
|
91
|
+
* getFileNameFromPath('file.txt') // "file.txt"
|
|
92
|
+
* getFileNameFromPath('') // ""
|
|
93
|
+
* getFileNameFromPath('/path/to/directory/') // ""
|
|
94
|
+
* getFileNameFromPath('/path/to/file.txt', { withExtension: false }) // "file"
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export function getFileNameFromPath(
|
|
98
|
+
filePath: string,
|
|
99
|
+
options: { withExtension?: boolean } = {
|
|
100
|
+
withExtension: true,
|
|
101
|
+
}
|
|
102
|
+
): string {
|
|
103
|
+
const fileName = filePath.split('/').pop() || '';
|
|
104
|
+
return options.withExtension ? fileName : fileName.replace(/\.[^.]+$/g, '');
|
|
105
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { getRoutePath } from './path';
|
|
2
|
+
import { routeMap } from '../createRoute';
|
|
3
|
+
import { RequireContext, RouteScreen } from '../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @kind function
|
|
7
|
+
* @name getRouteScreens
|
|
8
|
+
* @description
|
|
9
|
+
* Gets screens from the pages folder.
|
|
10
|
+
*
|
|
11
|
+
* @param {RequireContext} context - Object containing information about screens in Router
|
|
12
|
+
* @returns {RouteScreen[]} screens - Returns a list of screens that can be navigated to.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* import { getRouteScreens } from '@granite-js/react-native';
|
|
17
|
+
*
|
|
18
|
+
* const context = require.context('../pages');
|
|
19
|
+
* const screens = getRouteScreens(context);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function getRouteScreens(context: RequireContext): RouteScreen[] {
|
|
23
|
+
const screens = context.keys().map((key) => {
|
|
24
|
+
const path = getRoutePath(key);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Keep export default option for backward compatibility.
|
|
28
|
+
* If migrated to type-safe, only export Route will be needed.
|
|
29
|
+
*/
|
|
30
|
+
const component = context(key)?.default ?? routeMap.get(context(key)?.Route?._path)?.component;
|
|
31
|
+
|
|
32
|
+
if (component == null) {
|
|
33
|
+
throw new Error(`Page component not found in ${key}.`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
path,
|
|
38
|
+
component,
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return screens;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @kind function
|
|
47
|
+
* @name getScreenPathMapConfig
|
|
48
|
+
* @description Maps paths of screens.
|
|
49
|
+
*
|
|
50
|
+
* @param {RouteScreen[]} routeScreens - List of screens that can be navigated to
|
|
51
|
+
*/
|
|
52
|
+
export function getScreenPathMapConfig(routeScreens: RouteScreen[]) {
|
|
53
|
+
const screensConfig: ScreenPath = {};
|
|
54
|
+
|
|
55
|
+
routeScreens.forEach((routeScreen) => {
|
|
56
|
+
const routePath = routeScreen.path;
|
|
57
|
+
|
|
58
|
+
if (screensConfig[routePath] != null) {
|
|
59
|
+
throw new Error(`${routePath} is already registered. Please check for duplicate paths.`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
screensConfig[routePath] = {
|
|
63
|
+
path: routePath,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// @see https://reactnavigation.org/docs/configuring-links/#matching-exact-paths
|
|
68
|
+
// This is a mapping for the root path ('/') for deep link handling.
|
|
69
|
+
// Example: To handle URLs like scheme://{service_name}?name=John,
|
|
70
|
+
// map the root path to an empty string to correctly extract query parameters.
|
|
71
|
+
screensConfig['/'] = {
|
|
72
|
+
path: '',
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// https://reactnavigation.org/docs/configuring-links/#handling-unmatched-routes-or-404
|
|
76
|
+
screensConfig['/_404'] = {
|
|
77
|
+
path: '*',
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return screensConfig;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @name ScreenPath
|
|
85
|
+
* @description
|
|
86
|
+
* Type representing screen paths.
|
|
87
|
+
*
|
|
88
|
+
* @typedef {Record<string, { path?: string }>} ScreenPath
|
|
89
|
+
*/
|
|
90
|
+
export type ScreenPath = Record<
|
|
91
|
+
string,
|
|
92
|
+
{
|
|
93
|
+
path?: string;
|
|
94
|
+
}
|
|
95
|
+
>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
import { useWindowDimensions, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
interface ScrollViewInertialBackgroundProps {
|
|
5
|
+
topColor?: string;
|
|
6
|
+
bottomColor?: string;
|
|
7
|
+
spacer?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @public
|
|
12
|
+
* @category UI
|
|
13
|
+
* @name ScrollViewInertialBackground
|
|
14
|
+
* @description
|
|
15
|
+
* Adds background colors to the top and bottom spaces of iOS `ScrollView` content to provide a natural visual effect when scrolling.
|
|
16
|
+
* In iOS, when scrolling reaches the end, a slight bouncing effect occurs, known as the [Bounce effect](https://medium.com/@wcandillon/ios-bounce-list-effect-with-react-native-5102e3a83999). Setting background colors in the spaces above and below the content can provide a more consistent user experience.
|
|
17
|
+
*
|
|
18
|
+
* @param {object} [props] - `props` object passed to the component.
|
|
19
|
+
* @param {string} [props.topColor] - Background color to apply to the top area of the scroll. Default is `adaptive.background` which is automatically applied based on the system theme.
|
|
20
|
+
* @param {string} [props.bottomColor] - Background color to apply to the bottom area of the scroll. Default is `adaptive.background` which is automatically applied based on the system theme.
|
|
21
|
+
* @param {number} [props.spacer] - Specifies the size of the space between the top and bottom areas where the background color is applied. Default uses the screen height obtained from [`useWindowDimensions`](https://reactnative.dev/docs/next/usewindowdimensions).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
*
|
|
25
|
+
* ### Adding background colors to the top and bottom of a scroll view
|
|
26
|
+
*
|
|
27
|
+
* Adds red background color to the top and blue to the bottom of the scroll view. The background color is applied to areas outside the scroll.
|
|
28
|
+
*
|
|
29
|
+
* ```tsx
|
|
30
|
+
* import { ScrollView, View, Text } from 'react-native';
|
|
31
|
+
* import { ScrollViewInertialBackground } from '@granite-js/react-native';
|
|
32
|
+
*
|
|
33
|
+
* const dummies = Array.from({ length: 20 }, (_, i) => i);
|
|
34
|
+
*
|
|
35
|
+
* export function InertialBackgroundExample() {
|
|
36
|
+
* return (
|
|
37
|
+
* <ScrollView>
|
|
38
|
+
* <ScrollViewInertialBackground topColor="red" bottomColor="blue" />
|
|
39
|
+
* {dummies.map((i) => (
|
|
40
|
+
* <View
|
|
41
|
+
* key={`dummy-${i}`}
|
|
42
|
+
* style={{ width: '100%', height: 100, borderBottomColor: 'black', borderBottomWidth: 1 }}
|
|
43
|
+
* >
|
|
44
|
+
* <Text>Try scrolling.</Text>
|
|
45
|
+
* </View>
|
|
46
|
+
* ))}
|
|
47
|
+
* </ScrollView>
|
|
48
|
+
* );
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export function ScrollViewInertialBackground({
|
|
53
|
+
topColor,
|
|
54
|
+
bottomColor,
|
|
55
|
+
spacer: _spacer,
|
|
56
|
+
}: ScrollViewInertialBackgroundProps) {
|
|
57
|
+
const windowHeight = useWindowDimensions().height;
|
|
58
|
+
|
|
59
|
+
const spacer = _spacer ?? windowHeight;
|
|
60
|
+
const topBackgroundColor = topColor ?? DEFAULT_BACKGROUND_COLOR;
|
|
61
|
+
const bottomBackgroundColor = bottomColor ?? DEFAULT_BACKGROUND_COLOR;
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
<HiddenView
|
|
66
|
+
style={{
|
|
67
|
+
height: spacer,
|
|
68
|
+
top: -spacer,
|
|
69
|
+
backgroundColor: topBackgroundColor,
|
|
70
|
+
}}
|
|
71
|
+
/>
|
|
72
|
+
<HiddenView
|
|
73
|
+
style={{
|
|
74
|
+
height: spacer,
|
|
75
|
+
/** Reduces by the CTA GradientHeight area. */
|
|
76
|
+
bottom: -spacer + GRADIENT_HEIGHT,
|
|
77
|
+
backgroundColor: bottomBackgroundColor,
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
</>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function HiddenView({ style, pointerEvents = 'none', ...props }: ComponentProps<typeof View>) {
|
|
85
|
+
return (
|
|
86
|
+
<View
|
|
87
|
+
pointerEvents={pointerEvents}
|
|
88
|
+
style={[{ position: 'absolute', width: '100%', zIndex: -1, left: 0, right: 0 }, style]}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* FIXME:
|
|
96
|
+
* Gradient value for BottomCTA. Set as a fixed value to avoid peerDeps.
|
|
97
|
+
*/
|
|
98
|
+
const GRADIENT_HEIGHT = 37;
|
|
99
|
+
const DEFAULT_BACKGROUND_COLOR = '#ffffff';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ScrollViewInertialBackground';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useLayoutEffect } from 'react';
|
|
2
|
+
import { StatusBar as BaseStatusBar, useColorScheme } from 'react-native';
|
|
3
|
+
import type { StatusBarProps } from './types';
|
|
4
|
+
import { toStatusBarContentStyle } from './utils';
|
|
5
|
+
|
|
6
|
+
export function StatusBar({ style, animated, hidden, backgroundColor, translucent = true }: StatusBarProps) {
|
|
7
|
+
const colorScheme = useColorScheme() ?? 'light';
|
|
8
|
+
const translucentWithNoBackgroundColor = translucent && backgroundColor == null;
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<BaseStatusBar
|
|
12
|
+
translucent={translucent}
|
|
13
|
+
barStyle={toStatusBarContentStyle(style, colorScheme)}
|
|
14
|
+
backgroundColor={translucentWithNoBackgroundColor ? 'transparent' : backgroundColor}
|
|
15
|
+
animated={animated}
|
|
16
|
+
hidden={hidden}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useStatusBar({ style, animated, hidden, backgroundColor, translucent = true }: StatusBarProps) {
|
|
22
|
+
const colorScheme = useColorScheme() ?? 'light';
|
|
23
|
+
const translucentWithNoBackgroundColor = translucent && backgroundColor == null;
|
|
24
|
+
|
|
25
|
+
useLayoutEffect(() => {
|
|
26
|
+
BaseStatusBar.setTranslucent(translucent);
|
|
27
|
+
BaseStatusBar.setBarStyle(toStatusBarContentStyle(style, colorScheme));
|
|
28
|
+
BaseStatusBar.setBackgroundColor(
|
|
29
|
+
translucentWithNoBackgroundColor ? 'transparent' : (backgroundColor ?? 'transparent')
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (hidden !== undefined) {
|
|
33
|
+
BaseStatusBar.setHidden(hidden);
|
|
34
|
+
}
|
|
35
|
+
}, [colorScheme, translucentWithNoBackgroundColor, style, animated, hidden, backgroundColor, translucent]);
|
|
36
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useLayoutEffect } from 'react';
|
|
2
|
+
import { useColorScheme } from 'react-native';
|
|
3
|
+
import type { StatusBarProps } from './types';
|
|
4
|
+
import { toStatusBarContentStyle } from './utils';
|
|
5
|
+
import { useNavigation } from '../router/createRoute';
|
|
6
|
+
|
|
7
|
+
export function StatusBar(props: StatusBarProps) {
|
|
8
|
+
useStatusBar(props);
|
|
9
|
+
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function useStatusBar({ style, hidden }: StatusBarProps) {
|
|
14
|
+
const navigation = useNavigation();
|
|
15
|
+
const colorScheme = useColorScheme() ?? 'light';
|
|
16
|
+
|
|
17
|
+
useLayoutEffect(() => {
|
|
18
|
+
let statusBarStyle: 'light' | 'dark';
|
|
19
|
+
|
|
20
|
+
switch (toStatusBarContentStyle(style, colorScheme)) {
|
|
21
|
+
case 'light-content':
|
|
22
|
+
statusBarStyle = 'light';
|
|
23
|
+
break;
|
|
24
|
+
case 'dark-content':
|
|
25
|
+
statusBarStyle = 'dark';
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
navigation.setOptions({
|
|
30
|
+
statusBarStyle,
|
|
31
|
+
statusBarHidden: hidden,
|
|
32
|
+
});
|
|
33
|
+
}, [navigation, colorScheme, style, hidden]);
|
|
34
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type StatusBarStyle = 'light' | 'dark' | 'auto' | 'inverted';
|
|
2
|
+
|
|
3
|
+
export type StatusBarProps = {
|
|
4
|
+
/**
|
|
5
|
+
* @default 'auto'
|
|
6
|
+
*/
|
|
7
|
+
style?: StatusBarStyle;
|
|
8
|
+
hidden?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* @platform android
|
|
11
|
+
*/
|
|
12
|
+
animated?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* @platform android
|
|
15
|
+
*/
|
|
16
|
+
backgroundColor?: string;
|
|
17
|
+
/**
|
|
18
|
+
* @platform android
|
|
19
|
+
*/
|
|
20
|
+
translucent?: boolean;
|
|
21
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { StatusBarStyle } from './types';
|
|
2
|
+
import type { ColorPreference } from '../initial-props';
|
|
3
|
+
|
|
4
|
+
export function toStatusBarContentStyle(
|
|
5
|
+
statusBarStyle: StatusBarStyle = 'auto',
|
|
6
|
+
colorPreference: ColorPreference = 'light'
|
|
7
|
+
): 'light-content' | 'dark-content' {
|
|
8
|
+
const resolvedStyle = (() => {
|
|
9
|
+
switch (statusBarStyle) {
|
|
10
|
+
case 'auto':
|
|
11
|
+
return colorPreference === 'light' ? 'dark' : 'light';
|
|
12
|
+
case 'inverted':
|
|
13
|
+
return colorPreference === 'light' ? 'light' : 'dark';
|
|
14
|
+
default:
|
|
15
|
+
return statusBarStyle;
|
|
16
|
+
}
|
|
17
|
+
})();
|
|
18
|
+
|
|
19
|
+
return resolvedStyle === 'light' ? 'light-content' : 'dark-content';
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
|
+
/* eslint-disable no-var */
|
|
3
|
+
import type { ComponentType } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface GraniteGlobal {
|
|
6
|
+
app: {
|
|
7
|
+
name: string;
|
|
8
|
+
scheme: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare global {
|
|
13
|
+
// @internal
|
|
14
|
+
var __granite: GraniteGlobal;
|
|
15
|
+
|
|
16
|
+
// @internal
|
|
17
|
+
var Page: ComponentType<any>;
|
|
18
|
+
|
|
19
|
+
var window: { __granite: GraniteGlobal };
|
|
20
|
+
var global: { __granite: GraniteGlobal };
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useBackEvent';
|