UNPKG

rn-meter

Version:

Introducing our comprehensive React Native UI library, designed to streamline your app development process. This library includes customizable App Themes, Cross-platform compatibility for both iOS and Android & web a rich collection of pre-built component

1,749 lines (1,636 loc) 60.2 kB
#!/usr/bin/env node "use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/meter.ts var import_chalk3 = __toESM(require("chalk")); // src/content.ts var import_promises = __toESM(require("fs/promises")); var import_path = __toESM(require("path")); var configContent = (params) => { const config = { "schema": "https://github.com/DeveloperRejaul/ui-meter", "path": { "components": params.components_path, "utils": params.utils_path, "hook": params.hook_path } }; return JSON.stringify(config); }; var buttonContent = () => { const content = ` import { Pressable, Text, ViewStyle, StyleSheet, TextStyle, PressableProps } from 'react-native'; export interface IButton extends PressableProps { text?:string leftIcon?: React.JSX.Element rightIcon?: React.JSX.Element containerStyle?: ViewStyle; textStyle?: TextStyle variant?: 'solid' | 'outline' | 'link' action?:'primary' | 'secondary' | 'positive' | 'negative' isDisabled?:boolean; } export default function Button(props : IButton) { const { text = 'Hello World!', containerStyle, leftIcon, rightIcon, textStyle, variant = 'solid', action = 'primary', isDisabled, } = props; const color = { primary: '#1E88E5', secondary: '#8E24AA', positive: '#43A047', negative: '#D32F2F', }; const style = { solid: { backgroundColor: color[action], }, outline: { borderWidth: 1, borderColor: color[action], }, link: {}, }; return ( <Pressable pointerEvents={isDisabled ? 'none' : 'auto'} style={[styles.container, { opacity: isDisabled ? 0.5 : 1 }, style[variant], containerStyle]} {...props} > {leftIcon} <Text style={[styles.text, textStyle, variant === 'link' && { textDecorationLine: 'underline' }]}>{text}</Text> {rightIcon} </Pressable> ); } const styles = StyleSheet.create({ container: { flexDirection: 'row', paddingVertical: 10, paddingHorizontal: 20, borderRadius: 7, }, text: { fontWeight: '600', fontSize: 14, }, }); `; return content; }; function configTheme(isExpo, fontPath) { return __async(this, null, function* () { const fontName = yield import_promises.default.readdir(import_path.default.join(process.cwd(), fontPath)); const fonts = {}; const fontFamily = {}; fontName.forEach((name) => { const key = name.split(".")[0].replace("-", ""); fonts[key] = `require('${fontPath}/${name}')`; fontFamily[key.toUpperCase()] = key; }); return ` export const meterConfig = { colors: { dark: { primary: '#1E88E5', secondary: '#8E24AA', positive: '#43A047', negative: '#D32F2F', error: '#E53935', warning: '#FB8C00', }, light: { primary: '#2196F3', secondary: '#AB47BC', positive: '#66BB6A', negative: '#E57373', error: '#EF5350', warning: '#FFA726', }, }, fonts:{ ${Object.keys(fonts).map((key) => `'${key}': ${fonts[key]}`).join(",\n")} }, fontFamily:${JSON.stringify(fontFamily)}, spacing:{ '8xl': '96rem', '9xl': '128rem', }, borderRadius:{ '4xl': '2rem', }, } `; }); } function reactNativeConfigContent(path4) { return `module.exports = { project: { ios: {}, android: {}, }, assets: ["${path4}"], };`; } function eslintConfigContent() { return ` { "extends": [ "airbnb", "airbnb/hooks", "plugin:@typescript-eslint/recommended" ], "plugins": [ "@typescript-eslint", "react" ], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaFeatures": { "jsx": true }, "ecmaVersion": 2018, "sourceType": "module", "project": "./tsconfig.json" }, "rules": { "import/no-unresolved": 0, "react/jsx-filename-extension": [ 1, { "extensions": [ ".ts", ".tsx" ] } ], "no-use-before-define": "off", "import/extensions": [ "error", "never" ], "react/prop-types": 0, "no-shadow": "off", "max-len": "off", "react/react-in-jsx-scope": "off", "object-curly-newline": "off", "react/no-unused-prop-types":1 } } `; } function vscodeJsonContent() { return ` { "files.autoSave": "onFocusChange", "editor.defaultFormatter": "esbenp.prettier-vscode", "[javascript]": { "editor.formatOnSave": true }, "[javascriptreact]": { "editor.formatOnSave": true }, "editor.codeActionsOnSave": { "source.fixAll": "explicit" }, "eslint.validate": [ "javascript", "javascriptreact", "typescript", "typescriptreact" ], "editor.formatOnSave": true, "cSpell.words": [ "actualy" ] } `; } function cardCarousalContent() { return ` import { FlatList, Image, View, useWindowDimensions, NativeSyntheticEvent, NativeScrollEvent, ViewStyle } from 'react-native'; import React, { useEffect, useRef, useState, forwardRef, useImperativeHandle } from 'react'; interface IData { id?: string; imgUrl: string } interface ICardCarousal { data: IData[]; loop?:boolean; duration?:number; play?: boolean; onStart?:(value: boolean)=>void; onEnd?:(value: boolean)=> void; onChange?:(index: number) => void; height?: number; width?: number; nextBtn?: React.ReactNode; prevuesBtn?: React.ReactNode dotActiveColor?: string; dotInactiveColor?:string; containerStyle?:ViewStyle dotStyle?: ViewStyle dotActiveStyle?:ViewStyle, dotInactiveStyle?:ViewStyle, } let interval : NodeJS.Timeout; export default forwardRef((props: ICardCarousal, ref) => { const { width: WIDTH } = useWindowDimensions(); const { data = [], loop = true, play = true, height = 200, width = WIDTH, onEnd, onStart, duration = 2000, onChange, nextBtn, prevuesBtn, dotActiveColor = 'green', dotInactiveColor = 'red', containerStyle, dotStyle, dotActiveStyle, dotInactiveStyle, } = props; const [activeIndex, setActiveIndex] = useState(0); const [start, setStart] = useState(play); const slider = useRef<FlatList>(null); useImperativeHandle(ref, () => ({ scrollToIndex: (index: number) => { if (slider.current) { slider.current.scrollToIndex({ index, animated: true }); } }, next: () => { if (slider.current) { if (activeIndex === data.length - 1) { slider.current.scrollToIndex({ index: 0, animated: true }); } else { slider.current.scrollToIndex({ index: activeIndex + 1, animated: true }); } } }, prevues: () => { if (slider.current) { if (activeIndex === 0) { slider.current.scrollToIndex({ index: data.length - 1, animated: true }); } else { slider.current.scrollToIndex({ index: activeIndex - 1, animated: true }); } } }, })); useEffect(() => { if (play) { setStart(true); } else { setStart(false); } }, [play]); // off on control slider useEffect(() => { if (loop && start && data.length > 0) { interval = setInterval(() => { if (slider.current && activeIndex === data.length - 1) { onEnd?.(true); onChange?.(0); slider.current.scrollToIndex({ index: 0, animated: true, }); } else if (slider.current) { if (activeIndex === 0) onStart?.(true); onChange?.(activeIndex + 1); slider.current.scrollToIndex({ index: activeIndex + 1, animated: true, }); } }, duration); } else clearInterval(interval); return () => clearInterval(interval); }); // for scrollToIndex // eslint-disable-next-line @typescript-eslint/no-explicit-any const getItemLayout = (_data:any, index:number) => ({ length: width, offset: width * index, index, }); const onScroll = (event:NativeSyntheticEvent<NativeScrollEvent>) => { const scrollPosition = event.nativeEvent.contentOffset.x; const currentIndex = Math.ceil(Number(scrollPosition / width)); setActiveIndex(currentIndex); }; // dot indicator view const dotIndicator = () => data.map((_e, i) => <View key={Math.random()} style={[{ height: 10, width: 10, borderRadius: 100, backgroundColor: activeIndex === i ? dotActiveColor : dotInactiveColor, ...dotStyle }, activeIndex === i ? dotActiveStyle : dotInactiveStyle]} />); // handle key const keyExtractor = (item:IData, index:number) => item?.id || index.toString(); const renderItem = ({ index, item }:{index:number, item:IData}) => ( <Image key={index} style={{ width, height }} source={{ uri: item.imgUrl }} /> ); return ( <View style={{ borderRadius: 10, overflow: 'hidden', ...containerStyle }}> <FlatList ref={slider} data={data} renderItem={renderItem} keyExtractor={keyExtractor} horizontal pagingEnabled onScroll={onScroll} getItemLayout={getItemLayout} showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} /> {nextBtn} {prevuesBtn} <View style={{ flexDirection: 'row', justifyContent: 'center', columnGap: 10, position: 'absolute', left: 0, right: 0, bottom: 10 }}>{dotIndicator()}</View> </View> ); }); `; } function radioContent() { return ` /* eslint-disable react/jsx-no-constructed-context-values */ import { createContext, useContext, useState } from 'react'; import { Pressable, Text, TextStyle, View, ViewProps, ViewStyle } from 'react-native'; export interface IRadioProps { size?: number; inActiveColor?: string; activeColor?: string; inValidColor?: string; label?: string; isInvalid?: boolean; isDisabled?: boolean; onChange?: (value: string) => void; value: string; borderStyle?: ViewStyle borderActiveStyle?: ViewStyle borderInactiveStyle?: ViewStyle ballStyle?: ViewStyle ballActiveStyle?: ViewStyle ballInactiveStyle?: ViewStyle containerStyle?: ViewStyle containerActiveStyle?: ViewStyle containerInactiveStyle?: ViewStyle labelStyle?: TextStyle labelActiveStyle?: TextStyle labelInactiveStyle?: TextStyle text?: string; gap?: number textStyle?: TextStyle textActiveStyle?: TextStyle textInactiveStyle?: TextStyle } export interface IContextRadioGroup { setActiveValue: React.Dispatch<React.SetStateAction<string>>; activeValue: string onChange?: (value: string) => void; } export interface IRadioGroup extends ViewProps { onChange?: (value: string) => void; children: React.ReactNode; defaultActive?: string; } export const Context = createContext<IContextRadioGroup>({} as IContextRadioGroup); export function RadioGroup(props: IRadioGroup) { const [activeValue, setActiveValue] = useState(props.defaultActive ? props.defaultActive : ''); const { onChange, children } = props || {}; return ( <Context.Provider value={{ activeValue, setActiveValue, onChange }}> <View style={{ flexDirection: 'row' }} {...props}> {children} </View> </Context.Provider> ); } export function Radio(props: IRadioProps) { const color = { primary: '#1E88E5', dark: '#000000', active: '#43A047' } const { size = 25, inActiveColor = color.dark, activeColor = color.active, inValidColor = color.primary, borderStyle, ballStyle, ballActiveStyle, ballInactiveStyle, borderActiveStyle, borderInactiveStyle, containerStyle, containerActiveStyle, containerInactiveStyle, labelStyle, labelActiveStyle, labelInactiveStyle, value, isDisabled, isInvalid, label, onChange: radioOnChange, text, gap = 0.6, textActiveStyle, textInactiveStyle, textStyle, } = props; const { activeValue, setActiveValue, onChange } = useContext(Context); const BOX_SIZE = size * gap; const BOX_RADIUS = BOX_SIZE / 2; const CONTAINER_RADIUS = size * 0.5; const BORDER_WIDTH = size * 0.1; const DISABLE_OPACITY = 0.5; const isActive = activeValue === value; return ( <View style={[{ justifyContent: 'center', alignItems: 'center', columnGap: 20, opacity: isDisabled ? DISABLE_OPACITY : 1, ...containerStyle, }, isActive ? containerActiveStyle : containerInactiveStyle]} > <Pressable onPress={isDisabled ? () => { } : () => { setActiveValue(value); onChange?.(value); radioOnChange?.(value); }} style={[{ borderWidth: BORDER_WIDTH, // eslint-disable-next-line no-nested-ternary borderColor: isInvalid ? inValidColor : isActive ? activeColor : inActiveColor, width: size, height: size, borderRadius: CONTAINER_RADIUS, justifyContent: 'center', alignItems: 'center', ...borderStyle, }, isActive ? borderActiveStyle : borderInactiveStyle]} > <View style={[{ width: BOX_SIZE, height: BOX_SIZE, borderRadius: BOX_RADIUS, backgroundColor: isActive ? activeColor : 'transparent', justifyContent: 'center', alignItems: 'center', ...ballStyle, }, isActive ? ballActiveStyle : ballInactiveStyle]} > {text && ( <Text style={[{ fontSize: 10, color: color.dark, fontWeight: '800', ...textStyle, }, isActive ? textActiveStyle : textInactiveStyle]} > {text} </Text> )} </View> </Pressable> {label && ( <Text style={[{ fontWeight: 'bold', fontSize: 15, color: color.dark, ...labelStyle }, isActive ? labelActiveStyle : labelInactiveStyle]}> {label} </Text> )} </View> ); } `; } function colorReduceOpacity() { return ` export function reduceColorOpacity(hex: string, opacityPercent: number) { // Convert hex to RGB const hexToRgb = (hex: string) => { let trimmedHex = hex.replace('#', ''); if (trimmedHex.length === 3) { trimmedHex = trimmedHex.split('').map((hexChar) => hexChar + hexChar).join(''); } const bigint = parseInt(trimmedHex, 16); const r = (bigint >> 16) & 255; const g = (bigint >> 8) & 255; const b = bigint & 255; return { r, g, b }; }; // Convert opacity percent (0-100) to a range between 0 and 1 const opacity = Math.min(Math.max(opacityPercent, 0), 100) / 100; const { r, g, b } = hexToRgb(hex); // Return as RGBA string return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + opacity + ')'; } `; } function random() { return ` export const randomId = (length = 8) => { const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; let id = ''; for (let i = 0; i < length; i++) { const randomIndex = Math.floor(Math.random() * charset.length); id += charset[randomIndex]; } return id; }; `; } function box() { return ` import React from "react"; import { View, ViewProps } from "react-native"; export interface IBox extends ViewProps { className?: string } export function Box(props: IBox) { return <View {...props} /> } `; } function center() { return ` import { ViewProps, View } from "react-native"; interface ICenter extends ViewProps { className?: string } export function Center(props: ICenter) { return <View {...props} style={[{ flex: 1, justifyContent: 'center', alignItems: 'center' }, props.style]} /> } `; } function divider() { return ` import { View, ViewProps } from "react-native"; export interface IDivider extends ViewProps { className?: string; orientation?: "horizontal" | "vertical" backgroundColor?: string; m?: number } export default function Divider(props: IDivider) { const { orientation = "vertical", backgroundColor = "#000", m, } = props; const vm = orientation === "vertical" ? m : 0; const hm = orientation === "horizontal" ? m : 0 const h = orientation === "horizontal" ? "100%" : 1; const w = orientation === "vertical" ? null : 1; return ( <View {...props} style={[{ height: h, width: w, backgroundColor, marginVertical: vm, marginHorizontal: hm, }, props.style]} /> ); } `; } function HStackContent() { return ` import { View, ViewProps } from "react-native"; export interface IHStack extends ViewProps { space?: number; reversed?: boolean; className?: string } export function HStack(props: IHStack) { const { reversed = false, space = 5, } = props; return <View {...props} style={[{ flexDirection: reversed ? "row-reverse" : "row", columnGap: space }, props.style]} /> } `; } function VStackContent() { return ` import { View, ViewProps } from "react-native"; interface IVStack extends ViewProps { className?: string; space?: number; reversed?: boolean; } export function VStack(props: IVStack) { const { space = 5, reversed = false } = props; return <View {...props} style={[{ flexDirection: reversed ? "column-reverse" : "column", rowGap: space }, props.style]} /> } `; } function checkBoxContent() { return ` import React, { useState } from 'react'; import { Pressable, Text, TextStyle, ViewProps, ViewStyle } from 'react-native'; interface ICheckBox extends ViewProps { onChange?: (value: string | number) => void; defaultIsChecked?: boolean; isDisabled?: boolean borderColor?: string size?: number checkIcon?: React.JSX.Element boxStyle?: ViewStyle boxActiveStyle?: ViewStyle boxInactiveStyle?: ViewStyle checkStyle?: TextStyle checkActiveStyle?: TextStyle checkInactiveStyle?: TextStyle boxStyleClassName?: string; checkStyleClassName?: string; value: string | number } export function CheckBox(props: ICheckBox) { const { defaultIsChecked = false, isDisabled = false, borderColor = 'black', size = 20, checkIcon, checkStyle, boxStyle, boxActiveStyle, boxInactiveStyle, boxStyleClassName, checkStyleClassName, checkActiveStyle, checkInactiveStyle, onChange, value, } = props; const [select, setSelect] = useState<boolean>(defaultIsChecked); return ( <Pressable pointerEvents={isDisabled ? 'none' : 'auto'} style={[{ borderWidth: 1, borderColor, height: size, width: size, justifyContent: 'center', alignItems: 'center', borderRadius: 3, opacity: isDisabled ? 0.5 : 1, ...boxStyle, }, select ? boxActiveStyle : boxInactiveStyle]} onPress={() => setSelect((pre) => { onChange?.(value); return !pre; })} {...{ className: boxStyleClassName }} > {select ? checkIcon || <Text style={[checkStyle, select ? checkActiveStyle : checkInactiveStyle]} {...{ className: checkStyleClassName }}>\u2713</Text> : null} </Pressable> ); } `; } function switchContent() { return ` /* eslint-disable react/require-default-props */ import { Pressable, Animated, ViewStyle } from 'react-native'; import React, { useRef } from 'react'; export interface ISwitchProps { size?: number; onChange?:(value: boolean)=> void; borderStyle?:ViewStyle; borderActiveStyle?:ViewStyle; borderInActiveStyle?:ViewStyle; ballStyle?:ViewStyle ballActiveStyle?:ViewStyle ballInActiveStyle?:ViewStyle, switchMoveDuration?:number ballColor?:string ballActiveColor?:string ballInActiveColor?:string borderColor?:string; borderActiveColor?:string; borderInActiveColor?:string; } export default function Switch(props: ISwitchProps) { let isActive = false; const { size = 50, onChange = () => {}, ballActiveStyle, ballInActiveStyle, ballStyle, borderActiveStyle, borderInActiveStyle, borderStyle, switchMoveDuration = 200, ballActiveColor, ballColor = 'red', ballInActiveColor, borderActiveColor, borderColor = 'red', borderInActiveColor, } = props; const WIDTH = size; const HEIGHT = size * 0.6; const BALL_WIDTH = WIDTH * 0.6; const MOVE = WIDTH - BALL_WIDTH; const position = useRef(new Animated.Value(0)).current; const handlePress = () => { if (isActive) { Animated.timing(position, { duration: switchMoveDuration, toValue: 0, useNativeDriver: false, }).start(); isActive = false; onChange(false); return; } Animated.timing(position, { duration: switchMoveDuration, toValue: MOVE, useNativeDriver: false, }).start(); isActive = true; onChange(true); }; return ( <Pressable style={[{ borderWidth: 1, width: WIDTH, height: HEIGHT, borderRadius: WIDTH / 2, borderColor: isActive ? borderActiveColor || borderColor : borderInActiveColor || borderColor, overflow: 'hidden', ...borderStyle, }, isActive ? borderActiveStyle : borderInActiveStyle]} onPress={handlePress} > <Animated.View style={[{ height: '100%', width: BALL_WIDTH, backgroundColor: isActive ? ballActiveColor || ballColor : ballInActiveColor || ballColor, borderRadius: HEIGHT / 2, position: 'absolute', left: position, ...ballStyle, }, isActive ? ballActiveStyle : ballInActiveStyle]} /> </Pressable> ); } `; } function inputContent() { return ` /* eslint-disable react/require-default-props */ /* eslint-disable react/jsx-props-no-spreading */ import { TextInput, TextInputProps, View, ViewStyle } from 'react-native'; import React from 'react'; interface IInputProps extends TextInputProps{ leftIcon?: React.JSX.Element rightIcon?: React.JSX.Element containerStyle?:ViewStyle } export default function Input(props : IInputProps) { const { leftIcon, rightIcon, containerStyle, style, } = props; return ( <View style={[{ borderWidth: 1, flexDirection: 'row', borderRadius: 7, alignItems: 'center', }, containerStyle]} > {leftIcon && leftIcon} <TextInput {...props} style={[{ flex: 1, paddingHorizontal: 10, paddingVertical: 5, fontSize: 16, }, style]} /> {rightIcon && rightIcon} </View> ); } `; } function animatedInput() { return ` /* eslint-disable react/require-default-props */ /* eslint-disable react/jsx-props-no-spreading */ import { TextInput, TextInputProps, ViewStyle, Animated, View, Text } from 'react-native'; import React, { useRef } from 'react'; interface IInputProps extends TextInputProps{ leftIcon?: React.JSX.Element rightIcon?: React.JSX.Element containerStyle?:ViewStyle inputType?: 'password' | 'text' label?:string activeBorderColor?:string; inActiveBorderColor?:string; activeBg?:string; inActiveBg?:string; } export default function AnimatedInput(props : IInputProps) { const { leftIcon, rightIcon, containerStyle, style, label, activeBorderColor = '#fe4487', inActiveBorderColor = '#0000005b', activeBg = 'transparent', inActiveBg = '#00000002', } = props; const animated = useRef(new Animated.Value(0.5)).current; const viewRef = useRef<View>(null); const handleBlur = () => { viewRef.current?.setNativeProps({ borderWidth: 0.8 }); Animated.timing(animated, { toValue: 0.5, useNativeDriver: false, duration: 200, }).start(); }; const handleFocus = () => { viewRef.current?.setNativeProps({ borderWidth: 1 }); Animated.timing(animated, { toValue: 1, useNativeDriver: false, duration: 200, }).start(); }; const animColor = animated.interpolate({ inputRange: [0.5, 1], outputRange: [inActiveBorderColor, activeBorderColor], }); const animBg = animated.interpolate({ inputRange: [0.5, 1], outputRange: [inActiveBg, activeBg], }); return ( <View> {label && ( <Text style={{ color: '#0000008a', fontSize: 15, fontWeight: '400', paddingBottom: 4, }} > {props.label} </Text> )} <Animated.View ref={viewRef} style={[{ flexDirection: 'row', borderRadius: 7, alignItems: 'center', paddingHorizontal: 10, borderWidth: 0.8, borderColor: animColor, backgroundColor: animBg, }, containerStyle]} > {leftIcon && leftIcon} <TextInput onBlur={handleBlur} onFocus={handleFocus} {...props} style={[{ flex: 1, paddingHorizontal: 10, paddingVertical: 10, fontSize: 16, }, style]} /> {rightIcon && rightIcon} </Animated.View> </View> ); } `; } function bottomSheet() { return ` /* eslint-disable no-unsafe-optional-chaining */ /* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable func-names */ /* eslint-disable @typescript-eslint/no-explicit-any */ import React, { useEffect, useRef, useState } from 'react'; import { Animated, View, StyleSheet, useWindowDimensions, StatusBar, Pressable } from 'react-native'; interface IShowParams { render: React.JSX.Element, duration?:number; } interface IHideParams { duration?:number; } interface IBottomSheet { show: (ele: IShowParams) => void; hide: (ele?:IHideParams) => void; } export const bottomSheet:IBottomSheet = { show: () => {}, hide: () => {} }; export function BottomSheetContainer() { const { height: HEIGHT, width: WIDTH } = useWindowDimensions(); const TOTAL_HEIGHT = (HEIGHT + (StatusBar?.currentHeight || 0)); const translateY = useRef(new Animated.Value(0)).current; const opacity = useRef(new Animated.Value(0)).current; const [content, setContent] = useState<React.JSX.Element>(); useEffect(() => { bottomSheet.show = function (ele) { setContent(ele.render); Animated.sequence([ Animated.timing(translateY, { toValue: -TOTAL_HEIGHT, useNativeDriver: true, duration: ele.duration || 100, }), Animated.timing(opacity, { toValue: 1, useNativeDriver: true, duration: ele.duration || 100, }), ]).start(); }; bottomSheet.hide = function (data) { Animated.sequence([ Animated.timing(opacity, { toValue: 0, useNativeDriver: true, duration: data?.duration || 100, }), Animated.timing(translateY, { toValue: 0, useNativeDriver: true, duration: data?.duration || 100, }), ]).start(); }; }, []); return ( <Animated.View style={[styles.container, { height: TOTAL_HEIGHT, width: WIDTH, bottom: -TOTAL_HEIGHT, transform: [{ translateY }], zIndex: 1000 }]} > <Animated.View style={{ flex: 1, backgroundColor: '#00000062', opacity }}> <Pressable style={{ flex: 1 }} onPress={() => bottomSheet.hide()} /> </Animated.View> <View style={[styles.sheet]}> <View style={styles.bar} /> {content} </View> </Animated.View> ); } const styles = StyleSheet.create({ container: { position: 'absolute', backgroundColor: 'transparent', justifyContent: 'flex-end', }, sheet: { backgroundColor: '#fff', padding: 10, borderTopStartRadius: 15, borderTopEndRadius: 15, }, bar: { width: 100, alignSelf: 'center', height: 5, borderRadius: 50, backgroundColor: '#7272722c', }, }); `; } function keyboardAvoidingScrollView() { return ` /* eslint-disable react-hooks/exhaustive-deps */ import { Keyboard, ScrollViewProps, ScrollView, TextInput, useWindowDimensions} from 'react-native'; import React, { useEffect, useRef } from 'react'; interface IKeyboardAvoidingViewProps extends ScrollViewProps{ children: React.ReactNode; refs: React.RefObject<TextInput>[] inputPadding?:number } /* * @example * // Example usage of KeyboardAvoidingScrollView * export default function Example() { * const refs = Array.from({length: 20}, () => createRef<TextInput>()); * return ( * <KeyboardAvoidingScrollView * inputPadding={15} * refs={refs} * contentContainerStyle={{paddingBottom: 280}} * > * {refs.map((ele, index) => ( * <TextInput key={index} ref={ele} style={{borderWidth: 1, padding: 10}} placeholder={index} /> * ))} * </KeyboardAvoidingScrollView> * ); * } */ export default function KeyboardAvoidingScrollView(props : IKeyboardAvoidingViewProps) { const {children, refs,inputPadding = 0, ...extra} = props; const scrollViewRef = useRef<ScrollView | null>(null); const {height: HEIGHT} = useWindowDimensions(); useEffect(()=>{ Keyboard.addListener('keyboardDidShow', (params) => { for (const inputRef of refs) { if(inputRef.current?.isFocused()) { inputRef.current?.measure((...pr) =>{ const inputY = pr[5]; const keyboardHeight = params.endCoordinates.height; const offset = HEIGHT - keyboardHeight; if(inputY > offset) { const scrollHeight = (inputY - offset) + inputPadding; scrollViewRef.current?.scrollTo({y:scrollHeight}); } }); } } }); Keyboard.addListener('keyboardDidHide', () => { scrollViewRef.current?.scrollTo({y:0}); }); return () => { Keyboard.removeAllListeners('keyboardDidShow'); Keyboard.removeAllListeners('keyboardDidHide'); }; },[]); return ( <ScrollView ref={scrollViewRef}{...extra}> {children} </ScrollView> ); } `; } function audioRecordHook() { return ` import { useContext, useState } from 'react'; import AudioRecord from 'react-native-audio-record'; import SoundPlayer from 'react-native-sound-player'; import { Context } from './AudionRecordProvider'; import fs from 'react-native-fs'; export default function useAudioRecorder() { const {startRecording, PACKAGE_ID} = useContext(Context); const [isPlaying, setIsPlaying] = useState(false); const [path, setPath] = useState<string | null>(null); /** * Stops the current audio recording and returns the file path. * @returns {Promise<string | undefined>} The saved audio file path or undefined on error. */ const stopRecording = async () => { try { const filePath = await AudioRecord.stop(); setPath(filePath); return filePath; } catch (error) { console.log(error); } }; /** * Plays an audio file from a given URL or from the last recorded path. * @param {string} [playUrl] Optional URL to play audio from. */ const playRecording = (playUrl?: string) => { try { setIsPlaying(true); if (playUrl) { SoundPlayer.playUrl(playUrl); return; } if (path) { SoundPlayer.playUrl(path); return; } console.log('file path not found'); } catch (error) { console.log(error); } }; /** * Stops the audio player if currently playing. */ const stopPlayer = () => { try { if (!isPlaying) { return; } SoundPlayer.stop(); setIsPlaying(false); } catch (error) { console.log(error); } }; /** * Fetches all audio recordings from the app's document directory. * Filters files by PACKAGE_ID. * @returns {Promise<fs.ReadDirItem[] | undefined>} Array of file objects or undefined on error. */ const getAllRecording = async () => { try { const files = await fs.readDir(fs.DocumentDirectoryPath); const recordFiles = files.filter(file => file.path.includes(PACKAGE_ID)); console.log(recordFiles); return recordFiles; } catch (error) { console.log(error); } }; /** * Gets the most recent audio recording file. * @returns {Promise<fs.ReadDirItem | undefined>} Last recording file or undefined on error. */ const getLastRecording = async () => { try { const files = await getAllRecording(); if (files) { return files.pop(); } } catch (error) { console.log(error); } }; /** * Deletes all audio recordings in the app's document directory. */ const deleteAllRecording = async () => { try { const recordings = await getAllRecording(); if (recordings) { for (const element of recordings) { await fs.unlink(element.path); } console.log('All file delete success'); return; } console.log('file not found'); } catch (error) { console.log(error); } }; /** * Deletes a specific recording by its file name. * @param {string} id The file name of the recording to delete. */ const deleteRecordingById = async (id: string) => { try { await fs.unlink(\`\${fs.DocumentDirectoryPath}/\${id}\`); } catch (error) { console.log(error); } }; return { isPlaying, stopRecording, startRecording, playRecording, stopPlayer, getAllRecording, getLastRecording, deleteAllRecording, deleteRecordingById, utils: { ...SoundPlayer, onRecording: AudioRecord.on }, }; } `.trim(); } function audionRecordProvider() { return ` import { Component, createContext, ReactNode } from 'react'; import AudioRecord from 'react-native-audio-record'; import { check, PERMISSIONS, request } from 'react-native-permissions'; interface AudioProviderProps { children: ReactNode; } export interface IContextValuesTypes { startRecording:() => Promise<void> PACKAGE_ID: 'recorder-salespype' } export const Context = createContext({} as IContextValuesTypes); const PACKAGE_ID = 'recorder-salespype'; export default class AudioProvider extends Component<AudioProviderProps> { async componentDidMount(): Promise<void> { await this.checkPermission(); } async checkPermission () { const result = await check(PERMISSIONS.ANDROID.RECORD_AUDIO); if(result !== 'granted') { await request(PERMISSIONS.ANDROID.RECORD_AUDIO); } } async startRecording() { try { const options = { sampleRate: 16000, channels: 1, bitsPerSample: 16, audioSource: 6, wavFile: \`\${PACKAGE_ID}-\${Date.now()}.wav\`, }; AudioRecord.init(options); AudioRecord.start(); } catch (error) { console.log(error); } } render() { return ( <Context.Provider value={{startRecording: this.startRecording, PACKAGE_ID}}> {this.props.children} </Context.Provider> ); } }`; } function babelConfigContent() { return `module.exports = { presets: ['module:@react-native/babel-preset'], plugins: [ // if you already have other plugin just paste this lines below [ 'module-resolver', { extensions: [ '.ios.js', '.android.js', '.ios.jsx', '.android.jsx', '.js', '.jsx', '.json', '.ts', '.tsx', '.d.ts', ], root: ['.'], alias: { '@src': './src' }, }, ], ], }; `; } // src/core/2_setup.ts var import_promises3 = __toESM(require("fs/promises")); var import_inquirer2 = __toESM(require("inquirer")); var import_chalk2 = __toESM(require("chalk")); var import_ora2 = __toESM(require("ora")); var import_node_path2 = __toESM(require("path")); var recast = __toESM(require("recast")); // src/core/utils.ts var import_promises2 = __toESM(require("fs/promises")); var import_inquirer = __toESM(require("inquirer")); var import_node_child_process = require("child_process"); var import_figlet = __toESM(require("figlet")); var import_chalk = __toESM(require("chalk")); var import_ora = __toESM(require("ora")); var import_node_path = __toESM(require("path")); var import_node_fs = require("fs"); var Utils = class { /*********************************************************************************************** *@description Here all utility methods are available ************************************************************************************************/ /** * @description this method used check project configuration ts or js * @returns boolean */ checkIsTsProject() { return __async(this, null, function* () { const files = yield import_promises2.default.readdir(process.cwd()); return files.includes("tsconfig.json"); }); } /** * @description this function using for check config file exists in project * @returns boolean data */ checkConfigFileExists() { return __async(this, null, function* () { const isTS = yield this.checkIsTsProject(); const files = yield import_promises2.default.readdir(process.cwd()); return files.includes("meter.config.json") && files.includes(`meter.config.${isTS ? "ts" : "js"}`); }); } /** * @description this function handle to crete config file for cli * file name must be: meter.config.json */ creteConfig() { return __async(this, null, function* () { console.log(import_figlet.default.textSync("RN METER CLI", { horizontalLayout: "full" })); const ans = yield import_inquirer.default.prompt([ { type: "input", name: "components_path", message: "Type components path to configure config", default: "./meter/components" }, { type: "input", name: "utils_path", message: "Type utils path to configure config", default: "./meter/utils" }, { type: "input", name: "hook_path", message: "Type hook path to configure config", default: "./meter/hook" } ]); yield import_promises2.default.writeFile(`meter.config.json`, configContent(ans)); }); } /** * @description this function using for get config information * @returns config file information */ getConfig() { return __async(this, null, function* () { const config = yield import_promises2.default.readFile(import_node_path.default.join(process.cwd(), "meter.config.json"), "utf-8"); return JSON.parse(config); }); } /** * @description this function using for creating folder * @param destination path like : src/components/etc */ createFolder(destination) { return __async(this, null, function* () { let pt = process.cwd(); for (const element of destination.split("/")) { pt = import_node_path.default.join(pt, element); if (!(0, import_node_fs.existsSync)(pt)) yield import_promises2.default.mkdir(pt); } return pt; }); } /** * @description this function use for create folder when init command execute */ creteNecessaryFolder() { return __async(this, null, function* () { const config = yield this.getConfig(); const componentsPath = import_node_path.default.join(process.cwd(), config.path.components); const utilsPath = import_node_path.default.join(process.cwd(), config.path.utils); const hookPath = import_node_path.default.join(process.cwd(), config.path.hook); if (!(0, import_node_fs.existsSync)(componentsPath)) yield this.createFolder(config.path.components); if (!(0, import_node_fs.existsSync)(utilsPath)) yield this.createFolder(config.path.utils); if (!(0, import_node_fs.existsSync)(hookPath)) yield this.createFolder(config.path.hook); }); } /** * @description this function use for checking is expo project or native cli project; * if return true :this project is expo project S * if return is false : this project is not expo project. this is cli project or anything else * @returns boolean data */ checkIsExpoProject() { return __async(this, null, function* () { const files = yield import_promises2.default.readdir(process.cwd()); return files.includes(".expo"); }); } /** * @description this functiion useing install dependencies * */ install(command) { return __async(this, null, function* () { return new Promise((res, rej) => { const spinier = (0, import_ora.default)(import_chalk.default.blue(`Installing ${command.split(" ").pop()}`)).start(); (0, import_node_child_process.exec)(command, (error, stdout, stderr) => { if (error) { spinier.fail(import_chalk.default.red(error.message)).stop(); rej(); } ; spinier.succeed(import_chalk.default.green("Done task")).stop(); res(null); }); }); }); } /** * @description this function using execute npx command */ executeNpx(command) { return __async(this, null, function* () { return new Promise((res, rej) => { const spinier = (0, import_ora.default)(`Loading ${import_chalk.default.blue(`Exexute: ${command}`)}`).start(); (0, import_node_child_process.exec)(command, (error, stdout, stderr) => { if (error) { console.log(error); spinier.fail(import_chalk.default.red(error.message)).stop(); rej(); } ; console.log(stdout); console.log(stderr); spinier.succeed(import_chalk.default.green("Done all task")).stop(); res(null); }); }); }); } /** * @description this function using for checking configuration file exists */ checkConfigExists() { return __async(this, null, function* () { const configExists = yield this.checkConfigFileExists(); if (!configExists) { console.log(import_chalk.default.red("Config file does not exist")); console.log(import_chalk.default.green("Run: `npx rn-meter init`")); return false; } return true; }); } /** * @description this function using for write any kinds of file */ writeFile(content, filename, pt) { return __async(this, null, function* () { try { yield import_promises2.default.writeFile(import_node_path.default.join(pt, filename), content); } catch (error) { console.log(error); } }); } /** * @description this function using for creating components */ createComponent(content, componentName) { return __async(this, null, function* () { const isConfigExists = yield this.checkConfigExists(); if (isConfigExists) { const config = yield this.getConfig(); const isTs = yield this.checkIsTsProject(); const componentPath = import_node_path.default.join(process.cwd(), config.path.components, `${componentName}.${isTs ? "tsx" : "jsx"}`); if ((0, import_node_fs.existsSync)(componentPath)) return console.log(`${componentName} already exists if you overwrite`); yield import_promises2.default.writeFile(componentPath, content); } else { console.log(import_chalk.default.red("Library configuration not exists")); console.log(import_chalk.default.blue("Run: npx rn-meter init ")); } }); } /** * @description this function using for creating utils function */ createUtility(content, name) { return __async(this, null, function* () { const isConfigExists = yield this.checkConfigExists(); if (isConfigExists) { const config = yield this.getConfig(); const isTs = yield this.checkIsTsProject(); const componentPath = import_node_path.default.join(process.cwd(), config.path.utils, `${name}.${isTs ? "ts" : "js"}`); if ((0, import_node_fs.existsSync)(componentPath)) return console.log(`${name} already exists if you overwrite`); yield import_promises2.default.writeFile(componentPath, content); } else { console.log(import_chalk.default.red("Library configuration not exists")); console.log(import_chalk.default.blue("Run: npx rn-meter init ")); } }); } }; // src/core/3_audio.ts var Audio = class extends Utils { audioRecord() { return __async(this, null, function* () { const pt = yield this.createFolder("audio"); yield this.writeFile(audioRecordHook(), "useAudioRecorder.ts", pt); yield this.writeFile(audionRecordProvider(), "AudionRecordProvider.tsx", pt); }); } }; // src/core/2_setup.ts var Setup = class extends Audio { /** * @description this method used for initial project configuration * @param */ init() { return __async(this, null, function* () { const existsConfig = yield this.checkConfigFileExists(); if (!existsConfig) { yield this.creteConfig(); yield this.creteNecessaryFolder(); yield this.setupTheme(); yield this.install("npm install react-native-svg --save"); } else { import_chalk2.default.blue("Already initialized "); } }); } /** * @description this function using for setup theme */ setupTheme() { return __async(this, null, function* () { const isTS = yield this.checkIsTsProject(); const isExpo = yield this.checkIsExpoProject(); console.log(import_chalk2.default.green("Setup theme in project, like font, color, border radius, gap, space, etc")); const themeAns = yield import_inquirer2.default.prompt([ { type: "confirm", message: "Are you sure to setup theme in your project", name: "theme" } ]); if (themeAns.theme) { const fontPath = yield import_inquirer2.default.prompt([ { type: "input", message: "Please gave m