react-native-yastools 1.0.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,173 @@
1
+ # Yastools React Native Utils
2
+
3
+ [![CI](https://github.com/YassineBenZriouil/react-native-yastools/actions/workflows/ci.yml/badge.svg)](https://github.com/YassineBenZriouil/react-native-yastools/actions/workflows/ci.yml)
4
+ [![npm version](https://badge.fury.io/js/react-native-yastools.svg)](https://www.npmjs.com/package/react-native-yastools)
5
+
6
+ A collection of reusable React Native utility components.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install react-native-yastools
12
+ # or
13
+ yarn add react-native-yastools
14
+ ```
15
+
16
+ ### Peer Dependencies
17
+
18
+ This package requires the following peer dependencies to be installed in your project:
19
+
20
+ - `react` >= 17.0.0
21
+ - `react-native` >= 0.64.0
22
+
23
+ ## Components
24
+
25
+ ### YasButton
26
+
27
+ A customizable button component with built-in loading state, debounce functionality, and theming support.
28
+
29
+ #### Basic Usage
30
+
31
+ ```tsx
32
+ import { YasButton } from 'react-native-yastools';
33
+
34
+ const MyComponent = () => {
35
+ return (
36
+ <YasButton
37
+ text="Click Me"
38
+ onPress={() => console.log('Button pressed!')}
39
+ />
40
+ );
41
+ };
42
+ ```
43
+
44
+ #### With Loading State
45
+
46
+ ```tsx
47
+ import { YasButton } from 'react-native-yastools';
48
+
49
+ const MyComponent = () => {
50
+ const [loading, setLoading] = useState(false);
51
+
52
+ const handlePress = async () => {
53
+ setLoading(true);
54
+ await someAsyncOperation();
55
+ setLoading(false);
56
+ };
57
+
58
+ return (
59
+ <YasButton
60
+ text="Submit"
61
+ onPress={handlePress}
62
+ fetching={loading}
63
+ loaderColor="#FFFFFF"
64
+ />
65
+ );
66
+ };
67
+ ```
68
+
69
+ #### With Custom Styling
70
+
71
+ ```tsx
72
+ import { YasButton } from 'react-native-yastools';
73
+
74
+ const MyComponent = () => {
75
+ return (
76
+ <YasButton
77
+ text="Custom Button"
78
+ onPress={() => {}}
79
+ primaryColor="#FF6B6B"
80
+ disabledColor="#CCCCCC"
81
+ additionalStyle={{
82
+ width: 200,
83
+ height: 60,
84
+ borderRadius: 30,
85
+ }}
86
+ textStyle={{
87
+ fontSize: 18,
88
+ fontWeight: 'bold',
89
+ }}
90
+ />
91
+ );
92
+ };
93
+ ```
94
+
95
+ #### With Icon
96
+
97
+ ```tsx
98
+ import { YasButton } from 'react-native-yastools';
99
+
100
+ const MyComponent = () => {
101
+ return (
102
+ <YasButton
103
+ text="Settings"
104
+ icon={require('./assets/settings-icon.png')}
105
+ onPress={() => navigation.navigate('Settings')}
106
+ />
107
+ );
108
+ };
109
+ ```
110
+
111
+ #### Props
112
+
113
+ | Prop | Type | Default | Description |
114
+ |------|------|---------|-------------|
115
+ | `onPress` | `() => void` | **Required** | Callback function when button is pressed |
116
+ | `text` | `string` | `undefined` | Text to display on the button |
117
+ | `icon` | `ImageSourcePropType` | `undefined` | Icon to display on the button |
118
+ | `disabled` | `boolean` | `false` | Whether the button is disabled |
119
+ | `fetching` | `boolean` | `false` | Whether to show loading indicator |
120
+ | `additionalStyle` | `StyleProp<ViewStyle>` | `undefined` | Additional styles for the button container |
121
+ | `textStyle` | `StyleProp<TextStyle>` | `undefined` | Custom styles for the button text |
122
+ | `iconStyle` | `StyleProp<ImageStyle>` | `undefined` | Custom styles for the icon |
123
+ | `loaderColor` | `string` | Theme primary | Color of the loading indicator |
124
+ | `primaryColor` | `string` | `#007AFF` | Custom primary/background color |
125
+ | `disabledColor` | `string` | `#E5E5E5` | Custom disabled background color |
126
+ | `debounceTime` | `number` | `1000` | Debounce time in ms to prevent multiple clicks |
127
+ | `activeOpacity` | `number` | `0.8` | Opacity when pressing the button |
128
+ | `testID` | `string` | `undefined` | Test ID for testing purposes |
129
+
130
+ ## Theme Customization
131
+
132
+ You can import the default theme values to use in your own components:
133
+
134
+ ```tsx
135
+ import { COLORS, FONT_SIZES, FONT_FAMILY } from 'react-native-yastools';
136
+
137
+ const styles = StyleSheet.create({
138
+ container: {
139
+ backgroundColor: COLORS.primary,
140
+ },
141
+ text: {
142
+ fontSize: FONT_SIZES.f16,
143
+ },
144
+ });
145
+ ```
146
+
147
+ ## Development
148
+
149
+ ### Running Tests
150
+
151
+ ```bash
152
+ npm test
153
+
154
+ # Watch mode
155
+ npm run test:watch
156
+
157
+ # Coverage report
158
+ npm run test:coverage
159
+ ```
160
+
161
+ ### Building
162
+
163
+ ```bash
164
+ npm run build
165
+ ```
166
+
167
+ ## Contributing
168
+
169
+ Contributions are welcome! Please feel free to submit a Pull Request.
170
+
171
+ ## License
172
+
173
+ ISC
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import { render, fireEvent } from '@testing-library/react-native';
3
+ import YasButton from '../index';
4
+ describe('YasButton', () => {
5
+ it('renders correctly with text', () => {
6
+ const { getByText } = render(<YasButton text="Press Me" onPress={() => { }}/>);
7
+ expect(getByText('Press Me')).toBeTruthy();
8
+ });
9
+ it('calls onPress when pressed', () => {
10
+ const onPressMock = jest.fn();
11
+ const { getByText } = render(<YasButton text="Press Me" onPress={onPressMock}/>);
12
+ fireEvent.press(getByText('Press Me'));
13
+ expect(onPressMock).toHaveBeenCalledTimes(1);
14
+ });
15
+ it('does not call onPress when disabled', () => {
16
+ const onPressMock = jest.fn();
17
+ const { getByText } = render(<YasButton text="Press Me" onPress={onPressMock} disabled/>);
18
+ fireEvent.press(getByText('Press Me'));
19
+ expect(onPressMock).not.toHaveBeenCalled();
20
+ });
21
+ it('shows loading indicator when fetching', () => {
22
+ const { queryByText, getByTestId } = render(<YasButton text="Press Me" onPress={() => { }} fetching testID="button"/>);
23
+ expect(queryByText('Press Me')).toBeNull();
24
+ });
25
+ it('debounces multiple rapid presses', () => {
26
+ jest.useFakeTimers();
27
+ const onPressMock = jest.fn();
28
+ const { getByText } = render(<YasButton text="Press Me" onPress={onPressMock} debounceTime={1000}/>);
29
+ const button = getByText('Press Me');
30
+ fireEvent.press(button);
31
+ fireEvent.press(button);
32
+ fireEvent.press(button);
33
+ expect(onPressMock).toHaveBeenCalledTimes(1);
34
+ jest.advanceTimersByTime(1000);
35
+ fireEvent.press(button);
36
+ expect(onPressMock).toHaveBeenCalledTimes(2);
37
+ jest.useRealTimers();
38
+ });
39
+ it('applies custom primary color', () => {
40
+ const { getByTestId } = render(<YasButton text="Press Me" onPress={() => { }} primaryColor="#FF0000" testID="custom-button"/>);
41
+ const button = getByTestId('custom-button');
42
+ const flattenedStyle = Array.isArray(button.props.style)
43
+ ? button.props.style.flat().reduce((acc, style) => ({ ...acc, ...style }), {})
44
+ : button.props.style;
45
+ expect(flattenedStyle.backgroundColor).toBe('#FF0000');
46
+ });
47
+ it('renders without text when only icon is provided', () => {
48
+ const mockIcon = { uri: 'https://example.com/icon.png' };
49
+ const { queryByText } = render(<YasButton onPress={() => { }} icon={mockIcon}/>);
50
+ expect(queryByText('Press Me')).toBeNull();
51
+ });
52
+ });
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import { YasButtonProps } from './type';
3
+ export type { YasButtonProps };
4
+ declare const _default: React.NamedExoticComponent<YasButtonProps>;
5
+ export default _default;
@@ -0,0 +1,50 @@
1
+ import React, { memo, useRef } from 'react';
2
+ import { TouchableOpacity, Text, ActivityIndicator, View, Image, } from 'react-native';
3
+ // Internal styles and theme
4
+ import styles from './styles';
5
+ import COLORS from '../../theme';
6
+ /**
7
+ * YasButton - A customizable button component for React Native
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * import { YasButton } from 'react-native-yastools';
12
+ *
13
+ * <YasButton
14
+ * text="Click Me"
15
+ * onPress={() => console.log('Pressed!')}
16
+ * primaryColor="#FF6B6B"
17
+ * />
18
+ * ```
19
+ */
20
+ const YasButton = ({ onPress, additionalStyle, additonalStyle, // Support legacy prop name
21
+ disabled, fetching, text, textStyle, icon, iconStyle, loaderColor, primaryColor, disabledColor, debounceTime = 1000, activeOpacity = 0.8, testID, }) => {
22
+ const lastPressTime = useRef(0);
23
+ const handlePress = () => {
24
+ // Prevent multiple clicks with configurable debounce
25
+ const now = Date.now();
26
+ if (now - lastPressTime.current < debounceTime) {
27
+ return;
28
+ }
29
+ lastPressTime.current = now;
30
+ onPress();
31
+ };
32
+ // Determine container style based on state
33
+ const containerStyle = [
34
+ disabled ? styles.disabled : styles.container,
35
+ // Apply custom colors if provided
36
+ !disabled && primaryColor && { backgroundColor: primaryColor },
37
+ disabled && disabledColor && { backgroundColor: disabledColor },
38
+ // Support both prop names for backwards compatibility
39
+ additionalStyle || additonalStyle,
40
+ ];
41
+ return (<TouchableOpacity onPress={handlePress} activeOpacity={activeOpacity} disabled={disabled || fetching}>
42
+ <View style={containerStyle} testID={testID}>
43
+ {fetching ? (<ActivityIndicator size="small" color={loaderColor || primaryColor || COLORS.primary}/>) : (<>
44
+ {text && <Text style={[styles.text, textStyle]}>{text}</Text>}
45
+ {icon && <Image source={icon} style={[styles.icon, iconStyle]}/>}
46
+ </>)}
47
+ </View>
48
+ </TouchableOpacity>);
49
+ };
50
+ export default memo(YasButton);
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Default styles for the YasButton component
3
+ * Uses standard React Native StyleSheet for maximum compatibility
4
+ */
5
+ declare const _default: {
6
+ container: {
7
+ width: number;
8
+ height: number;
9
+ justifyContent: "center";
10
+ alignItems: "center";
11
+ borderRadius: number;
12
+ backgroundColor: string;
13
+ flexDirection: "row";
14
+ gap: number;
15
+ };
16
+ disabled: {
17
+ width: number;
18
+ height: number;
19
+ justifyContent: "center";
20
+ alignItems: "center";
21
+ borderRadius: number;
22
+ backgroundColor: string;
23
+ flexDirection: "row";
24
+ gap: number;
25
+ };
26
+ text: {
27
+ fontSize: number;
28
+ color: string;
29
+ fontWeight: "600";
30
+ };
31
+ icon: {
32
+ width: number;
33
+ height: number;
34
+ };
35
+ };
36
+ export default _default;
@@ -0,0 +1,37 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import COLORS, { FONT_SIZES } from '../../theme';
3
+ /**
4
+ * Default styles for the YasButton component
5
+ * Uses standard React Native StyleSheet for maximum compatibility
6
+ */
7
+ export default StyleSheet.create({
8
+ container: {
9
+ width: 176,
10
+ height: 53,
11
+ justifyContent: 'center',
12
+ alignItems: 'center',
13
+ borderRadius: 8,
14
+ backgroundColor: COLORS.primary,
15
+ flexDirection: 'row',
16
+ gap: 20,
17
+ },
18
+ disabled: {
19
+ width: 176,
20
+ height: 53,
21
+ justifyContent: 'center',
22
+ alignItems: 'center',
23
+ borderRadius: 8,
24
+ backgroundColor: COLORS.grayBg,
25
+ flexDirection: 'row',
26
+ gap: 20,
27
+ },
28
+ text: {
29
+ fontSize: FONT_SIZES.f16,
30
+ color: COLORS.white,
31
+ fontWeight: '600',
32
+ },
33
+ icon: {
34
+ width: 24,
35
+ height: 26,
36
+ },
37
+ });
@@ -0,0 +1,33 @@
1
+ import { StyleProp, ViewStyle, TextStyle, ImageSourcePropType, ImageStyle } from "react-native";
2
+ export interface YasButtonProps {
3
+ /** Callback function when button is pressed */
4
+ onPress: () => void;
5
+ /** Additional styles to apply to the button container */
6
+ additionalStyle?: StyleProp<ViewStyle>;
7
+ /** @deprecated Use additionalStyle instead */
8
+ additonalStyle?: StyleProp<ViewStyle>;
9
+ /** Whether the button is disabled */
10
+ disabled?: boolean;
11
+ /** Whether to show loading indicator */
12
+ fetching?: boolean;
13
+ /** Text to display on the button */
14
+ text?: string;
15
+ /** Icon to display on the button */
16
+ icon?: ImageSourcePropType;
17
+ /** Custom styles for the button text */
18
+ textStyle?: StyleProp<TextStyle>;
19
+ /** Custom styles for the icon */
20
+ iconStyle?: StyleProp<ImageStyle>;
21
+ /** Color of the loading indicator (defaults to theme primary color) */
22
+ loaderColor?: string;
23
+ /** Custom primary color to override theme */
24
+ primaryColor?: string;
25
+ /** Custom disabled background color to override theme */
26
+ disabledColor?: string;
27
+ /** Debounce time in milliseconds to prevent multiple clicks (default: 1000) */
28
+ debounceTime?: number;
29
+ /** Active opacity when pressing the button (default: 0.8) */
30
+ activeOpacity?: number;
31
+ /** Test ID for testing purposes */
32
+ testID?: string;
33
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export { default as YasButton } from './components/Button';
2
+ export type { YasButtonProps } from './components/Button';
3
+ export { default as COLORS, FONT_FAMILY, FONT_SIZES } from './theme';
4
+ export { default } from './components/Button';
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ // Components
2
+ export { default as YasButton } from './components/Button';
3
+ // Theme utilities (for customization)
4
+ export { default as COLORS, FONT_FAMILY, FONT_SIZES } from './theme';
5
+ // Re-export Button as default for convenience
6
+ export { default } from './components/Button';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Default theme configuration for YasButton
3
+ * Users can override these values by passing custom props
4
+ */
5
+ export declare const COLORS: {
6
+ primary: string;
7
+ white: string;
8
+ grayBg: string;
9
+ gray: string;
10
+ };
11
+ export declare const FONT_FAMILY: {
12
+ InterSemiBold: string;
13
+ InterRegular: string;
14
+ InterBold: string;
15
+ };
16
+ export declare const FONT_SIZES: {
17
+ f12: number;
18
+ f14: number;
19
+ f16: number;
20
+ f18: number;
21
+ f20: number;
22
+ f24: number;
23
+ };
24
+ export default COLORS;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Default theme configuration for YasButton
3
+ * Users can override these values by passing custom props
4
+ */
5
+ export const COLORS = {
6
+ primary: '#007AFF',
7
+ white: '#FFFFFF',
8
+ grayBg: '#E5E5E5',
9
+ gray: '#666666',
10
+ };
11
+ export const FONT_FAMILY = {
12
+ InterSemiBold: 'Inter-SemiBold',
13
+ InterRegular: 'Inter-Regular',
14
+ InterBold: 'Inter-Bold',
15
+ };
16
+ export const FONT_SIZES = {
17
+ f12: 12,
18
+ f14: 14,
19
+ f16: 16,
20
+ f18: 18,
21
+ f20: 20,
22
+ f24: 24,
23
+ };
24
+ export default COLORS;
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "react-native-yastools",
3
+ "version": "1.0.1",
4
+ "main": "dist/index.js",
5
+ "module": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "src"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "prepare": "npm run build",
14
+ "test": "jest",
15
+ "test:watch": "jest --watch",
16
+ "test:coverage": "jest --coverage"
17
+ },
18
+ "keywords": [
19
+ "react-native",
20
+ "components",
21
+ "button",
22
+ "utils"
23
+ ],
24
+ "author": "",
25
+ "license": "ISC",
26
+ "description": "React Native Utility Files and Utility Components ",
27
+ "peerDependencies": {
28
+ "react": ">=17.0.0",
29
+ "react-native": ">=0.64.0"
30
+ },
31
+ "peerDependenciesMeta": {
32
+ "react": {
33
+ "optional": false
34
+ },
35
+ "react-native": {
36
+ "optional": false
37
+ }
38
+ },
39
+ "devDependencies": {
40
+ "@babel/core": "^7.24.0",
41
+ "@babel/preset-env": "^7.24.0",
42
+ "@babel/preset-react": "^7.24.0",
43
+ "@babel/preset-flow": "^7.24.0",
44
+ "@babel/preset-typescript": "^7.24.0",
45
+ "@testing-library/react-native": "^12.4.0",
46
+ "@types/jest": "^29.5.0",
47
+ "@types/react": "^18.2.0",
48
+ "@types/react-native": "^0.72.0",
49
+ "jest": "^29.7.0",
50
+ "react": "18.2.0",
51
+ "react-native": "^0.73.0",
52
+ "react-test-renderer": "18.2.0",
53
+ "typescript": "^5.0.0"
54
+ },
55
+ "jest": {
56
+ "preset": "react-native",
57
+ "moduleFileExtensions": [
58
+ "ts",
59
+ "tsx",
60
+ "js",
61
+ "jsx",
62
+ "json"
63
+ ],
64
+ "testMatch": [
65
+ "<rootDir>/src/**/__tests__/**/*.(ts|tsx|js)",
66
+ "<rootDir>/src/**/*.(test|spec).(ts|tsx|js)"
67
+ ],
68
+ "testPathIgnorePatterns": [
69
+ "/node_modules/",
70
+ "/dist/"
71
+ ],
72
+ "transformIgnorePatterns": [
73
+ "node_modules/(?!((jest-)?react-native|@react-native(-community)?)/)"
74
+ ],
75
+ "setupFilesAfterEnv": [
76
+ "<rootDir>/jest.setup.js"
77
+ ],
78
+ "collectCoverageFrom": [
79
+ "src/**/*.{ts,tsx}",
80
+ "!src/**/*.d.ts"
81
+ ]
82
+ }
83
+ }
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+ import { render, fireEvent } from '@testing-library/react-native';
3
+ import YasButton from '../index';
4
+
5
+ describe('YasButton', () => {
6
+ it('renders correctly with text', () => {
7
+ const { getByText } = render(
8
+ <YasButton text="Press Me" onPress={() => {}} />
9
+ );
10
+ expect(getByText('Press Me')).toBeTruthy();
11
+ });
12
+
13
+ it('calls onPress when pressed', () => {
14
+ const onPressMock = jest.fn();
15
+ const { getByText } = render(
16
+ <YasButton text="Press Me" onPress={onPressMock} />
17
+ );
18
+
19
+ fireEvent.press(getByText('Press Me'));
20
+ expect(onPressMock).toHaveBeenCalledTimes(1);
21
+ });
22
+
23
+ it('does not call onPress when disabled', () => {
24
+ const onPressMock = jest.fn();
25
+ const { getByText } = render(
26
+ <YasButton text="Press Me" onPress={onPressMock} disabled />
27
+ );
28
+
29
+ fireEvent.press(getByText('Press Me'));
30
+ expect(onPressMock).not.toHaveBeenCalled();
31
+ });
32
+
33
+ it('shows loading indicator when fetching', () => {
34
+ const { queryByText, getByTestId } = render(
35
+ <YasButton text="Press Me" onPress={() => {}} fetching testID="button" />
36
+ );
37
+
38
+ expect(queryByText('Press Me')).toBeNull();
39
+ });
40
+
41
+ it('debounces multiple rapid presses', () => {
42
+ jest.useFakeTimers();
43
+ const onPressMock = jest.fn();
44
+ const { getByText } = render(
45
+ <YasButton text="Press Me" onPress={onPressMock} debounceTime={1000} />
46
+ );
47
+
48
+ const button = getByText('Press Me');
49
+
50
+ fireEvent.press(button);
51
+ fireEvent.press(button);
52
+ fireEvent.press(button);
53
+
54
+ expect(onPressMock).toHaveBeenCalledTimes(1);
55
+
56
+ jest.advanceTimersByTime(1000);
57
+ fireEvent.press(button);
58
+
59
+ expect(onPressMock).toHaveBeenCalledTimes(2);
60
+ jest.useRealTimers();
61
+ });
62
+
63
+ it('applies custom primary color', () => {
64
+ const { getByTestId } = render(
65
+ <YasButton
66
+ text="Press Me"
67
+ onPress={() => {}}
68
+ primaryColor="#FF0000"
69
+ testID="custom-button"
70
+ />
71
+ );
72
+
73
+ const button = getByTestId('custom-button');
74
+ const flattenedStyle = Array.isArray(button.props.style)
75
+ ? button.props.style.flat().reduce((acc: object, style: object) => ({ ...acc, ...style }), {})
76
+ : button.props.style;
77
+ expect(flattenedStyle.backgroundColor).toBe('#FF0000');
78
+ });
79
+
80
+ it('renders without text when only icon is provided', () => {
81
+ const mockIcon = { uri: 'https://example.com/icon.png' };
82
+ const { queryByText } = render(
83
+ <YasButton onPress={() => {}} icon={mockIcon} />
84
+ );
85
+
86
+ expect(queryByText('Press Me')).toBeNull();
87
+ });
88
+ });
@@ -0,0 +1,96 @@
1
+ import React, { memo, useRef } from 'react';
2
+ import {
3
+ TouchableOpacity,
4
+ Text,
5
+ ActivityIndicator,
6
+ StyleProp,
7
+ ViewStyle,
8
+ View,
9
+ Image,
10
+ } from 'react-native';
11
+
12
+ // Internal styles and theme
13
+ import styles from './styles';
14
+ import COLORS from '../../theme';
15
+ import { YasButtonProps } from './type';
16
+
17
+ export type { YasButtonProps };
18
+
19
+
20
+ /**
21
+ * YasButton - A customizable button component for React Native
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * import { YasButton } from 'react-native-yastools';
26
+ *
27
+ * <YasButton
28
+ * text="Click Me"
29
+ * onPress={() => console.log('Pressed!')}
30
+ * primaryColor="#FF6B6B"
31
+ * />
32
+ * ```
33
+ */
34
+ const YasButton: React.FC<YasButtonProps> = ({
35
+ onPress,
36
+ additionalStyle,
37
+ additonalStyle, // Support legacy prop name
38
+ disabled,
39
+ fetching,
40
+ text,
41
+ textStyle,
42
+ icon,
43
+ iconStyle,
44
+ loaderColor,
45
+ primaryColor,
46
+ disabledColor,
47
+ debounceTime = 1000,
48
+ activeOpacity = 0.8,
49
+ testID,
50
+ }) => {
51
+ const lastPressTime = useRef(0);
52
+
53
+ const handlePress = () => {
54
+ // Prevent multiple clicks with configurable debounce
55
+ const now = Date.now();
56
+ if (now - lastPressTime.current < debounceTime) {
57
+ return;
58
+ }
59
+ lastPressTime.current = now;
60
+ onPress();
61
+ };
62
+
63
+ // Determine container style based on state
64
+ const containerStyle: StyleProp<ViewStyle> = [
65
+ disabled ? styles.disabled : styles.container,
66
+ // Apply custom colors if provided
67
+ !disabled && primaryColor && { backgroundColor: primaryColor },
68
+ disabled && disabledColor && { backgroundColor: disabledColor },
69
+ // Support both prop names for backwards compatibility
70
+ additionalStyle || additonalStyle,
71
+ ];
72
+
73
+ return (
74
+ <TouchableOpacity
75
+ onPress={handlePress}
76
+ activeOpacity={activeOpacity}
77
+ disabled={disabled || fetching}
78
+ >
79
+ <View style={containerStyle} testID={testID}>
80
+ {fetching ? (
81
+ <ActivityIndicator
82
+ size="small"
83
+ color={loaderColor || primaryColor || COLORS.primary}
84
+ />
85
+ ) : (
86
+ <>
87
+ {text && <Text style={[styles.text, textStyle]}>{text}</Text>}
88
+ {icon && <Image source={icon} style={[styles.icon, iconStyle]} />}
89
+ </>
90
+ )}
91
+ </View>
92
+ </TouchableOpacity>
93
+ );
94
+ };
95
+
96
+ export default memo(YasButton);
@@ -0,0 +1,38 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import COLORS, { FONT_SIZES } from '../../theme';
3
+
4
+ /**
5
+ * Default styles for the YasButton component
6
+ * Uses standard React Native StyleSheet for maximum compatibility
7
+ */
8
+ export default StyleSheet.create({
9
+ container: {
10
+ width: 176,
11
+ height: 53,
12
+ justifyContent: 'center',
13
+ alignItems: 'center',
14
+ borderRadius: 8,
15
+ backgroundColor: COLORS.primary,
16
+ flexDirection: 'row',
17
+ gap: 20,
18
+ },
19
+ disabled: {
20
+ width: 176,
21
+ height: 53,
22
+ justifyContent: 'center',
23
+ alignItems: 'center',
24
+ borderRadius: 8,
25
+ backgroundColor: COLORS.grayBg,
26
+ flexDirection: 'row',
27
+ gap: 20,
28
+ },
29
+ text: {
30
+ fontSize: FONT_SIZES.f16,
31
+ color: COLORS.white,
32
+ fontWeight: '600',
33
+ },
34
+ icon: {
35
+ width: 24,
36
+ height: 26,
37
+ },
38
+ });
@@ -0,0 +1,40 @@
1
+ import {
2
+ StyleProp,
3
+ ViewStyle,
4
+ TextStyle,
5
+ ImageSourcePropType,
6
+ ImageStyle,
7
+ } from "react-native";
8
+
9
+ export interface YasButtonProps {
10
+ /** Callback function when button is pressed */
11
+ onPress: () => void;
12
+ /** Additional styles to apply to the button container */
13
+ additionalStyle?: StyleProp<ViewStyle>;
14
+ /** @deprecated Use additionalStyle instead */
15
+ additonalStyle?: StyleProp<ViewStyle>;
16
+ /** Whether the button is disabled */
17
+ disabled?: boolean;
18
+ /** Whether to show loading indicator */
19
+ fetching?: boolean;
20
+ /** Text to display on the button */
21
+ text?: string;
22
+ /** Icon to display on the button */
23
+ icon?: ImageSourcePropType;
24
+ /** Custom styles for the button text */
25
+ textStyle?: StyleProp<TextStyle>;
26
+ /** Custom styles for the icon */
27
+ iconStyle?: StyleProp<ImageStyle>;
28
+ /** Color of the loading indicator (defaults to theme primary color) */
29
+ loaderColor?: string;
30
+ /** Custom primary color to override theme */
31
+ primaryColor?: string;
32
+ /** Custom disabled background color to override theme */
33
+ disabledColor?: string;
34
+ /** Debounce time in milliseconds to prevent multiple clicks (default: 1000) */
35
+ debounceTime?: number;
36
+ /** Active opacity when pressing the button (default: 0.8) */
37
+ activeOpacity?: number;
38
+ /** Test ID for testing purposes */
39
+ testID?: string;
40
+ }
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ // Components
2
+ export { default as YasButton } from './components/Button';
3
+ export type { YasButtonProps } from './components/Button';
4
+
5
+ // Theme utilities (for customization)
6
+ export { default as COLORS, FONT_FAMILY, FONT_SIZES } from './theme';
7
+
8
+ // Re-export Button as default for convenience
9
+ export { default } from './components/Button';
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Default theme configuration for YasButton
3
+ * Users can override these values by passing custom props
4
+ */
5
+
6
+ export const COLORS = {
7
+ primary: '#007AFF',
8
+ white: '#FFFFFF',
9
+ grayBg: '#E5E5E5',
10
+ gray: '#666666',
11
+ };
12
+
13
+ export const FONT_FAMILY = {
14
+ InterSemiBold: 'Inter-SemiBold',
15
+ InterRegular: 'Inter-Regular',
16
+ InterBold: 'Inter-Bold',
17
+ };
18
+
19
+ export const FONT_SIZES = {
20
+ f12: 12,
21
+ f14: 14,
22
+ f16: 16,
23
+ f18: 18,
24
+ f20: 20,
25
+ f24: 24,
26
+ };
27
+
28
+ export default COLORS;