@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 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,8 @@
1
+ import React from 'react';
2
+ import { GestureResponderEvent } from 'react-native';
3
+ type ButtonProps = {
4
+ title: string;
5
+ onPress: (event: GestureResponderEvent) => void;
6
+ };
7
+ export declare const Button: React.FC<ButtonProps>;
8
+ export {};
@@ -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
@@ -0,0 +1,3 @@
1
+ export { Button } from './components/Button';
2
+ export { TcbsButton } from './components/TcbsButton';
3
+ export { BUTTON_SIZE, BUTTON_VARIANT, BORDER_RADIUS, } from './components/TcbsButton.types';
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
+ }));