nx-react-native-cli 1.0.20 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +69 -21
  2. package/lib/index.cjs +49 -39
  3. package/package.json +3 -3
  4. package/templates/.husky/pre-commit +0 -3
  5. package/templates/.husky/pre-push +0 -3
  6. package/templates/.vscode/settings.json +2 -5
  7. package/templates/apps/mobile/.eslintrc.json +4 -1
  8. package/templates/apps/mobile/Gemfile +5 -2
  9. package/templates/apps/mobile/android/app/build.gradle +5 -4
  10. package/templates/apps/mobile/android/app/src/main/java/com/appsmobile/MainActivity.kt +28 -0
  11. package/templates/apps/mobile/android/app/src/main/java/com/appsmobile/MainApplication.kt +44 -0
  12. package/templates/apps/mobile/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  13. package/templates/apps/mobile/android/build.gradle +6 -6
  14. package/templates/apps/mobile/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  15. package/templates/apps/mobile/android/gradle.properties +44 -0
  16. package/templates/apps/mobile/android/gradlew +251 -0
  17. package/templates/apps/mobile/android/gradlew.bat +94 -0
  18. package/templates/apps/mobile/android/settings.gradle +6 -0
  19. package/templates/apps/mobile/babel.config.json +23 -0
  20. package/templates/apps/mobile/fastlane/.env.template +7 -2
  21. package/templates/apps/mobile/fastlane/Fastfile +46 -5
  22. package/templates/apps/mobile/fastlane/Matchfile +3 -3
  23. package/templates/apps/mobile/ios/AppDelegate.mm +33 -0
  24. package/templates/apps/mobile/metro.config.js +42 -0
  25. package/templates/apps/mobile/package.json +2 -3
  26. package/templates/apps/mobile/project.json +2 -1
  27. package/templates/apps/mobile/src/app/index.tsx +22 -7
  28. package/templates/apps/mobile/src/components/atoms/BottomSheet/bottom-sheet.component.tsx +9 -4
  29. package/templates/apps/mobile/src/components/atoms/Button/button.component.tsx +6 -6
  30. package/templates/apps/mobile/src/components/atoms/Divider/divider-component.tsx +4 -4
  31. package/templates/apps/mobile/src/components/atoms/ExcludedEdges/excluded-edges.component.tsx +37 -0
  32. package/templates/apps/mobile/src/components/atoms/ExcludedEdges/index.ts +1 -0
  33. package/templates/apps/mobile/src/components/atoms/InputLayout/input-layout.component.tsx +8 -9
  34. package/templates/apps/mobile/src/components/atoms/KeyboardAwareScrollView/index.ts +1 -0
  35. package/templates/apps/mobile/src/components/atoms/KeyboardAwareScrollView/keyboard-aware-scroll-view.component.tsx +60 -0
  36. package/templates/apps/mobile/src/components/atoms/ListLoadingItem/list-loading-item.component.tsx +20 -20
  37. package/templates/apps/mobile/src/components/atoms/Modal/modal.component.tsx +4 -5
  38. package/templates/apps/mobile/src/components/atoms/ScreenLoader/screen-loader.component.tsx +5 -6
  39. package/templates/apps/mobile/src/components/atoms/Skeleton/skeleton.component.tsx +9 -8
  40. package/templates/apps/mobile/src/components/atoms/TextInput/text-input.component.tsx +9 -7
  41. package/templates/apps/mobile/src/components/atoms/Typography/typography.component.tsx +2 -2
  42. package/templates/apps/mobile/src/components/atoms/index.ts +2 -1
  43. package/templates/apps/mobile/src/components/molecules/BackButton/back-button.component.tsx +8 -8
  44. package/templates/apps/mobile/src/components/molecules/BottomActionsContainer/BottomActionsContainer.component.tsx +2 -2
  45. package/templates/apps/mobile/src/components/molecules/ScreenContainer/screen-container.component.tsx +19 -25
  46. package/templates/apps/mobile/src/components/molecules/ScreenHeader/screen-header.component.tsx +12 -13
  47. package/templates/apps/mobile/src/components/molecules/StorageManager/StorageManager.component.tsx +3 -1
  48. package/templates/apps/mobile/src/hooks/useGetLayoutHeight.hook.tsx +1 -1
  49. package/templates/apps/mobile/src/hooks/useGetLayoutWidth.hook.tsx +1 -1
  50. package/templates/apps/mobile/src/hooks/useNavigation.hook.tsx +1 -1
  51. package/templates/apps/mobile/src/hooks/useTextInputChangeFocus.hook.tsx +2 -2
  52. package/templates/apps/mobile/src/hooks/useToggleDarkMode.hook.tsx +1 -1
  53. package/templates/apps/mobile/src/main.tsx +1 -1
  54. package/templates/apps/mobile/src/routes/index.tsx +7 -7
  55. package/templates/apps/mobile/src/routes/privateRoutes.tsx +3 -4
  56. package/templates/apps/mobile/src/routes/publicRoutes.tsx +3 -4
  57. package/templates/apps/mobile/src/screens/HomeScreen/home.screen.tsx +3 -2
  58. package/templates/apps/mobile/src/screens/LandingScreen/landing.screen.tsx +21 -13
  59. package/templates/apps/mobile/src/stores/local-storage.store.ts +1 -1
  60. package/templates/apps/mobile/src/stores/mmkvStorage.ts +1 -1
  61. package/templates/apps/mobile/tsconfig.app.json +5 -1
  62. package/templates/clean-generated-outputs.sh +1 -1
  63. package/templates/apps/mobile/.env +0 -3
  64. package/templates/apps/mobile/src/components/atoms/Box/box.component.tsx +0 -21
  65. package/templates/apps/mobile/src/components/atoms/Box/index.ts +0 -1
@@ -1,13 +1,18 @@
1
1
  # Fastlane ENV
2
2
  IS_CI=false
3
3
 
4
+ # Match
5
+ GIT_URL=
6
+ GIT_BRANCH=
7
+ GIT_API_TOKEN=
8
+
4
9
  # Firebase
10
+ FIREBASE_SERVICE_ACCOUNT=
5
11
  FASTLANE_FIREBASE_APP_ID=
6
12
  FASTLANE_FIREBASE_GROUP_TESTERS=
7
13
 
8
14
  # AppStore
9
15
  MATCH_PASSWORD=
10
- GITHUB_API_TOKEN=
11
16
  FASTLANE_APPSTORE_KEY_ID=
12
17
  FASTLANE_APPSTORE_ISSUER_ID=
13
18
  FASTLANE_APPSTORE_KEY_CONTENT=
@@ -18,7 +23,7 @@ FASTLANE_APPSTORE_EXTERNAL_DESTINATIONS=""
18
23
  # MISC
19
24
  FASTLANE_CHANGELOGS="Performance and Feature updates"
20
25
 
21
- # Android (Production ONLY)
26
+ # Android (Production ONLY, Dev is already handled in build.gradle)
22
27
  STORE_PASSWORD=
23
28
  KEY_PASSWORD=
24
29
  KEY_ALIAS=
@@ -1,7 +1,35 @@
1
1
 
2
2
  require 'fileutils'
3
+ require 'base64'
4
+ require 'json'
3
5
 
4
6
  def app_name = "AppsMobile"
7
+ def service_account_path = '/tmp/fbd-sa.json'
8
+
9
+ def decode_base64_to_json(encoded_string, output_path)
10
+ begin
11
+ # Decode the Base64 string
12
+ decoded_data = Base64.decode64(encoded_string)
13
+
14
+ # Parse JSON from the decoded data
15
+ json_data = JSON.parse(decoded_data)
16
+
17
+ # Write to the specified file
18
+ File.open(output_path, 'w') do |file|
19
+ file.write(JSON.pretty_generate(json_data))
20
+ end
21
+
22
+ puts "JSON file successfully written to: #{output_path}"
23
+ rescue JSON::ParserError
24
+ puts "Error: Decoded data is not valid JSON."
25
+ rescue StandardError => e
26
+ puts "Error: #{e.message}"
27
+ end
28
+ end
29
+
30
+ def get_service_account_credentials()
31
+ decode_base64_to_json(ENV['FIREBASE_SERVICE_ACCOUNT'], service_account_path)
32
+ end
5
33
 
6
34
  def delete_gradle_temp()
7
35
  UI.message("Deleting Gradle Temp...")
@@ -136,6 +164,10 @@ def setup_android()
136
164
  gradle_file_path: "./android/app/build.gradle",
137
165
  version_code: build_number
138
166
  )
167
+
168
+ # Retrieving Service Account Credentials
169
+ UI.message("Retrieving Service Account Credentials...")
170
+ get_service_account_credentials()
139
171
  end
140
172
 
141
173
  def ios_build_ipa(configuration, scheme)
@@ -216,7 +248,7 @@ end
216
248
  def publish_bundle(flavor)
217
249
  UI.message("Uploading aab build to Firebase...")
218
250
  release = firebase_app_distribution(
219
- service_credentials_file: to_boolean(ENV['IS_CI']) ? '/tmp/google-service-account-key.json' : nil,
251
+ service_credentials_file: service_account_path,
220
252
  app: ENV['FASTLANE_FIREBASE_APP_ID'],
221
253
  android_artifact_type: "AAB",
222
254
  android_artifact_path: "./android/app/build/outputs/bundle/#{flavor}Release/app-#{flavor}-release.aab",
@@ -228,7 +260,7 @@ end
228
260
  def publish_apk(flavor)
229
261
  UI.message("Uploading apk build to Firebase...")
230
262
  release = firebase_app_distribution(
231
- service_credentials_file: to_boolean(ENV['IS_CI']) ? '/tmp/google-service-account-key.json' : nil,
263
+ service_credentials_file: service_account_path,
232
264
  app: ENV['FASTLANE_FIREBASE_APP_ID'],
233
265
  android_artifact_type: "APK",
234
266
  android_artifact_path: "./android/app/build/outputs/apk/#{flavor}/release/app-#{flavor}-release.apk",
@@ -238,6 +270,11 @@ def publish_apk(flavor)
238
270
  end
239
271
 
240
272
  platform :android do
273
+ desc "Get Service Account Credentials"
274
+ lane :get_service_account_credentials do
275
+ get_service_account_credentials()
276
+ end
277
+
241
278
  desc "Build Android (Dev Server)"
242
279
  lane :dev do
243
280
  begin
@@ -253,10 +290,14 @@ platform :android do
253
290
  desc "Build Android (Production Server)"
254
291
  lane :prod do
255
292
  begin
256
- flavor = 'production'
293
+ flavor = 'prod'
257
294
  setup_android()
258
- android_build_bundle(flavor)
259
- publish_bundle(flavor)
295
+ # TODO: COMMENT IF FIREBASE PROD IS LINKED TO GOOGLE PLAY
296
+ android_build_apk(flavor)
297
+ publish_apk(flavor)
298
+ # TODO: UNCOMMENT IF FIREBASE PROD IS LINKED TO GOOGLE PLAY
299
+ # android_build_bundle(flavor)
300
+ # publish_bundle(flavor)
260
301
  rescue => exception
261
302
  UI.error(exception)
262
303
  end
@@ -1,5 +1,5 @@
1
- git_url("")
2
- git_branch("main")
3
- git_basic_authorization(ENV['GITHUB_API_TOKEN'])
1
+ git_url(ENV['GIT_URL'])
2
+ git_branch(ENV['GIT_BRANCH'])
3
+ git_basic_authorization(ENV['GIT_API_TOKEN'])
4
4
  storage_mode("git")
5
5
  type("appstore")
@@ -0,0 +1,33 @@
1
+ #import "AppDelegate.h"
2
+
3
+ #import <React/RCTBundleURLProvider.h>
4
+ #import <ReactAppDependencyProvider/RCTAppDependencyProvider.h>
5
+
6
+ @implementation AppDelegate
7
+
8
+ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
9
+ {
10
+ self.moduleName = @"AppsMobile";
11
+ self.dependencyProvider = [RCTAppDependencyProvider new];
12
+ // You can add your custom initial props in the dictionary below.
13
+ // They will be passed down to the ViewController used by React Native.
14
+ self.initialProps = @{};
15
+
16
+ return [super application:application didFinishLaunchingWithOptions:launchOptions];
17
+ }
18
+
19
+ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
20
+ {
21
+ return [self bundleURL];
22
+ }
23
+
24
+ - (NSURL *)bundleURL
25
+ {
26
+ #if DEBUG
27
+ return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"src/main"];
28
+ #else
29
+ return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
30
+ #endif
31
+ }
32
+
33
+ @end
@@ -0,0 +1,42 @@
1
+ const path = require('path');
2
+ const { withNxMetro } = require('@nx/react-native');
3
+ const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
4
+
5
+ const defaultConfig = getDefaultConfig(__dirname);
6
+ const { assetExts, sourceExts } = defaultConfig.resolver;
7
+
8
+ /**
9
+ * Metro configuration
10
+ * https://reactnative.dev/docs/metro
11
+ *
12
+ * @type {import('metro-config').MetroConfig}
13
+ */
14
+ const customConfig = {
15
+ cacheVersion: 'mobile',
16
+ transformer: {
17
+ babelTransformerPath: require.resolve('react-native-svg-transformer'),
18
+ getTransformOptions: async () => ({
19
+ transform: {
20
+ experimentalImportSupport: false,
21
+ inlineRequires: true,
22
+ },
23
+ }),
24
+ },
25
+ resolver: {
26
+ extraNodeModules: {
27
+ '@': path.resolve(__dirname, 'src'),
28
+ },
29
+ assetExts: assetExts.filter((ext) => ext !== 'svg'),
30
+ sourceExts: [...sourceExts, 'cjs', 'mjs', 'svg'],
31
+ },
32
+ };
33
+
34
+ module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
35
+ // Change this to true to see debugging info.
36
+ // Useful if you have issues resolving modules
37
+ debug: false,
38
+ // all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
39
+ extensions: [],
40
+ // Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
41
+ watchFolders: [],
42
+ });
@@ -15,7 +15,7 @@
15
15
  "pre-build": "npm run ensure-symlink && npm run sync-deps && npm run pod-install",
16
16
  "list:ios-configurations": "cd ios && xcodebuild -list",
17
17
  "list:ios-devices": "xcrun xctrace list devices",
18
- "xcode": "open ios/AppsMobile.xcworkspace",
18
+ "xcode": "open -a Xcode ios/AppsMobile.xcworkspace",
19
19
  "touch-xcode": "cd ios && touch .xcode.env",
20
20
  "check-env:mobile": "cd ../.. && npm run check-env:mobile",
21
21
  "setup-fastlane": "rbenv local && bundle install && bundle update",
@@ -26,7 +26,6 @@
26
26
  "dependencies": {
27
27
  "@gorhom/bottom-sheet": "*",
28
28
  "@hookform/resolvers": "*",
29
- "@react-native-async-storage/async-storage": "*",
30
29
  "@react-native-community/datetimepicker": "*",
31
30
  "@react-native-community/hooks": "*",
32
31
  "@react-native/metro-config": "*",
@@ -54,7 +53,6 @@
54
53
  "metro-config": "*",
55
54
  "react-hook-form": "*",
56
55
  "react-native-dotenv": "*",
57
- "react-native-fast-image": "*",
58
56
  "react-native-gesture-handler": "*",
59
57
  "react-native-get-random-values": "*",
60
58
  "react-native-haptic-feedback": "*",
@@ -69,6 +67,7 @@
69
67
  "react-native-simple-toast": "*",
70
68
  "react-native-svg-transformer": "*",
71
69
  "react-native-svg": "*",
70
+ "react-native-turbo-image": "*",
72
71
  "react-native-url-polyfill": "*",
73
72
  "react-native": "*",
74
73
  "react": "*",
@@ -17,7 +17,8 @@
17
17
  "dependsOn": ["check-env", "ensure-symlink", "pod-install"],
18
18
  "options": {
19
19
  "port": 8081,
20
- "resetCache": true
20
+ "resetCache": true,
21
+ "client-logs": true
21
22
  }
22
23
  },
23
24
  "serve": {
@@ -1,21 +1,21 @@
1
- import AsyncStorage from '@react-native-async-storage/async-storage';
2
1
  import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister';
3
2
  import { QueryClient } from '@tanstack/react-query';
4
- import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
3
+ import { AsyncStorage, PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
5
4
  import React from 'react';
6
5
  import { LogBox } from 'react-native';
7
6
  import { GestureHandlerRootView } from 'react-native-gesture-handler';
8
7
  import 'react-native-get-random-values';
8
+ import { MMKV } from 'react-native-mmkv';
9
9
  import { SafeAreaProvider } from 'react-native-safe-area-context';
10
10
  import { enableFreeze } from 'react-native-screens';
11
11
  import { useDeviceContext } from 'twrnc';
12
12
 
13
+ import { StorageManager } from '@/components';
14
+ import CONFIG from '@/config';
15
+ import ApplicationRoutes from '@/routes';
16
+ import { tw } from '@/tailwind';
13
17
  import 'react-native-url-polyfill/auto';
14
18
 
15
- import { StorageManager } from '../components';
16
- import ApplicationRoutes from '../routes';
17
- import { tw } from '../tailwind';
18
-
19
19
  LogBox.ignoreLogs(['VirtualizedLists', 'onAnimatedValueUpdate']);
20
20
 
21
21
  enableFreeze(true);
@@ -31,8 +31,23 @@ const queryClient = new QueryClient({
31
31
  },
32
32
  });
33
33
 
34
+ const storage = new MMKV({
35
+ encryptionKey: CONFIG.STORAGE_KEY,
36
+ id: 'react-query-persist',
37
+ });
38
+
39
+ export const MmkvStorage: AsyncStorage = {
40
+ getItem: (name) => {
41
+ const value = storage.getString(name);
42
+
43
+ return value ?? null;
44
+ },
45
+ removeItem: (name) => storage.delete(name),
46
+ setItem: (name, value) => storage.set(name, value),
47
+ };
48
+
34
49
  const persister = createAsyncStoragePersister({
35
- storage: AsyncStorage,
50
+ storage: MmkvStorage as AsyncStorage,
36
51
  });
37
52
 
38
53
  const persistOptions = { maxAge: CACHE_TIME, persister };
@@ -13,8 +13,8 @@ import { Keyboard, StyleProp, ViewStyle } from 'react-native';
13
13
  import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
14
14
  import Animated, { Extrapolation, interpolate, useAnimatedStyle } from 'react-native-reanimated';
15
15
 
16
- import { tw } from '../../../tailwind';
17
- import { DefaultComponentProps } from '../../../types';
16
+ import { tw } from '@/tailwind';
17
+ import { DefaultComponentProps } from '@/types';
18
18
 
19
19
  export type BottomSheetProps = DefaultComponentProps & {
20
20
  backgroundStyle?: StyleProp<Omit<ViewStyle, 'left' | 'right' | 'position' | 'top' | 'bottom'>>;
@@ -127,7 +127,12 @@ export function BottomSheet(props: BottomSheetProps) {
127
127
  );
128
128
  }
129
129
 
130
- export function useBottomSheet() {
130
+ export function useBottomSheet(): {
131
+ closeSheet: () => void;
132
+ expandSheet: () => void;
133
+ isVisible: boolean;
134
+ sheetRef: RefObject<BottomSheetModal>;
135
+ } {
131
136
  const [isVisible, setIsVisible] = useState<boolean>(false);
132
137
  const sheetRef = useRef<BottomSheetModal>(null);
133
138
 
@@ -143,5 +148,5 @@ export function useBottomSheet() {
143
148
  setIsVisible(false);
144
149
  }, []);
145
150
 
146
- return { closeSheet, expandSheet, isVisible, sheetRef };
151
+ return { closeSheet, expandSheet, isVisible, sheetRef: sheetRef as RefObject<BottomSheetModal> };
147
152
  }
@@ -5,13 +5,13 @@ import {
5
5
  TextStyle,
6
6
  TouchableOpacity,
7
7
  TouchableOpacityProps,
8
+ View,
8
9
  ViewStyle,
9
10
  } from 'react-native';
10
11
 
11
- import { colors, disabledInputStyle, tw } from '../../../tailwind';
12
- import { DefaultComponentProps } from '../../../types/component.type';
13
- import { Box } from '../Box';
14
- import { Typography } from '../Typography';
12
+ import { Typography } from '@/components/atoms/Typography';
13
+ import { colors, disabledInputStyle, tw } from '@/tailwind';
14
+ import { DefaultComponentProps } from '@/types';
15
15
 
16
16
  type Props = DefaultComponentProps &
17
17
  TouchableOpacityProps & {
@@ -45,7 +45,7 @@ export function Button(props: Props) {
45
45
 
46
46
  return (
47
47
  <TouchableOpacity activeOpacity={activeOpacity} disabled={disabled} style={[style]} {...rest}>
48
- <Box
48
+ <View
49
49
  style={[
50
50
  tw`bg-primary-700 items-center justify-center rounded-xl p-3`,
51
51
  buttonStyle,
@@ -53,7 +53,7 @@ export function Button(props: Props) {
53
53
  ]}
54
54
  >
55
55
  {isLoading ? <ActivityIndicator color={activityIndicatorColor} /> : display}
56
- </Box>
56
+ </View>
57
57
  </TouchableOpacity>
58
58
  );
59
59
  }
@@ -1,13 +1,13 @@
1
1
  import React from 'react';
2
+ import { View } from 'react-native';
2
3
 
3
- import { tw } from '../../../tailwind';
4
- import { DefaultComponentProps } from '../../../types';
5
- import { Box } from '../Box';
4
+ import { tw } from '@/tailwind';
5
+ import { DefaultComponentProps } from '@/types';
6
6
 
7
7
  type Props = DefaultComponentProps & {};
8
8
 
9
9
  export function Divider(props: Props) {
10
10
  const { style } = props;
11
11
 
12
- return <Box style={[tw`h-[1px] w-full bg-gray-200`, style]}></Box>;
12
+ return <View style={[tw`h-[1px] w-full bg-gray-200`, style]}></View>;
13
13
  }
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import { Platform } from 'react-native';
3
+ import { Edge, SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
4
+
5
+ import { tw } from '@/tailwind';
6
+ import { DefaultComponentProps } from '@/types';
7
+
8
+ type Props = DefaultComponentProps & {
9
+ children?: React.ReactNode;
10
+ excludedEdges?: Edge[];
11
+ };
12
+
13
+ const safeAreaViewEdges: Edge[] = Platform.select({
14
+ android: ['left', 'right', 'bottom'],
15
+ default: [],
16
+ ios: ['left', 'right', 'bottom', 'top'],
17
+ });
18
+
19
+ export function ExcludedEdges(props: Props) {
20
+ const { children, excludedEdges = [] } = props;
21
+ const insets = useSafeAreaInsets();
22
+ const { style } = props;
23
+ const edges =
24
+ excludedEdges.length > 0
25
+ ? safeAreaViewEdges.filter((edge) => !excludedEdges.includes(edge))
26
+ : safeAreaViewEdges;
27
+
28
+ function getExcludedEdgesStyles() {
29
+ return excludedEdges.map((edge) => tw`-${edge}-[${insets[edge]}px]`);
30
+ }
31
+
32
+ return (
33
+ <SafeAreaView edges={edges} style={[getExcludedEdgesStyles(), style]}>
34
+ {children}
35
+ </SafeAreaView>
36
+ );
37
+ }
@@ -0,0 +1 @@
1
+ export * from './excluded-edges.component';
@@ -1,10 +1,9 @@
1
1
  import React from 'react';
2
- import { StyleProp, TextStyle } from 'react-native';
2
+ import { StyleProp, TextStyle, View } from 'react-native';
3
3
 
4
- import { tw } from '../../../tailwind';
5
- import { DefaultComponentProps } from '../../../types';
6
- import { Box } from '../Box';
7
- import { Typography } from '../Typography';
4
+ import { Typography } from '@/components/atoms/Typography';
5
+ import { tw } from '@/tailwind';
6
+ import { DefaultComponentProps } from '@/types';
8
7
 
9
8
  type Props = DefaultComponentProps & {
10
9
  children?: React.ReactNode;
@@ -17,7 +16,7 @@ export function InputLayout(props: Props) {
17
16
  const { children, error, isRequired, label, style, textStyle } = props;
18
17
 
19
18
  return (
20
- <Box style={[style]}>
19
+ <View style={[style]}>
21
20
  {label && (
22
21
  <Typography style={[tw`mb-2 text-gray-600`, textStyle]}>
23
22
  {label}
@@ -26,10 +25,10 @@ export function InputLayout(props: Props) {
26
25
  )}
27
26
  {children}
28
27
  {!!error && (
29
- <Box style={tw`items-end`}>
28
+ <View style={tw`items-end`}>
30
29
  <Typography style={tw`text-right text-red-500`}>{error}</Typography>
31
- </Box>
30
+ </View>
32
31
  )}
33
- </Box>
32
+ </View>
34
33
  );
35
34
  }
@@ -0,0 +1 @@
1
+ export * from './keyboard-aware-scroll-view.component';
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+ import {
3
+ NativeScrollEvent,
4
+ NativeSyntheticEvent,
5
+ RefreshControlProps,
6
+ StyleProp,
7
+ ViewStyle,
8
+ } from 'react-native';
9
+ import { KeyboardAwareScrollView as RNKeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
10
+ import Animated from 'react-native-reanimated';
11
+
12
+ import { tw } from '@/tailwind';
13
+ import { DefaultComponentProps } from '@/types';
14
+
15
+ type Props = DefaultComponentProps & {
16
+ children?: React.ReactNode;
17
+ scrollViewRef?: React.RefObject<RNKeyboardAwareScrollView>;
18
+ containerStyle?: StyleProp<ViewStyle>;
19
+ extraBottomPadding?: number;
20
+ refreshControl?: React.ReactElement<RefreshControlProps> | undefined;
21
+ onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
22
+ };
23
+
24
+ const defaultStyle = tw`grow`;
25
+
26
+ const AnimatedKeyboardAwareScrollView = Animated.createAnimatedComponent(RNKeyboardAwareScrollView);
27
+
28
+ export function KeyboardAwareScrollView(props: Props) {
29
+ const {
30
+ children,
31
+ containerStyle,
32
+ extraBottomPadding,
33
+ onScroll,
34
+ refreshControl,
35
+ scrollViewRef,
36
+ style,
37
+ } = props;
38
+
39
+ const defaultContainerStyle = [
40
+ defaultStyle,
41
+ containerStyle,
42
+ // eslint-disable-next-line no-magic-numbers
43
+ extraBottomPadding && tw`pb-[${extraBottomPadding + 50}px]`,
44
+ ];
45
+
46
+ return (
47
+ <AnimatedKeyboardAwareScrollView
48
+ ref={scrollViewRef}
49
+ contentContainerStyle={[defaultContainerStyle, containerStyle]}
50
+ enableResetScrollToCoords={false}
51
+ keyboardShouldPersistTaps="handled"
52
+ refreshControl={refreshControl}
53
+ scrollEventThrottle={16}
54
+ style={style}
55
+ onScroll={onScroll}
56
+ >
57
+ {children}
58
+ </AnimatedKeyboardAwareScrollView>
59
+ );
60
+ }
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
+ import { View } from 'react-native';
2
3
 
3
- import { tw } from '../../../tailwind';
4
- import { DefaultComponentProps } from '../../../types';
5
- import { Box } from '../Box';
6
- import { Skeleton } from '../Skeleton';
4
+ import { Skeleton } from '@/components/atoms/Skeleton';
5
+ import { tw } from '@/tailwind';
6
+ import { DefaultComponentProps } from '@/types';
7
7
 
8
8
  type Props = DefaultComponentProps & {
9
9
  isLoading: boolean;
@@ -13,19 +13,19 @@ export function ListLoadingItemComponent(props: Props) {
13
13
  const { isLoading, style } = props;
14
14
 
15
15
  return (
16
- <Box style={[style]}>
17
- <Box style={[tw`gap-2`, style]}>
16
+ <View style={[style]}>
17
+ <View style={[tw`gap-2`, style]}>
18
18
  <Skeleton isLoading={isLoading}>
19
- {isLoading && <Box style={tw`h-[100px] w-full`} />}
19
+ {isLoading && <View style={tw`h-[100px] w-full`} />}
20
20
  </Skeleton>
21
21
  <Skeleton isLoading={isLoading}>
22
- {isLoading && <Box style={tw`h-[100px] w-full`} />}
22
+ {isLoading && <View style={tw`h-[100px] w-full`} />}
23
23
  </Skeleton>
24
24
  <Skeleton isLoading={isLoading}>
25
- {isLoading && <Box style={tw`h-[100px] w-full`} />}
25
+ {isLoading && <View style={tw`h-[100px] w-full`} />}
26
26
  </Skeleton>
27
- </Box>
28
- </Box>
27
+ </View>
28
+ </View>
29
29
  );
30
30
  }
31
31
 
@@ -33,24 +33,24 @@ export function ListLoadingHorizontalItemComponent(props: Props) {
33
33
  const { isLoading, style } = props;
34
34
 
35
35
  return (
36
- <Box style={[style]}>
37
- <Box style={[tw`flex-row gap-2`, style]}>
36
+ <View style={[style]}>
37
+ <View style={[tw`flex-row gap-2`, style]}>
38
38
  <Skeleton isLoading={isLoading}>
39
- {isLoading && <Box style={tw`h-[200px] w-[100px]`} />}
39
+ {isLoading && <View style={tw`h-[200px] w-[100px]`} />}
40
40
  </Skeleton>
41
41
  <Skeleton isLoading={isLoading}>
42
- {isLoading && <Box style={tw`h-[200px] w-[100px]`} />}
42
+ {isLoading && <View style={tw`h-[200px] w-[100px]`} />}
43
43
  </Skeleton>
44
44
  <Skeleton isLoading={isLoading}>
45
- {isLoading && <Box style={tw`h-[200px] w-[100px]`} />}
45
+ {isLoading && <View style={tw`h-[200px] w-[100px]`} />}
46
46
  </Skeleton>
47
47
  <Skeleton isLoading={isLoading}>
48
- {isLoading && <Box style={tw`h-[200px] w-[100px]`} />}
48
+ {isLoading && <View style={tw`h-[200px] w-[100px]`} />}
49
49
  </Skeleton>
50
50
  <Skeleton isLoading={isLoading}>
51
- {isLoading && <Box style={tw`h-[200px] w-[100px]`} />}
51
+ {isLoading && <View style={tw`h-[200px] w-[100px]`} />}
52
52
  </Skeleton>
53
- </Box>
54
- </Box>
53
+ </View>
54
+ </View>
55
55
  );
56
56
  }
@@ -1,12 +1,11 @@
1
1
  /* eslint-disable no-magic-numbers */
2
2
  import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
3
3
  import React, { useCallback, useState } from 'react';
4
- import { Keyboard, ModalBaseProps, StyleProp, ViewStyle } from 'react-native';
4
+ import { Keyboard, ModalBaseProps, StyleProp, View, ViewStyle } from 'react-native';
5
5
  import RNModal from 'react-native-modal';
6
6
 
7
- import { tw } from '../../../tailwind';
8
- import { DefaultComponentProps } from '../../../types';
9
- import { Box } from '../Box';
7
+ import { tw } from '@/tailwind';
8
+ import { DefaultComponentProps } from '@/types';
10
9
 
11
10
  export type ModalProps = DefaultComponentProps &
12
11
  ModalBaseProps & {
@@ -32,7 +31,7 @@ export function Modal(props: ModalProps) {
32
31
  onBackdropPress={onBackdropPress}
33
32
  >
34
33
  <BottomSheetModalProvider>
35
- <Box style={[tw`px-4`, containerStyle]}>{children}</Box>
34
+ <View style={[tw`px-4`, containerStyle]}>{children}</View>
36
35
  </BottomSheetModalProvider>
37
36
  </RNModal>
38
37
  );
@@ -1,9 +1,8 @@
1
1
  import React from 'react';
2
- import { ActivityIndicator } from 'react-native';
2
+ import { ActivityIndicator, View } from 'react-native';
3
3
 
4
- import { colors, tw } from '../../../tailwind';
5
- import { DefaultComponentProps } from '../../../types/component.type';
6
- import { Box } from '../Box';
4
+ import { colors, tw } from '@/tailwind';
5
+ import { DefaultComponentProps } from '@/types';
7
6
 
8
7
  type Props = DefaultComponentProps;
9
8
 
@@ -11,8 +10,8 @@ export function ScreenLoader(props: Props) {
11
10
  const { style } = props;
12
11
 
13
12
  return (
14
- <Box style={[tw`h-full w-full items-center justify-center bg-gray-50 p-8`, style]}>
13
+ <View style={[tw`h-full w-full items-center justify-center bg-gray-50 p-8`, style]}>
15
14
  <ActivityIndicator color={colors.primary[400]} />
16
- </Box>
15
+ </View>
17
16
  );
18
17
  }