@umituz/react-native-design-system 1.15.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +26 -19
- package/src/atoms/AtomicAvatar.tsx +161 -0
- package/src/atoms/AtomicButton.tsx +241 -0
- package/src/atoms/AtomicChip.tsx +226 -0
- package/src/atoms/AtomicDatePicker.tsx +255 -0
- package/src/atoms/AtomicFab.tsx +99 -0
- package/src/atoms/AtomicIcon.tsx +149 -0
- package/src/atoms/AtomicInput.tsx +308 -0
- package/src/atoms/AtomicPicker.tsx +310 -0
- package/src/atoms/AtomicProgress.tsx +149 -0
- package/src/atoms/AtomicText.tsx +55 -0
- package/src/atoms/__tests__/AtomicButton.test.tsx +107 -0
- package/src/atoms/__tests__/AtomicIcon.test.tsx +110 -0
- package/src/atoms/__tests__/AtomicInput.test.tsx +195 -0
- package/src/atoms/datepicker/components/DatePickerButton.tsx +112 -0
- package/src/atoms/datepicker/components/DatePickerModal.tsx +143 -0
- package/src/atoms/fab/styles/fabStyles.ts +98 -0
- package/src/atoms/fab/types/index.ts +88 -0
- package/src/atoms/index.ts +70 -0
- package/src/atoms/input/hooks/useInputState.ts +63 -0
- package/src/atoms/input/styles/inputStylesHelper.ts +120 -0
- package/src/atoms/picker/components/PickerChips.tsx +57 -0
- package/src/atoms/picker/components/PickerModal.tsx +214 -0
- package/src/atoms/picker/styles/pickerStyles.ts +223 -0
- package/src/atoms/picker/types/index.ts +42 -0
- package/src/index.ts +133 -56
- package/src/molecules/ConfirmationModal.tsx +42 -0
- package/src/molecules/ConfirmationModalContent.tsx +87 -0
- package/src/molecules/ConfirmationModalMain.tsx +91 -0
- package/src/molecules/FormField.tsx +155 -0
- package/src/molecules/IconContainer.tsx +79 -0
- package/src/molecules/ListItem.tsx +35 -0
- package/src/molecules/ScreenHeader.tsx +171 -0
- package/src/molecules/SearchBar.tsx +198 -0
- package/src/molecules/confirmation-modal/components.tsx +94 -0
- package/src/molecules/confirmation-modal/index.ts +7 -0
- package/src/molecules/confirmation-modal/styles/confirmationModalStyles.ts +133 -0
- package/src/molecules/confirmation-modal/types/index.ts +41 -0
- package/src/molecules/confirmation-modal/useConfirmationModal.ts +50 -0
- package/src/molecules/index.ts +19 -0
- package/src/molecules/listitem/index.ts +6 -0
- package/src/molecules/listitem/styles/listItemStyles.ts +37 -0
- package/src/molecules/listitem/types/index.ts +21 -0
- package/src/organisms/AppHeader.tsx +136 -0
- package/src/organisms/FormContainer.tsx +169 -0
- package/src/organisms/ScreenLayout.tsx +183 -0
- package/src/organisms/index.ts +31 -0
- package/src/responsive/config.ts +139 -0
- package/src/responsive/deviceDetection.ts +155 -0
- package/src/responsive/gridUtils.ts +79 -0
- package/src/responsive/index.ts +52 -0
- package/src/responsive/platformConstants.ts +98 -0
- package/src/responsive/responsive.ts +61 -0
- package/src/responsive/responsiveLayout.ts +137 -0
- package/src/responsive/responsiveSizing.ts +134 -0
- package/src/responsive/useResponsive.ts +140 -0
- package/src/responsive/validation.ts +158 -0
- package/src/theme/core/BaseTokens.ts +42 -0
- package/src/theme/core/ColorPalette.ts +29 -0
- package/src/theme/core/CustomColors.ts +122 -0
- package/src/theme/core/NavigationTheme.ts +72 -0
- package/src/theme/core/TokenFactory.ts +103 -0
- package/src/theme/core/colors/ColorUtils.ts +53 -0
- package/src/theme/core/colors/DarkColors.ts +146 -0
- package/src/theme/core/colors/LightColors.ts +146 -0
- package/src/theme/core/constants/DesignConstants.ts +31 -0
- package/src/theme/core/themes.ts +118 -0
- package/src/theme/core/tokens/BaseTokens.ts +144 -0
- package/src/theme/core/tokens/Borders.ts +43 -0
- package/src/theme/core/tokens/Sizes.ts +51 -0
- package/src/theme/core/tokens/Spacing.ts +38 -0
- package/src/theme/core/tokens/Typography.ts +143 -0
- package/src/theme/hooks/useAppDesignTokens.ts +45 -0
- package/src/theme/hooks/useCommonStyles.ts +248 -0
- package/src/theme/hooks/useThemedStyles.ts +68 -0
- package/src/theme/index.ts +94 -0
- package/src/theme/infrastructure/globalThemeStore.ts +69 -0
- package/src/theme/infrastructure/storage/ThemeStorage.ts +93 -0
- package/src/theme/infrastructure/stores/themeStore.ts +109 -0
- package/src/typography/__tests__/colorValidationUtils.test.ts +180 -0
- package/src/typography/__tests__/textColorUtils.test.ts +185 -0
- package/src/typography/__tests__/textStyleUtils.test.ts +168 -0
- package/src/typography/domain/entities/TypographyTypes.ts +88 -0
- package/src/typography/index.ts +53 -0
- package/src/typography/presentation/utils/colorValidationUtils.ts +133 -0
- package/src/typography/presentation/utils/textColorUtils.ts +205 -0
- package/src/typography/presentation/utils/textStyleUtils.ts +159 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AtomicIcon - Theme-aware Icon Component
|
|
3
|
+
*
|
|
4
|
+
* Uses @umituz/react-native-icons internally
|
|
5
|
+
* Adds theme-aware semantic colors and background support
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { View, StyleSheet, StyleProp, ViewStyle } from "react-native";
|
|
10
|
+
import { Icon, type IconSize as BaseIconSize, ICON_SIZES } from "@umituz/react-native-icons";
|
|
11
|
+
import { useAppDesignTokens } from '../theme';
|
|
12
|
+
|
|
13
|
+
// Re-export IconSize from icons package for convenience
|
|
14
|
+
export type IconSize = BaseIconSize;
|
|
15
|
+
|
|
16
|
+
// Semantic color names that map to theme tokens
|
|
17
|
+
export type IconColor =
|
|
18
|
+
| "primary"
|
|
19
|
+
| "secondary"
|
|
20
|
+
| "success"
|
|
21
|
+
| "warning"
|
|
22
|
+
| "error"
|
|
23
|
+
| "info"
|
|
24
|
+
| "onSurface"
|
|
25
|
+
| "surfaceVariant"
|
|
26
|
+
| "onPrimary"
|
|
27
|
+
| "onSecondary"
|
|
28
|
+
| "textInverse";
|
|
29
|
+
|
|
30
|
+
// Accept any string for flexibility
|
|
31
|
+
export type IconName = string;
|
|
32
|
+
|
|
33
|
+
export interface AtomicIconProps {
|
|
34
|
+
/** Icon name (Ionicons) */
|
|
35
|
+
name: IconName;
|
|
36
|
+
/** Semantic size preset */
|
|
37
|
+
size?: IconSize;
|
|
38
|
+
/** Custom size in pixels (overrides size) */
|
|
39
|
+
customSize?: number;
|
|
40
|
+
/** Semantic color from theme */
|
|
41
|
+
color?: IconColor;
|
|
42
|
+
/** Custom color (overrides color) */
|
|
43
|
+
customColor?: string;
|
|
44
|
+
/** Add circular background */
|
|
45
|
+
withBackground?: boolean;
|
|
46
|
+
/** Background color */
|
|
47
|
+
backgroundColor?: string;
|
|
48
|
+
/** Accessibility label */
|
|
49
|
+
accessibilityLabel?: string;
|
|
50
|
+
/** Test ID */
|
|
51
|
+
testID?: string;
|
|
52
|
+
/** Additional styles */
|
|
53
|
+
style?: StyleProp<ViewStyle>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const getSemanticColor = (
|
|
57
|
+
color: IconColor,
|
|
58
|
+
tokens: ReturnType<typeof useAppDesignTokens>
|
|
59
|
+
): string => {
|
|
60
|
+
const colorMap: Record<IconColor, string> = {
|
|
61
|
+
primary: tokens.colors.primary,
|
|
62
|
+
secondary: tokens.colors.secondary,
|
|
63
|
+
success: tokens.colors.success,
|
|
64
|
+
warning: tokens.colors.warning,
|
|
65
|
+
error: tokens.colors.error,
|
|
66
|
+
info: tokens.colors.info,
|
|
67
|
+
onSurface: tokens.colors.onSurface,
|
|
68
|
+
surfaceVariant: tokens.colors.surfaceVariant,
|
|
69
|
+
onPrimary: tokens.colors.onPrimary,
|
|
70
|
+
onSecondary: tokens.colors.onSecondary,
|
|
71
|
+
textInverse: tokens.colors.textInverse,
|
|
72
|
+
};
|
|
73
|
+
return colorMap[color];
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Theme-aware icon component
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* <AtomicIcon name="heart-outline" size="md" color="primary" />
|
|
81
|
+
* <AtomicIcon name="star" customSize={32} customColor="#FFD700" />
|
|
82
|
+
* <AtomicIcon name="settings" size="lg" withBackground />
|
|
83
|
+
*/
|
|
84
|
+
export const AtomicIcon: React.FC<AtomicIconProps> = React.memo(({
|
|
85
|
+
name,
|
|
86
|
+
size = "md",
|
|
87
|
+
customSize,
|
|
88
|
+
color,
|
|
89
|
+
customColor,
|
|
90
|
+
withBackground = false,
|
|
91
|
+
backgroundColor,
|
|
92
|
+
accessibilityLabel,
|
|
93
|
+
testID,
|
|
94
|
+
style,
|
|
95
|
+
}) => {
|
|
96
|
+
const tokens = useAppDesignTokens();
|
|
97
|
+
|
|
98
|
+
// Calculate size
|
|
99
|
+
const iconSize = customSize ?? (typeof size === "number" ? size : ICON_SIZES[size]);
|
|
100
|
+
|
|
101
|
+
// Calculate color
|
|
102
|
+
const iconColor = customColor
|
|
103
|
+
? customColor
|
|
104
|
+
: color
|
|
105
|
+
? getSemanticColor(color, tokens)
|
|
106
|
+
: tokens.colors.textPrimary;
|
|
107
|
+
|
|
108
|
+
if (withBackground) {
|
|
109
|
+
const bgColor = backgroundColor || tokens.colors.surfaceVariant;
|
|
110
|
+
const containerSize = iconSize + 16;
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<View
|
|
114
|
+
style={[
|
|
115
|
+
styles.backgroundContainer,
|
|
116
|
+
{
|
|
117
|
+
width: containerSize,
|
|
118
|
+
height: containerSize,
|
|
119
|
+
borderRadius: containerSize / 2,
|
|
120
|
+
backgroundColor: bgColor,
|
|
121
|
+
},
|
|
122
|
+
style,
|
|
123
|
+
]}
|
|
124
|
+
testID={testID}
|
|
125
|
+
accessibilityLabel={accessibilityLabel}
|
|
126
|
+
>
|
|
127
|
+
<Icon name={name} size={iconSize} color={iconColor} />
|
|
128
|
+
</View>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<View accessibilityLabel={accessibilityLabel} testID={testID} style={style}>
|
|
134
|
+
<Icon name={name} size={iconSize} color={iconColor} />
|
|
135
|
+
</View>
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
AtomicIcon.displayName = "AtomicIcon";
|
|
140
|
+
|
|
141
|
+
const styles = StyleSheet.create({
|
|
142
|
+
backgroundContainer: {
|
|
143
|
+
justifyContent: "center",
|
|
144
|
+
alignItems: "center",
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Legacy type alias for backward compatibility
|
|
149
|
+
export type IconProps = AtomicIconProps;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { View, TextInput, Pressable, StyleSheet, StyleProp, ViewStyle, TextStyle } from 'react-native';
|
|
3
|
+
import { useAppDesignTokens } from '../theme';
|
|
4
|
+
import { AtomicIcon } from './AtomicIcon';
|
|
5
|
+
import { AtomicText } from './AtomicText';
|
|
6
|
+
import { useInputState } from './input/hooks/useInputState';
|
|
7
|
+
import { getSizeConfig, getVariantStyle, getTextColor } from './input/styles/inputStylesHelper';
|
|
8
|
+
import type { IconName } from './AtomicIcon';
|
|
9
|
+
|
|
10
|
+
export type AtomicInputVariant = 'outlined' | 'filled' | 'flat';
|
|
11
|
+
export type AtomicInputState = 'default' | 'error' | 'success' | 'disabled';
|
|
12
|
+
export type AtomicInputSize = 'sm' | 'md' | 'lg';
|
|
13
|
+
|
|
14
|
+
export interface AtomicInputProps {
|
|
15
|
+
/** Input label */
|
|
16
|
+
label?: string;
|
|
17
|
+
/** Current input value */
|
|
18
|
+
value?: string;
|
|
19
|
+
/** Value change callback */
|
|
20
|
+
onChangeText?: (text: string) => void;
|
|
21
|
+
/** Input variant (outlined, filled, flat) */
|
|
22
|
+
variant?: AtomicInputVariant;
|
|
23
|
+
/** Input state (default, error, success, disabled) */
|
|
24
|
+
state?: AtomicInputState;
|
|
25
|
+
/** Input size (sm, md, lg) */
|
|
26
|
+
size?: AtomicInputSize;
|
|
27
|
+
/** Placeholder text */
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
/** Helper text below input */
|
|
30
|
+
helperText?: string;
|
|
31
|
+
/** Leading icon (Lucide icon name) */
|
|
32
|
+
leadingIcon?: IconName;
|
|
33
|
+
/** Trailing icon (Lucide icon name) */
|
|
34
|
+
trailingIcon?: IconName;
|
|
35
|
+
/** Callback when trailing icon is pressed */
|
|
36
|
+
onTrailingIconPress?: () => void;
|
|
37
|
+
/** Show password toggle for secure inputs */
|
|
38
|
+
showPasswordToggle?: boolean;
|
|
39
|
+
/** Secure text entry (password field) */
|
|
40
|
+
secureTextEntry?: boolean;
|
|
41
|
+
/** Maximum character length */
|
|
42
|
+
maxLength?: number;
|
|
43
|
+
/** Show character counter */
|
|
44
|
+
showCharacterCount?: boolean;
|
|
45
|
+
/** Keyboard type */
|
|
46
|
+
keyboardType?: 'default' | 'email-address' | 'numeric' | 'phone-pad' | 'url' | 'number-pad' | 'decimal-pad';
|
|
47
|
+
/** Auto-capitalize */
|
|
48
|
+
autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
|
|
49
|
+
/** Auto-correct */
|
|
50
|
+
autoCorrect?: boolean;
|
|
51
|
+
/** Disabled state */
|
|
52
|
+
disabled?: boolean;
|
|
53
|
+
/** Container style */
|
|
54
|
+
style?: StyleProp<ViewStyle>;
|
|
55
|
+
/** Input text style */
|
|
56
|
+
inputStyle?: StyleProp<TextStyle>;
|
|
57
|
+
/** Test ID for E2E testing */
|
|
58
|
+
testID?: string;
|
|
59
|
+
/** Blur callback */
|
|
60
|
+
onBlur?: () => void;
|
|
61
|
+
/** Focus callback */
|
|
62
|
+
onFocus?: () => void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* AtomicInput - Pure React Native Text Input
|
|
67
|
+
*
|
|
68
|
+
* Features:
|
|
69
|
+
* - Pure React Native implementation (no Paper dependency)
|
|
70
|
+
* - Lucide icons for password toggle and custom icons
|
|
71
|
+
* - Outlined/filled/flat variants
|
|
72
|
+
* - Error, success, disabled states
|
|
73
|
+
* - Character counter
|
|
74
|
+
* - Responsive sizing
|
|
75
|
+
* - Full accessibility support
|
|
76
|
+
*/
|
|
77
|
+
export const AtomicInput: React.FC<AtomicInputProps> = ({
|
|
78
|
+
variant = 'outlined',
|
|
79
|
+
state = 'default',
|
|
80
|
+
size = 'md',
|
|
81
|
+
label,
|
|
82
|
+
value = '',
|
|
83
|
+
onChangeText,
|
|
84
|
+
placeholder,
|
|
85
|
+
helperText,
|
|
86
|
+
leadingIcon,
|
|
87
|
+
trailingIcon,
|
|
88
|
+
onTrailingIconPress,
|
|
89
|
+
showPasswordToggle = false,
|
|
90
|
+
secureTextEntry = false,
|
|
91
|
+
maxLength,
|
|
92
|
+
showCharacterCount = false,
|
|
93
|
+
keyboardType = 'default',
|
|
94
|
+
autoCapitalize = 'sentences',
|
|
95
|
+
autoCorrect = true,
|
|
96
|
+
disabled = false,
|
|
97
|
+
style,
|
|
98
|
+
inputStyle,
|
|
99
|
+
testID,
|
|
100
|
+
onBlur,
|
|
101
|
+
onFocus,
|
|
102
|
+
}) => {
|
|
103
|
+
const tokens = useAppDesignTokens();
|
|
104
|
+
|
|
105
|
+
const {
|
|
106
|
+
localValue,
|
|
107
|
+
isFocused,
|
|
108
|
+
isPasswordVisible,
|
|
109
|
+
characterCount,
|
|
110
|
+
isAtMaxLength,
|
|
111
|
+
setIsFocused,
|
|
112
|
+
handleTextChange,
|
|
113
|
+
togglePasswordVisibility,
|
|
114
|
+
} = useInputState({
|
|
115
|
+
value,
|
|
116
|
+
onChangeText,
|
|
117
|
+
secureTextEntry,
|
|
118
|
+
showPasswordToggle,
|
|
119
|
+
maxLength,
|
|
120
|
+
showCharacterCount,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const isDisabled = state === 'disabled' || disabled;
|
|
124
|
+
const hasError = state === 'error';
|
|
125
|
+
const hasSuccess = state === 'success';
|
|
126
|
+
|
|
127
|
+
const sizeConfig = getSizeConfig({ size, tokens });
|
|
128
|
+
const variantStyle = getVariantStyle({
|
|
129
|
+
variant,
|
|
130
|
+
isFocused,
|
|
131
|
+
hasError,
|
|
132
|
+
hasSuccess,
|
|
133
|
+
isDisabled,
|
|
134
|
+
tokens,
|
|
135
|
+
});
|
|
136
|
+
const textColor = getTextColor({
|
|
137
|
+
isDisabled,
|
|
138
|
+
hasError,
|
|
139
|
+
hasSuccess,
|
|
140
|
+
tokens,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const iconColor = isDisabled ? tokens.colors.textDisabled : tokens.colors.textSecondary;
|
|
144
|
+
|
|
145
|
+
const containerStyle: StyleProp<ViewStyle> = [
|
|
146
|
+
styles.container,
|
|
147
|
+
variantStyle,
|
|
148
|
+
{
|
|
149
|
+
paddingTop: sizeConfig.paddingVertical,
|
|
150
|
+
paddingBottom: sizeConfig.paddingVertical,
|
|
151
|
+
paddingHorizontal: sizeConfig.paddingHorizontal,
|
|
152
|
+
minHeight: sizeConfig.minHeight,
|
|
153
|
+
justifyContent: 'center',
|
|
154
|
+
opacity: isDisabled ? 0.5 : 1,
|
|
155
|
+
},
|
|
156
|
+
style,
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
const textInputStyle: StyleProp<TextStyle> = [
|
|
160
|
+
styles.input,
|
|
161
|
+
{
|
|
162
|
+
fontSize: sizeConfig.fontSize,
|
|
163
|
+
lineHeight: (sizeConfig.fontSize || 16) * 1.2,
|
|
164
|
+
color: textColor,
|
|
165
|
+
paddingVertical: 0,
|
|
166
|
+
},
|
|
167
|
+
leadingIcon ? { paddingLeft: sizeConfig.iconSize + 8 } : undefined,
|
|
168
|
+
(trailingIcon || showPasswordToggle) ? { paddingRight: sizeConfig.iconSize + 8 } : undefined,
|
|
169
|
+
inputStyle,
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<View testID={testID}>
|
|
174
|
+
{label && (
|
|
175
|
+
<AtomicText
|
|
176
|
+
type="labelMedium"
|
|
177
|
+
color={hasError ? 'error' : hasSuccess ? 'success' : 'secondary'}
|
|
178
|
+
style={styles.label}
|
|
179
|
+
>
|
|
180
|
+
{label}
|
|
181
|
+
</AtomicText>
|
|
182
|
+
)}
|
|
183
|
+
|
|
184
|
+
<View style={containerStyle}>
|
|
185
|
+
{leadingIcon && (
|
|
186
|
+
<View style={styles.leadingIcon}>
|
|
187
|
+
<AtomicIcon
|
|
188
|
+
name={leadingIcon}
|
|
189
|
+
customSize={sizeConfig.iconSize}
|
|
190
|
+
customColor={iconColor}
|
|
191
|
+
/>
|
|
192
|
+
</View>
|
|
193
|
+
)}
|
|
194
|
+
|
|
195
|
+
<TextInput
|
|
196
|
+
value={localValue}
|
|
197
|
+
onChangeText={handleTextChange}
|
|
198
|
+
placeholder={placeholder}
|
|
199
|
+
placeholderTextColor={tokens.colors.textSecondary}
|
|
200
|
+
secureTextEntry={secureTextEntry && !isPasswordVisible}
|
|
201
|
+
maxLength={maxLength}
|
|
202
|
+
keyboardType={keyboardType}
|
|
203
|
+
autoCapitalize={autoCapitalize}
|
|
204
|
+
autoCorrect={autoCorrect}
|
|
205
|
+
editable={!isDisabled}
|
|
206
|
+
style={textInputStyle}
|
|
207
|
+
onBlur={() => {
|
|
208
|
+
setIsFocused(false);
|
|
209
|
+
onBlur?.();
|
|
210
|
+
}}
|
|
211
|
+
onFocus={() => {
|
|
212
|
+
setIsFocused(true);
|
|
213
|
+
onFocus?.();
|
|
214
|
+
}}
|
|
215
|
+
testID={testID ? `${testID}-input` : undefined}
|
|
216
|
+
/>
|
|
217
|
+
|
|
218
|
+
{(showPasswordToggle && secureTextEntry) && (
|
|
219
|
+
<Pressable
|
|
220
|
+
onPress={() => togglePasswordVisibility()}
|
|
221
|
+
style={styles.trailingIcon}
|
|
222
|
+
>
|
|
223
|
+
<AtomicIcon
|
|
224
|
+
name={isPasswordVisible ? "EyeOff" : "Eye"}
|
|
225
|
+
customSize={sizeConfig.iconSize}
|
|
226
|
+
customColor={iconColor}
|
|
227
|
+
/>
|
|
228
|
+
</Pressable>
|
|
229
|
+
)}
|
|
230
|
+
|
|
231
|
+
{trailingIcon && !showPasswordToggle && (
|
|
232
|
+
<Pressable
|
|
233
|
+
onPress={onTrailingIconPress}
|
|
234
|
+
style={styles.trailingIcon}
|
|
235
|
+
disabled={!onTrailingIconPress}
|
|
236
|
+
>
|
|
237
|
+
<AtomicIcon
|
|
238
|
+
name={trailingIcon}
|
|
239
|
+
customSize={sizeConfig.iconSize}
|
|
240
|
+
customColor={iconColor}
|
|
241
|
+
/>
|
|
242
|
+
</Pressable>
|
|
243
|
+
)}
|
|
244
|
+
</View>
|
|
245
|
+
|
|
246
|
+
{(helperText || showCharacterCount) && (
|
|
247
|
+
<View style={styles.helperRow}>
|
|
248
|
+
{helperText && (
|
|
249
|
+
<AtomicText
|
|
250
|
+
style={styles.helperText}
|
|
251
|
+
color={hasError ? 'error' : 'secondary'}
|
|
252
|
+
testID={testID ? `${testID}-helper` : undefined}
|
|
253
|
+
>
|
|
254
|
+
{helperText}
|
|
255
|
+
</AtomicText>
|
|
256
|
+
)}
|
|
257
|
+
{showCharacterCount && maxLength && (
|
|
258
|
+
<AtomicText
|
|
259
|
+
style={[styles.helperText, styles.characterCount]}
|
|
260
|
+
color="secondary"
|
|
261
|
+
testID={testID ? `${testID}-count` : undefined}
|
|
262
|
+
>
|
|
263
|
+
{characterCount}/{maxLength}
|
|
264
|
+
</AtomicText>
|
|
265
|
+
)}
|
|
266
|
+
</View>
|
|
267
|
+
)}
|
|
268
|
+
</View>
|
|
269
|
+
);
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const styles = StyleSheet.create({
|
|
273
|
+
container: {
|
|
274
|
+
flexDirection: 'row',
|
|
275
|
+
alignItems: 'center',
|
|
276
|
+
},
|
|
277
|
+
input: {
|
|
278
|
+
flex: 1,
|
|
279
|
+
margin: 0,
|
|
280
|
+
padding: 0,
|
|
281
|
+
},
|
|
282
|
+
label: {
|
|
283
|
+
marginBottom: 4,
|
|
284
|
+
},
|
|
285
|
+
leadingIcon: {
|
|
286
|
+
position: 'absolute',
|
|
287
|
+
left: 12,
|
|
288
|
+
zIndex: 1,
|
|
289
|
+
},
|
|
290
|
+
trailingIcon: {
|
|
291
|
+
position: 'absolute',
|
|
292
|
+
right: 12,
|
|
293
|
+
zIndex: 1,
|
|
294
|
+
},
|
|
295
|
+
helperRow: {
|
|
296
|
+
flexDirection: 'row',
|
|
297
|
+
justifyContent: 'space-between',
|
|
298
|
+
marginTop: 4,
|
|
299
|
+
},
|
|
300
|
+
helperText: {
|
|
301
|
+
flex: 1,
|
|
302
|
+
},
|
|
303
|
+
characterCount: {
|
|
304
|
+
marginLeft: 8,
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
export type { AtomicInputProps as InputProps };
|