aport-tools 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/app.json +5 -0
- package/package.json +30 -0
- package/rollup.config.js +31 -0
- package/src/Button.tsx +104 -0
- package/src/components/ThemeToggle.tsx +39 -0
- package/src/context/ThemeContext.tsx +70 -0
- package/src/index.ts +1 -0
- package/src/styles/colors.ts +60 -0
- package/src/styles/theme.ts +50 -0
- package/tsconfig.json +30 -0
package/app.json
ADDED
package/package.json
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
{
|
2
|
+
"name": "aport-tools",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "",
|
5
|
+
|
6
|
+
"scripts": {
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
8
|
+
},
|
9
|
+
"keywords": [],
|
10
|
+
"author": "EvansGxz",
|
11
|
+
"license": "ISC",
|
12
|
+
"devDependencies": {
|
13
|
+
"@rollup/plugin-commonjs": "^28.0.0",
|
14
|
+
"@rollup/plugin-node-resolve": "^15.3.0",
|
15
|
+
"@rollup/plugin-typescript": "^12.1.0",
|
16
|
+
"@types/react": "^18.3.10",
|
17
|
+
"@types/react-native": "^0.73.0",
|
18
|
+
"@types/styled-components-react-native": "^5.2.5",
|
19
|
+
"rollup": "^4.22.5",
|
20
|
+
"rollup-plugin-peer-deps-external": "^2.2.4",
|
21
|
+
"typescript": "^5.6.2"
|
22
|
+
},
|
23
|
+
"dependencies": {
|
24
|
+
"@react-native-async-storage/async-storage": "^2.0.0",
|
25
|
+
"expo": "^51.0.34",
|
26
|
+
"react": "^18.3.1",
|
27
|
+
"react-native": "^0.75.3",
|
28
|
+
"styled-components": "^6.1.13"
|
29
|
+
}
|
30
|
+
}
|
package/rollup.config.js
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
// rollup.config.js
|
2
|
+
|
3
|
+
import typescript from '@rollup/plugin-typescript';
|
4
|
+
import resolve from '@rollup/plugin-node-resolve';
|
5
|
+
import commonjs from '@rollup/plugin-commonjs';
|
6
|
+
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
|
7
|
+
|
8
|
+
export default {
|
9
|
+
input: 'src/index.ts', // Entry point of your library
|
10
|
+
output: [
|
11
|
+
{
|
12
|
+
file: 'dist/index.js', // CommonJS format
|
13
|
+
format: 'cjs',
|
14
|
+
sourcemap: true,
|
15
|
+
},
|
16
|
+
{
|
17
|
+
file: 'dist/index.es.js', // ES Module format
|
18
|
+
format: 'esm',
|
19
|
+
sourcemap: true,
|
20
|
+
},
|
21
|
+
],
|
22
|
+
plugins: [
|
23
|
+
peerDepsExternal(), // Automatically externalize peer dependencies
|
24
|
+
resolve({
|
25
|
+
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
26
|
+
}), // So Rollup can find node_modules
|
27
|
+
commonjs(), // So Rollup can convert commonjs to ES modules
|
28
|
+
typescript({ tsconfig: './tsconfig.json' }), // So Rollup can handle TypeScript
|
29
|
+
],
|
30
|
+
external: ['react', 'react-native'], // Explicitly externalize dependencies
|
31
|
+
};
|
package/src/Button.tsx
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
// src/components/Button.tsx
|
2
|
+
|
3
|
+
import React, { useMemo, useContext } from 'react';
|
4
|
+
import { Text, ViewStyle, StyleSheet, TouchableOpacity } from 'react-native';
|
5
|
+
import { ThemeContext } from './context/ThemeContext'; // Adjust path to your ThemeContext
|
6
|
+
import { ThemeColors } from './styles/colors';
|
7
|
+
|
8
|
+
// Define the interface for the props that the Button component will accept
|
9
|
+
interface ButtonProps {
|
10
|
+
disabled?: boolean; // Optional: Disables the button if true
|
11
|
+
isFullWidth?: boolean; // Optional: Expands the button to full width if true
|
12
|
+
children?: string; // Optional: Text content of the button
|
13
|
+
onPress?: () => void; // Optional: Function to call when the button is pressed
|
14
|
+
rounded?: boolean; // Optional: Adds border radius if true
|
15
|
+
borderRadius?: number; // Optional: Custom border radius value
|
16
|
+
type?: 'submit' | 'button' | 'cancel'; // Optional: Specifies the button type for styling
|
17
|
+
}
|
18
|
+
|
19
|
+
// Function to determine the styles based on the button type and whether it is disabled
|
20
|
+
function typeStyles(type?: string, disabled?: boolean, themeColors?: ThemeColors): ViewStyle {
|
21
|
+
switch (type) {
|
22
|
+
case 'submit':
|
23
|
+
return {
|
24
|
+
backgroundColor: `rgba(${themeColors?.primary.rgb.r}, ${themeColors?.primary.rgb.g}, ${themeColors?.primary.rgb.b}, ${
|
25
|
+
disabled ? 0.5 : 1
|
26
|
+
})`,
|
27
|
+
borderWidth: 2,
|
28
|
+
borderColor: themeColors?.primary.hex,
|
29
|
+
};
|
30
|
+
case 'button':
|
31
|
+
return {
|
32
|
+
backgroundColor: themeColors?.primary.hex,
|
33
|
+
borderColor: themeColors?.secondary.hex,
|
34
|
+
opacity: disabled ? 0.5 : 1,
|
35
|
+
borderWidth: 2,
|
36
|
+
};
|
37
|
+
case 'cancel':
|
38
|
+
return {
|
39
|
+
backgroundColor: themeColors?.background.hex,
|
40
|
+
borderWidth: 0,
|
41
|
+
};
|
42
|
+
default:
|
43
|
+
return {};
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
// Button component definition
|
48
|
+
const Button: React.FC<ButtonProps> = ({
|
49
|
+
children,
|
50
|
+
disabled = false,
|
51
|
+
type = 'button',
|
52
|
+
rounded = true,
|
53
|
+
borderRadius = 30,
|
54
|
+
isFullWidth = false,
|
55
|
+
onPress,
|
56
|
+
}) => {
|
57
|
+
// Access the current theme from ThemeContext
|
58
|
+
const { theme } = useContext(ThemeContext);
|
59
|
+
const { colors } = theme; // Extract colors from the theme
|
60
|
+
|
61
|
+
// Use useMemo to memoize the computed styles
|
62
|
+
const computedStyles = useMemo(() => {
|
63
|
+
return StyleSheet.flatten([
|
64
|
+
styles.button,
|
65
|
+
typeStyles(type, disabled, colors), // Use colors directly
|
66
|
+
rounded && { borderRadius },
|
67
|
+
isFullWidth && { width: '100%' },
|
68
|
+
disabled && styles.disabled,
|
69
|
+
]);
|
70
|
+
}, [type, disabled, rounded, borderRadius, isFullWidth, colors]);
|
71
|
+
|
72
|
+
// Determine text color based on button type and theme
|
73
|
+
const textColor = useMemo(() => {
|
74
|
+
return { color: colors.text.hex }; // Access text color from the theme
|
75
|
+
}, [type, colors]);
|
76
|
+
|
77
|
+
return (
|
78
|
+
<TouchableOpacity
|
79
|
+
style={computedStyles as ViewStyle}
|
80
|
+
disabled={disabled}
|
81
|
+
onPress={onPress}
|
82
|
+
activeOpacity={0.7}
|
83
|
+
>
|
84
|
+
<Text style={textColor}>
|
85
|
+
{Array.isArray(children) ? children.join('').toUpperCase() : children?.toUpperCase()}
|
86
|
+
</Text>
|
87
|
+
</TouchableOpacity>
|
88
|
+
);
|
89
|
+
};
|
90
|
+
|
91
|
+
// Define basic styles for the button
|
92
|
+
const styles = StyleSheet.create({
|
93
|
+
button: {
|
94
|
+
justifyContent: 'center',
|
95
|
+
alignItems: 'center',
|
96
|
+
paddingVertical: 10,
|
97
|
+
paddingHorizontal: 20,
|
98
|
+
},
|
99
|
+
disabled: {
|
100
|
+
opacity: 0.6,
|
101
|
+
},
|
102
|
+
});
|
103
|
+
|
104
|
+
export default Button;
|
@@ -0,0 +1,39 @@
|
|
1
|
+
// src/components/ThemeToggle.tsx
|
2
|
+
|
3
|
+
import React, { useContext } from 'react';
|
4
|
+
import { Switch, View, Text, StyleSheet } from 'react-native';
|
5
|
+
import { ThemeContext } from '../context/ThemeContext';
|
6
|
+
import { darkTheme, lightTheme } from '../styles/colors';
|
7
|
+
|
8
|
+
const ThemeToggle: React.FC = () => {
|
9
|
+
const { theme, toggleTheme } = useContext(ThemeContext);
|
10
|
+
|
11
|
+
// Determine if the current theme is dark by checking the colors property
|
12
|
+
const isDarkMode = theme.colors === darkTheme;
|
13
|
+
|
14
|
+
return (
|
15
|
+
<View style={styles.container}>
|
16
|
+
<Text style={[styles.text, { color: theme.colors.text.hex }]}>Dark Mode</Text>
|
17
|
+
<Switch
|
18
|
+
value={isDarkMode}
|
19
|
+
onValueChange={toggleTheme}
|
20
|
+
trackColor={{ false: lightTheme.secondary.hex, true: darkTheme.primary.hex }}
|
21
|
+
thumbColor={isDarkMode ? darkTheme.secondary.hex : lightTheme.primary.hex}
|
22
|
+
/>
|
23
|
+
</View>
|
24
|
+
);
|
25
|
+
};
|
26
|
+
|
27
|
+
const styles = StyleSheet.create({
|
28
|
+
container: {
|
29
|
+
marginTop: 20,
|
30
|
+
flexDirection: 'row',
|
31
|
+
alignItems: 'center',
|
32
|
+
},
|
33
|
+
text: {
|
34
|
+
marginRight: 10,
|
35
|
+
fontSize: 16,
|
36
|
+
},
|
37
|
+
});
|
38
|
+
|
39
|
+
export default ThemeToggle;
|
@@ -0,0 +1,70 @@
|
|
1
|
+
// src/context/ThemeContext.tsx
|
2
|
+
|
3
|
+
import React, { createContext, useState, useEffect, ReactNode } from 'react';
|
4
|
+
import { Appearance, ColorSchemeName } from 'react-native';
|
5
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
6
|
+
import { lightTheme, darkTheme, ThemeColors } from '../styles/colors';
|
7
|
+
import { Theme } from '../styles/theme';
|
8
|
+
|
9
|
+
interface ThemeContextProps {
|
10
|
+
theme: Theme;
|
11
|
+
toggleTheme: () => void;
|
12
|
+
}
|
13
|
+
|
14
|
+
export const ThemeContext = createContext<ThemeContextProps>({
|
15
|
+
theme: { colors: lightTheme },
|
16
|
+
toggleTheme: () => {},
|
17
|
+
});
|
18
|
+
|
19
|
+
interface ThemeProviderProps {
|
20
|
+
children: ReactNode;
|
21
|
+
}
|
22
|
+
|
23
|
+
export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
|
24
|
+
const [colorScheme, setColorScheme] = useState<ColorSchemeName | null>(null);
|
25
|
+
|
26
|
+
useEffect(() => {
|
27
|
+
const loadTheme = async () => {
|
28
|
+
try {
|
29
|
+
const storedTheme = await AsyncStorage.getItem('theme');
|
30
|
+
if (storedTheme === 'dark' || storedTheme === 'light') {
|
31
|
+
setColorScheme(storedTheme);
|
32
|
+
} else {
|
33
|
+
const systemTheme = Appearance.getColorScheme();
|
34
|
+
setColorScheme(systemTheme);
|
35
|
+
}
|
36
|
+
} catch (error) {
|
37
|
+
console.error('Failed to load theme.', error);
|
38
|
+
setColorScheme(Appearance.getColorScheme());
|
39
|
+
}
|
40
|
+
};
|
41
|
+
|
42
|
+
loadTheme();
|
43
|
+
|
44
|
+
const subscription = Appearance.addChangeListener(({ colorScheme }) => {
|
45
|
+
setColorScheme(colorScheme);
|
46
|
+
});
|
47
|
+
|
48
|
+
return () => subscription.remove();
|
49
|
+
}, []);
|
50
|
+
|
51
|
+
const toggleTheme = async () => {
|
52
|
+
try {
|
53
|
+
const newTheme = colorScheme === 'dark' ? 'light' : 'dark';
|
54
|
+
setColorScheme(newTheme);
|
55
|
+
await AsyncStorage.setItem('theme', newTheme);
|
56
|
+
} catch (error) {
|
57
|
+
console.error('Failed to toggle theme.', error);
|
58
|
+
}
|
59
|
+
};
|
60
|
+
|
61
|
+
const theme: Theme = {
|
62
|
+
colors: colorScheme === 'dark' ? darkTheme : lightTheme,
|
63
|
+
};
|
64
|
+
|
65
|
+
return (
|
66
|
+
<ThemeContext.Provider value={{ theme, toggleTheme }}>
|
67
|
+
{children}
|
68
|
+
</ThemeContext.Provider>
|
69
|
+
);
|
70
|
+
};
|
package/src/index.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export { default as ButtonComponent } from "./Button";
|
@@ -0,0 +1,60 @@
|
|
1
|
+
// src/styles/colors.ts
|
2
|
+
|
3
|
+
export interface RGB {
|
4
|
+
r: number;
|
5
|
+
g: number;
|
6
|
+
b: number;
|
7
|
+
}
|
8
|
+
|
9
|
+
export interface Color {
|
10
|
+
hex: string;
|
11
|
+
rgb: RGB;
|
12
|
+
}
|
13
|
+
|
14
|
+
export interface ThemeColors {
|
15
|
+
primary: Color;
|
16
|
+
secondary: Color;
|
17
|
+
background: Color;
|
18
|
+
text: Color;
|
19
|
+
// Add more categories as needed
|
20
|
+
}
|
21
|
+
|
22
|
+
export const lightTheme: ThemeColors = {
|
23
|
+
primary: {
|
24
|
+
hex: '#1A73E8',
|
25
|
+
rgb: { r: 26, g: 115, b: 232 },
|
26
|
+
},
|
27
|
+
secondary: {
|
28
|
+
hex: '#F0F6FF',
|
29
|
+
rgb: { r: 240, g: 246, b: 255 },
|
30
|
+
},
|
31
|
+
background: {
|
32
|
+
hex: '#FFFFFF',
|
33
|
+
rgb: { r: 255, g: 255, b: 255 },
|
34
|
+
},
|
35
|
+
text: {
|
36
|
+
hex: '#000000',
|
37
|
+
rgb: { r: 0, g: 0, b: 0 },
|
38
|
+
},
|
39
|
+
// Add more categories as needed
|
40
|
+
};
|
41
|
+
|
42
|
+
export const darkTheme: ThemeColors = {
|
43
|
+
primary: {
|
44
|
+
hex: '#BB86FC',
|
45
|
+
rgb: { r: 187, g: 134, b: 252 },
|
46
|
+
},
|
47
|
+
secondary: {
|
48
|
+
hex: '#03DAC6',
|
49
|
+
rgb: { r: 3, g: 218, b: 198 },
|
50
|
+
},
|
51
|
+
background: {
|
52
|
+
hex: '#121212',
|
53
|
+
rgb: { r: 18, g: 18, b: 18 },
|
54
|
+
},
|
55
|
+
text: {
|
56
|
+
hex: '#FFFFFF',
|
57
|
+
rgb: { r: 255, g: 255, b: 255 },
|
58
|
+
},
|
59
|
+
// Add more categories as needed
|
60
|
+
};
|
@@ -0,0 +1,50 @@
|
|
1
|
+
// src/styles/theme.ts
|
2
|
+
|
3
|
+
import { ThemeColors } from "./colors";
|
4
|
+
|
5
|
+
// Define the light theme with full ThemeColors structure
|
6
|
+
export const lightTheme: ThemeColors = {
|
7
|
+
primary: {
|
8
|
+
hex: "#1A73E8",
|
9
|
+
rgb: { r: 26, g: 115, b: 232 },
|
10
|
+
},
|
11
|
+
secondary: {
|
12
|
+
hex: "#F0F6FF",
|
13
|
+
rgb: { r: 240, g: 246, b: 255 },
|
14
|
+
},
|
15
|
+
background: {
|
16
|
+
hex: "#FFFFFF",
|
17
|
+
rgb: { r: 255, g: 255, b: 255 },
|
18
|
+
},
|
19
|
+
text: {
|
20
|
+
hex: "#000000",
|
21
|
+
rgb: { r: 0, g: 0, b: 0 },
|
22
|
+
},
|
23
|
+
};
|
24
|
+
|
25
|
+
// Define the dark theme with full ThemeColors structure
|
26
|
+
export const darkTheme: ThemeColors = {
|
27
|
+
primary: {
|
28
|
+
hex: "#BB86FC",
|
29
|
+
rgb: { r: 187, g: 134, b: 252 },
|
30
|
+
},
|
31
|
+
secondary: {
|
32
|
+
hex: "#03DAC6",
|
33
|
+
rgb: { r: 3, g: 218, b: 198 },
|
34
|
+
},
|
35
|
+
background: {
|
36
|
+
hex: "#121212",
|
37
|
+
rgb: { r: 18, g: 18, b: 18 },
|
38
|
+
},
|
39
|
+
text: {
|
40
|
+
hex: "#FFFFFF",
|
41
|
+
rgb: { r: 255, g: 255, b: 255 },
|
42
|
+
},
|
43
|
+
};
|
44
|
+
|
45
|
+
// src/styles/theme.ts
|
46
|
+
|
47
|
+
export interface Theme {
|
48
|
+
colors: ThemeColors;
|
49
|
+
}
|
50
|
+
|
package/tsconfig.json
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"outDir": "dist",
|
4
|
+
"module": "ES6",
|
5
|
+
"target": "ES5",
|
6
|
+
"lib": [
|
7
|
+
"ES2015",
|
8
|
+
"DOM"
|
9
|
+
],
|
10
|
+
"jsx": "react-native",
|
11
|
+
"declaration": true,
|
12
|
+
"esModuleInterop": true,
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
14
|
+
"strict": true,
|
15
|
+
"skipLibCheck": true,
|
16
|
+
"moduleResolution": "node",
|
17
|
+
"types": [
|
18
|
+
"react",
|
19
|
+
"react-native"
|
20
|
+
]
|
21
|
+
},
|
22
|
+
"include": [
|
23
|
+
"src" ],
|
24
|
+
"exclude": [
|
25
|
+
"node_modules",
|
26
|
+
"dist",
|
27
|
+
"App.tsx"
|
28
|
+
],
|
29
|
+
"extends": "expo/tsconfig.base"
|
30
|
+
}
|