aport-tools 1.0.0
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/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
|
+
}
|