@tcbs/react-native-mazic-ui 0.1.1
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/README.md +216 -0
- package/lib/components/Button.d.ts +8 -0
- package/lib/components/Button.js +18 -0
- package/lib/components/TcbsButton.d.ts +20 -0
- package/lib/components/TcbsButton.js +117 -0
- package/lib/components/TcbsButton.types.d.ts +52 -0
- package/lib/components/TcbsButton.types.js +15 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/package.json +52 -0
- package/src/components/Button.tsx +26 -0
- package/src/components/ExampleScreen.tsx +19 -0
- package/src/components/TcbsButton.tsx +235 -0
- package/src/components/TcbsButton.types.ts +70 -0
- package/src/index.ts +14 -0
- package/src/store/themeStore.ts +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# @tcbs/react-native-mazic-ui
|
|
2
|
+
|
|
3
|
+
A customizable React Native UI component library.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @tcbs/react-native-mazic-ui
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Theme Setup Example
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { useTcbsColorStore } from '@tcbs/react-native-mazic-ui';
|
|
16
|
+
|
|
17
|
+
const { setTcbsColor } = useTcbsColorStore();
|
|
18
|
+
|
|
19
|
+
React.useEffect(() => {
|
|
20
|
+
setTcbsColor({
|
|
21
|
+
light: {
|
|
22
|
+
btnColor: '#007AFF',
|
|
23
|
+
btnBorderColor: '#007AFF',
|
|
24
|
+
btnIconColor: '#16a62bff',
|
|
25
|
+
themeColor: '#007AFF',
|
|
26
|
+
btnTextColor: '#FFFFFF',
|
|
27
|
+
},
|
|
28
|
+
dark: {
|
|
29
|
+
btnColor: '#222222',
|
|
30
|
+
btnBorderColor: '#222222',
|
|
31
|
+
btnIconColor: '#FFFFFF',
|
|
32
|
+
themeColor: '#222222',
|
|
33
|
+
btnTextColor: '#FFFFFF',
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}, []);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { Button } from '@tcbs/react-native-mazic-ui';
|
|
43
|
+
|
|
44
|
+
<Button title="Hello" onPress={() => {}} />
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## TcbsButton Component Usage
|
|
48
|
+
|
|
49
|
+
### Basic Usage
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { TcbsButton } from '@tcbs/react-native-mazic-ui';
|
|
53
|
+
|
|
54
|
+
<TcbsButton
|
|
55
|
+
title="Submit"
|
|
56
|
+
onPress={() => console.log('Pressed')}
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Size Variations
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
<TcbsButton title="Large" size="large" onPress={...} />
|
|
64
|
+
<TcbsButton title="Medium" size="medium" onPress={...} />
|
|
65
|
+
<TcbsButton title="Small" size="small" onPress={...} />
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
### Variant Styles
|
|
70
|
+
|
|
71
|
+
You can choose from three variants:
|
|
72
|
+
|
|
73
|
+
- `primary`: Filled button with main color
|
|
74
|
+
- `secondary`: Outlined button with border
|
|
75
|
+
- `no_border`: Button with no border and matches screen background
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
<View>
|
|
79
|
+
<TcbsButton
|
|
80
|
+
title="TCBS Button"
|
|
81
|
+
variant="primary"
|
|
82
|
+
onPress={() => console.log('TCBS Button Pressed')}
|
|
83
|
+
iconName="heart"
|
|
84
|
+
iconPosition="left"
|
|
85
|
+
/>
|
|
86
|
+
</View>
|
|
87
|
+
<View>
|
|
88
|
+
<TcbsButton
|
|
89
|
+
title="TCBS Button"
|
|
90
|
+
variant="secondary"
|
|
91
|
+
onPress={() => console.log('TCBS Button Pressed')}
|
|
92
|
+
iconName="star"
|
|
93
|
+
iconPosition="right"
|
|
94
|
+
/>
|
|
95
|
+
</View>
|
|
96
|
+
<View>
|
|
97
|
+
<TcbsButton
|
|
98
|
+
title="TCBS Button"
|
|
99
|
+
variant="no_border"
|
|
100
|
+
onPress={() => console.log('TCBS Button Pressed')}
|
|
101
|
+
iconName="home"
|
|
102
|
+
iconPosition="left"
|
|
103
|
+
/>
|
|
104
|
+
</View>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Size Options
|
|
108
|
+
|
|
109
|
+
You can choose from three sizes:
|
|
110
|
+
|
|
111
|
+
- `large`: Large button (default)
|
|
112
|
+
- `medium`: Medium button
|
|
113
|
+
- `small`: Small button
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
<TcbsButton title="Large" size="large" onPress={...} />
|
|
117
|
+
<TcbsButton title="Medium" size="medium" onPress={...} />
|
|
118
|
+
<TcbsButton title="Small" size="small" onPress={...} />
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
### Supported Icon Groups
|
|
123
|
+
|
|
124
|
+
You can use the following icon groups for the `iconGroup` prop:
|
|
125
|
+
|
|
126
|
+
- `AntDesign`
|
|
127
|
+
- `Feather`
|
|
128
|
+
- `FontAwesome`
|
|
129
|
+
- `Foundation`
|
|
130
|
+
- `Ionicons`
|
|
131
|
+
- `MaterialDesignIcons`
|
|
132
|
+
- `Octicons`
|
|
133
|
+
- `Lucide` (if available in your project)
|
|
134
|
+
|
|
135
|
+
Example usage:
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
<TcbsButton
|
|
139
|
+
title="AntDesign"
|
|
140
|
+
iconName="check"
|
|
141
|
+
iconGroup="AntDesign"
|
|
142
|
+
iconPosition="left"
|
|
143
|
+
onPress={...}
|
|
144
|
+
/>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
<TcbsButton
|
|
150
|
+
title="AntDesign"
|
|
151
|
+
iconName="check"
|
|
152
|
+
iconGroup="AntDesign"
|
|
153
|
+
iconPosition="left"
|
|
154
|
+
onPress={...}
|
|
155
|
+
/>
|
|
156
|
+
|
|
157
|
+
<TcbsButton
|
|
158
|
+
title="Feather"
|
|
159
|
+
iconName="arrow-right"
|
|
160
|
+
iconGroup="Feather"
|
|
161
|
+
iconPosition="right"
|
|
162
|
+
onPress={...}
|
|
163
|
+
/>
|
|
164
|
+
|
|
165
|
+
<TcbsButton
|
|
166
|
+
title="Top Icon"
|
|
167
|
+
iconName="star"
|
|
168
|
+
iconGroup="FontAwesome"
|
|
169
|
+
iconPosition="top"
|
|
170
|
+
onPress={...}
|
|
171
|
+
/>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Custom Colors and Styles
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
<TcbsButton
|
|
178
|
+
title="Custom"
|
|
179
|
+
style={{ backgroundColor: '#222' }}
|
|
180
|
+
textStyle={{ color: '#FFD700' }}
|
|
181
|
+
onPress={...}
|
|
182
|
+
/>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Accessibility
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
<TcbsButton
|
|
189
|
+
title="Accessible"
|
|
190
|
+
accessibilityLabel="Submit button"
|
|
191
|
+
accessibilityHint="Submits the form"
|
|
192
|
+
accessibilityRole="button"
|
|
193
|
+
onPress={...}
|
|
194
|
+
/>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Disabled State
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
<TcbsButton
|
|
201
|
+
title="Disabled"
|
|
202
|
+
disabled
|
|
203
|
+
onPress={...}
|
|
204
|
+
/>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
See the exported `TcbsButtonProps` type for all available options.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## License
|
|
213
|
+
|
|
214
|
+
MIT
|
|
215
|
+
|
|
216
|
+
Copyright (c) TechCraft By Subrata
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
|
|
3
|
+
export const Button = ({ title, onPress }) => (React.createElement(TouchableOpacity, { style: styles.button, onPress: onPress },
|
|
4
|
+
React.createElement(Text, { style: styles.text },
|
|
5
|
+
"TCBS 1: ",
|
|
6
|
+
title)));
|
|
7
|
+
const styles = StyleSheet.create({
|
|
8
|
+
button: {
|
|
9
|
+
backgroundColor: '#007AFF',
|
|
10
|
+
padding: 12,
|
|
11
|
+
borderRadius: 8,
|
|
12
|
+
alignItems: 'center'
|
|
13
|
+
},
|
|
14
|
+
text: {
|
|
15
|
+
color: '#fff',
|
|
16
|
+
fontWeight: 'bold'
|
|
17
|
+
}
|
|
18
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { BUTTON_SIZE, BUTTON_VARIANT, BORDER_RADIUS, ButtonSize, ButtonVariant, IconGroupType, IconPosition, TcbsButtonProps } from './TcbsButton.types';
|
|
3
|
+
/**
|
|
4
|
+
* TcbsButton - A themeable, accessible button component with icon support
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* <TcbsButton
|
|
9
|
+
* title="Submit"
|
|
10
|
+
* onPress={() => console.log('Pressed')}
|
|
11
|
+
* size="large"
|
|
12
|
+
* variant="primary"
|
|
13
|
+
* iconName="check"
|
|
14
|
+
* iconPosition="left"
|
|
15
|
+
* />
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare const TcbsButton: React.FC<TcbsButtonProps>;
|
|
19
|
+
export { BUTTON_SIZE, BUTTON_VARIANT, BORDER_RADIUS };
|
|
20
|
+
export type { ButtonSize, ButtonVariant, IconGroupType, IconPosition };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { TouchableOpacity, Text, View } from 'react-native';
|
|
3
|
+
import AntDesign from 'react-native-vector-icons/AntDesign';
|
|
4
|
+
import Feather from 'react-native-vector-icons/Feather';
|
|
5
|
+
import FontAwesome from 'react-native-vector-icons/FontAwesome';
|
|
6
|
+
import Foundation from 'react-native-vector-icons/Foundation';
|
|
7
|
+
import Ionicons from 'react-native-vector-icons/Ionicons';
|
|
8
|
+
import MaterialDesignIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
9
|
+
import Octicons from 'react-native-vector-icons/Octicons';
|
|
10
|
+
// import Lucide from 'react-native-vector-icons/Lucide';
|
|
11
|
+
import { BUTTON_SIZE, BUTTON_VARIANT, BORDER_RADIUS, } from './TcbsButton.types';
|
|
12
|
+
const HEIGHTS = {
|
|
13
|
+
[BUTTON_SIZE.LARGE]: 56,
|
|
14
|
+
[BUTTON_SIZE.MEDIUM]: 40,
|
|
15
|
+
[BUTTON_SIZE.SMALL]: 32,
|
|
16
|
+
};
|
|
17
|
+
const FONT_SIZES = {
|
|
18
|
+
[BUTTON_SIZE.LARGE]: 20,
|
|
19
|
+
[BUTTON_SIZE.MEDIUM]: 16,
|
|
20
|
+
[BUTTON_SIZE.SMALL]: 14,
|
|
21
|
+
};
|
|
22
|
+
const BORDER_RADIUSES = {
|
|
23
|
+
[BUTTON_SIZE.LARGE]: BORDER_RADIUS.MEDIUM,
|
|
24
|
+
[BUTTON_SIZE.MEDIUM]: BORDER_RADIUS.SMALL,
|
|
25
|
+
[BUTTON_SIZE.SMALL]: BORDER_RADIUS.SMALL,
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* TcbsButton - A themeable, accessible button component with icon support
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* <TcbsButton
|
|
33
|
+
* title="Submit"
|
|
34
|
+
* onPress={() => console.log('Pressed')}
|
|
35
|
+
* size="large"
|
|
36
|
+
* variant="primary"
|
|
37
|
+
* iconName="check"
|
|
38
|
+
* iconPosition="left"
|
|
39
|
+
* />
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export const TcbsButton = ({ title, onPress, size = BUTTON_SIZE.LARGE, variant = BUTTON_VARIANT.PRIMARY, borderRadius, disabled = false, style, textStyle, iconName, iconGroup = 'MaterialDesignIcons', iconColor, iconSize, iconPosition = 'top', accessibilityLabel, accessibilityHint, accessibilityRole = 'button', accessibilityState, themeColor = {
|
|
43
|
+
btnColor: '#007AFF',
|
|
44
|
+
themeColor: '#007AFF',
|
|
45
|
+
btnTextColor: '#FFFFFF',
|
|
46
|
+
btnTxtColor: '#FFFFFF',
|
|
47
|
+
}, }) => {
|
|
48
|
+
const colors = themeColor;
|
|
49
|
+
const buttonStyle = useMemo(() => {
|
|
50
|
+
const baseStyle = {
|
|
51
|
+
height: HEIGHTS[size],
|
|
52
|
+
borderRadius: borderRadius !== null && borderRadius !== void 0 ? borderRadius : BORDER_RADIUSES[size],
|
|
53
|
+
alignItems: 'center',
|
|
54
|
+
justifyContent: 'center',
|
|
55
|
+
opacity: disabled ? 0.6 : 1,
|
|
56
|
+
paddingHorizontal: 24,
|
|
57
|
+
};
|
|
58
|
+
if (variant === BUTTON_VARIANT.SECONDARY) {
|
|
59
|
+
return Object.assign(Object.assign(Object.assign({}, baseStyle), { backgroundColor: '#fff', borderWidth: 2, borderColor: colors.btnColor || colors.themeColor }), style);
|
|
60
|
+
}
|
|
61
|
+
if (variant === BUTTON_VARIANT.NO_BORDER) {
|
|
62
|
+
return Object.assign(Object.assign(Object.assign({}, baseStyle), { backgroundColor: '#fff' }), style);
|
|
63
|
+
}
|
|
64
|
+
// Primary variant (default)
|
|
65
|
+
return Object.assign(Object.assign(Object.assign({}, baseStyle), { backgroundColor: colors.btnColor || colors.themeColor, shadowColor: colors.btnColor, shadowOpacity: 0.15, shadowRadius: 6, shadowOffset: { width: 0, height: 2 }, elevation: 2 }), style);
|
|
66
|
+
}, [size, variant, colors, style, disabled, borderRadius]);
|
|
67
|
+
const themedTextStyle = useMemo(() => {
|
|
68
|
+
const baseTextColor = variant === BUTTON_VARIANT.PRIMARY
|
|
69
|
+
? colors.btnTextColor || colors.btnTxtColor || '#fff'
|
|
70
|
+
: colors.btnColor || colors.themeColor;
|
|
71
|
+
return Object.assign({ color: baseTextColor, fontSize: FONT_SIZES[size], fontWeight: '700' }, textStyle);
|
|
72
|
+
}, [size, variant, colors, textStyle]);
|
|
73
|
+
const renderIcon = (IconComponent) => (React.createElement(IconComponent, { name: iconName, size: iconSize || FONT_SIZES[size] * 2, color: iconColor || themedTextStyle.color, style: iconPosition === 'top'
|
|
74
|
+
? { marginBottom: 2 }
|
|
75
|
+
: iconPosition === 'left'
|
|
76
|
+
? { marginRight: 8 }
|
|
77
|
+
: { marginLeft: 8 } }));
|
|
78
|
+
const renderText = (customStyle) => {
|
|
79
|
+
if (!title)
|
|
80
|
+
return null;
|
|
81
|
+
const finalStyle = customStyle
|
|
82
|
+
? Object.assign({ color: themedTextStyle.color, fontSize: FONT_SIZES[size] - 4, fontWeight: '500' }, customStyle) : themedTextStyle;
|
|
83
|
+
return React.createElement(Text, { style: finalStyle }, title);
|
|
84
|
+
};
|
|
85
|
+
const renderContent = () => {
|
|
86
|
+
// If no icon, just render text
|
|
87
|
+
if (!iconName) {
|
|
88
|
+
return renderText();
|
|
89
|
+
}
|
|
90
|
+
// Map iconGroup string to actual component
|
|
91
|
+
const IconComponent = iconGroup === 'AntDesign' ? AntDesign :
|
|
92
|
+
iconGroup === 'Feather' ? Feather :
|
|
93
|
+
iconGroup === 'FontAwesome' ? FontAwesome :
|
|
94
|
+
iconGroup === 'Foundation' ? Foundation :
|
|
95
|
+
iconGroup === 'Ionicons' ? Ionicons :
|
|
96
|
+
iconGroup === 'MaterialDesignIcons' ? MaterialDesignIcons :
|
|
97
|
+
iconGroup === 'Octicons' ? Octicons :
|
|
98
|
+
// iconGroup === 'Lucide' ? Lucide : // Uncomment if Lucide is available
|
|
99
|
+
MaterialDesignIcons;
|
|
100
|
+
if (iconPosition === 'top') {
|
|
101
|
+
return (React.createElement(View, { style: { alignItems: 'center', justifyContent: 'center' } },
|
|
102
|
+
renderIcon(IconComponent),
|
|
103
|
+
renderText({ marginTop: 2 })));
|
|
104
|
+
}
|
|
105
|
+
const flexDirection = iconPosition === 'left' ? 'row' : 'row-reverse';
|
|
106
|
+
return (React.createElement(View, { style: {
|
|
107
|
+
flexDirection,
|
|
108
|
+
alignItems: 'center',
|
|
109
|
+
justifyContent: 'center',
|
|
110
|
+
} },
|
|
111
|
+
renderIcon(IconComponent),
|
|
112
|
+
renderText()));
|
|
113
|
+
};
|
|
114
|
+
return (React.createElement(TouchableOpacity, { onPress: onPress, disabled: disabled, style: buttonStyle, accessibilityLabel: accessibilityLabel || title, accessibilityHint: accessibilityHint, accessibilityRole: accessibilityRole, accessibilityState: accessibilityState || { disabled } }, renderContent()));
|
|
115
|
+
};
|
|
116
|
+
// Export constants for use in consuming applications
|
|
117
|
+
export { BUTTON_SIZE, BUTTON_VARIANT, BORDER_RADIUS };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { StyleProp, ViewStyle, TextStyle, AccessibilityRole, AccessibilityState, GestureResponderEvent } from 'react-native';
|
|
3
|
+
export type IconComponentType = React.ComponentType<{
|
|
4
|
+
name: string;
|
|
5
|
+
size: number;
|
|
6
|
+
color: string;
|
|
7
|
+
style?: StyleProp<ViewStyle>;
|
|
8
|
+
}>;
|
|
9
|
+
export declare const BUTTON_SIZE: {
|
|
10
|
+
readonly LARGE: "large";
|
|
11
|
+
readonly MEDIUM: "medium";
|
|
12
|
+
readonly SMALL: "small";
|
|
13
|
+
};
|
|
14
|
+
export declare const BUTTON_VARIANT: {
|
|
15
|
+
readonly PRIMARY: "primary";
|
|
16
|
+
readonly SECONDARY: "secondary";
|
|
17
|
+
readonly NO_BORDER: "no_border";
|
|
18
|
+
};
|
|
19
|
+
export declare const BORDER_RADIUS: {
|
|
20
|
+
readonly SMALL: 8;
|
|
21
|
+
readonly MEDIUM: 12;
|
|
22
|
+
readonly LARGE: 16;
|
|
23
|
+
};
|
|
24
|
+
export type ButtonSize = (typeof BUTTON_SIZE)[keyof typeof BUTTON_SIZE];
|
|
25
|
+
export type ButtonVariant = (typeof BUTTON_VARIANT)[keyof typeof BUTTON_VARIANT];
|
|
26
|
+
export type IconGroupType = 'AntDesign' | 'Feather' | 'FontAwesome' | 'Foundation' | 'Ionicons' | 'MaterialDesignIcons' | 'Octicons' | 'Lucide';
|
|
27
|
+
export type IconPosition = 'top' | 'left' | 'right';
|
|
28
|
+
export interface TcbsButtonProps {
|
|
29
|
+
title?: string;
|
|
30
|
+
onPress: (event: GestureResponderEvent) => void;
|
|
31
|
+
size?: ButtonSize;
|
|
32
|
+
variant?: ButtonVariant;
|
|
33
|
+
borderRadius?: number;
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
style?: StyleProp<ViewStyle>;
|
|
36
|
+
textStyle?: StyleProp<TextStyle>;
|
|
37
|
+
iconName?: string;
|
|
38
|
+
iconGroup?: IconGroupType;
|
|
39
|
+
iconColor?: string;
|
|
40
|
+
iconSize?: number;
|
|
41
|
+
iconPosition?: IconPosition;
|
|
42
|
+
accessibilityLabel?: string;
|
|
43
|
+
accessibilityHint?: string;
|
|
44
|
+
accessibilityRole?: AccessibilityRole;
|
|
45
|
+
accessibilityState?: AccessibilityState;
|
|
46
|
+
themeColor?: {
|
|
47
|
+
btnColor?: string;
|
|
48
|
+
themeColor?: string;
|
|
49
|
+
btnTextColor?: string;
|
|
50
|
+
btnTxtColor?: string;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const BUTTON_SIZE = {
|
|
2
|
+
LARGE: 'large',
|
|
3
|
+
MEDIUM: 'medium',
|
|
4
|
+
SMALL: 'small',
|
|
5
|
+
};
|
|
6
|
+
export const BUTTON_VARIANT = {
|
|
7
|
+
PRIMARY: 'primary',
|
|
8
|
+
SECONDARY: 'secondary',
|
|
9
|
+
NO_BORDER: 'no_border',
|
|
10
|
+
};
|
|
11
|
+
export const BORDER_RADIUS = {
|
|
12
|
+
SMALL: 8,
|
|
13
|
+
MEDIUM: 12,
|
|
14
|
+
LARGE: 16,
|
|
15
|
+
};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { Button } from './components/Button';
|
|
2
|
+
export { TcbsButton } from './components/TcbsButton';
|
|
3
|
+
export { BUTTON_SIZE, BUTTON_VARIANT, BORDER_RADIUS, ButtonSize, ButtonVariant, IconGroupType, IconPosition, TcbsButtonProps, IconComponentType, } from './components/TcbsButton.types';
|
package/lib/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tcbs/react-native-mazic-ui",
|
|
3
|
+
"description": "A customizable React Native UI component library with theme support, modern buttons, and icon integration By TechCraft By Subrata.",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"react-native",
|
|
6
|
+
"react-native-mazic-components",
|
|
7
|
+
"ui",
|
|
8
|
+
"component-library",
|
|
9
|
+
"button",
|
|
10
|
+
"theme",
|
|
11
|
+
"mazic",
|
|
12
|
+
"tcbs",
|
|
13
|
+
"react-native-vector-icons"
|
|
14
|
+
],
|
|
15
|
+
"author": "TechCraft By Subrata <subraatakumar@gmail.com>",
|
|
16
|
+
"version": "0.1.1",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"main": "lib/commonjs/index.js",
|
|
21
|
+
"module": "lib/module/index.js",
|
|
22
|
+
"types": "lib/types/index.d.ts",
|
|
23
|
+
"react-native": "src/index.ts",
|
|
24
|
+
"files": [
|
|
25
|
+
"lib/",
|
|
26
|
+
"src/"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"lint": "eslint src --ext .ts,.tsx",
|
|
31
|
+
"test": "jest"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"react": ">=17.0.0",
|
|
35
|
+
"react-native": ">=0.68.0",
|
|
36
|
+
"react-native-vector-icons": ">=10.2.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/react": "^17.0.0",
|
|
40
|
+
"@types/react-native": "^0.68.0",
|
|
41
|
+
"eslint": "^8.0.0",
|
|
42
|
+
"jest": "^29.0.0",
|
|
43
|
+
"react-native-vector-icons": "^10.3.0",
|
|
44
|
+
"typescript": "^4.9.5"
|
|
45
|
+
},
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"homepage": "https://tcbscli.subraatakumar.com/ui-home",
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "https://github.com/TechCraft-By-Subrata/react-native-mazic-ui.git"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TouchableOpacity, Text, StyleSheet, GestureResponderEvent } from 'react-native';
|
|
3
|
+
|
|
4
|
+
type ButtonProps = {
|
|
5
|
+
title: string;
|
|
6
|
+
onPress: (event: GestureResponderEvent) => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const Button: React.FC<ButtonProps> = ({ title, onPress }) => (
|
|
10
|
+
<TouchableOpacity style={styles.button} onPress={onPress}>
|
|
11
|
+
<Text style={styles.text}>TCBS 1: {title}</Text>
|
|
12
|
+
</TouchableOpacity>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const styles = StyleSheet.create({
|
|
16
|
+
button: {
|
|
17
|
+
backgroundColor: '#007AFF',
|
|
18
|
+
padding: 12,
|
|
19
|
+
borderRadius: 8,
|
|
20
|
+
alignItems: 'center'
|
|
21
|
+
},
|
|
22
|
+
text: {
|
|
23
|
+
color: '#fff',
|
|
24
|
+
fontWeight: 'bold'
|
|
25
|
+
}
|
|
26
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text } from 'react-native';
|
|
3
|
+
import { useTcbsColorStore } from '../store/themeStore';
|
|
4
|
+
|
|
5
|
+
const ExampleScreen: React.FC = () => {
|
|
6
|
+
const { colors, mode } = useTcbsColorStore();
|
|
7
|
+
const screenBgColor = colors[mode].screenBgColor;
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<View style={{ flex: 1, backgroundColor: screenBgColor }}>
|
|
11
|
+
<Text style={{ color: colors[mode].btnTextColor }}>
|
|
12
|
+
This screen uses theme screenBgColor!
|
|
13
|
+
</Text>
|
|
14
|
+
{/* Add your other components here */}
|
|
15
|
+
</View>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default ExampleScreen;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { TouchableOpacity, Text, View , StyleProp, ViewStyle, TextStyle } from 'react-native';
|
|
3
|
+
import AntDesign from 'react-native-vector-icons/AntDesign';
|
|
4
|
+
import Feather from 'react-native-vector-icons/Feather';
|
|
5
|
+
import FontAwesome from 'react-native-vector-icons/FontAwesome';
|
|
6
|
+
import Foundation from 'react-native-vector-icons/Foundation';
|
|
7
|
+
import Ionicons from 'react-native-vector-icons/Ionicons';
|
|
8
|
+
import MaterialDesignIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
9
|
+
import Octicons from 'react-native-vector-icons/Octicons';
|
|
10
|
+
// import Lucide from 'react-native-vector-icons/Lucide';
|
|
11
|
+
import {
|
|
12
|
+
BUTTON_SIZE,
|
|
13
|
+
BUTTON_VARIANT,
|
|
14
|
+
BORDER_RADIUS,
|
|
15
|
+
ButtonSize,
|
|
16
|
+
ButtonVariant,
|
|
17
|
+
IconGroupType,
|
|
18
|
+
IconPosition,
|
|
19
|
+
TcbsButtonProps,
|
|
20
|
+
IconComponentType,
|
|
21
|
+
} from './TcbsButton.types';
|
|
22
|
+
import { useTcbsColorStore } from '../store/themeStore';
|
|
23
|
+
|
|
24
|
+
const HEIGHTS: Record<ButtonSize, number> = {
|
|
25
|
+
[BUTTON_SIZE.LARGE]: 56,
|
|
26
|
+
[BUTTON_SIZE.MEDIUM]: 40,
|
|
27
|
+
[BUTTON_SIZE.SMALL]: 32,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const FONT_SIZES: Record<ButtonSize, number> = {
|
|
31
|
+
[BUTTON_SIZE.LARGE]: 20,
|
|
32
|
+
[BUTTON_SIZE.MEDIUM]: 16,
|
|
33
|
+
[BUTTON_SIZE.SMALL]: 14,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const BORDER_RADIUSES: Record<ButtonSize, number> = {
|
|
37
|
+
[BUTTON_SIZE.LARGE]: BORDER_RADIUS.MEDIUM,
|
|
38
|
+
[BUTTON_SIZE.MEDIUM]: BORDER_RADIUS.SMALL,
|
|
39
|
+
[BUTTON_SIZE.SMALL]: BORDER_RADIUS.SMALL,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* TcbsButton - A themeable, accessible button component with icon support
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* <TcbsButton
|
|
48
|
+
* title="Submit"
|
|
49
|
+
* onPress={() => console.log('Pressed')}
|
|
50
|
+
* size="large"
|
|
51
|
+
* variant="primary"
|
|
52
|
+
* iconName="check"
|
|
53
|
+
* iconPosition="left"
|
|
54
|
+
* />
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export const TcbsButton: React.FC<TcbsButtonProps> = ({
|
|
58
|
+
title,
|
|
59
|
+
onPress,
|
|
60
|
+
size = BUTTON_SIZE.LARGE,
|
|
61
|
+
variant = BUTTON_VARIANT.PRIMARY,
|
|
62
|
+
borderRadius,
|
|
63
|
+
disabled = false,
|
|
64
|
+
style,
|
|
65
|
+
textStyle,
|
|
66
|
+
iconName,
|
|
67
|
+
iconGroup = 'MaterialDesignIcons',
|
|
68
|
+
iconColor,
|
|
69
|
+
iconSize,
|
|
70
|
+
iconPosition = 'top',
|
|
71
|
+
accessibilityLabel,
|
|
72
|
+
accessibilityHint,
|
|
73
|
+
accessibilityRole = 'button',
|
|
74
|
+
accessibilityState,
|
|
75
|
+
themeColor,
|
|
76
|
+
screenBgColor,
|
|
77
|
+
}) => {
|
|
78
|
+
// Use theme from store if not provided as prop
|
|
79
|
+
const { colors, mode } = useTcbsColorStore();
|
|
80
|
+
const effectiveThemeColor = themeColor ?? colors[mode];
|
|
81
|
+
// Normalize colors: if only one color is set, use it for all
|
|
82
|
+
const normalizedColors = {
|
|
83
|
+
btnColor: effectiveThemeColor?.btnColor ?? effectiveThemeColor?.themeColor ?? '#007AFF',
|
|
84
|
+
btnBorderColor: effectiveThemeColor?.btnBorderColor ?? effectiveThemeColor?.btnColor ?? '#007AFF',
|
|
85
|
+
btnIconColor: effectiveThemeColor?.btnIconColor,
|
|
86
|
+
btnTextColor: effectiveThemeColor?.btnTextColor ?? effectiveThemeColor?.btnTxtColor ,
|
|
87
|
+
themeColor: effectiveThemeColor?.themeColor ?? effectiveThemeColor?.btnColor ?? '#007AFF',
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const buttonStyle = useMemo<StyleProp<ViewStyle>>(() => {
|
|
91
|
+
const baseStyle: ViewStyle = {
|
|
92
|
+
height: HEIGHTS[size],
|
|
93
|
+
borderRadius: borderRadius ?? BORDER_RADIUSES[size],
|
|
94
|
+
alignItems: 'center',
|
|
95
|
+
justifyContent: 'center',
|
|
96
|
+
opacity: disabled ? 0.6 : 1,
|
|
97
|
+
paddingHorizontal: 24,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (variant === BUTTON_VARIANT.SECONDARY) {
|
|
101
|
+
return {
|
|
102
|
+
...baseStyle,
|
|
103
|
+
backgroundColor: '#fff',
|
|
104
|
+
borderWidth: 2,
|
|
105
|
+
borderColor: normalizedColors.btnBorderColor,
|
|
106
|
+
...(style as ViewStyle),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (variant === BUTTON_VARIANT.NO_BORDER) {
|
|
111
|
+
return {
|
|
112
|
+
...baseStyle,
|
|
113
|
+
backgroundColor: 'transparent',
|
|
114
|
+
...(style as ViewStyle),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Primary variant (default)
|
|
119
|
+
return {
|
|
120
|
+
...baseStyle,
|
|
121
|
+
backgroundColor: normalizedColors.btnColor,
|
|
122
|
+
shadowColor: normalizedColors.btnColor,
|
|
123
|
+
shadowOpacity: 0.15,
|
|
124
|
+
shadowRadius: 6,
|
|
125
|
+
shadowOffset: { width: 0, height: 2 },
|
|
126
|
+
elevation: 2,
|
|
127
|
+
...(style as ViewStyle),
|
|
128
|
+
};
|
|
129
|
+
}, [size, variant, normalizedColors, style, disabled, borderRadius]);
|
|
130
|
+
|
|
131
|
+
const themedTextStyle = useMemo<TextStyle>(() => {
|
|
132
|
+
const baseTextColor =
|
|
133
|
+
variant === BUTTON_VARIANT.PRIMARY
|
|
134
|
+
? normalizedColors.btnTextColor || '#FFFFFF'
|
|
135
|
+
: variant === BUTTON_VARIANT.NO_BORDER && mode === 'dark' ? normalizedColors.btnTextColor || "#FFFFFF" : normalizedColors?.btnColor || normalizedColors?.themeColor || "#FFFFFF";
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
color: baseTextColor,
|
|
139
|
+
fontSize: FONT_SIZES[size],
|
|
140
|
+
fontWeight: '700',
|
|
141
|
+
...(textStyle as TextStyle),
|
|
142
|
+
};
|
|
143
|
+
}, [size, variant, normalizedColors, textStyle]);
|
|
144
|
+
|
|
145
|
+
const renderIcon = (IconComponent: IconComponentType) => (
|
|
146
|
+
<IconComponent
|
|
147
|
+
name={iconName!}
|
|
148
|
+
size={iconSize || FONT_SIZES[size] * 2}
|
|
149
|
+
color={iconColor || normalizedColors.btnIconColor || themedTextStyle.color}
|
|
150
|
+
style={
|
|
151
|
+
iconPosition === 'top'
|
|
152
|
+
? { marginBottom: 2 }
|
|
153
|
+
: iconPosition === 'left'
|
|
154
|
+
? { marginRight: 8 }
|
|
155
|
+
: { marginLeft: 8 }
|
|
156
|
+
}
|
|
157
|
+
/>
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const renderText = (customStyle?: TextStyle) => {
|
|
161
|
+
if (!title) return null;
|
|
162
|
+
|
|
163
|
+
const finalStyle: TextStyle = customStyle
|
|
164
|
+
? {
|
|
165
|
+
color: themedTextStyle.color,
|
|
166
|
+
fontSize: FONT_SIZES[size] - 4,
|
|
167
|
+
fontWeight: '500',
|
|
168
|
+
...customStyle,
|
|
169
|
+
}
|
|
170
|
+
: themedTextStyle;
|
|
171
|
+
|
|
172
|
+
return <Text style={finalStyle}>{title}</Text>;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const renderContent = () => {
|
|
176
|
+
// If no icon, just render text
|
|
177
|
+
if (!iconName) {
|
|
178
|
+
return renderText();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Map iconGroup string to actual component
|
|
182
|
+
const IconComponent: IconComponentType =
|
|
183
|
+
iconGroup === 'AntDesign' ? AntDesign :
|
|
184
|
+
iconGroup === 'Feather' ? Feather :
|
|
185
|
+
iconGroup === 'FontAwesome' ? FontAwesome :
|
|
186
|
+
iconGroup === 'Foundation' ? Foundation :
|
|
187
|
+
iconGroup === 'Ionicons' ? Ionicons :
|
|
188
|
+
iconGroup === 'MaterialDesignIcons' ? MaterialDesignIcons :
|
|
189
|
+
iconGroup === 'Octicons' ? Octicons :
|
|
190
|
+
// iconGroup === 'Lucide' ? Lucide : // Uncomment if Lucide is available
|
|
191
|
+
MaterialDesignIcons;
|
|
192
|
+
|
|
193
|
+
if (iconPosition === 'top') {
|
|
194
|
+
return (
|
|
195
|
+
<View style={{ alignItems: 'center', justifyContent: 'center' }}>
|
|
196
|
+
{renderIcon(IconComponent)}
|
|
197
|
+
{renderText({ marginTop: 2 })}
|
|
198
|
+
</View>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const flexDirection = iconPosition === 'left' ? 'row' : 'row-reverse';
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<View
|
|
206
|
+
style={{
|
|
207
|
+
flexDirection,
|
|
208
|
+
alignItems: 'center',
|
|
209
|
+
justifyContent: 'center',
|
|
210
|
+
}}
|
|
211
|
+
>
|
|
212
|
+
{renderIcon(IconComponent)}
|
|
213
|
+
{renderText()}
|
|
214
|
+
</View>
|
|
215
|
+
);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
return (
|
|
219
|
+
<TouchableOpacity
|
|
220
|
+
onPress={onPress}
|
|
221
|
+
disabled={disabled}
|
|
222
|
+
style={buttonStyle}
|
|
223
|
+
accessibilityLabel={accessibilityLabel || title}
|
|
224
|
+
accessibilityHint={accessibilityHint}
|
|
225
|
+
accessibilityRole={accessibilityRole}
|
|
226
|
+
accessibilityState={accessibilityState || { disabled }}
|
|
227
|
+
>
|
|
228
|
+
{renderContent()}
|
|
229
|
+
</TouchableOpacity>
|
|
230
|
+
);
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Export constants for use in consuming applications
|
|
234
|
+
export { BUTTON_SIZE, BUTTON_VARIANT, BORDER_RADIUS };
|
|
235
|
+
export type { ButtonSize, ButtonVariant, IconGroupType, IconPosition };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { StyleProp, ViewStyle, TextStyle, AccessibilityRole, AccessibilityState, GestureResponderEvent } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export type IconComponentType = React.ComponentType<{
|
|
4
|
+
name: string;
|
|
5
|
+
size: number;
|
|
6
|
+
color: string;
|
|
7
|
+
style?: StyleProp<ViewStyle>;
|
|
8
|
+
}>;
|
|
9
|
+
|
|
10
|
+
export const BUTTON_SIZE = {
|
|
11
|
+
LARGE: 'large',
|
|
12
|
+
MEDIUM: 'medium',
|
|
13
|
+
SMALL: 'small',
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
16
|
+
export const BUTTON_VARIANT = {
|
|
17
|
+
PRIMARY: 'primary',
|
|
18
|
+
SECONDARY: 'secondary',
|
|
19
|
+
NO_BORDER: 'no_border',
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
export const BORDER_RADIUS = {
|
|
23
|
+
SMALL: 8,
|
|
24
|
+
MEDIUM: 12,
|
|
25
|
+
LARGE: 16,
|
|
26
|
+
} as const;
|
|
27
|
+
|
|
28
|
+
export type ButtonSize = (typeof BUTTON_SIZE)[keyof typeof BUTTON_SIZE];
|
|
29
|
+
export type ButtonVariant = (typeof BUTTON_VARIANT)[keyof typeof BUTTON_VARIANT];
|
|
30
|
+
|
|
31
|
+
export type IconGroupType =
|
|
32
|
+
| 'AntDesign'
|
|
33
|
+
| 'Feather'
|
|
34
|
+
| 'FontAwesome'
|
|
35
|
+
| 'Foundation'
|
|
36
|
+
| 'Ionicons'
|
|
37
|
+
| 'MaterialDesignIcons'
|
|
38
|
+
| 'Octicons'
|
|
39
|
+
| 'Lucide';
|
|
40
|
+
|
|
41
|
+
export type IconPosition = 'top' | 'left' | 'right';
|
|
42
|
+
|
|
43
|
+
export interface TcbsButtonProps {
|
|
44
|
+
title?: string;
|
|
45
|
+
onPress: (event: GestureResponderEvent) => void;
|
|
46
|
+
size?: ButtonSize;
|
|
47
|
+
variant?: ButtonVariant;
|
|
48
|
+
borderRadius?: number;
|
|
49
|
+
disabled?: boolean;
|
|
50
|
+
style?: StyleProp<ViewStyle>;
|
|
51
|
+
textStyle?: StyleProp<TextStyle>;
|
|
52
|
+
iconName?: string;
|
|
53
|
+
iconGroup?: IconGroupType;
|
|
54
|
+
iconColor?: string;
|
|
55
|
+
iconSize?: number;
|
|
56
|
+
iconPosition?: IconPosition;
|
|
57
|
+
accessibilityLabel?: string;
|
|
58
|
+
accessibilityHint?: string;
|
|
59
|
+
accessibilityRole?: AccessibilityRole;
|
|
60
|
+
accessibilityState?: AccessibilityState;
|
|
61
|
+
themeColor?: {
|
|
62
|
+
btnColor?: string;
|
|
63
|
+
btnBorderColor?: string;
|
|
64
|
+
btnIconColor?: string;
|
|
65
|
+
themeColor?: string;
|
|
66
|
+
btnTextColor?: string;
|
|
67
|
+
btnTxtColor?: string;
|
|
68
|
+
};
|
|
69
|
+
screenBgColor?: string;
|
|
70
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { Button } from './components/Button';
|
|
2
|
+
export { TcbsButton } from './components/TcbsButton';
|
|
3
|
+
export {
|
|
4
|
+
BUTTON_SIZE,
|
|
5
|
+
BUTTON_VARIANT,
|
|
6
|
+
BORDER_RADIUS,
|
|
7
|
+
ButtonSize,
|
|
8
|
+
ButtonVariant,
|
|
9
|
+
IconGroupType,
|
|
10
|
+
IconPosition,
|
|
11
|
+
TcbsButtonProps,
|
|
12
|
+
IconComponentType,
|
|
13
|
+
} from './components/TcbsButton.types';
|
|
14
|
+
export { useTcbsColorStore } from './store/themeStore';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
|
|
3
|
+
export type ThemeColor = {
|
|
4
|
+
btnColor: string;
|
|
5
|
+
btnBorderColor?: string;
|
|
6
|
+
btnIconColor?: string;
|
|
7
|
+
themeColor: string;
|
|
8
|
+
btnTextColor: string;
|
|
9
|
+
screenBgColor?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type ThemeMode = 'light' | 'dark';
|
|
13
|
+
|
|
14
|
+
export type ThemeColors = {
|
|
15
|
+
light: ThemeColor;
|
|
16
|
+
dark: ThemeColor;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export interface ThemeStore {
|
|
20
|
+
colors: ThemeColors;
|
|
21
|
+
mode: ThemeMode;
|
|
22
|
+
setTcbsColor: (colors: Partial<ThemeColor> & { light?: Partial<ThemeColor>; dark?: Partial<ThemeColor> }) => void;
|
|
23
|
+
setTcbsTheme: (mode: ThemeMode) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const defaultColors: ThemeColors = {
|
|
27
|
+
light: {
|
|
28
|
+
// btnColor: '#007AFF',
|
|
29
|
+
// btnBorderColor: '#007AFF',
|
|
30
|
+
// btnIconColor: '#16a62bff',
|
|
31
|
+
// themeColor: '#007AFF',
|
|
32
|
+
// btnTextColor: '#FFFFFF',
|
|
33
|
+
},
|
|
34
|
+
dark: {
|
|
35
|
+
// btnColor: '#222222',
|
|
36
|
+
// btnBorderColor: '#222222',
|
|
37
|
+
// btnIconColor: '#FFFFFF',
|
|
38
|
+
// themeColor: '#222222',
|
|
39
|
+
// btnTextColor: '#FFFFFF',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const useTcbsColorStore = create<ThemeStore>((set: (fn: (state: ThemeStore) => Partial<ThemeStore>) => void) => ({
|
|
44
|
+
colors: defaultColors,
|
|
45
|
+
mode: 'light',
|
|
46
|
+
setTcbsColor: (colors: Partial<ThemeColor> & { light?: Partial<ThemeColor>; dark?: Partial<ThemeColor> }) => {
|
|
47
|
+
set((state: ThemeStore) => {
|
|
48
|
+
let newColors = { ...state.colors };
|
|
49
|
+
// If colors for both themes are provided
|
|
50
|
+
if (colors.light || colors.dark) {
|
|
51
|
+
if (colors.light) {
|
|
52
|
+
newColors.light = { ...newColors.light, ...colors.light };
|
|
53
|
+
}
|
|
54
|
+
if (colors.dark) {
|
|
55
|
+
newColors.dark = { ...newColors.dark, ...colors.dark };
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
// If only one set, update both themes
|
|
59
|
+
newColors.light = { ...newColors.light, ...colors };
|
|
60
|
+
newColors.dark = { ...newColors.dark, ...colors };
|
|
61
|
+
}
|
|
62
|
+
return { colors: newColors };
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
setTcbsTheme: (mode: ThemeMode) => set((state) => ({ ...state, mode })),
|
|
66
|
+
}));
|