@funtools/create-react-native-app 1.1.2 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +19 -17
- package/package.json +12 -1
- package/.gitattributes +0 -2
- package/templates/base/babel.config.js +0 -11
- package/templates/base/global.css +0 -3
- package/templates/base/index.js +0 -6
- package/templates/base/metro.config.js +0 -18
- package/templates/base/nativewind-env.d.ts +0 -1
- package/templates/base/package.json +0 -54
- package/templates/base/react-native.config.js +0 -4
- package/templates/base/src/App.tsx +0 -16
- package/templates/base/src/Navigation/NavigationContainer/App.tsx +0 -16
- package/templates/base/src/Navigation/StackNavigators/Root/index.tsx +0 -23
- package/templates/base/src/Navigation/StackNavigators/Root/types.ts +0 -5
- package/templates/base/src/Navigation/index.ts +0 -2
- package/templates/base/src/Screens/Home.tsx +0 -10
- package/templates/base/src/Shared/Assets/Fonts/Roboto-Bold.ttf +0 -0
- package/templates/base/src/Shared/Assets/Fonts/Roboto-ExtraBold.ttf +0 -0
- package/templates/base/src/Shared/Assets/Fonts/Roboto-ExtraLight.ttf +0 -0
- package/templates/base/src/Shared/Assets/Fonts/Roboto-Light.ttf +0 -0
- package/templates/base/src/Shared/Assets/Fonts/Roboto-Medium.ttf +0 -0
- package/templates/base/src/Shared/Assets/Fonts/Roboto-Regular.ttf +0 -0
- package/templates/base/src/Shared/Assets/Fonts/Roboto-SemiBold.ttf +0 -0
- package/templates/base/src/Shared/Assets/Fonts/Roboto-Thin.ttf +0 -0
- package/templates/base/src/Shared/Components/Core/Icon.tsx +0 -24
- package/templates/base/src/Shared/Components/Core/Modals/CenterModal.tsx +0 -134
- package/templates/base/src/Shared/Components/Core/Modals/index.tsx +0 -1
- package/templates/base/src/Shared/Components/Core/RippleContainer.tsx +0 -91
- package/templates/base/src/Shared/Components/Core/ShowWhen.tsx +0 -11
- package/templates/base/src/Shared/Components/UI/Buttons/Button.tsx +0 -53
- package/templates/base/src/Shared/Components/UI/Buttons/IconButton.tsx +0 -32
- package/templates/base/src/Shared/Components/UI/Buttons/Utils/constance.ts +0 -39
- package/templates/base/src/Shared/Components/UI/Buttons/Utils/functions.ts +0 -55
- package/templates/base/src/Shared/Components/UI/Buttons/Utils/types.ts +0 -12
- package/templates/base/src/Shared/Components/UI/Buttons/index.ts +0 -2
- package/templates/base/src/Shared/Hooks/useUpdateEffect.ts +0 -14
- package/templates/base/src/Shared/Stores/Theme/Components/ThemeText.tsx +0 -22
- package/templates/base/src/Shared/Stores/Theme/Components/ThemeView.tsx +0 -25
- package/templates/base/src/Shared/Stores/Theme/Components/index.ts +0 -2
- package/templates/base/src/Shared/Stores/Theme/constance.ts +0 -25
- package/templates/base/src/Shared/Stores/Theme/index.ts +0 -19
- package/templates/base/src/Shared/Stores/Theme/types.ts +0 -3
- package/templates/base/src/Shared/Types/native.type.ts +0 -4
- package/templates/base/src/Shared/Types/number.type.ts +0 -8
- package/templates/base/src/Shared/Types/svg.d.ts +0 -7
- package/templates/base/tailwind.config.js +0 -22
- package/templates/base/tsconfig.json +0 -17
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
import path from "path";
|
|
@@ -13,17 +13,17 @@ let appName = "MyApp";
|
|
|
13
13
|
|
|
14
14
|
try {
|
|
15
15
|
console.log("Welcome to @funtools");
|
|
16
|
+
console.log('Thanks for using @funtools/create-react-native-app@1.1.4');
|
|
16
17
|
|
|
17
18
|
await getAppName();
|
|
18
19
|
|
|
19
20
|
initApp();
|
|
20
21
|
initTemplates();
|
|
21
|
-
updatePackageJson();
|
|
22
22
|
installDependencies();
|
|
23
23
|
|
|
24
24
|
console.log("✅ App setup complete");
|
|
25
25
|
process.exit(0);
|
|
26
|
-
} catch (
|
|
26
|
+
} catch (error) {
|
|
27
27
|
console.error("❌ Failed to create app");
|
|
28
28
|
console.error(error);
|
|
29
29
|
process.exit(1);
|
|
@@ -65,29 +65,31 @@ function initTemplates() {
|
|
|
65
65
|
|
|
66
66
|
fs.unlinkSync(path.join(process.cwd(), 'App.tsx'));
|
|
67
67
|
|
|
68
|
-
fs.cpSync(path.join(__dirname, `templates/base`), process.cwd(), {
|
|
68
|
+
fs.cpSync(path.join(__dirname, `templates/base/files`), process.cwd(), {
|
|
69
69
|
recursive: true,
|
|
70
70
|
force: true,
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
function updatePackageJson() {
|
|
76
|
-
console.log("Updating package.json...");
|
|
77
|
-
|
|
78
|
-
fs.writeFileSync(
|
|
79
|
-
`${process.cwd()}/package.json`,
|
|
80
|
-
fs
|
|
81
|
-
.readFileSync(`${process.cwd()}/package.json`, "utf-8")
|
|
82
|
-
.replaceAll("$AppName", appName),
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
75
|
function installDependencies() {
|
|
76
|
+
|
|
77
|
+
const {dependencies, devDependencies} = JSON.parse(
|
|
78
|
+
fs.readFileSync(path.join(__dirname, "templates/base/dependencies.json"), "utf-8")
|
|
79
|
+
);
|
|
80
|
+
|
|
88
81
|
console.log("Installing dependencies...");
|
|
82
|
+
execSync(`npm install ${dependencies.join(" ")}`, {
|
|
83
|
+
stdio: "inherit",
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
console.log("Installing dev dependencies...");
|
|
87
|
+
execSync(`npm install -D ${devDependencies.join(" ")}`, {
|
|
88
|
+
stdio: "inherit",
|
|
89
|
+
});
|
|
89
90
|
|
|
90
|
-
|
|
91
|
+
console.log('Remove @react-native/new-app-screen');
|
|
92
|
+
execSync(`npm uninstall @react-native/new-app-screen`, {
|
|
91
93
|
stdio: "inherit",
|
|
92
94
|
});
|
|
93
95
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funtools/create-react-native-app",
|
|
3
3
|
"description": "Create a React Native app with preconfigured setup",
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
"version": "1.1.4",
|
|
6
|
+
|
|
5
7
|
"type": "module",
|
|
8
|
+
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
|
|
6
11
|
"bin": {
|
|
7
12
|
"create-react-native-app": "index.js"
|
|
8
13
|
},
|
|
14
|
+
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"initApp.js"
|
|
18
|
+
],
|
|
19
|
+
|
|
9
20
|
"dependencies": {
|
|
10
21
|
"prompts": "^2.4.2"
|
|
11
22
|
}
|
package/.gitattributes
DELETED
package/templates/base/index.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
|
|
2
|
-
const { withNativeWind } = require("nativewind/metro");
|
|
3
|
-
|
|
4
|
-
const defaultConfig = getDefaultConfig(__dirname);
|
|
5
|
-
const { assetExts, sourceExts } = defaultConfig.resolver;
|
|
6
|
-
|
|
7
|
-
const config = mergeConfig(defaultConfig, {
|
|
8
|
-
/* your config */
|
|
9
|
-
transformer: {
|
|
10
|
-
babelTransformerPath: require.resolve("react-native-svg-transformer")
|
|
11
|
-
},
|
|
12
|
-
resolver: {
|
|
13
|
-
assetExts: assetExts.filter((ext) => ext !== "svg"),
|
|
14
|
-
sourceExts: [...sourceExts, "svg"]
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
module.exports = withNativeWind(config, { input: "./global.css" });
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="nativewind/types" />
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "$AppName",
|
|
3
|
-
"version": "0.0.1",
|
|
4
|
-
"private": true,
|
|
5
|
-
"scripts": {
|
|
6
|
-
"android": "react-native run-android",
|
|
7
|
-
"ios": "react-native run-ios",
|
|
8
|
-
"lint": "eslint .",
|
|
9
|
-
"start": "react-native start",
|
|
10
|
-
"test": "jest"
|
|
11
|
-
},
|
|
12
|
-
|
|
13
|
-
"dependencies": {
|
|
14
|
-
"@funtools/store": "^1.0.4",
|
|
15
|
-
"@react-navigation/native": "^7.1.26",
|
|
16
|
-
"@react-navigation/native-stack": "^7.9.0",
|
|
17
|
-
"lucide-react-native": "^0.562.0",
|
|
18
|
-
"nativewind": "^4.2.1",
|
|
19
|
-
"react": "19.2.0",
|
|
20
|
-
"react-native": "0.83.1",
|
|
21
|
-
"react-native-gesture-handler": "^2.30.0",
|
|
22
|
-
"react-native-safe-area-context": "^5.6.2",
|
|
23
|
-
"react-native-screens": "^4.19.0",
|
|
24
|
-
"react-native-svg": "^15.15.1"
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
"devDependencies": {
|
|
28
|
-
"@babel/core": "^7.25.2",
|
|
29
|
-
"@babel/preset-env": "^7.25.3",
|
|
30
|
-
"@babel/runtime": "^7.25.0",
|
|
31
|
-
"@react-native-community/cli": "20.0.0",
|
|
32
|
-
"@react-native-community/cli-platform-android": "20.0.0",
|
|
33
|
-
"@react-native-community/cli-platform-ios": "20.0.0",
|
|
34
|
-
"@react-native/babel-preset": "0.83.1",
|
|
35
|
-
"@react-native/eslint-config": "0.83.1",
|
|
36
|
-
"@react-native/metro-config": "0.83.1",
|
|
37
|
-
"@react-native/typescript-config": "0.83.1",
|
|
38
|
-
"@types/jest": "^29.5.13",
|
|
39
|
-
"@types/react": "^19.2.0",
|
|
40
|
-
"@types/react-test-renderer": "^19.1.0",
|
|
41
|
-
"babel-plugin-module-resolver": "^5.0.2",
|
|
42
|
-
"eslint": "^8.19.0",
|
|
43
|
-
"jest": "^29.6.3",
|
|
44
|
-
"prettier": "2.8.8",
|
|
45
|
-
"react-native-svg-transformer": "^1.5.2",
|
|
46
|
-
"react-test-renderer": "19.2.0",
|
|
47
|
-
"tailwindcss": "^3.4.19",
|
|
48
|
-
"typescript": "^5.8.3"
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
"engines": {
|
|
52
|
-
"node": ">=20"
|
|
53
|
-
}
|
|
54
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
2
|
-
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
3
|
-
import { AppNavigationContainer } from './Navigation';
|
|
4
|
-
import ThemeView from './Shared/Stores/Theme/Components/ThemeView';
|
|
5
|
-
|
|
6
|
-
export default function App() {
|
|
7
|
-
return (
|
|
8
|
-
<GestureHandlerRootView className='flex-1' >
|
|
9
|
-
<SafeAreaProvider>
|
|
10
|
-
<ThemeView className='flex-1 w-full h-full' >
|
|
11
|
-
<AppNavigationContainer/>
|
|
12
|
-
</ThemeView>
|
|
13
|
-
</SafeAreaProvider>
|
|
14
|
-
</GestureHandlerRootView>
|
|
15
|
-
);
|
|
16
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createNavigationContainerRef,
|
|
3
|
-
NavigationContainer,
|
|
4
|
-
} from '@react-navigation/native';
|
|
5
|
-
import RootStackNavigator from '../StackNavigators/Root';
|
|
6
|
-
import { RootStackParamList } from '../StackNavigators/Root/types';
|
|
7
|
-
|
|
8
|
-
export const navigationRef = createNavigationContainerRef<RootStackParamList>();
|
|
9
|
-
|
|
10
|
-
export default function AppNavigationContainer() {
|
|
11
|
-
return (
|
|
12
|
-
<NavigationContainer ref={navigationRef}>
|
|
13
|
-
<RootStackNavigator />
|
|
14
|
-
</NavigationContainer>
|
|
15
|
-
);
|
|
16
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
|
2
|
-
import { RootStackParamList } from './types';
|
|
3
|
-
import HomeScreen from '@/Screens/Home';
|
|
4
|
-
|
|
5
|
-
const Stack = createNativeStackNavigator<RootStackParamList>();
|
|
6
|
-
|
|
7
|
-
const screens: Array<Parameters<typeof Stack.Screen>[0]> = [
|
|
8
|
-
{ name: 'Home', component: HomeScreen },
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
export default function RootStackNavigator() {
|
|
12
|
-
return (
|
|
13
|
-
<Stack.Navigator
|
|
14
|
-
screenOptions={{
|
|
15
|
-
headerShown: false,
|
|
16
|
-
}}
|
|
17
|
-
>
|
|
18
|
-
{screens.map((screen, index) => (
|
|
19
|
-
<Stack.Screen key={index} {...screen} />
|
|
20
|
-
))}
|
|
21
|
-
</Stack.Navigator>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import * as icons from 'lucide-react-native/icons';
|
|
2
|
-
import { LucideProps } from "lucide-react-native";
|
|
3
|
-
import { useThemeStore } from "../../Stores/Theme";
|
|
4
|
-
import { ColorStates } from '../../Stores/Theme/types';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export type IconName = keyof typeof icons;
|
|
8
|
-
|
|
9
|
-
export type IconProps = LucideProps & {
|
|
10
|
-
name: IconName,
|
|
11
|
-
|
|
12
|
-
size?: number,
|
|
13
|
-
color?: ColorStates,
|
|
14
|
-
customColor?: string,
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default function Icon({name, size=16, color='text', customColor, ...props}: IconProps){
|
|
18
|
-
|
|
19
|
-
const colors = useThemeStore(states => customColor ?? states.colors[color])
|
|
20
|
-
|
|
21
|
-
const LucideIcon = icons[name];
|
|
22
|
-
|
|
23
|
-
return <LucideIcon {...props} color={colors} size={size} />;
|
|
24
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { Dispatch, ReactNode, SetStateAction, useEffect, useRef, useState } from "react";
|
|
2
|
-
import { Animated, Modal, ModalProps, PanResponder, useAnimatedValue, useWindowDimensions } from "react-native";
|
|
3
|
-
import RippleContainer from "../RippleContainer";
|
|
4
|
-
import ThemeView, { ThemeViewProps } from "../../../Stores/Theme/Components/ThemeView";
|
|
5
|
-
import { useThemeStore } from "../../../Stores/Theme";
|
|
6
|
-
import { ColorStates } from "../../../Stores/Theme/types";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export type CenterModalProps = Omit<ModalProps, 'animationType'> & {
|
|
11
|
-
children: ReactNode,
|
|
12
|
-
visible: boolean,
|
|
13
|
-
setVisible: Dispatch<SetStateAction<boolean>>,
|
|
14
|
-
preventCloseRequest?: boolean,
|
|
15
|
-
containerProps?: ThemeViewProps,
|
|
16
|
-
backdropColor?: ColorStates
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export default function CenterModal({children, visible, setVisible, preventCloseRequest=false, onRequestClose, style, containerProps, backdropColor: backdropVariant='text', ...props}: CenterModalProps) {
|
|
21
|
-
|
|
22
|
-
const backdropColor = useThemeStore(states => states.colors[backdropVariant].replace(')', ', 0.8)'));
|
|
23
|
-
|
|
24
|
-
const {width: windowWidth, height: windowHeight} = useWindowDimensions();
|
|
25
|
-
|
|
26
|
-
const [show, setShow] = useState(visible);
|
|
27
|
-
|
|
28
|
-
const animatedValue = useAnimatedValue(0);
|
|
29
|
-
|
|
30
|
-
const translate = useRef(new Animated.ValueXY({x: 0, y: 0})).current;
|
|
31
|
-
|
|
32
|
-
const {panHandlers} = useRef(PanResponder.create({
|
|
33
|
-
onStartShouldSetPanResponder: () => true,
|
|
34
|
-
onMoveShouldSetPanResponder: (_, gestureState) => {
|
|
35
|
-
return Math.abs(gestureState.dy) > 5;
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
onPanResponderTerminationRequest: () => false,
|
|
39
|
-
|
|
40
|
-
onPanResponderMove: (_, {dx, dy}) => {
|
|
41
|
-
translate.setValue({x: dx, y: dy});
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
onPanResponderRelease: (_, {vx, vy, dx, dy}) => {
|
|
45
|
-
const isNearEdge = [
|
|
46
|
-
Math.abs(dx) > windowWidth * 0.4,
|
|
47
|
-
Math.abs(dy) > windowHeight * 0.4
|
|
48
|
-
].some(Boolean);
|
|
49
|
-
|
|
50
|
-
const isMovingFast = [Math.abs(vx) > 2, Math.abs(vy) > 2].some(Boolean);
|
|
51
|
-
|
|
52
|
-
if((isNearEdge || isMovingFast) && !preventCloseRequest) {
|
|
53
|
-
return setVisible(false);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
Animated.spring(translate, {
|
|
57
|
-
toValue: {x: 0, y: 0},
|
|
58
|
-
bounciness: 12,
|
|
59
|
-
useNativeDriver: true
|
|
60
|
-
}).start()
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
})).current;
|
|
64
|
-
|
|
65
|
-
function handleClose() {
|
|
66
|
-
setTimeout(() => setShow(false), 150)
|
|
67
|
-
|
|
68
|
-
Animated.spring(animatedValue, {
|
|
69
|
-
toValue: 0,
|
|
70
|
-
bounciness: 12,
|
|
71
|
-
useNativeDriver: true
|
|
72
|
-
}).start()
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function handleOnRequestClose() {
|
|
76
|
-
if(preventCloseRequest) return;
|
|
77
|
-
|
|
78
|
-
setVisible(false);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
if(visible){
|
|
83
|
-
setShow(true);
|
|
84
|
-
translate.setValue({x: 0, y: 0});
|
|
85
|
-
|
|
86
|
-
Animated.spring(animatedValue, {
|
|
87
|
-
toValue: 1,
|
|
88
|
-
bounciness: 12,
|
|
89
|
-
useNativeDriver: true
|
|
90
|
-
}).start()
|
|
91
|
-
} else {
|
|
92
|
-
handleClose();
|
|
93
|
-
}
|
|
94
|
-
}, [visible])
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return (
|
|
98
|
-
<Modal {...props}
|
|
99
|
-
visible={show}
|
|
100
|
-
transparent
|
|
101
|
-
|
|
102
|
-
onRequestClose={handleOnRequestClose}
|
|
103
|
-
>
|
|
104
|
-
<Animated.View className="flex-1 w-full h-full items-center justify-center"
|
|
105
|
-
style={{
|
|
106
|
-
opacity: animatedValue,
|
|
107
|
-
backgroundColor: backdropColor
|
|
108
|
-
}}
|
|
109
|
-
>
|
|
110
|
-
<RippleContainer className="flex-1 w-full" onPress={handleOnRequestClose} rippleOpacity={0.2} />
|
|
111
|
-
|
|
112
|
-
<Animated.View {...panHandlers}
|
|
113
|
-
className={'w-full p-2 relative'}
|
|
114
|
-
style={{
|
|
115
|
-
opacity: animatedValue,
|
|
116
|
-
transform: [
|
|
117
|
-
{translateX: translate.x}, {translateY: translate.y},
|
|
118
|
-
{scale: animatedValue.interpolate({inputRange: [0, 1], outputRange: [0.4, 1]})}
|
|
119
|
-
]
|
|
120
|
-
}}
|
|
121
|
-
>
|
|
122
|
-
<ThemeView
|
|
123
|
-
{...containerProps}
|
|
124
|
-
style={[{ borderRadius: 12, padding: 4 }, style, {overflow: 'hidden', width: '100%'}]}
|
|
125
|
-
>
|
|
126
|
-
{children}
|
|
127
|
-
</ThemeView>
|
|
128
|
-
</Animated.View>
|
|
129
|
-
|
|
130
|
-
<RippleContainer className="flex-1 w-full" onPress={handleOnRequestClose} rippleOpacity={0.2} />
|
|
131
|
-
</Animated.View>
|
|
132
|
-
</Modal>
|
|
133
|
-
)
|
|
134
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {default as CenterModal} from './CenterModal';
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { ReactNode, useRef, useState } from "react";
|
|
2
|
-
import { useThemeStore } from "../../Stores/Theme";
|
|
3
|
-
import { ColorStates } from "../../Stores/Theme/types";
|
|
4
|
-
import { Animated, GestureResponderEvent, Pressable, PressableProps, useAnimatedValue, View, ViewStyle } from "react-native";
|
|
5
|
-
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export type RippleContainerProps = PressableProps & {
|
|
9
|
-
children?: ReactNode,
|
|
10
|
-
color?: ColorStates
|
|
11
|
-
style?: ViewStyle
|
|
12
|
-
rippleOpacity?: number,
|
|
13
|
-
rippleColor?: string,
|
|
14
|
-
rippleScale?: number,
|
|
15
|
-
rippleCount?: number,
|
|
16
|
-
duration?: number,
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export default function RippleContainer({children, style, onPress, color='text', rippleColor, rippleOpacity=0.4, rippleScale=1, duration=300, rippleCount=3, ...props}: RippleContainerProps) {
|
|
21
|
-
|
|
22
|
-
const {top, left} = useSafeAreaInsets();
|
|
23
|
-
|
|
24
|
-
const rgb = useThemeStore(s => s.colors[color]);
|
|
25
|
-
rippleColor ??= `rgb(${rgb})`;
|
|
26
|
-
|
|
27
|
-
const [position, setPosition] = useState<{top: number, left: number}>({top: 0, left: 0});
|
|
28
|
-
|
|
29
|
-
const animatedValue = useAnimatedValue(0);
|
|
30
|
-
|
|
31
|
-
const button = useRef<View>(null);
|
|
32
|
-
|
|
33
|
-
function handleOnPress(event: GestureResponderEvent) {
|
|
34
|
-
const {pageX, pageY} = event.nativeEvent;
|
|
35
|
-
|
|
36
|
-
button.current?.measureInWindow((x, y, w) => {
|
|
37
|
-
x += left;
|
|
38
|
-
y += top;
|
|
39
|
-
|
|
40
|
-
setPosition({top: pageY - y - w / 2, left: pageX - x - w / 2})
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
startAnimation();
|
|
44
|
-
|
|
45
|
-
onPress?.(event);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
function startAnimation() {
|
|
50
|
-
Animated.timing(animatedValue, {
|
|
51
|
-
toValue: 1,
|
|
52
|
-
duration,
|
|
53
|
-
useNativeDriver: true
|
|
54
|
-
}).start(() => {
|
|
55
|
-
animatedValue.setValue(0);
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<Pressable ref={button} {...props} onPress={handleOnPress} style={[style, { overflow: 'hidden', position: 'relative' }]}>
|
|
62
|
-
<View className="absolute aspect-square" style={{...position, width: '100%'}} >
|
|
63
|
-
{
|
|
64
|
-
[...new Array(Math.min(rippleCount, 5))].map((_, index) => (
|
|
65
|
-
<Animated.View key={index}
|
|
66
|
-
className={'absolute w-full aspect-square rounded-full'}
|
|
67
|
-
style={{
|
|
68
|
-
backgroundColor: rippleColor,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
opacity: animatedValue.interpolate({
|
|
72
|
-
inputRange: [0, 1],
|
|
73
|
-
outputRange: [rippleOpacity * ((rippleCount - index) / rippleCount), 0],
|
|
74
|
-
}),
|
|
75
|
-
|
|
76
|
-
transform: [{
|
|
77
|
-
scale: animatedValue.interpolate({
|
|
78
|
-
inputRange: [0, 0.1, 1],
|
|
79
|
-
outputRange: [0, rippleScale * 0.1, rippleScale + (index * 0.1)]
|
|
80
|
-
})
|
|
81
|
-
}]
|
|
82
|
-
}}
|
|
83
|
-
/>
|
|
84
|
-
))
|
|
85
|
-
}
|
|
86
|
-
</View>
|
|
87
|
-
|
|
88
|
-
{children}
|
|
89
|
-
</Pressable>
|
|
90
|
-
);
|
|
91
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import RippleContainer, { RippleContainerProps } from '@/Shared/Components/Core/RippleContainer';
|
|
2
|
-
import Icon, { IconName } from '@/Shared/Components/Core/Icon';
|
|
3
|
-
import { ButtonSize, ButtonVariants } from './Utils/types';
|
|
4
|
-
import { RANGE } from '@/Shared/Types/number.type';
|
|
5
|
-
import { getButtonStyle } from './Utils/functions';
|
|
6
|
-
import { BUTTON_LAYOUT } from './Utils/constance';
|
|
7
|
-
import ThemeText from '@/Shared/Stores/Theme/Components/ThemeText';
|
|
8
|
-
import ShowWhen from '@/Shared/Components/Core/ShowWhen';
|
|
9
|
-
|
|
10
|
-
type ButtonProp = Omit<RippleContainerProps, 'rippleColor' | 'rippleScale'> & {
|
|
11
|
-
title: string;
|
|
12
|
-
|
|
13
|
-
startIcon?: IconName;
|
|
14
|
-
endIcon?: IconName;
|
|
15
|
-
variant?: ButtonVariants;
|
|
16
|
-
size?: ButtonSize;
|
|
17
|
-
rounded?: number | `${RANGE<0, 100>}%`;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export default function Button({ title, startIcon, endIcon, variant = 'solid', color = 'primary', size = 'md', rounded, style, ...props}: ButtonProp) {
|
|
22
|
-
|
|
23
|
-
const { color: textColor, borderColor, backgroundColor } = getButtonStyle(variant, color);
|
|
24
|
-
|
|
25
|
-
const {fontSize, ...containerStyle} = {
|
|
26
|
-
...BUTTON_LAYOUT[size],
|
|
27
|
-
...rounded !== undefined ? {borderRadius: rounded} : {}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<RippleContainer
|
|
32
|
-
{...props}
|
|
33
|
-
rippleColor={textColor}
|
|
34
|
-
rippleScale={2}
|
|
35
|
-
|
|
36
|
-
style={{
|
|
37
|
-
backgroundColor, borderColor, flexDirection: 'row', gap: Math.floor(fontSize / 2), alignItems: 'center', justifyContent: 'center',
|
|
38
|
-
...containerStyle,
|
|
39
|
-
...style,
|
|
40
|
-
}}
|
|
41
|
-
>
|
|
42
|
-
<ShowWhen when={!!startIcon}>
|
|
43
|
-
<Icon name={startIcon as IconName} customColor={textColor} />
|
|
44
|
-
</ShowWhen>
|
|
45
|
-
|
|
46
|
-
<ThemeText textColor={textColor} style={{fontSize}} >{title}</ThemeText>
|
|
47
|
-
|
|
48
|
-
<ShowWhen when={!!endIcon} >
|
|
49
|
-
<Icon name={endIcon as IconName} customColor={textColor} />
|
|
50
|
-
</ShowWhen>
|
|
51
|
-
</RippleContainer>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { RANGE } from "../../../Types/number.type"
|
|
2
|
-
import Icon, { IconName } from "../../Core/Icon"
|
|
3
|
-
import RippleContainer, { RippleContainerProps } from "../../Core/RippleContainer"
|
|
4
|
-
import { BUTTON_LAYOUT } from "./Utils/constance"
|
|
5
|
-
import { getButtonStyle } from "./Utils/functions"
|
|
6
|
-
import { ButtonSize, ButtonVariants } from "./Utils/types"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export type IconButtonProps = RippleContainerProps & {
|
|
10
|
-
icon: IconName,
|
|
11
|
-
|
|
12
|
-
variant?: ButtonVariants,
|
|
13
|
-
size?: number | ButtonSize,
|
|
14
|
-
rounded?: number | `${RANGE<0, 100>}%`
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default function IconButton({variant='soft', color='primary', icon, size='md', rounded='50%', ...props}: IconButtonProps) {
|
|
18
|
-
|
|
19
|
-
const {color: textColor, ...style} = getButtonStyle(variant, color);
|
|
20
|
-
|
|
21
|
-
const height = typeof size === 'number' ? size : BUTTON_LAYOUT[size].height;
|
|
22
|
-
const borderWidth = typeof size === 'number' ? 1 : BUTTON_LAYOUT[size].borderWidth;
|
|
23
|
-
return (
|
|
24
|
-
<RippleContainer {...props}
|
|
25
|
-
rippleScale={2}
|
|
26
|
-
rippleColor={textColor}
|
|
27
|
-
style={{...style, height, borderRadius: rounded, borderWidth, alignItems: 'center', justifyContent: 'center', aspectRatio: 1}}
|
|
28
|
-
>
|
|
29
|
-
<Icon customColor={textColor} name={icon} size={Math.floor(height * 0.6)} />
|
|
30
|
-
</RippleContainer>
|
|
31
|
-
)
|
|
32
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { ButtonLayout, ButtonSize } from "./types";
|
|
2
|
-
|
|
3
|
-
export const BUTTON_LAYOUT: Record<ButtonSize, ButtonLayout> = {
|
|
4
|
-
'xs': {
|
|
5
|
-
height: 24,
|
|
6
|
-
borderRadius: 8,
|
|
7
|
-
paddingInline: 8,
|
|
8
|
-
fontSize: 10,
|
|
9
|
-
borderWidth: 1
|
|
10
|
-
},
|
|
11
|
-
'sm': {
|
|
12
|
-
height: 32,
|
|
13
|
-
borderRadius: 12,
|
|
14
|
-
paddingInline: 12,
|
|
15
|
-
fontSize: 14,
|
|
16
|
-
borderWidth: 1
|
|
17
|
-
},
|
|
18
|
-
'md': {
|
|
19
|
-
height: 40,
|
|
20
|
-
borderRadius: 14,
|
|
21
|
-
paddingInline: 14,
|
|
22
|
-
fontSize: 16,
|
|
23
|
-
borderWidth: 1
|
|
24
|
-
},
|
|
25
|
-
'lg': {
|
|
26
|
-
height: 48,
|
|
27
|
-
borderRadius: 16,
|
|
28
|
-
paddingInline: 16,
|
|
29
|
-
fontSize: 20,
|
|
30
|
-
borderWidth: 2
|
|
31
|
-
},
|
|
32
|
-
'xl': {
|
|
33
|
-
height: 56,
|
|
34
|
-
borderRadius: 18,
|
|
35
|
-
paddingInline: 18,
|
|
36
|
-
fontSize: 24,
|
|
37
|
-
borderWidth: 2
|
|
38
|
-
}
|
|
39
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
|
-
import { useThemeStore } from "../../../../Stores/Theme";
|
|
3
|
-
import { ColorStates } from "../../../../Stores/Theme/types";
|
|
4
|
-
import { ButtonVariants } from "./types";
|
|
5
|
-
|
|
6
|
-
export function getButtonStyle(variant: ButtonVariants, color: ColorStates) {
|
|
7
|
-
const {textColor, bgColor} = useThemeStore((states) => {
|
|
8
|
-
if(['text', 'bg'].includes(color ?? '')) {
|
|
9
|
-
return {
|
|
10
|
-
bgColor: states.colors[color],
|
|
11
|
-
textColor: states.colors[color === 'text' ? 'bg' : 'text']
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return {
|
|
16
|
-
bgColor: states.colors[color],
|
|
17
|
-
textColor: 'rgb(255,255,255)'
|
|
18
|
-
}
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const style = useMemo(() => ({
|
|
23
|
-
'solid': {
|
|
24
|
-
color: textColor,
|
|
25
|
-
backgroundColor: bgColor,
|
|
26
|
-
borderColor: bgColor,
|
|
27
|
-
},
|
|
28
|
-
|
|
29
|
-
'outlined': {
|
|
30
|
-
color: bgColor,
|
|
31
|
-
backgroundColor: 'transparent',
|
|
32
|
-
borderColor: bgColor,
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
'soft': {
|
|
36
|
-
color: bgColor,
|
|
37
|
-
backgroundColor: bgColor.replace(')', ', 0.2)'),
|
|
38
|
-
borderColor: bgColor.replace(')', ', 0.2)'),
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
'soft-outlined': {
|
|
42
|
-
color: bgColor,
|
|
43
|
-
backgroundColor: bgColor.replace(')', ', 0.2)'),
|
|
44
|
-
borderColor: bgColor
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
'text': {
|
|
48
|
-
color: bgColor,
|
|
49
|
-
backgroundColor: 'transparent',
|
|
50
|
-
borderColor: 'transparent',
|
|
51
|
-
}
|
|
52
|
-
}[variant]), [bgColor, textColor]);
|
|
53
|
-
|
|
54
|
-
return style;
|
|
55
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export type ButtonVariants = 'solid' | 'outlined' | 'soft' | 'soft-outlined' | 'text';
|
|
2
|
-
|
|
3
|
-
export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
4
|
-
|
|
5
|
-
// add more according to need;
|
|
6
|
-
export type ButtonLayout = {
|
|
7
|
-
height: number,
|
|
8
|
-
borderRadius: number,
|
|
9
|
-
paddingInline: number,
|
|
10
|
-
fontSize: number,
|
|
11
|
-
borderWidth: number
|
|
12
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { DependencyList, EffectCallback, useEffect, useRef } from "react";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export default function useUpdateEffect(effect: EffectCallback, deps: DependencyList) {
|
|
5
|
-
const mountedRef = useRef(false);
|
|
6
|
-
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
if (mountedRef.current) {
|
|
9
|
-
return effect();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
mountedRef.current = true;
|
|
13
|
-
}, deps);
|
|
14
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Animated, TextProps } from "react-native";
|
|
2
|
-
import { useThemeStore } from "..";
|
|
3
|
-
import { ColorStates } from "../types";
|
|
4
|
-
import { AnimatedInterpolValue } from "../../../Types/native.type";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export type ThemeTextProps = TextProps & {
|
|
8
|
-
color?: ColorStates,
|
|
9
|
-
textColor?: string | AnimatedInterpolValue
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default function ThemeText({style, color: _color = 'text', textColor, className, ...props}: ThemeTextProps): React.JSX.Element {
|
|
13
|
-
|
|
14
|
-
const color = useThemeStore(states => textColor ?? states.colors[_color]);
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<Animated.Text {...props}
|
|
18
|
-
style={[style, {color}]}
|
|
19
|
-
className={`font-regular ${className}`}
|
|
20
|
-
/>
|
|
21
|
-
)
|
|
22
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Animated, ViewProps } from "react-native";
|
|
2
|
-
import { useThemeStore } from "..";
|
|
3
|
-
import { ColorStates } from "../types";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export type ThemeViewProps = ViewProps & {
|
|
7
|
-
color?: ColorStates,
|
|
8
|
-
backgroundColor?: string,
|
|
9
|
-
useWindBackground?: boolean
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default function ThemeView({style, color = 'bg', backgroundColor, useWindBackground=false, ...props}: ThemeViewProps): React.JSX.Element {
|
|
13
|
-
|
|
14
|
-
const {_backgroundColor} = useThemeStore(states => ({
|
|
15
|
-
_backgroundColor: states.colors[color]
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
if(!backgroundColor) backgroundColor = _backgroundColor;
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<Animated.View {...props}
|
|
22
|
-
style={[style, useWindBackground === false ? {backgroundColor} : null]}
|
|
23
|
-
/>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Theme, ColorStates } from './types';
|
|
2
|
-
|
|
3
|
-
export const _theme: Theme = 'light';
|
|
4
|
-
|
|
5
|
-
export const _colors: Record<Theme, Record<ColorStates, string>> = {
|
|
6
|
-
light: {
|
|
7
|
-
text: 'rgb(0, 0, 0)',
|
|
8
|
-
bg: 'rgb(240, 242, 245)',
|
|
9
|
-
|
|
10
|
-
primary: 'rgb(40, 120, 255)',
|
|
11
|
-
error: 'rgb(245, 34, 45)',
|
|
12
|
-
warning: 'rgb(255, 184, 0)',
|
|
13
|
-
info: 'rgb(74, 108, 135)',
|
|
14
|
-
},
|
|
15
|
-
|
|
16
|
-
dark: {
|
|
17
|
-
text: 'rgb(240, 242, 245)',
|
|
18
|
-
bg: 'rgb(0, 0, 0)',
|
|
19
|
-
|
|
20
|
-
primary: 'rgb(40, 120, 255)',
|
|
21
|
-
error: 'rgb(245, 34, 45)',
|
|
22
|
-
warning: 'rgb(255, 184, 0)',
|
|
23
|
-
info: 'rgb(74, 108, 135)',
|
|
24
|
-
},
|
|
25
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { Theme } from './types';
|
|
2
|
-
import { _colors, _theme } from './constance';
|
|
3
|
-
import { createStore } from '@fun-tools/store';
|
|
4
|
-
|
|
5
|
-
const { useStore, useHandlers } = createStore({
|
|
6
|
-
states: {
|
|
7
|
-
theme: _theme,
|
|
8
|
-
colors: _colors[_theme],
|
|
9
|
-
},
|
|
10
|
-
|
|
11
|
-
syncHandlers: {
|
|
12
|
-
toggleTheme(state) {
|
|
13
|
-
state.theme = state.theme === 'dark' ? 'light' : 'dark';
|
|
14
|
-
state.colors = _colors[state.theme];
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
export { useStore as useThemeStore, useHandlers as useThemeHandlers };
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export type FINITE_NUMBER<E extends number, A extends unknown[] = []> = (
|
|
2
|
-
A['length'] extends E ? A[number] | E : FINITE_NUMBER<E, [...A, A['length']]>
|
|
3
|
-
)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export type RANGE<S extends number, E extends number> = (
|
|
7
|
-
Exclude<FINITE_NUMBER<E>, FINITE_NUMBER<S>> | S
|
|
8
|
-
)
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/** @type {import('tailwindcss').Config} */
|
|
2
|
-
module.exports = {
|
|
3
|
-
content: [
|
|
4
|
-
"./src/**/*.{js,jsx,ts,tsx}",
|
|
5
|
-
],
|
|
6
|
-
presets: [require("nativewind/preset")],
|
|
7
|
-
theme: {
|
|
8
|
-
extend: {
|
|
9
|
-
fontFamily: {
|
|
10
|
-
thin: ['Roboto-Thin'],
|
|
11
|
-
extralight: ['Roboto-ExtraLight'],
|
|
12
|
-
light: ['Roboto-Light'],
|
|
13
|
-
regular: ['Roboto-Regular'],
|
|
14
|
-
medium: ['Roboto-Medium'],
|
|
15
|
-
semibold: ['Roboto-SemiBold'],
|
|
16
|
-
bold: ['Roboto-Bold'],
|
|
17
|
-
extrabold: ['Roboto-ExtraBold'],
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
plugins: [],
|
|
22
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "@react-native/typescript-config",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"jsx": "react-native",
|
|
5
|
-
"types": ["jest"],
|
|
6
|
-
"paths": {
|
|
7
|
-
"@/*": ["./src/*"]
|
|
8
|
-
}
|
|
9
|
-
},
|
|
10
|
-
"include": [
|
|
11
|
-
"**/*.ts",
|
|
12
|
-
"**/*.tsx",
|
|
13
|
-
"nativewind-env.d.ts",
|
|
14
|
-
"react-native.config.js"
|
|
15
|
-
],
|
|
16
|
-
"exclude": ["**/node_modules", "**/Pods"]
|
|
17
|
-
}
|