@navegarti/rn-design-system 0.8.1 → 0.8.2

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.
Files changed (95) hide show
  1. package/package.json +2 -1
  2. package/src/api/axios-adapter.ts +239 -0
  3. package/src/api/errors.ts +113 -0
  4. package/src/api/index.ts +27 -0
  5. package/src/api/retry-strategy.ts +86 -0
  6. package/src/api/stores/auth-store.ts +26 -0
  7. package/src/api/types.ts +74 -0
  8. package/src/api.tsx +17 -0
  9. package/src/components/Button/index.tsx +37 -0
  10. package/src/components/Button/styles.ts +28 -0
  11. package/src/components/Card/index.tsx +23 -0
  12. package/src/components/Card/styles.ts +45 -0
  13. package/src/components/Card/types.ts +22 -0
  14. package/src/components/Carousel/components/dot.tsx +43 -0
  15. package/src/components/Carousel/components/footer.tsx +21 -0
  16. package/src/components/Carousel/components/list.tsx +81 -0
  17. package/src/components/Carousel/components/pagination.tsx +15 -0
  18. package/src/components/Carousel/components/see-all-button.tsx +29 -0
  19. package/src/components/Carousel/context.tsx +19 -0
  20. package/src/components/Carousel/index.tsx +193 -0
  21. package/src/components/Carousel/styles.ts +8 -0
  22. package/src/components/Carousel/types.ts +45 -0
  23. package/src/components/Checkbox/index.tsx +42 -0
  24. package/src/components/Checkbox/styles.ts +7 -0
  25. package/src/components/FAB/components/extended-fab.tsx +17 -0
  26. package/src/components/FAB/index.tsx +24 -0
  27. package/src/components/FAB/styles.ts +61 -0
  28. package/src/components/FAB/utils.ts +45 -0
  29. package/src/components/Flex/index.tsx +1 -0
  30. package/src/components/Flex/styles.ts +47 -0
  31. package/src/components/FormLabel/index.tsx +6 -0
  32. package/src/components/FormLabel/styles.ts +8 -0
  33. package/src/components/Input/components/input-control.tsx +20 -0
  34. package/src/components/Input/components/input-error.tsx +14 -0
  35. package/src/components/Input/components/input-field.tsx +55 -0
  36. package/src/components/Input/components/input-icon.tsx +7 -0
  37. package/src/components/Input/components/input-password-toggle.tsx +19 -0
  38. package/src/components/Input/components/input-root.tsx +54 -0
  39. package/src/components/Input/context.tsx +26 -0
  40. package/src/components/Input/index.tsx +17 -0
  41. package/src/components/Input/styles.ts +46 -0
  42. package/src/components/Input/utils.ts +27 -0
  43. package/src/components/Margin/index.tsx +1 -0
  44. package/src/components/Margin/styles.ts +23 -0
  45. package/src/components/OTPInput/index.tsx +114 -0
  46. package/src/components/OTPInput/styles.ts +23 -0
  47. package/src/components/OTPInput/utils.ts +31 -0
  48. package/src/components/Padding/index.tsx +1 -0
  49. package/src/components/Padding/styles.ts +23 -0
  50. package/src/components/Radio/components/radio-item.tsx +53 -0
  51. package/src/components/Radio/context.tsx +10 -0
  52. package/src/components/Radio/index.tsx +32 -0
  53. package/src/components/Radio/styles.ts +38 -0
  54. package/src/components/Radio/utils.ts +19 -0
  55. package/src/components/Select/components/error-icon.tsx +3 -0
  56. package/src/components/Select/components/select-error.tsx +14 -0
  57. package/src/components/Select/components/select-field.tsx +72 -0
  58. package/src/components/Select/context.tsx +17 -0
  59. package/src/components/Select/index.tsx +24 -0
  60. package/src/components/Select/styles.ts +15 -0
  61. package/src/components/Skeleton/index.tsx +60 -0
  62. package/src/components/Switch/index.tsx +61 -0
  63. package/src/components/Switch/styles.ts +32 -0
  64. package/src/components/Switch/utils.ts +15 -0
  65. package/src/components/Text/index.tsx +2 -0
  66. package/src/components/Text/styles.ts +44 -0
  67. package/src/components.tsx +28 -0
  68. package/src/form.tsx +6 -0
  69. package/src/formValidators/ZodTypeValidator.ts +58 -0
  70. package/src/formValidators/index.ts +8 -0
  71. package/src/formValidators/types.ts +20 -0
  72. package/src/hooks/useAppIsActive.ts +28 -0
  73. package/src/hooks/useAppSecurity.tsx +35 -0
  74. package/src/hooks/useNetworkStatus.ts +75 -0
  75. package/src/hooks/useStatusBar.ts +34 -0
  76. package/src/hooks.tsx +9 -0
  77. package/src/index.tsx +76 -0
  78. package/src/utils/camelCase.ts +6 -0
  79. package/src/utils/camelCaseJSONKeys.ts +31 -0
  80. package/src/utils/capitalizeWord.ts +4 -0
  81. package/src/utils/createLinkingPhoneNumberString.ts +13 -0
  82. package/src/utils/dateFormatters.ts +48 -0
  83. package/src/utils/debounce.ts +15 -0
  84. package/src/utils/deepLinkParser.ts +87 -0
  85. package/src/utils/getAge.ts +12 -0
  86. package/src/utils/getOnlyNumbers.ts +1 -0
  87. package/src/utils/isArray.ts +4 -0
  88. package/src/utils/isObject.ts +4 -0
  89. package/src/utils/masks.ts +3 -0
  90. package/src/utils/priceFormatter.ts +11 -0
  91. package/src/utils/removeTextAccents.ts +4 -0
  92. package/src/utils/sortBy.ts +21 -0
  93. package/src/utils/uniqBy.ts +20 -0
  94. package/src/utils/userFullnameInitialsExtractor.ts +22 -0
  95. package/src/utils.tsx +22 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@navegarti/rn-design-system",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "Components and functions to help navegar projects",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/module/index.js",
@@ -71,6 +71,7 @@
71
71
  }
72
72
  },
73
73
  "files": [
74
+ "src",
74
75
  "lib",
75
76
  "components.js",
76
77
  "utils.js",
@@ -0,0 +1,239 @@
1
+ import axios, {
2
+ type AxiosError,
3
+ type AxiosInstance,
4
+ type AxiosRequestConfig,
5
+ type AxiosResponse,
6
+ } from 'axios';
7
+ import {
8
+ AuthError,
9
+ NetworkError,
10
+ NotFoundError,
11
+ RateLimitError,
12
+ ServerError,
13
+ TimeoutError,
14
+ ValidationError,
15
+ } from './errors';
16
+ import { executeWithRetry } from './retry-strategy';
17
+ import { useAuthStore } from './stores/auth-store';
18
+ import type { ApiConfig, IHttpAdapter, RetryConfig } from './types';
19
+
20
+ /**
21
+ * Axios-based HTTP adapter implementation
22
+ * Implements retry logic, error handling, and token management
23
+ */
24
+ export class AxiosAdapter implements IHttpAdapter {
25
+ private readonly axiosInstance: AxiosInstance;
26
+ private readonly retryConfig: RetryConfig;
27
+
28
+ constructor(config: ApiConfig) {
29
+ // Initialize axios instance with configuration
30
+ this.axiosInstance = axios.create({
31
+ baseURL: config.baseURL,
32
+ timeout: config.timeout ?? 30000, // 30 seconds default
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ ...config.headers,
36
+ },
37
+ });
38
+
39
+ // Configure retry behavior
40
+ this.retryConfig = {
41
+ maxAttempts: config.retryAttempts ?? 3,
42
+ baseDelay: 1000, // 1 second
43
+ maxDelay: 10000, // 10 seconds
44
+ retryableStatusCodes: [408, 429, 500, 502, 503, 504],
45
+ };
46
+
47
+ // Setup request interceptor to inject auth token
48
+ this.setupRequestInterceptor();
49
+ }
50
+
51
+ /**
52
+ * Sets up request interceptor to automatically add auth token
53
+ */
54
+ private setupRequestInterceptor(): void {
55
+ this.axiosInstance.interceptors.request.use(
56
+ (config) => {
57
+ const token = useAuthStore.getState().token;
58
+
59
+ if (token && config.headers) {
60
+ config.headers.Authorization = `Bearer ${token}`;
61
+ }
62
+
63
+ return config;
64
+ },
65
+ (error) => Promise.reject(error),
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Maps axios error to appropriate custom error type
71
+ */
72
+ private mapError(error: AxiosError, url?: string, method?: string): Error {
73
+ // Network errors (no response received)
74
+ if (!error.response) {
75
+ if (error.code === 'ECONNABORTED' || error.message.includes('timeout')) {
76
+ return new TimeoutError(
77
+ 'Request timeout - server took too long to respond',
78
+ url,
79
+ method,
80
+ );
81
+ }
82
+ return new NetworkError(
83
+ error.message || 'Network error - please check your connection',
84
+ url,
85
+ method,
86
+ );
87
+ }
88
+
89
+ const statusCode = error.response.status;
90
+ const responseData = error.response.data as {
91
+ message?: string;
92
+ errors?: Record<string, string[]>;
93
+ };
94
+ const message =
95
+ responseData?.message || error.message || 'An error occurred';
96
+
97
+ // Map status codes to error types
98
+ switch (true) {
99
+ case statusCode === 401 || statusCode === 403:
100
+ return new AuthError(message, statusCode, url, method);
101
+
102
+ case statusCode === 404:
103
+ return new NotFoundError(message, url, method);
104
+
105
+ case statusCode === 429:
106
+ return new RateLimitError(
107
+ message,
108
+ url,
109
+ method,
110
+ Number(error.response.headers['retry-after']),
111
+ );
112
+
113
+ case statusCode === 400 || statusCode === 422:
114
+ return new ValidationError(
115
+ message,
116
+ statusCode,
117
+ url,
118
+ method,
119
+ responseData?.errors,
120
+ );
121
+
122
+ case statusCode >= 500:
123
+ return new ServerError(message, statusCode, url, method);
124
+
125
+ default:
126
+ return new NetworkError(message, url, method);
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Transforms axios response to standardized ApiResponse format
132
+ */
133
+ private transformResponse<T>(response: AxiosResponse<T>): T {
134
+ return response.data;
135
+ }
136
+
137
+ /**
138
+ * Executes HTTP request with retry logic and error handling
139
+ */
140
+ private async executeRequest<T>(
141
+ requestFn: () => Promise<AxiosResponse<T>>,
142
+ url?: string,
143
+ method?: string,
144
+ ): Promise<T> {
145
+ try {
146
+ const response = await executeWithRetry(
147
+ requestFn,
148
+ this.retryConfig,
149
+ (attempt, delay, error) => {
150
+ console.log(
151
+ `[API Retry] Attempt ${attempt} failed, retrying in ${delay}ms...`,
152
+ error.message,
153
+ );
154
+ },
155
+ );
156
+
157
+ return this.transformResponse(response);
158
+ } catch (error) {
159
+ throw this.mapError(error as AxiosError, url, method);
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Performs GET request
165
+ */
166
+ async get<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<T> {
167
+ return this.executeRequest(
168
+ () => this.axiosInstance.get<T>(url, config),
169
+ url,
170
+ 'GET',
171
+ );
172
+ }
173
+
174
+ /**
175
+ * Performs POST request
176
+ */
177
+ async post<T = unknown>(
178
+ url: string,
179
+ data?: unknown,
180
+ config?: AxiosRequestConfig,
181
+ ): Promise<T> {
182
+ return this.executeRequest(
183
+ () => this.axiosInstance.post<T>(url, data, config),
184
+ url,
185
+ 'POST',
186
+ );
187
+ }
188
+
189
+ /**
190
+ * Performs PUT request
191
+ */
192
+ async put<T = unknown>(
193
+ url: string,
194
+ data?: unknown,
195
+ config?: AxiosRequestConfig,
196
+ ): Promise<T> {
197
+ return this.executeRequest(
198
+ () => this.axiosInstance.put<T>(url, data, config),
199
+ url,
200
+ 'PUT',
201
+ );
202
+ }
203
+
204
+ /**
205
+ * Performs DELETE request
206
+ */
207
+ async delete<T = unknown>(
208
+ url: string,
209
+ config?: AxiosRequestConfig,
210
+ ): Promise<T> {
211
+ return this.executeRequest(
212
+ () => this.axiosInstance.delete<T>(url, config),
213
+ url,
214
+ 'DELETE',
215
+ );
216
+ }
217
+
218
+ /**
219
+ * Adds authentication token to store
220
+ * Token will be automatically included in all subsequent requests
221
+ */
222
+ addToken(token: string): void {
223
+ useAuthStore.getState().setToken(token);
224
+ }
225
+
226
+ /**
227
+ * Removes authentication token from store
228
+ */
229
+ removeToken(): void {
230
+ useAuthStore.getState().clearToken();
231
+ }
232
+
233
+ /**
234
+ * Get token from store
235
+ */
236
+ getToken(): string | null {
237
+ return useAuthStore.getState().token;
238
+ }
239
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Base class for all API errors
3
+ * Provides common structure and debugging information
4
+ */
5
+ export class ApiError extends Error {
6
+ public readonly statusCode: number;
7
+ public readonly url?: string;
8
+ public readonly method?: string;
9
+ public readonly timestamp: Date;
10
+
11
+ constructor(
12
+ message: string,
13
+ statusCode: number,
14
+ url?: string,
15
+ method?: string,
16
+ ) {
17
+ super(message);
18
+ this.name = this.constructor.name;
19
+ this.statusCode = statusCode;
20
+ this.url = url;
21
+ this.method = method;
22
+ this.timestamp = new Date();
23
+
24
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
25
+ if (Error.captureStackTrace) {
26
+ Error.captureStackTrace(this, this.constructor);
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Returns a formatted error message for debugging
32
+ */
33
+ toDebugString(): string {
34
+ return `[${this.name}] ${this.message}\nStatus: ${this.statusCode}\nURL: ${this.url}\nMethod: ${this.method}\nTime: ${this.timestamp.toISOString()}`;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Network-related errors (no internet, DNS failure, timeout)
40
+ * HTTP Status: N/A (network layer)
41
+ */
42
+ export class NetworkError extends ApiError {
43
+ constructor(message: string, url?: string, method?: string) {
44
+ super(message, 0, url, method);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Authentication errors (401, 403)
50
+ * Client lacks valid credentials or permissions
51
+ */
52
+ export class AuthError extends ApiError {}
53
+
54
+ /**
55
+ * Client validation errors (400, 422)
56
+ * Request malformed or validation failed
57
+ */
58
+ export class ValidationError extends ApiError {
59
+ public readonly validationErrors?: Record<string, string[]>;
60
+
61
+ constructor(
62
+ message: string,
63
+ statusCode: number,
64
+ url?: string,
65
+ method?: string,
66
+ validationErrors?: Record<string, string[]>,
67
+ ) {
68
+ super(message, statusCode, url, method);
69
+ this.validationErrors = validationErrors;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Server errors (500, 502, 503, 504)
75
+ * Something went wrong on the server
76
+ */
77
+ export class ServerError extends ApiError {}
78
+
79
+ /**
80
+ * Request timeout errors
81
+ * Request took too long to complete
82
+ */
83
+ export class TimeoutError extends ApiError {
84
+ constructor(message: string, url?: string, method?: string) {
85
+ super(message, 408, url, method);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Resource not found errors (404)
91
+ */
92
+ export class NotFoundError extends ApiError {
93
+ constructor(message: string, url?: string, method?: string) {
94
+ super(message, 404, url, method);
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Rate limit errors (429)
100
+ */
101
+ export class RateLimitError extends ApiError {
102
+ public readonly retryAfter?: number;
103
+
104
+ constructor(
105
+ message: string,
106
+ url?: string,
107
+ method?: string,
108
+ retryAfter?: number,
109
+ ) {
110
+ super(message, 429, url, method);
111
+ this.retryAfter = retryAfter;
112
+ }
113
+ }
@@ -0,0 +1,27 @@
1
+ import { AxiosAdapter } from './axios-adapter';
2
+
3
+ /**
4
+ * Default API request instance
5
+ * Pre-configured with default settings
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * import { apiRequest } from '@navegarti/rn-design-system/api';
10
+ *
11
+ * const response = await apiRequest.get<User>('/users/123');
12
+ * if (response.success) {
13
+ * console.log(response.data);
14
+ * }
15
+ * ```
16
+ */
17
+ export const apiRequest = new AxiosAdapter({
18
+ baseURL: 'https://api.example.com', // Will be overridden by consumers
19
+ timeout: 30000,
20
+ retryAttempts: 3,
21
+ });
22
+
23
+ export { AxiosAdapter } from './axios-adapter';
24
+ export * from './errors';
25
+ export { useAuthStore } from './stores/auth-store';
26
+ // Re-export types and classes for consumer use
27
+ export * from './types';
@@ -0,0 +1,86 @@
1
+ import type { AxiosError } from 'axios';
2
+ import type { RetryConfig } from './types';
3
+
4
+ /**
5
+ * Calculates exponential backoff delay
6
+ * Formula: min(baseDelay * (2 ^ attempt), maxDelay)
7
+ */
8
+ export function calculateBackoffDelay(
9
+ attempt: number,
10
+ baseDelay: number,
11
+ maxDelay: number,
12
+ ): number {
13
+ const exponentialDelay = baseDelay * 2 ** attempt;
14
+ return Math.min(exponentialDelay, maxDelay);
15
+ }
16
+
17
+ /**
18
+ * Determines if an error is retryable based on configuration
19
+ */
20
+ export function isRetryableError(
21
+ error: AxiosError,
22
+ retryConfig: RetryConfig,
23
+ ): boolean {
24
+ // Network errors (no response) are always retryable
25
+ if (!error.response) {
26
+ return true;
27
+ }
28
+
29
+ const statusCode = error.response.status;
30
+
31
+ // Check if status code is in retryable list
32
+ return retryConfig.retryableStatusCodes.includes(statusCode);
33
+ }
34
+
35
+ /**
36
+ * Sleep utility for retry delays
37
+ */
38
+ export function sleep(ms: number): Promise<void> {
39
+ return new Promise((resolve) => {
40
+ setTimeout(resolve, ms);
41
+ });
42
+ }
43
+
44
+ /**
45
+ * Executes a request with retry logic
46
+ * @template T - The expected response type
47
+ */
48
+ export async function executeWithRetry<T>(
49
+ requestFn: () => Promise<T>,
50
+ retryConfig: RetryConfig,
51
+ onRetry?: (attempt: number, delay: number, error: AxiosError) => void,
52
+ ): Promise<T> {
53
+ let lastError: AxiosError | undefined;
54
+
55
+ for (let attempt = 0; attempt < retryConfig.maxAttempts; attempt++) {
56
+ try {
57
+ return await requestFn();
58
+ } catch (error) {
59
+ const axiosError = error as AxiosError;
60
+ lastError = axiosError;
61
+
62
+ // Don't retry if not retryable or if this was the last attempt
63
+ const isLastAttempt = attempt === retryConfig.maxAttempts - 1;
64
+ if (!isRetryableError(axiosError, retryConfig) || isLastAttempt) {
65
+ throw error;
66
+ }
67
+
68
+ // Calculate delay and wait before retrying
69
+ const delay = calculateBackoffDelay(
70
+ attempt,
71
+ retryConfig.baseDelay,
72
+ retryConfig.maxDelay,
73
+ );
74
+
75
+ // Notify callback if provided
76
+ if (onRetry) {
77
+ onRetry(attempt + 1, delay, axiosError);
78
+ }
79
+
80
+ await sleep(delay);
81
+ }
82
+ }
83
+
84
+ // This should never be reached, but TypeScript needs it
85
+ throw lastError;
86
+ }
@@ -0,0 +1,26 @@
1
+ import { create } from 'zustand';
2
+
3
+ /**
4
+ * Authentication store state
5
+ */
6
+ interface AuthState {
7
+ token: string | null;
8
+ setToken: (token: string) => void;
9
+ clearToken: () => void;
10
+ }
11
+
12
+ /**
13
+ * Zustand store for managing authentication token
14
+ * Token is stored in memory and used for Authorization header
15
+ */
16
+ export const useAuthStore = create<AuthState>((set) => ({
17
+ token: null,
18
+
19
+ setToken: (token: string) => {
20
+ set({ token });
21
+ },
22
+
23
+ clearToken: () => {
24
+ set({ token: null });
25
+ },
26
+ }));
@@ -0,0 +1,74 @@
1
+ import type { AxiosRequestConfig } from 'axios';
2
+
3
+ /**
4
+ * Configuration options for API adapter
5
+ */
6
+ export interface ApiConfig {
7
+ baseURL: string;
8
+ timeout?: number;
9
+ retryAttempts?: number;
10
+ headers?: Record<string, string>;
11
+ }
12
+
13
+ /**
14
+ * Interface for HTTP client adapter
15
+ * Defines the contract all HTTP adapters must implement
16
+ */
17
+ export interface IHttpAdapter {
18
+ /**
19
+ * Performs a GET request
20
+ * @template T - Expected response data type
21
+ */
22
+ get<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<T>;
23
+
24
+ /**
25
+ * Performs a POST request
26
+ * @template T - Expected response data type
27
+ */
28
+ post<T = unknown>(
29
+ url: string,
30
+ data?: unknown,
31
+ config?: AxiosRequestConfig,
32
+ ): Promise<T>;
33
+
34
+ /**
35
+ * Performs a PUT request
36
+ * @template T - Expected response data type
37
+ */
38
+ put<T = unknown>(
39
+ url: string,
40
+ data?: unknown,
41
+ config?: AxiosRequestConfig,
42
+ ): Promise<T>;
43
+
44
+ /**
45
+ * Performs a DELETE request
46
+ * @template T - Expected response data type
47
+ */
48
+ delete<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<T>;
49
+
50
+ /**
51
+ * Adds authentication token to all subsequent requests
52
+ */
53
+ addToken(token: string): void;
54
+
55
+ /**
56
+ * Removes authentication token from requests
57
+ */
58
+ removeToken(): void;
59
+
60
+ /**
61
+ * Get token from store
62
+ */
63
+ getToken(): string | null;
64
+ }
65
+
66
+ /**
67
+ * Retry configuration
68
+ */
69
+ export interface RetryConfig {
70
+ maxAttempts: number;
71
+ baseDelay: number;
72
+ maxDelay: number;
73
+ retryableStatusCodes: number[];
74
+ }
package/src/api.tsx ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * API module entry point.
3
+ * Exports HTTP client, types, errors, and stores.
4
+ */
5
+
6
+ export {
7
+ ApiError,
8
+ AuthError,
9
+ NetworkError,
10
+ NotFoundError,
11
+ RateLimitError,
12
+ ServerError,
13
+ TimeoutError,
14
+ ValidationError,
15
+ } from './api/errors';
16
+ export { AxiosAdapter, apiRequest, useAuthStore } from './api/index';
17
+ export type { ApiConfig, IHttpAdapter } from './api/types';
@@ -0,0 +1,37 @@
1
+ import styled from '@emotion/native';
2
+
3
+ import { type ButtonProps, ButtonText, DefaultButtonStyle } from './styles';
4
+
5
+ export type { ButtonProps } from './styles';
6
+
7
+ const DefaultButton = (props: ButtonProps) => (
8
+ <DefaultButtonStyle activeOpacity={0.5} {...props} />
9
+ );
10
+
11
+ const SquareButton = styled(DefaultButton)(() => ({
12
+ borderRadius: 0,
13
+ }));
14
+
15
+ const OutlineButton = styled(DefaultButton)(() => ({
16
+ borderWidth: 1,
17
+ borderColor: '#333',
18
+ backgroundColor: 'transparent',
19
+ }));
20
+
21
+ const RoundedButton = styled(DefaultButton)(() => ({
22
+ borderRadius: 8,
23
+ }));
24
+
25
+ const PillButton = styled(DefaultButton)(() => ({
26
+ borderRadius: 999,
27
+ }));
28
+
29
+ const Button = Object.assign(DefaultButton, {
30
+ Square: SquareButton,
31
+ Outline: OutlineButton,
32
+ Rounded: RoundedButton,
33
+ Pill: PillButton,
34
+ Text: ButtonText,
35
+ });
36
+
37
+ export { Button };
@@ -0,0 +1,28 @@
1
+ import styled from '@emotion/native';
2
+ import type { DimensionValue, TouchableOpacityProps } from 'react-native';
3
+ import { Text } from '../Text';
4
+
5
+ export type ButtonProps = {
6
+ color?: string;
7
+ paddingVertical?: number;
8
+ paddingHorizontal?: number;
9
+ margin?: DimensionValue;
10
+ } & TouchableOpacityProps;
11
+
12
+ export const DefaultButtonStyle = styled.TouchableOpacity<ButtonProps>(
13
+ ({ paddingVertical, paddingHorizontal, color, margin }) => ({
14
+ flexDirection: 'row',
15
+ alignItems: 'center',
16
+ justifyContent: 'center',
17
+ paddingHorizontal: paddingHorizontal ?? 20,
18
+ paddingVertical: paddingVertical ?? 14,
19
+ gap: 8,
20
+ backgroundColor: color ?? '#333',
21
+ margin,
22
+ }),
23
+ );
24
+
25
+ export const ButtonText = styled(Text)(({ color, size }) => ({
26
+ color: color ?? '#fff',
27
+ fontSize: size ?? 16,
28
+ }));
@@ -0,0 +1,23 @@
1
+ import {
2
+ CardContainer,
3
+ CardContent,
4
+ CardDescription,
5
+ CardImage,
6
+ CardTitle,
7
+ } from './styles';
8
+ import type { CardProps } from './types';
9
+
10
+ export type { CardProps } from './types';
11
+
12
+ const CardRoot = (props: CardProps) => (
13
+ <CardContainer activeOpacity={0.7} {...props} />
14
+ );
15
+
16
+ const Card = Object.assign(CardRoot, {
17
+ Image: CardImage,
18
+ Content: CardContent,
19
+ Title: CardTitle,
20
+ Description: CardDescription,
21
+ });
22
+
23
+ export { Card };