diff --git a/.husky/commit-msg b/.husky/commit-msg deleted file mode 100755 index 988eb59..0000000 --- a/.husky/commit-msg +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -npx --no -- commitlint --edit diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 64778a1..0000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.vscode/settings.json b/.vscode/settings.json index d618a2c..499df69 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "eslint.options": {}, "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit" + "source.fixAll.eslint": "always" }, "eslint.run": "onSave", "editor.defaultFormatter": "esbenp.prettier-vscode", diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 0000000..e18689e --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,42 @@ +# EXAMPLE USAGE: +# +# Refer for explanation to following link: +# https://evilmartians.github.io/lefthook/configuration/ +# +# pre-push: +# jobs: +# - name: packages audit +# tags: +# - frontend +# - security +# run: yarn audit +# +# - name: gems audit +# tags: +# - backend +# - security +# run: bundle audit +# +# pre-commit: +# parallel: true +# jobs: +# - run: yarn eslint {staged_files} +# glob: "*.{js,ts,jsx,tsx}" +# +# - name: rubocop +# glob: "*.rb" +# exclude: +# - config/application.rb +# - config/routes.rb +# run: bundle exec rubocop --force-exclusion {all_files} +# +# - name: govet +# files: git ls-files -m +# glob: "*.go" +# run: go vet {files} +# +# - script: "hello.js" +# runner: node +# +# - script: "hello.go" +# runner: go run diff --git a/package.json b/package.json index 44358bd..841a116 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mindinventory/react-native-boilerplate", - "version": "3.0.0", + "version": "3.0.1", "description": "React Native Template", "repository": "https://github.com/Mindinventory/react-native-boilerplate.git", "author": "Mindinventory", @@ -9,8 +9,7 @@ "private": false, "scripts": { "android": "cd MIBoilerplate/ && npx react-native run-android", - "ios": "cd MIBoilerplate/ && npx react-native run-ios", - "prepare": "husky" + "ios": "cd MIBoilerplate/ && npx react-native run-ios" }, "bin": { "@mindinventory/react-native-boilerplate": "./bin/index.js" @@ -39,7 +38,7 @@ "devDependencies": { "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", - "husky": "^9.1.6" + "lefthook": "^1.10.4" }, "dependencies": { "chalk": "^5.3.0", diff --git a/script.js b/script.js index e9fd8b7..d01e5f3 100755 --- a/script.js +++ b/script.js @@ -12,12 +12,13 @@ const installDependencies = () => { ); console.log("Installing dependencies... 🛠️\n"); + console.log("dependencies Installation started... 🛠️\n"); execSync(`yarn`, { stdio: "inherit" }); console.log("Dependencies installed successfully. 🚀\n"); - console.log("bundle Installing 🛠️\n"); - execSync(`bundle`, { stdio: "inherit" }); - console.log("bundle installed successfully.🚀\n"); + // console.log("bundle Installing 🛠️\n"); + // execSync(`bundle`, { stdio: "inherit" }); + // console.log("bundle installed successfully.🚀\n"); console.log("pod-install Installing 🛠️\n"); execSync(`npx pod-install`, { stdio: "inherit" }); @@ -60,21 +61,27 @@ yarn-error.log* stdio: "inherit", shell: true, }); + + console.log("Installing lefthook... 🛠️\n"); + execSync(`npx lefthook install`, { stdio: "inherit" }); + console.log("lefthook installed successfully. 🚀\n"); } catch (error) { console.error(`🚨 An error occurred while initializing git: ${error}`); } }; const main = async () => { - execSync("git init", { stdio: "inherit" }); - installDependencies(); - initializeGit(); + try { + installDependencies(); + execSync("git init", { stdio: "inherit" }); + initializeGit(); + } catch (error) { + console.error(`🚨 Critical error: ${error}`); + process.exit(1); + } }; -new Promise((resolve) => { - main(); - resolve(); -}) +main() .then(() => { console.log( "- 🎉 Congrats! Your project is ready with @mindinventory/react-native-boilerplate! 🎉\n" @@ -89,6 +96,7 @@ new Promise((resolve) => { console.log( "- ⭐ If you love this boilerplate, give us a star, you will be a ray of sunshine in our lives :) https://github.com/Mindinventory/react-native-boilerplate\n" ); + console.log("completed :::::::: Mindinventory React native boilerplate 🚀"); }) .catch((error) => { console.error(`🚨 An error occurred with post init script: ${error}`); diff --git a/src/gitHandler.js b/src/gitHandler.js index abf3084..1d045b4 100644 --- a/src/gitHandler.js +++ b/src/gitHandler.js @@ -1,5 +1,6 @@ const { exec } = require('child_process'); var util = require('util') +const {loading} = require("./helper"); const execAsync = util.promisify(exec) @@ -7,6 +8,9 @@ async function gitInitialize() { await execAsync("git init", { stdio: "inherit" }); await execAsync("git add ."); await execAsync(`git commit -m 'Initial commit'`); + let installingLeftHook = await loading('🛠️\u00A0Installing LeftHook...') + await execAsync(`npx lefthook install`); + installingLeftHook.succeed("LeftHook installed successfully"); } module.exports = { diff --git a/templates/.DS_Store b/templates/.DS_Store index 8e9023e..f1580b9 100644 Binary files a/templates/.DS_Store and b/templates/.DS_Store differ diff --git a/templates/CliTemplate/.husky/commit-msg b/templates/CliTemplate/.husky/commit-msg deleted file mode 100755 index 4002db7..0000000 --- a/templates/CliTemplate/.husky/commit-msg +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -npx --no -- commitlint --edit diff --git a/templates/CliTemplate/.husky/pre-commit b/templates/CliTemplate/.husky/pre-commit deleted file mode 100755 index 476e744..0000000 --- a/templates/CliTemplate/.husky/pre-commit +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -yarn fix:lint diff --git a/templates/CliTemplate/Gemfile b/templates/CliTemplate/Gemfile deleted file mode 100644 index 2a7ce35..0000000 --- a/templates/CliTemplate/Gemfile +++ /dev/null @@ -1,8 +0,0 @@ -source 'https://rubygems.org' - -# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version -ruby ">= 2.6.10" - -# Exclude problematic versions of cocoapods and activesupport that causes build failures. -gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' -gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' diff --git a/templates/CliTemplate/blueprints/Button/Button.tsx b/templates/CliTemplate/blueprints/Button/Button.tsx index 0a8df5b..da56684 100644 --- a/templates/CliTemplate/blueprints/Button/Button.tsx +++ b/templates/CliTemplate/blueprints/Button/Button.tsx @@ -1,5 +1,6 @@ -import React from 'react'; +import React, { memo } from 'react'; import { + ActivityIndicator, StyleProp, StyleSheet, TextStyle, @@ -13,6 +14,7 @@ import Animated, { useAnimatedStyle, useSharedValue, withSpring, + WithSpringConfig, } from 'react-native-reanimated'; import { useColor } from '@src/context'; @@ -20,16 +22,25 @@ import { moderateScale, Palette, scaleHeight } from '@src/utils'; import { Text } from '../Text/Text'; -const AnimatedButtonComponent = - Animated.createAnimatedComponent(TouchableOpacity); +const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity); + +type ButtonVariant = 'standard' | 'outlined'; interface ExtraButtonProps { + variant?: ButtonVariant; // Outlined or standard buttonContainerStyle?: StyleProp; titleContainerStyle?: StyleProp; titleStyle?: StyleProp; title?: React.ReactNode; rightIcon?: JSX.Element; leftIcon?: JSX.Element; + activeOpacity?: number; + springConfig?: WithSpringConfig; + disableScaleAnimation?: boolean; + isLoading?: boolean; // Show loader + loaderColor?: string; // Default loader color customization + customLoader?: React.ReactNode; // Custom loader component + disabled?: boolean; // Disable the button } export type AnimatedButtonProps = Omit< @@ -41,9 +52,15 @@ export type AnimatedButtonProps = Omit< export type ButtonProps = AnimatedButtonProps & ExtraButtonProps; -export const AnimatedTouchableOpacity = React.memo( - (props: AnimatedButtonProps) => { - const { containerStyle } = props; +export const AnimatedTouchableOpacity = memo( + ({ + activeOpacity = 0.8, + children, + containerStyle, + disableScaleAnimation = false, + springConfig, + ...rest + }: AnimatedButtonProps & ExtraButtonProps) => { const scaleValue = useSharedValue(1); const animatedButtonStyle = useAnimatedStyle(() => { @@ -52,51 +69,125 @@ export const AnimatedTouchableOpacity = React.memo( }; }); + const handlePressIn = () => { + if (!disableScaleAnimation) { + scaleValue.value = withSpring(0.9, springConfig); + } + }; + + const handlePressOut = () => { + if (!disableScaleAnimation) { + scaleValue.value = withSpring(1, springConfig); + } + }; + return ( - (scaleValue.value = withSpring(0.9))} - onPressOut={() => (scaleValue.value = withSpring(1))} - activeOpacity={0.8} - {...props}> - {props.children} - + onPressIn={handlePressIn} + onPressOut={handlePressOut} + activeOpacity={activeOpacity} + {...rest}> + {children} + ); } ); -export const Button = React.memo((props: ButtonProps) => { - const { buttonContainerStyle, title, titleContainerStyle, titleStyle } = - props; +export const Button = memo((props: ButtonProps) => { + const { + activeOpacity, + buttonContainerStyle, + customLoader, + disabled = false, + disableScaleAnimation, + isLoading = false, + leftIcon, + loaderColor, + rightIcon, + springConfig, + title, + titleContainerStyle, + titleStyle, + variant = 'standard', + ...rest + } = props; const { color } = useColor(); + const styles = createButtonStyles(color); - const styles = buttonStyles(color); + // Determine styles based on variant + const variantStyle = + variant === 'outlined' + ? { + backgroundColor: 'transparent', + borderColor: color.primaryColor, + borderWidth: 2, + } + : { + backgroundColor: disabled ? color.secondaryColor : color.primaryColor, + }; return ( + containerStyle={[ + styles.buttonContainer, + variantStyle, + buttonContainerStyle, + disabled && styles.disabledContainer, + ]} + activeOpacity={disabled ? 1 : activeOpacity} + springConfig={springConfig} + disableScaleAnimation={disableScaleAnimation || disabled} + disabled={disabled} + {...rest}> - {props.leftIcon} - - {title} - - {props.rightIcon} + {isLoading ? ( + customLoader || ( + + ) + ) : ( + <> + {leftIcon} + {title && ( + + {title} + + )} + {rightIcon} + + )} ); }); -const buttonStyles = ({ primaryColor }: Palette) => +const createButtonStyles = ({ secondaryColor }: Palette) => StyleSheet.create({ buttonContainer: { alignItems: 'center', - backgroundColor: primaryColor, borderRadius: moderateScale(60), height: scaleHeight(45), width: '100%', }, + disabledContainer: { + backgroundColor: secondaryColor, + }, + disabledText: { + color: secondaryColor, + }, + loader: { + height: scaleHeight(20), + }, titleContainer: { alignItems: 'center', flexDirection: 'row', diff --git a/templates/CliTemplate/blueprints/Indicator/Indicator.tsx b/templates/CliTemplate/blueprints/Indicator/Indicator.tsx index eeccacb..3bd233a 100644 --- a/templates/CliTemplate/blueprints/Indicator/Indicator.tsx +++ b/templates/CliTemplate/blueprints/Indicator/Indicator.tsx @@ -13,6 +13,9 @@ import { Text } from '../Text/Text'; export interface IndicatorProps { isLoading: boolean; + text?: string; + textColor?: string; + backgroundColor?: string; } export type IndicatorRef = { @@ -24,7 +27,12 @@ export const IndicatorViewRef = ( props: IndicatorProps, ref: React.Ref ) => { - const { isLoading = true } = props; + const { + backgroundColor = 'black', + isLoading = true, + text = 'Please wait ...', + textColor = 'white', + } = props; const { color } = useColor(); const [loading, setIsLoading] = useState(isLoading); @@ -43,16 +51,12 @@ export const IndicatorViewRef = ( } }, []); - useImperativeHandle( - ref, - () => { - return { - hide, - show, - }; - }, - [hide, show] - ); + useImperativeHandle(ref, () => { + return { + hide, + show, + }; + }, [hide, show]); const styles = indicatorStyles(color); @@ -60,15 +64,13 @@ export const IndicatorViewRef = ( return ( - + - - Please wait ... - + {text} ); diff --git a/templates/CliTemplate/blueprints/Text/Text.tsx b/templates/CliTemplate/blueprints/Text/Text.tsx index eb897c2..621b55b 100644 --- a/templates/CliTemplate/blueprints/Text/Text.tsx +++ b/templates/CliTemplate/blueprints/Text/Text.tsx @@ -8,105 +8,37 @@ import { } from 'react-native'; import { useColor } from '@src/context'; -import { scaledSize } from '@src/utils'; export enum Fonts { Poppins = 'Poppins', } -const BASE_TEXT: TextStyle = { - fontSize: scaledSize(7), -}; - -export const presets = { - default: BASE_TEXT, - font400: { - ...BASE_TEXT, - //add your font normal for weight 400 - fontFamily: Fonts.Poppins, - } as TextStyle, - font500: { - ...BASE_TEXT, - //add your font medium for weight 500 - fontFamily: Fonts.Poppins, - } as TextStyle, - font600: { - ...BASE_TEXT, - //add your font semi-bold for weight 600 - fontFamily: Fonts.Poppins, - } as TextStyle, - font700: { - ...BASE_TEXT, - //add your font bold for weight 700 - fontFamily: Fonts.Poppins, - } as TextStyle, - h1: { - ...BASE_TEXT, - fontFamily: Fonts.Poppins, - fontSize: scaledSize(24), - fontWeight: '700', - } as TextStyle, - h2: { - ...BASE_TEXT, - fontFamily: Fonts.Poppins, - fontSize: scaledSize(21), - fontWeight: '700', - } as TextStyle, - h3: { - ...BASE_TEXT, - fontFamily: Fonts.Poppins, - fontSize: scaledSize(18), - fontWeight: '500', - } as TextStyle, - h4: { - ...BASE_TEXT, - fontFamily: Fonts.Poppins, - fontSize: scaledSize(15), - fontWeight: '500', - } as TextStyle, - h5: { - ...BASE_TEXT, - fontFamily: Fonts.Poppins, - fontSize: scaledSize(12), - fontWeight: '400', - } as TextStyle, - h6: { - ...BASE_TEXT, - fontFamily: Fonts.Poppins, - fontSize: scaledSize(9), - fontWeight: '400', - } as TextStyle, - small: { - ...BASE_TEXT, - fontFamily: Fonts.Poppins, - fontSize: scaledSize(6), - fontWeight: '300', - } as TextStyle, - title: { - ...BASE_TEXT, - fontFamily: Fonts.Poppins, - fontSize: scaledSize(13), - fontWeight: '700', - } as TextStyle, -}; - -export type TextPresets = keyof typeof presets; - export interface TextProps extends TextProperties { style?: StyleProp; - preset?: TextPresets; color?: string; textAlign?: 'auto' | 'left' | 'right' | 'center' | 'justify'; + fontSize?: number; + fontWeight?: + | 'normal' + | 'bold' + | '100' + | '200' + | '300' + | '400' + | '500' + | '600' + | '700' + | '800' + | '900'; } -export const Text = ({ children, ...props }: TextProps) => { - const { - color, - preset = 'default', - style: styleOverride, - textAlign = 'auto', - ...rest - } = props; +export const Text = ({ + children, + fontSize = 16, + fontWeight = 'normal', + ...props +}: TextProps) => { + const { color, style: styleOverride, textAlign = 'auto', ...rest } = props; const { color: palette } = useColor(); @@ -114,8 +46,12 @@ export const Text = ({ children, ...props }: TextProps) => { {children} diff --git a/templates/CliTemplate/blueprints/TextInput/TextInput.tsx b/templates/CliTemplate/blueprints/TextInput/TextInput.tsx index e097d93..447a593 100644 --- a/templates/CliTemplate/blueprints/TextInput/TextInput.tsx +++ b/templates/CliTemplate/blueprints/TextInput/TextInput.tsx @@ -1,34 +1,168 @@ import React from 'react'; -import { TextInput as RNTextInput } from 'react-native'; +import { + TextInput as RNTextInput, + StyleProp, + StyleSheet, + TouchableOpacity, + View, + ViewStyle, +} from 'react-native'; -import { Field, FieldProps } from 'formik'; +import { useField } from 'formik'; + +import { moderateScale, scaleHeight } from '@src/utils'; // Replace with your project utils -import { Input } from './Input'; import { TextInputProps } from './TextInputProps'; +import { Text } from '../Text/Text'; + +export const TextInput = React.forwardRef( + ( + { + borderRadius = moderateScale(8), + borderWidth = 1, + colors = {}, + containerStyle, + disabled = false, + errorStyle, + inputStyle, + label, + labelFontSize = 16, + labelStyle, + leftIcon, + name, + onLeftIconPress, + onRightIconPress, + placeholder, + rightIcon, + variant = 'standard', + ...rest + }: TextInputProps, + ref?: React.Ref + ) => { + const [field, meta, helpers] = useField(name); + const hasError = meta.touched && meta.error; + + // Default color values + const { + backgroundColor = '#ffffff', + borderColor = 'gray', + disabledBackgroundColor = '#f5f5f5', + disabledBorderColor = '#d3d3d3', + disabledTextColor = '#a9a9a9', + errorColor = 'red', + errorTextColor = 'red', + placeholderColor = 'darkgray', + textColor = 'black', + } = colors; -export const TextInput = React.memo( - React.forwardRef( - ({ ...props }: TextInputProps, ref?: React.Ref) => { - const { name } = props; - return ( - - {({ form, meta }: FieldProps) => { - return ( - ) => { - form?.handleChange(name)(text); - }} - onBlur={form?.handleBlur(name)} - value={meta?.value} - error={meta?.error && meta?.touched ? meta?.error ?? '' : ''} - ref={ref} - {...props} - /> - ); - }} - - ); - } - ) + // Dynamic styles based on the variant and state + const getVariantStyle = (): StyleProp => { + switch (variant) { + case 'outlined': + return { + borderColor: hasError + ? errorColor + : disabled + ? disabledBorderColor + : borderColor, + borderRadius: borderRadius, + borderWidth: borderWidth, + }; + case 'filled': + return { + backgroundColor: hasError + ? errorColor + : disabled + ? disabledBackgroundColor + : backgroundColor, + borderRadius: borderRadius, + }; + case 'standard': + default: + return { + borderBottomColor: hasError + ? errorColor + : disabled + ? disabledBorderColor + : borderColor, + borderBottomWidth: 1, + }; + } + }; + + const getInputColor = (): string => { + if (disabled) return disabledTextColor; + if (hasError) return errorTextColor; + return textColor; + }; + + return ( + + {label && ( + + {label} + + )} + + {leftIcon && ( + + {leftIcon} + + )} + helpers.setTouched(true)} + {...rest} + /> + {rightIcon && ( + + {rightIcon} + + )} + + {hasError && ( + + {meta.error} + + )} + + ); + } ); + +const styles = StyleSheet.create({ + errorText: { + fontSize: moderateScale(12), + marginTop: scaleHeight(4), + }, + input: { + flex: 1, + fontSize: moderateScale(14), + height: '100%', + }, + inputContainer: { + alignItems: 'center', + flexDirection: 'row', + height: scaleHeight(45), + paddingHorizontal: moderateScale(8), + }, + label: { + marginBottom: scaleHeight(8), + }, +}); diff --git a/templates/CliTemplate/blueprints/TextInput/TextInputProps.ts b/templates/CliTemplate/blueprints/TextInput/TextInputProps.ts index e1a4814..2108413 100644 --- a/templates/CliTemplate/blueprints/TextInput/TextInputProps.ts +++ b/templates/CliTemplate/blueprints/TextInput/TextInputProps.ts @@ -1,114 +1,140 @@ import { - NativeSyntheticEvent, TextInputProps as RNTextInputProps, StyleProp, - TargetedEvent, TextStyle, ViewStyle, } from 'react-native'; -export interface TextInputProps extends InputProps { - name: string; -} - -export type Variant = 'filled' | 'outlined' | 'standard'; +export type Variant = 'outlined' | 'filled' | 'standard'; -export interface InputProps extends RNTextInputProps { - /** - * The variant of the TextInput style. - * @default "filled" - */ - variant?: Variant; +export interface TextInputProps extends RNTextInputProps { /** - * The label to display. + * The label text to display above the input field. */ label?: string; + /** - * The element placed before the text input. - */ - leftIcon?: React.ReactNode | null; - /** - * The element placed after the text input. - */ - rightIcon?: React.ReactNode | null; - /** - * The helper text to display. - */ - error?: string; - /** - * Callback function to call when user moves pointer over the input. - */ - onMouseEnter?: (event: NativeSyntheticEvent) => void; - /** - * Callback function to call when user moves pointer away from the input. + * The font size of the label. */ - onMouseLeave?: (event: NativeSyntheticEvent) => void; + labelFontSize?: number; + /** - * The style of the container view. + * Placeholder text to display when the input is empty. */ - style?: StyleProp; + placeholder?: string; + /** - * The style of the text input container view. + * Custom style for the container wrapping the input and label. */ - inputContainerStyle?: StyleProp; + containerStyle?: StyleProp; + /** - * The style of the text input. + * Custom style for the label text. */ - inputStyle?: RNTextInputProps['style']; + labelStyle?: StyleProp; + /** - * The style of the text input's leading element container. + * Custom style for the input field text. */ - leftIconContainerStyle?: StyleProp; + inputStyle?: StyleProp; + /** - * The style of the text input's trailing element container. + * Custom style for the error message text. */ - rightIconContainerStyle?: StyleProp; + errorStyle?: StyleProp; + /** - * Background color of the input container style. - * @default "white" + * JSX element to display as an icon on the left side of the input field. */ - backgroundColor?: string; + leftIcon?: JSX.Element; + /** - * On focus background color of the input container style. - * @default "#e9e9e9" + * JSX element to display as an icon on the right side of the input field. */ - onFocusBackgroundColor?: string; + rightIcon?: JSX.Element; + /** - * Border color of the outline input container style. - * @default "black" + * Callback function triggered when the right icon is pressed. */ - borderColor?: string; + onRightIconPress?: () => void; + /** - * On focus Border color of the outline input container style. - * @default "#0c5fed" + * Callback function triggered when the left icon is pressed. */ - onFocusBorderColor?: string; + onLeftIconPress?: () => void; + /** - * On hover background color of the filled input container style. - * @default "#e9e9e9" + * Defines the visual variant of the input field (e.g., outline, filled). */ - onHoverBackgroundColor?: string; + variant?: Variant; + /** - * Label text color of the input. - * @default "black" + * Determines if the input field is disabled. */ - labelColor?: string; + disabled?: boolean; + /** - * On focus Label text color change. - * @default "#0c5fed" + * Name of the field, used in Formik for identifying the input. */ - onFocusLabelColor?: string; + name: string; + /** - * On error or any helper text below text Input container style. + * Border width for the input field. */ - errorContainerStyle?: StyleProp; + borderWidth?: number; + /** - * On error or any helper text below text Input Text-Style. + * Border radius for the input field corners. */ - errorStyle?: StyleProp; + borderRadius?: number; + /** - * In outlined variant the gap border color. - * @default white + * Object to define various colors for different input field states. */ - outlineGapColor?: string; + colors?: { + /** + * Border color of the input field. + */ + borderColor?: string; + + /** + * Background color of the input field. + */ + backgroundColor?: string; + + /** + * Border color to display when there is an error. + */ + errorColor?: string; + + /** + * Text color for the error message. + */ + errorTextColor?: string; + + /** + * Border color to use when the input is disabled. + */ + disabledBorderColor?: string; + + /** + * Background color to use when the input is disabled. + */ + disabledBackgroundColor?: string; + + /** + * Text color to use when the input is disabled. + */ + disabledTextColor?: string; + + /** + * Default text color for the input. + */ + textColor?: string; + + /** + * Color for the placeholder text in the input field. + */ + placeholderColor?: string; + }; } diff --git a/templates/CliTemplate/env.d.ts b/templates/CliTemplate/env.d.ts index 221cce8..15e4984 100644 --- a/templates/CliTemplate/env.d.ts +++ b/templates/CliTemplate/env.d.ts @@ -1,4 +1,6 @@ declare module '@env' { export const API_URL: string; export const ENV: string; + export const APP_STORE_URL: string; + export const PLAY_STORE_URL: string; } diff --git a/templates/CliTemplate/ios/Podfile.lock b/templates/CliTemplate/ios/Podfile.lock index dd61375..79b5805 100644 --- a/templates/CliTemplate/ios/Podfile.lock +++ b/templates/CliTemplate/ios/Podfile.lock @@ -19,9 +19,9 @@ PODS: - libwebp/sharpyuv (1.3.2) - libwebp/webp (1.3.2): - libwebp/sharpyuv - - MMKV (1.3.5): - - MMKVCore (~> 1.3.5) - - MMKVCore (1.3.5) + - MMKV (2.0.2): + - MMKVCore (~> 2.0.2) + - MMKVCore (2.0.2) - RCT-Folly (2024.01.01.00): - boost - DoubleConversion @@ -1252,10 +1252,6 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-config (1.5.3): - - react-native-config/App (= 1.5.3) - - react-native-config/App (1.5.3): - - React-Core - react-native-mmkv (2.12.2): - DoubleConversion - glog @@ -1280,7 +1276,7 @@ PODS: - Yoga - react-native-netinfo (11.4.1): - React-Core - - react-native-safe-area-context (4.11.0): + - react-native-safe-area-context (4.14.1): - React-Core - React-nativeconfig (0.75.3) - React-NativeModulesApple (0.75.3): @@ -1546,7 +1542,7 @@ PODS: - React-Core - SDWebImage (~> 5.11.1) - SDWebImageWebPCoder (~> 0.8.4) - - RNGestureHandler (2.20.0): + - RNGestureHandler (2.22.1): - DoubleConversion - glog - hermes-engine @@ -1567,9 +1563,32 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNLocalize (3.2.1): + - RNLocalize (3.4.1): - React-Core - - RNReanimated (3.15.3): + - RNReanimated (3.16.7): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNReanimated/reanimated (= 3.16.7) + - RNReanimated/worklets (= 3.16.7) + - Yoga + - RNReanimated/reanimated (3.16.7): - DoubleConversion - glog - hermes-engine @@ -1589,10 +1608,9 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated (= 3.15.3) - - RNReanimated/worklets (= 3.15.3) + - RNReanimated/reanimated/apple (= 3.16.7) - Yoga - - RNReanimated/reanimated (3.15.3): + - RNReanimated/reanimated/apple (3.16.7): - DoubleConversion - glog - hermes-engine @@ -1613,7 +1631,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNReanimated/worklets (3.15.3): + - RNReanimated/worklets (3.16.7): - DoubleConversion - glog - hermes-engine @@ -1634,7 +1652,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNScreens (3.34.0): + - RNScreens (3.35.0): - DoubleConversion - glog - hermes-engine @@ -1656,7 +1674,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNSVG (15.7.1): + - RNSVG (15.11.1): - React-Core - SDWebImage (5.11.1): - SDWebImage/Core (= 5.11.1) @@ -1705,7 +1723,6 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) - - react-native-config (from `../node_modules/react-native-config`) - react-native-mmkv (from `../node_modules/react-native-mmkv`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) @@ -1824,8 +1841,6 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" React-microtasksnativemodule: :path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" - react-native-config: - :path: "../node_modules/react-native-config" react-native-mmkv: :path: "../node_modules/react-native-mmkv" react-native-netinfo: @@ -1904,12 +1919,12 @@ SPEC CHECKSUMS: DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 FBLazyVector: 7b438dceb9f904bd85ca3c31d64cce32a035472b fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 - glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f + glog: 69ef571f3de08433d766d614c73a9838a06bf7eb hermes-engine: 8d2103d6c0176779aea4e25df6bb1410f9946680 libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009 - MMKV: 506311d0494023c2f7e0b62cc1f31b7370fa3cfb - MMKVCore: 9e2e5fd529b64a9fe15f1a7afb3d73b2e27b4db9 - RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47 + MMKV: 3eacda84cd1c4fc95cf848d3ecb69d85ed56006c + MMKVCore: 508b4d3a8ce031f1b5c8bd235f0517fb3f4c73a9 + RCT-Folly: 4464f4d875961fce86008d45f4ecf6cef6de0740 RCTDeprecation: 4191f6e64b72d9743f6fe1a8a16e89e868f5e9e7 RCTRequired: 9bb589570f2bb3abc6518761e3fd1ad9b7f7f06c RCTTypeSafety: 1c1a8741c86df0a0ac1a99cf3fb0e29eedbc2c88 @@ -1938,10 +1953,9 @@ SPEC CHECKSUMS: React-logger: 4072f39df335ca443932e0ccece41fbeb5ca8404 React-Mapbuffer: 714f2fae68edcabfc332b754e9fbaa8cfc68fdd4 React-microtasksnativemodule: 4943ad8f99be8ccf5a63329fa7d269816609df9e - react-native-config: 8f7283449bbb048902f4e764affbbf24504454af react-native-mmkv: 7d0b6c2a79e73100b933f2947a9c8741d664e18b react-native-netinfo: f0a9899081c185db1de5bb2fdc1c88c202a059ac - react-native-safe-area-context: 851c62c48dce80ccaa5637b6aa5991a1bc36eca9 + react-native-safe-area-context: 141eca0fd4e4191288dfc8b96a7c7e1c2983447a React-nativeconfig: 4a9543185905fe41014c06776bf126083795aed9 React-NativeModulesApple: 0506da59fc40d2e1e6e12a233db5e81c46face27 React-perflogger: 3bbb82f18e9ac29a1a6931568e99d6305ef4403b @@ -1969,16 +1983,16 @@ SPEC CHECKSUMS: ReactCodegen: ff95a93d5ab5d9b2551571886271478eaa168565 ReactCommon: 289214026502e6a93484f4a46bcc0efa4f3f2864 RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8 - RNGestureHandler: 6dfe7692a191ee224748964127114edf057a1475 - RNLocalize: 4f22418187ecd5ca693231093ff1d912d1b3c9bc - RNReanimated: c543567b3482ea54dec3216811d2360e581d35d0 - RNScreens: 19719a9c326e925498ac3b2d35c4e50fe87afc06 - RNSVG: 4590aa95758149fa27c5c83e54a6a466349a1688 + RNGestureHandler: f62e64d9e5b74159b0a64af0cc01663db4d3e5e7 + RNLocalize: 06991b9c31e7a898a9fa6ddb204ce0f53a967248 + RNReanimated: d1e1069c64b84cc1dc91c73d59a2106c139ed7a1 + RNScreens: c7ceced6a8384cb9be5e7a5e88e9e714401fd958 + RNSVG: 669ed128ab9005090c612a0d627dbecb6ab5c76f SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d - Yoga: 4ef80d96a5534f0e01b3055f17d1e19a9fc61b63 + Yoga: 1354c027ab07c7736f99a3bef16172d6f1b12b47 PODFILE CHECKSUM: 5e17b38168c3ecd0174088728d32ea46a5eeb764 -COCOAPODS: 1.15.2 +COCOAPODS: 1.14.3 diff --git a/templates/CliTemplate/lefthook.yml b/templates/CliTemplate/lefthook.yml new file mode 100644 index 0000000..c876085 --- /dev/null +++ b/templates/CliTemplate/lefthook.yml @@ -0,0 +1,9 @@ +pre-commit: + parallel: true + commands: + type-check: + glob: '*.{ts,tsx}' + run: yarn typescript + lint: + glob: '*.{js,ts,jsx,tsx}' + run: yarn lint diff --git a/templates/CliTemplate/package.json b/templates/CliTemplate/package.json index 94cc238..e766a24 100644 --- a/templates/CliTemplate/package.json +++ b/templates/CliTemplate/package.json @@ -13,8 +13,6 @@ "preandroid": "ENVFILE=.env", "ios": "react-native run-ios", "lint": "eslint . --ext .js,.jsx,.ts,.tsx", - "postinstall": "npx pod-install", - "prepare": "husky", "refresh": "bash ./scripts/refresh.sh", "start": "NODE_ENV=default APP_ENV=default npx react-native start --reset-cache", "start:development": "NODE_ENV=development APP_ENV=development npx react-native start --reset-cache", @@ -46,6 +44,7 @@ "redux": "^5.0.1", "redux-persist": "^6.0.0", "redux-thunk": "^3.1.0", + "@react-navigation/bottom-tabs": "6.6.1", "yup": "^1.4.0" }, "devDependencies": { @@ -76,8 +75,8 @@ "eslint-plugin-react-native": "^4.1.0", "eslint-plugin-sort-destructure-keys": "^2.0.0", "eslint-plugin-sort-keys-fix": "^1.1.2", - "husky": "^9.1.6", "jest": "^29.7.0", + "lefthook": "^1.10.5", "prettier": "3.3.3", "react-native-dotenv": "^3.4.11", "react-native-svg-transformer": "^1.5.0", diff --git a/templates/CliTemplate/src/assets/svgIcons/index.ts b/templates/CliTemplate/src/assets/svgIcons/index.ts index 3735737..c4d4bba 100644 --- a/templates/CliTemplate/src/assets/svgIcons/index.ts +++ b/templates/CliTemplate/src/assets/svgIcons/index.ts @@ -1,9 +1,12 @@ +import NEWS_ICON from './news.svg'; import SETTING_ICON from './setting.svg'; export enum SVGIcons { - SETTING = 1, + SETTING = 'SETTING', + NEWS = 'NEWS', } export const SVGIconsMapper = { - 1: SETTING_ICON, + NEWS: NEWS_ICON, + SETTING: SETTING_ICON, }; diff --git a/templates/CliTemplate/src/assets/svgIcons/news.svg b/templates/CliTemplate/src/assets/svgIcons/news.svg new file mode 100644 index 0000000..02e6258 --- /dev/null +++ b/templates/CliTemplate/src/assets/svgIcons/news.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/CliTemplate/src/assets/svgIcons/setting.svg b/templates/CliTemplate/src/assets/svgIcons/setting.svg index ca0de71..e17441f 100644 --- a/templates/CliTemplate/src/assets/svgIcons/setting.svg +++ b/templates/CliTemplate/src/assets/svgIcons/setting.svg @@ -1,3 +1 @@ - - - + \ No newline at end of file diff --git a/templates/CliTemplate/src/constants/config.ts b/templates/CliTemplate/src/constants/config.ts index b810ff3..83a1dc6 100644 --- a/templates/CliTemplate/src/constants/config.ts +++ b/templates/CliTemplate/src/constants/config.ts @@ -1,6 +1,8 @@ -import { API_URL, ENV } from '@env'; +import { API_URL, APP_STORE_URL, ENV, PLAY_STORE_URL } from '@env'; export const AppConfig = { API_URL, ENV, + APP_STORE_URL, + PLAY_STORE_URL }; diff --git a/templates/CliTemplate/src/navigation/AppNavigation.tsx b/templates/CliTemplate/src/navigation/AppNavigation.tsx index 3cc9f3e..745b964 100644 --- a/templates/CliTemplate/src/navigation/AppNavigation.tsx +++ b/templates/CliTemplate/src/navigation/AppNavigation.tsx @@ -1,57 +1,74 @@ import React from 'react'; -import { NavigationContainerRef } from '@react-navigation/native'; import { - createNativeStackNavigator, - NativeStackNavigationOptions, -} from '@react-navigation/native-stack'; + BottomTabNavigationOptions, + createBottomTabNavigator, +} from '@react-navigation/bottom-tabs'; +import { NavigationContainerRef } from '@react-navigation/native'; import { useSelector } from 'react-redux'; -import { - LoginScreen, - NetworkLoggerScreen, - NewsDetailScreen, - NewsListScreen, - SettingScreen, -} from '@src/screens'; +import { SVGIcons } from '@src/assets'; +import { SvgIcon } from '@src/components'; +import { useAppContext } from '@src/context'; import { isForceUpdate } from '@src/store'; import { NavStackParams, Screen } from './appNavigation.type'; import { ForUpdateStack } from './ForceupdateStack'; +import { NewsListNavigation } from './NewsListNavigation'; +import { SettingNavigation } from './SettingNavigation'; export const navigationRef = React.createRef>(); -const Stack = createNativeStackNavigator(); +export const AppNavigation = () => { + const { color } = useAppContext(); -const screenOptions: NativeStackNavigationOptions = { - animation: 'slide_from_right', - headerShown: false, -}; + const screenOptions: BottomTabNavigationOptions = { + headerShown: false, + tabBarStyle: { backgroundColor: color.backgroundColor }, + }; -export const AppNavigation = () => { const isForceUpdateApp = useSelector(isForceUpdate); + const Tab = createBottomTabNavigator(); + return ( <> {isForceUpdateApp ? ( ) : ( - - - + ( + + ), + title: 'News', + }} + /> + ( + + ), + title: 'Setting', + }} /> - - - {__DEV__ && ( - - )} - + )} ); diff --git a/templates/CliTemplate/src/navigation/NewsListNavigation.tsx b/templates/CliTemplate/src/navigation/NewsListNavigation.tsx new file mode 100644 index 0000000..b260674 --- /dev/null +++ b/templates/CliTemplate/src/navigation/NewsListNavigation.tsx @@ -0,0 +1,38 @@ +import React from 'react'; + +import { + createNativeStackNavigator, + NativeStackNavigationOptions, +} from '@react-navigation/native-stack'; + +import { + NetworkLoggerScreen, + NewsDetailScreen, + NewsListScreen, +} from '@src/screens'; + +import { NavStackParams, Screen } from './appNavigation.type'; + +const Stack = createNativeStackNavigator(); + +const screenOptions: NativeStackNavigationOptions = { + animation: 'slide_from_right', + headerShown: false, +}; + +export const NewsListNavigation = () => { + return ( + <> + + + + {__DEV__ && ( + + )} + + + ); +}; diff --git a/templates/CliTemplate/src/navigation/SettingNavigation.tsx b/templates/CliTemplate/src/navigation/SettingNavigation.tsx new file mode 100644 index 0000000..3870648 --- /dev/null +++ b/templates/CliTemplate/src/navigation/SettingNavigation.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +import { + createNativeStackNavigator, + NativeStackNavigationOptions, +} from '@react-navigation/native-stack'; + +import { + LoginScreen, + NetworkLoggerScreen, + NewsDetailScreen, + NewsListScreen, + SettingScreen, +} from '@src/screens'; + +import { NavStackParams, Screen } from './appNavigation.type'; + +const Stack = createNativeStackNavigator(); + +const screenOptions: NativeStackNavigationOptions = { + animation: 'slide_from_right', + headerShown: false, +}; + +export const SettingNavigation = () => { + return ( + <> + + + + + + ); +}; diff --git a/templates/CliTemplate/src/navigation/appNavigation.type.ts b/templates/CliTemplate/src/navigation/appNavigation.type.ts index ed612be..78cdc40 100644 --- a/templates/CliTemplate/src/navigation/appNavigation.type.ts +++ b/templates/CliTemplate/src/navigation/appNavigation.type.ts @@ -11,6 +11,8 @@ export enum Screen { SETTING = 'SETTING', LOGIN = 'LOGIN', SIGNUP = 'SIGNUP', + NEWS_TAB = 'NEWS_TAB', + SETTING_TAB = 'SETTING_TAB', } export type NavStackParams = { diff --git a/templates/CliTemplate/src/screens/ForceUpdate/ForceUpdateScreen.tsx b/templates/CliTemplate/src/screens/ForceUpdate/ForceUpdateScreen.tsx index 6128026..dd021e2 100644 --- a/templates/CliTemplate/src/screens/ForceUpdate/ForceUpdateScreen.tsx +++ b/templates/CliTemplate/src/screens/ForceUpdate/ForceUpdateScreen.tsx @@ -12,7 +12,7 @@ const ForceUpdateScreen = () => { return ( - + {contents('forceUpdate.updateMessage')}