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
JavaScript
#!/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