@oxyhq/services 5.7.5 → 5.8.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.
Files changed (239) hide show
  1. package/README.md +76 -76
  2. package/lib/commonjs/core/index.js +177 -102
  3. package/lib/commonjs/core/index.js.map +1 -1
  4. package/lib/commonjs/index.js +88 -29
  5. package/lib/commonjs/index.js.map +1 -1
  6. package/lib/commonjs/node/createAuth.js +585 -7
  7. package/lib/commonjs/node/createAuth.js.map +1 -1
  8. package/lib/commonjs/node/index.js +38 -1
  9. package/lib/commonjs/node/index.js.map +1 -1
  10. package/lib/commonjs/ui/components/Avatar.js +15 -6
  11. package/lib/commonjs/ui/components/Avatar.js.map +1 -1
  12. package/lib/commonjs/ui/components/GroupedItem.js +58 -13
  13. package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
  14. package/lib/commonjs/ui/components/GroupedSection.js +7 -1
  15. package/lib/commonjs/ui/components/GroupedSection.js.map +1 -1
  16. package/lib/commonjs/ui/components/Header.js +322 -0
  17. package/lib/commonjs/ui/components/Header.js.map +1 -0
  18. package/lib/commonjs/ui/components/OxyProvider.js +23 -7
  19. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  20. package/lib/commonjs/ui/components/index.js +7 -0
  21. package/lib/commonjs/ui/components/index.js.map +1 -1
  22. package/lib/commonjs/ui/components/internal/GroupedPillButtons.js +1 -1
  23. package/lib/commonjs/ui/components/internal/GroupedPillButtons.js.map +1 -1
  24. package/lib/commonjs/ui/components/internal/TextField.js +606 -546
  25. package/lib/commonjs/ui/components/internal/TextField.js.map +1 -1
  26. package/lib/commonjs/ui/components/internal/TextField.md +436 -0
  27. package/lib/commonjs/ui/context/OxyContext.js +122 -78
  28. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  29. package/lib/commonjs/ui/hooks/useSessionSocket.js +5 -2
  30. package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
  31. package/lib/commonjs/ui/navigation/OxyRouter.js +1 -1
  32. package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -1
  33. package/lib/commonjs/ui/screens/AccountCenterScreen.js +6 -6
  34. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
  35. package/lib/commonjs/ui/screens/AccountManagementDemo.js +3 -3
  36. package/lib/commonjs/ui/screens/AccountManagementDemo.js.map +1 -1
  37. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +241 -598
  38. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  39. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +1151 -406
  40. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  41. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +135 -237
  42. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  43. package/lib/commonjs/ui/screens/AppInfoScreen.js +246 -463
  44. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  45. package/lib/commonjs/ui/screens/FeedbackScreen.js +3 -3
  46. package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
  47. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +808 -650
  48. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
  49. package/lib/commonjs/ui/screens/RecoverAccountScreen.js +51 -72
  50. package/lib/commonjs/ui/screens/RecoverAccountScreen.js.map +1 -1
  51. package/lib/commonjs/ui/screens/SessionManagementScreen.js +11 -29
  52. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  53. package/lib/commonjs/ui/screens/SignInScreen.js +30 -303
  54. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  55. package/lib/commonjs/ui/screens/SignUpScreen.js +4 -4
  56. package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
  57. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +19 -31
  58. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +1 -1
  59. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +7 -10
  60. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +1 -1
  61. package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js +11 -5
  62. package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js.map +1 -1
  63. package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js +11 -4
  64. package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js.map +1 -1
  65. package/lib/commonjs/ui/stores/authStore.js +12 -0
  66. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  67. package/lib/commonjs/ui/styles/authStyles.js +337 -0
  68. package/lib/commonjs/ui/styles/authStyles.js.map +1 -0
  69. package/lib/commonjs/ui/styles/index.js +11 -0
  70. package/lib/commonjs/ui/styles/index.js.map +1 -1
  71. package/lib/module/core/index.js +177 -41
  72. package/lib/module/core/index.js.map +1 -1
  73. package/lib/module/index.js +26 -4
  74. package/lib/module/index.js.map +1 -1
  75. package/lib/module/node/createAuth.js +584 -7
  76. package/lib/module/node/createAuth.js.map +1 -1
  77. package/lib/module/node/index.js +7 -1
  78. package/lib/module/node/index.js.map +1 -1
  79. package/lib/module/ui/components/Avatar.js +15 -6
  80. package/lib/module/ui/components/Avatar.js.map +1 -1
  81. package/lib/module/ui/components/GroupedItem.js +59 -14
  82. package/lib/module/ui/components/GroupedItem.js.map +1 -1
  83. package/lib/module/ui/components/GroupedSection.js +7 -1
  84. package/lib/module/ui/components/GroupedSection.js.map +1 -1
  85. package/lib/module/ui/components/Header.js +317 -0
  86. package/lib/module/ui/components/Header.js.map +1 -0
  87. package/lib/module/ui/components/OxyProvider.js +25 -9
  88. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  89. package/lib/module/ui/components/index.js +1 -0
  90. package/lib/module/ui/components/index.js.map +1 -1
  91. package/lib/module/ui/components/internal/GroupedPillButtons.js +1 -1
  92. package/lib/module/ui/components/internal/GroupedPillButtons.js.map +1 -1
  93. package/lib/module/ui/components/internal/TextField.js +607 -547
  94. package/lib/module/ui/components/internal/TextField.js.map +1 -1
  95. package/lib/module/ui/components/internal/TextField.md +436 -0
  96. package/lib/module/ui/context/OxyContext.js +121 -77
  97. package/lib/module/ui/context/OxyContext.js.map +1 -1
  98. package/lib/module/ui/hooks/useSessionSocket.js +5 -2
  99. package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
  100. package/lib/module/ui/navigation/OxyRouter.js +1 -1
  101. package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
  102. package/lib/module/ui/screens/AccountCenterScreen.js +6 -6
  103. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
  104. package/lib/module/ui/screens/AccountManagementDemo.js +3 -3
  105. package/lib/module/ui/screens/AccountManagementDemo.js.map +1 -1
  106. package/lib/module/ui/screens/AccountOverviewScreen.js +242 -597
  107. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  108. package/lib/module/ui/screens/AccountSettingsScreen.js +1152 -407
  109. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  110. package/lib/module/ui/screens/AccountSwitcherScreen.js +135 -237
  111. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  112. package/lib/module/ui/screens/AppInfoScreen.js +248 -465
  113. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  114. package/lib/module/ui/screens/FeedbackScreen.js +3 -3
  115. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  116. package/lib/module/ui/screens/PaymentGatewayScreen.js +809 -651
  117. package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
  118. package/lib/module/ui/screens/RecoverAccountScreen.js +53 -74
  119. package/lib/module/ui/screens/RecoverAccountScreen.js.map +1 -1
  120. package/lib/module/ui/screens/SessionManagementScreen.js +11 -29
  121. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  122. package/lib/module/ui/screens/SignInScreen.js +32 -305
  123. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  124. package/lib/module/ui/screens/SignUpScreen.js +5 -5
  125. package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
  126. package/lib/module/ui/screens/internal/SignInPasswordStep.js +19 -31
  127. package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +1 -1
  128. package/lib/module/ui/screens/internal/SignInUsernameStep.js +7 -10
  129. package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +1 -1
  130. package/lib/module/ui/screens/internal/SignUpIdentityStep.js +11 -5
  131. package/lib/module/ui/screens/internal/SignUpIdentityStep.js.map +1 -1
  132. package/lib/module/ui/screens/internal/SignUpSecurityStep.js +11 -4
  133. package/lib/module/ui/screens/internal/SignUpSecurityStep.js.map +1 -1
  134. package/lib/module/ui/stores/authStore.js +12 -0
  135. package/lib/module/ui/stores/authStore.js.map +1 -1
  136. package/lib/module/ui/styles/authStyles.js +332 -0
  137. package/lib/module/ui/styles/authStyles.js.map +1 -0
  138. package/lib/module/ui/styles/index.js +1 -0
  139. package/lib/module/ui/styles/index.js.map +1 -1
  140. package/lib/typescript/core/index.d.ts +68 -24
  141. package/lib/typescript/core/index.d.ts.map +1 -1
  142. package/lib/typescript/index.d.ts +13 -3
  143. package/lib/typescript/index.d.ts.map +1 -1
  144. package/lib/typescript/node/createAuth.d.ts +112 -0
  145. package/lib/typescript/node/createAuth.d.ts.map +1 -1
  146. package/lib/typescript/node/index.d.ts +2 -0
  147. package/lib/typescript/node/index.d.ts.map +1 -1
  148. package/lib/typescript/ui/components/Avatar.d.ts.map +1 -1
  149. package/lib/typescript/ui/components/GroupedItem.d.ts +6 -0
  150. package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -1
  151. package/lib/typescript/ui/components/GroupedSection.d.ts +6 -0
  152. package/lib/typescript/ui/components/GroupedSection.d.ts.map +1 -1
  153. package/lib/typescript/ui/components/Header.d.ts +22 -0
  154. package/lib/typescript/ui/components/Header.d.ts.map +1 -0
  155. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  156. package/lib/typescript/ui/components/index.d.ts +1 -0
  157. package/lib/typescript/ui/components/index.d.ts.map +1 -1
  158. package/lib/typescript/ui/components/internal/TextField.d.ts +31 -16
  159. package/lib/typescript/ui/components/internal/TextField.d.ts.map +1 -1
  160. package/lib/typescript/ui/context/OxyContext.d.ts +5 -2
  161. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  162. package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
  163. package/lib/typescript/ui/navigation/types.d.ts +9 -2
  164. package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
  165. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  166. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  167. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  168. package/lib/typescript/ui/screens/AppInfoScreen.d.ts.map +1 -1
  169. package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
  170. package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts +5 -1
  171. package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts.map +1 -1
  172. package/lib/typescript/ui/screens/SessionManagementScreen.d.ts.map +1 -1
  173. package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
  174. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts +1 -1
  175. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +1 -1
  176. package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts +0 -1
  177. package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts.map +1 -1
  178. package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts.map +1 -1
  179. package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts.map +1 -1
  180. package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
  181. package/lib/typescript/ui/styles/authStyles.d.ts +326 -0
  182. package/lib/typescript/ui/styles/authStyles.d.ts.map +1 -0
  183. package/lib/typescript/ui/styles/index.d.ts +1 -0
  184. package/lib/typescript/ui/styles/index.d.ts.map +1 -1
  185. package/package.json +1 -4
  186. package/src/core/index.ts +195 -41
  187. package/src/index.ts +72 -4
  188. package/src/node/createAuth.ts +623 -7
  189. package/src/node/index.ts +19 -1
  190. package/src/ui/components/Avatar.tsx +11 -5
  191. package/src/ui/components/GroupedItem.tsx +57 -9
  192. package/src/ui/components/GroupedSection.tsx +12 -0
  193. package/src/ui/components/Header.tsx +364 -0
  194. package/src/ui/components/OxyProvider.tsx +31 -15
  195. package/src/ui/components/index.ts +1 -0
  196. package/src/ui/components/internal/GroupedPillButtons.tsx +1 -1
  197. package/src/ui/components/internal/TextField.md +436 -0
  198. package/src/ui/components/internal/TextField.tsx +720 -620
  199. package/src/ui/context/OxyContext.tsx +150 -63
  200. package/src/ui/hooks/useSessionSocket.ts +5 -2
  201. package/src/ui/navigation/OxyRouter.tsx +1 -1
  202. package/src/ui/navigation/types.ts +10 -2
  203. package/src/ui/screens/AccountCenterScreen.tsx +5 -5
  204. package/src/ui/screens/AccountManagementDemo.tsx +9 -9
  205. package/src/ui/screens/AccountOverviewScreen.tsx +265 -414
  206. package/src/ui/screens/AccountSettingsScreen.tsx +1165 -403
  207. package/src/ui/screens/AccountSwitcherScreen.tsx +158 -202
  208. package/src/ui/screens/AppInfoScreen.tsx +270 -497
  209. package/src/ui/screens/FeedbackScreen.tsx +3 -3
  210. package/src/ui/screens/PaymentGatewayScreen.tsx +668 -365
  211. package/src/ui/screens/ProfileScreen.tsx +5 -5
  212. package/src/ui/screens/RecoverAccountScreen.tsx +46 -74
  213. package/src/ui/screens/SessionManagementScreen.tsx +14 -22
  214. package/src/ui/screens/SignInScreen.tsx +27 -294
  215. package/src/ui/screens/SignUpScreen.tsx +5 -5
  216. package/src/ui/screens/internal/SignInPasswordStep.tsx +11 -22
  217. package/src/ui/screens/internal/SignInUsernameStep.tsx +3 -10
  218. package/src/ui/screens/internal/SignUpIdentityStep.tsx +2 -5
  219. package/src/ui/screens/internal/SignUpSecurityStep.tsx +3 -4
  220. package/src/ui/stores/authStore.ts +12 -0
  221. package/src/ui/styles/authStyles.ts +352 -0
  222. package/src/ui/styles/index.ts +1 -0
  223. package/lib/commonjs/core/auth-manager.js +0 -440
  224. package/lib/commonjs/core/auth-manager.js.map +0 -1
  225. package/lib/commonjs/core/use-auth.js +0 -244
  226. package/lib/commonjs/core/use-auth.js.map +0 -1
  227. package/lib/module/core/auth-manager.js +0 -432
  228. package/lib/module/core/auth-manager.js.map +0 -1
  229. package/lib/module/core/use-auth.js +0 -235
  230. package/lib/module/core/use-auth.js.map +0 -1
  231. package/lib/typescript/core/auth-manager.d.ts +0 -136
  232. package/lib/typescript/core/auth-manager.d.ts.map +0 -1
  233. package/lib/typescript/core/use-auth.d.ts +0 -79
  234. package/lib/typescript/core/use-auth.d.ts.map +0 -1
  235. package/src/__tests__/middleware.test.ts +0 -105
  236. package/src/__tests__/setup.ts +0 -10
  237. package/src/__tests__/zero-config-auth.test.ts +0 -607
  238. package/src/core/auth-manager.ts +0 -500
  239. package/src/core/use-auth.tsx +0 -245
@@ -1,591 +1,651 @@
1
1
  "use strict";
2
2
 
3
- import React, { useState, useCallback, forwardRef, useEffect, useRef } from 'react';
3
+ import React, { useState, useCallback, forwardRef, useEffect, useRef, useMemo } from 'react';
4
4
  import { View, Text, TextInput, TouchableOpacity, ActivityIndicator, StyleSheet, Platform, Animated } from 'react-native';
5
5
  import { Ionicons } from '@expo/vector-icons';
6
- import Svg, { Path } from 'react-native-svg';
6
+ import { fontFamilies } from '../../styles/fonts';
7
7
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
8
+ // Color palette for different states
9
+ const colorPalette = {
10
+ primary: {
11
+ main: '#d169e5',
12
+ light: '#e8b5f0',
13
+ dark: '#a64db3'
14
+ },
15
+ secondary: {
16
+ main: '#666666',
17
+ light: '#999999',
18
+ dark: '#333333'
19
+ },
20
+ error: {
21
+ main: '#D32F2F',
22
+ light: '#ffcdd2',
23
+ dark: '#b71c1c'
24
+ },
25
+ success: {
26
+ main: '#2E7D32',
27
+ light: '#c8e6c9',
28
+ dark: '#1b5e20'
29
+ },
30
+ warning: {
31
+ main: '#FF9800',
32
+ light: '#ffe0b2',
33
+ dark: '#e65100'
34
+ }
35
+ };
36
+
37
+ // Surface scale for consistent theming
38
+ const surfaceScale = level => {
39
+ const base = 255;
40
+ const value = Math.round(base - level * 255);
41
+ return `#${value.toString(16).padStart(2, '0').repeat(3)}`;
42
+ };
43
+
44
+ // Password strength calculation
45
+ const calculatePasswordStrength = password => {
46
+ if (!password) return {
47
+ score: 0,
48
+ feedback: '',
49
+ color: '#E0E0E0',
50
+ label: ''
51
+ };
52
+ let score = 0;
53
+ const feedback = [];
54
+ if (password.length >= 8) score += 25;else feedback.push('At least 8 characters');
55
+ if (/[A-Z]/.test(password)) score += 25;else feedback.push('One uppercase letter');
56
+ if (/[a-z]/.test(password)) score += 25;else feedback.push('One lowercase letter');
57
+ if (/[\d\W]/.test(password)) score += 25;else feedback.push('One number or special character');
58
+ const colors = {
59
+ 0: '#E0E0E0',
60
+ 25: '#D32F2F',
61
+ 50: '#FF9800',
62
+ 75: '#2196F3',
63
+ 100: '#4CAF50'
64
+ };
65
+ const labels = {
66
+ 0: '',
67
+ 25: 'Weak',
68
+ 50: 'Fair',
69
+ 75: 'Good',
70
+ 100: 'Strong'
71
+ };
72
+ return {
73
+ score,
74
+ feedback: score === 100 ? 'Strong password!' : `Missing: ${feedback.join(', ')}`,
75
+ color: colors[score] || colors[0],
76
+ label: labels[score] || ''
77
+ };
78
+ };
79
+
80
+ // Input formatting utilities
81
+ const formatters = {
82
+ phone: value => {
83
+ const cleaned = value.replace(/\D/g, '');
84
+ const match = cleaned.match(/^(\d{3})(\d{3})(\d{4})$/);
85
+ if (match) return `(${match[1]}) ${match[2]}-${match[3]}`;
86
+ return value;
87
+ },
88
+ creditCard: value => {
89
+ const cleaned = value.replace(/\D/g, '');
90
+ const match = cleaned.match(/^(\d{4})(\d{4})(\d{4})(\d{4})$/);
91
+ if (match) return `${match[1]} ${match[2]} ${match[3]} ${match[4]}`;
92
+ return value.replace(/(.{4})/g, '$1 ').trim();
93
+ },
94
+ currency: value => {
95
+ const cleaned = value.replace(/[^\d.]/g, '');
96
+ const num = parseFloat(cleaned);
97
+ return isNaN(num) ? value : `$${num.toFixed(2)}`;
98
+ }
99
+ };
100
+
101
+ // Debounce hook
102
+ const useDebounce = (value, delay) => {
103
+ const [debouncedValue, setDebouncedValue] = useState(value);
104
+ useEffect(() => {
105
+ const handler = setTimeout(() => {
106
+ setDebouncedValue(value);
107
+ }, delay);
108
+ return () => {
109
+ clearTimeout(handler);
110
+ };
111
+ }, [value, delay]);
112
+ return debouncedValue;
113
+ };
8
114
  const TextField = /*#__PURE__*/forwardRef(({
115
+ // Basic props
9
116
  label,
10
- icon,
11
- iconColor,
117
+ variant = 'filled',
118
+ color = 'primary',
119
+ // Leading and trailing
120
+ leading,
121
+ trailing,
122
+ // States
12
123
  error,
13
124
  success = false,
14
125
  loading = false,
15
- rightComponent,
16
- leftComponent,
17
- colors,
18
- containerStyle,
126
+ disabled = false,
127
+ // Helper text
128
+ helperText,
129
+ // Enhanced features
130
+ maxLength,
131
+ showCharacterCount,
132
+ inputMask,
133
+ customMask,
134
+ formatValue,
135
+ validateOnChange,
136
+ debounceMs = 300,
137
+ passwordStrength = false,
138
+ clearable = false,
139
+ // Mouse events
140
+ onMouseEnter,
141
+ onMouseLeave,
142
+ // Styling
143
+ style,
144
+ inputContainerStyle,
19
145
  inputStyle,
20
- labelStyle,
21
- errorStyle,
22
- variant = 'outlined',
146
+ leadingContainerStyle,
147
+ trailingContainerStyle,
148
+ // Callbacks
149
+ onValidationChange,
150
+ onClear,
151
+ // TextInput props
152
+ placeholder,
23
153
  onFocus,
24
154
  onBlur,
25
155
  onChangeText,
26
- testID,
27
- secureTextEntry,
28
156
  value = '',
29
- validMessage,
30
- ...textInputProps
157
+ secureTextEntry,
158
+ ...rest
31
159
  }, ref) => {
32
- const [isFocused, setIsFocused] = useState(false);
160
+ // State management
161
+ const [focused, setFocused] = useState(false);
162
+ const [hovered, setHovered] = useState(false);
33
163
  const [showPassword, setShowPassword] = useState(false);
34
- const [isLabelFloating, setIsLabelFloating] = useState(value ? true : false);
35
- const [labelWidth, setLabelWidth] = useState(0);
36
- const [labelLeft, setLabelLeft] = useState(0);
37
- const [inputWidth, setInputWidth] = useState(0);
38
- const [inputHeight, setInputHeight] = useState(64);
39
- const borderRadius = 16;
40
- const borderWidth = 2;
41
-
42
- // Animation values
43
- const labelAnim = useRef(new Animated.Value(value ? 1 : 0)).current;
44
- const borderAnim = useRef(new Animated.Value(0)).current;
45
- const handleFocus = useCallback(() => {
46
- setIsFocused(true);
47
- onFocus?.();
48
-
49
- // Animate label to top
50
- Animated.timing(labelAnim, {
51
- toValue: 1,
52
- duration: 200,
53
- useNativeDriver: false
54
- }).start();
164
+ const [internalValue, setInternalValue] = useState(value);
165
+ const [isValidating, setIsValidating] = useState(false);
166
+
167
+ // Refs
168
+ const focusAnimation = useRef(new Animated.Value(0)).current;
169
+ const activeAnimation = useRef(new Animated.Value(Boolean(value) ? 1 : 0)).current;
170
+ const inputRef = useRef(null);
171
+
172
+ // Get color palette
173
+ const palette = colorPalette[color] || colorPalette.primary;
174
+
175
+ // Determine if we should show error colors
176
+ const effectiveColor = error ? 'error' : success ? 'success' : color;
177
+ const effectivePalette = colorPalette[effectiveColor] || colorPalette.primary;
178
+
179
+ // Get icon color based on focus state and error state
180
+ const iconColor = error ? effectivePalette.main // Always show error color when there's an error
181
+ : focused ? effectivePalette.main : surfaceScale(0.62);
182
+
183
+ // Helper function to clone React elements with updated color
184
+ const cloneWithColor = (element, color) => {
185
+ if (/*#__PURE__*/React.isValidElement(element) && element.type) {
186
+ return /*#__PURE__*/React.cloneElement(element, {
187
+ ...element.props,
188
+ color: color
189
+ });
190
+ }
191
+ return element;
192
+ };
193
+
194
+ // Render leading/trailing elements
195
+ const leadingNode = typeof leading === 'function' ? leading({
196
+ color: iconColor,
197
+ size: 24
198
+ }) : cloneWithColor(leading, iconColor);
199
+ const trailingNode = typeof trailing === 'function' ? trailing({
200
+ color: iconColor,
201
+ size: 24
202
+ }) : cloneWithColor(trailing, iconColor);
203
+
204
+ // Debounced value for validation
205
+ const debouncedValue = useDebounce(internalValue, debounceMs);
206
+
207
+ // Password strength calculation
208
+ const passwordStrengthData = useMemo(() => {
209
+ if (passwordStrength && secureTextEntry && internalValue) {
210
+ return calculatePasswordStrength(internalValue);
211
+ }
212
+ return null;
213
+ }, [passwordStrength, secureTextEntry, internalValue]);
214
+
215
+ // Format input value
216
+ const formatInputValue = useCallback(text => {
217
+ if (formatValue) return formatValue(text);
218
+ if (inputMask && inputMask !== 'custom' && formatters[inputMask]) {
219
+ return formatters[inputMask](text);
220
+ }
221
+ if (customMask) return customMask(text);
222
+ return text;
223
+ }, [formatValue, inputMask, customMask]);
224
+
225
+ // Handle focus
226
+ const handleFocus = useCallback(event => {
227
+ if (disabled) return;
228
+ setFocused(true);
229
+ onFocus?.(event);
230
+ }, [disabled, onFocus]);
231
+
232
+ // Handle blur
233
+ const handleBlur = useCallback(event => {
234
+ setFocused(false);
235
+ onBlur?.(event);
236
+ }, [onBlur]);
237
+
238
+ // Handle mouse events
239
+ const handleMouseEnter = useCallback(event => {
240
+ onMouseEnter?.(event);
241
+ setHovered(true);
242
+ }, [onMouseEnter]);
243
+ const handleMouseLeave = useCallback(event => {
244
+ onMouseLeave?.(event);
245
+ setHovered(false);
246
+ }, [onMouseLeave]);
247
+
248
+ // Handle text change
249
+ const handleChangeText = useCallback(text => {
250
+ const formattedText = formatInputValue(text);
251
+ setInternalValue(formattedText);
252
+ onChangeText?.(formattedText);
253
+ }, [formatInputValue, onChangeText]);
254
+
255
+ // Handle clear
256
+ const handleClear = useCallback(() => {
257
+ setInternalValue('');
258
+ onChangeText?.('');
259
+ onClear?.();
260
+ inputRef.current?.focus();
261
+ }, [onChangeText, onClear]);
262
+
263
+ // Toggle password visibility
264
+ const togglePasswordVisibility = useCallback(() => {
265
+ setShowPassword(prev => !prev);
266
+ }, []);
55
267
 
56
- // Animate border
57
- Animated.timing(borderAnim, {
58
- toValue: 1,
268
+ // Animate focus state
269
+ useEffect(() => {
270
+ Animated.timing(focusAnimation, {
271
+ toValue: focused ? 1 : 0,
59
272
  duration: 200,
60
273
  useNativeDriver: false
61
274
  }).start();
62
- }, [onFocus, labelAnim, borderAnim]);
63
- const handleBlur = useCallback(() => {
64
- setIsFocused(false);
65
- onBlur?.();
66
-
67
- // Animate border back
68
- Animated.timing(borderAnim, {
69
- toValue: 0,
275
+ }, [focused, focusAnimation]);
276
+
277
+ // Animate active state (when focused or has value)
278
+ useEffect(() => {
279
+ const shouldBeActive = focused || Boolean(internalValue);
280
+ Animated.timing(activeAnimation, {
281
+ toValue: shouldBeActive ? 1 : 0,
70
282
  duration: 200,
71
283
  useNativeDriver: false
72
284
  }).start();
285
+ }, [focused, internalValue, activeAnimation]);
73
286
 
74
- // Keep label at top if there's a value
75
- if (!value) {
76
- Animated.timing(labelAnim, {
77
- toValue: 0,
78
- duration: 200,
79
- useNativeDriver: false
80
- }).start();
81
- }
82
- }, [onBlur, borderAnim, labelAnim, value]);
83
- const handleChangeText = useCallback(text => {
84
- onChangeText?.(text);
85
-
86
- // Animate label if value changes from empty to filled or vice versa
87
- const shouldShowLabel = text.length > 0;
88
- if (shouldShowLabel !== isLabelFloating) {
89
- setIsLabelFloating(shouldShowLabel);
90
- Animated.timing(labelAnim, {
91
- toValue: shouldShowLabel ? 1 : 0,
92
- duration: 200,
93
- useNativeDriver: false
94
- }).start();
95
- }
96
- }, [onChangeText, labelAnim, isLabelFloating]);
287
+ // Validation effect
288
+ useEffect(() => {
289
+ if (!validateOnChange || !onValidationChange) return;
290
+ const timer = setTimeout(() => {
291
+ setIsValidating(true);
292
+ const isValid = !error && debouncedValue.length > 0;
293
+ onValidationChange(isValid, debouncedValue);
294
+ setIsValidating(false);
295
+ }, 100);
296
+ return () => clearTimeout(timer);
297
+ }, [debouncedValue, validateOnChange, onValidationChange, error]);
97
298
 
98
- // Initialize label position based on current value
299
+ // Update internal value when prop changes
99
300
  useEffect(() => {
100
- if (value && !isLabelFloating) {
101
- setIsLabelFloating(true);
102
- labelAnim.setValue(1);
103
- }
104
- }, [value, isLabelFloating, labelAnim]);
105
- const togglePasswordVisibility = useCallback(() => {
106
- setShowPassword(!showPassword);
107
- }, [showPassword]);
108
- const getBorderColor = () => {
109
- if (error) return colors?.error || '#D32F2F';
110
- if (success) return colors?.success || '#2E7D32';
111
- if (isFocused) return colors?.primary || '#d169e5';
112
- return colors?.border || '#E0E0E0';
113
- };
114
- const getIconColor = () => {
115
- if (isFocused) return colors?.primary || '#d169e5';
116
- return iconColor || colors?.secondaryText || '#666666';
301
+ setInternalValue(value);
302
+ }, [value]);
303
+
304
+ // Styles
305
+ const styles = useMemo(() => {
306
+ const isActive = focused || Boolean(internalValue);
307
+ return StyleSheet.create({
308
+ container: {
309
+ width: '100%',
310
+ marginBottom: 24
311
+ },
312
+ inputContainer: {
313
+ flexDirection: 'row',
314
+ alignItems: 'center',
315
+ minHeight: variant === 'standard' ? 48 : 56,
316
+ backgroundColor: variant === 'filled' ? focused ? surfaceScale(0.08) : hovered ? surfaceScale(0.08) : surfaceScale(0.04) : 'transparent',
317
+ borderRadius: variant === 'standard' ? 0 : variant === 'filled' ? 16 : 8,
318
+ borderTopLeftRadius: variant === 'filled' ? 16 : undefined,
319
+ borderTopRightRadius: variant === 'filled' ? 16 : undefined,
320
+ borderBottomLeftRadius: variant === 'filled' ? 0 : undefined,
321
+ borderBottomRightRadius: variant === 'filled' ? 0 : undefined,
322
+ borderWidth: variant === 'outlined' ? focused ? 2 : 1 : 0,
323
+ borderColor: error ? effectivePalette.main // Always show error color when there's an error
324
+ : focused ? effectivePalette.main : hovered ? surfaceScale(0.87) : surfaceScale(0.42),
325
+ position: 'relative',
326
+ ...Platform.select({
327
+ web: {
328
+ outlineStyle: 'none',
329
+ outlineWidth: 0,
330
+ outlineOffset: 0
331
+ },
332
+ default: {}
333
+ })
334
+ },
335
+ input: {
336
+ flex: 1,
337
+ minHeight: variant === 'standard' ? 48 : 56,
338
+ paddingStart: leadingNode ? 12 : variant === 'standard' ? 0 : 16,
339
+ paddingEnd: trailingNode || clearable || secureTextEntry ? 12 : variant === 'standard' ? 0 : 16,
340
+ paddingTop: variant === 'filled' && label ? 18 : 0,
341
+ color: surfaceScale(0.87),
342
+ fontSize: 16,
343
+ borderWidth: 0,
344
+ backgroundColor: 'transparent',
345
+ ...Platform.select({
346
+ web: {
347
+ border: 'none',
348
+ outlineStyle: 'none',
349
+ outlineWidth: 0,
350
+ outlineOffset: 0,
351
+ boxShadow: 'none',
352
+ '-webkit-appearance': 'none',
353
+ '-moz-appearance': 'none',
354
+ appearance: 'none'
355
+ },
356
+ default: {}
357
+ })
358
+ },
359
+ leading: {
360
+ justifyContent: 'center',
361
+ alignItems: 'center',
362
+ width: 24,
363
+ height: 24,
364
+ marginStart: variant === 'standard' ? 0 : 12,
365
+ marginVertical: variant === 'standard' ? 12 : 16
366
+ },
367
+ trailing: {
368
+ flexDirection: 'row',
369
+ justifyContent: 'center',
370
+ alignItems: 'center',
371
+ marginEnd: variant === 'standard' ? 0 : 12,
372
+ marginVertical: variant === 'standard' ? 12 : 16
373
+ },
374
+ underline: {
375
+ position: 'absolute',
376
+ start: 0,
377
+ end: 0,
378
+ bottom: 0,
379
+ height: 1,
380
+ backgroundColor: error ? effectivePalette.main // Always show error color when there's an error
381
+ : hovered ? surfaceScale(0.87) : surfaceScale(0.42)
382
+ },
383
+ underlineFocused: {
384
+ position: 'absolute',
385
+ start: 0,
386
+ end: 0,
387
+ bottom: 0,
388
+ height: 2,
389
+ backgroundColor: effectivePalette.main
390
+ },
391
+ labelContainer: {
392
+ justifyContent: 'center',
393
+ position: 'absolute',
394
+ top: 0,
395
+ start: variant === 'standard' ? leadingNode ? 36 : 0 : leadingNode ? 48 : 16,
396
+ height: variant === 'standard' ? 48 : 56
397
+ },
398
+ label: {
399
+ fontSize: 16,
400
+ fontFamily: fontFamilies.phuduSemiBold,
401
+ color: surfaceScale(0.87)
402
+ },
403
+ helperText: {
404
+ fontSize: 12,
405
+ fontFamily: fontFamilies.phuduMedium,
406
+ marginTop: 4,
407
+ marginHorizontal: 16,
408
+ color: surfaceScale(0.6)
409
+ },
410
+ passwordStrengthContainer: {
411
+ marginTop: 8,
412
+ marginHorizontal: 16
413
+ },
414
+ passwordStrengthBar: {
415
+ height: 4,
416
+ backgroundColor: '#E0E0E0',
417
+ borderRadius: 2,
418
+ overflow: 'hidden'
419
+ },
420
+ passwordStrengthFill: {
421
+ height: '100%',
422
+ borderRadius: 2
423
+ },
424
+ passwordStrengthText: {
425
+ fontSize: 11,
426
+ fontWeight: '600',
427
+ marginTop: 4
428
+ },
429
+ characterCount: {
430
+ fontSize: 11,
431
+ marginTop: 4,
432
+ marginHorizontal: 16,
433
+ textAlign: 'right',
434
+ color: surfaceScale(0.6)
435
+ },
436
+ clearButton: {
437
+ padding: 4,
438
+ marginLeft: 8
439
+ },
440
+ passwordToggle: {
441
+ padding: 4,
442
+ marginLeft: 8
443
+ },
444
+ validationIndicator: {
445
+ marginLeft: 8
446
+ }
447
+ });
448
+ }, [variant, focused, hovered, effectivePalette, leadingNode, trailingNode, clearable, secureTextEntry, label, error, internalValue]);
449
+
450
+ // Character count display
451
+ const characterCount = internalValue.length;
452
+ const showCount = showCharacterCount && maxLength;
453
+
454
+ // Render password strength indicator
455
+ const renderPasswordStrength = () => {
456
+ if (!passwordStrengthData) return null;
457
+ return /*#__PURE__*/_jsxs(View, {
458
+ style: styles.passwordStrengthContainer,
459
+ children: [/*#__PURE__*/_jsx(View, {
460
+ style: styles.passwordStrengthBar,
461
+ children: /*#__PURE__*/_jsx(View, {
462
+ style: [styles.passwordStrengthFill, {
463
+ width: `${passwordStrengthData.score}%`,
464
+ backgroundColor: passwordStrengthData.color
465
+ }]
466
+ })
467
+ }), /*#__PURE__*/_jsx(Text, {
468
+ style: [styles.passwordStrengthText, {
469
+ color: passwordStrengthData.color
470
+ }],
471
+ children: passwordStrengthData.label
472
+ })]
473
+ });
117
474
  };
118
- const getLabelColor = () => {
119
- if (error) return colors?.error || '#D32F2F';
120
- if (isFocused) return colors?.primary || '#d169e5';
121
- return colors?.secondaryText || '#666666';
475
+
476
+ // Render character count
477
+ const renderCharacterCount = () => {
478
+ if (!showCount) return null;
479
+ return /*#__PURE__*/_jsxs(Text, {
480
+ style: styles.characterCount,
481
+ children: [characterCount, "/", maxLength]
482
+ });
122
483
  };
123
- const getBackgroundColor = () => {
124
- if (variant === 'filled') {
125
- return colors?.inputBackground || '#F5F5F5';
484
+
485
+ // Get helper text content (error takes precedence over helper text)
486
+ const helperTextContent = error || helperText;
487
+
488
+ // Render trailing elements
489
+ const renderTrailingElements = () => {
490
+ const elements = [];
491
+
492
+ // Loading indicator
493
+ if (isValidating) {
494
+ elements.push(/*#__PURE__*/_jsx(ActivityIndicator, {
495
+ size: "small",
496
+ color: effectivePalette.main,
497
+ style: styles.validationIndicator
498
+ }, "validating"));
499
+ }
500
+
501
+ // Loading indicator
502
+ if (loading && !isValidating) {
503
+ elements.push(/*#__PURE__*/_jsx(ActivityIndicator, {
504
+ size: "small",
505
+ color: effectivePalette.main,
506
+ style: styles.validationIndicator
507
+ }, "loading"));
508
+ }
509
+
510
+ // Success indicator
511
+ if (success && !loading && !isValidating) {
512
+ elements.push(/*#__PURE__*/_jsx(Ionicons, {
513
+ name: "checkmark-circle",
514
+ size: 22,
515
+ color: colorPalette.success.main,
516
+ style: styles.validationIndicator
517
+ }, "success"));
518
+ }
519
+
520
+ // Clear button
521
+ if (clearable && internalValue && !secureTextEntry) {
522
+ elements.push(/*#__PURE__*/_jsx(TouchableOpacity, {
523
+ style: styles.clearButton,
524
+ onPress: handleClear,
525
+ hitSlop: {
526
+ top: 10,
527
+ bottom: 10,
528
+ left: 10,
529
+ right: 10
530
+ },
531
+ accessibilityLabel: "Clear input",
532
+ accessibilityRole: "button",
533
+ children: /*#__PURE__*/_jsx(Ionicons, {
534
+ name: "close-circle",
535
+ size: 20,
536
+ color: iconColor
537
+ })
538
+ }, "clear"));
539
+ }
540
+
541
+ // Password toggle
542
+ if (secureTextEntry) {
543
+ elements.push(/*#__PURE__*/_jsx(TouchableOpacity, {
544
+ style: styles.passwordToggle,
545
+ onPress: togglePasswordVisibility,
546
+ hitSlop: {
547
+ top: 10,
548
+ bottom: 10,
549
+ left: 10,
550
+ right: 10
551
+ },
552
+ accessibilityLabel: showPassword ? "Hide password" : "Show password",
553
+ accessibilityRole: "button",
554
+ children: /*#__PURE__*/_jsx(Ionicons, {
555
+ name: showPassword ? "eye-off" : "eye",
556
+ size: 22,
557
+ color: iconColor
558
+ })
559
+ }, "password"));
126
560
  }
127
- return 'transparent';
561
+ return elements;
128
562
  };
129
- const styles = createStyles(colors, variant);
130
- const BASE_PADDING = 20;
131
- const ICON_WIDTH = 22;
132
- const ICON_MARGIN = 12;
133
- const TEXT_LEFT = icon || leftComponent ? BASE_PADDING + ICON_WIDTH + ICON_MARGIN : BASE_PADDING;
134
- const FLOAT_LEFT_OFFSET = 10;
135
- const isLabelFloated = Boolean(value || isFocused);
136
-
137
- // For web, make TextInput the primary element with absolute positioned decorations
138
- if (Platform.OS === 'web') {
139
- return /*#__PURE__*/_jsxs(View, {
140
- style: [styles.container, containerStyle],
141
- children: [/*#__PURE__*/_jsxs(View, {
142
- style: styles.webInputContainer,
143
- children: [/*#__PURE__*/_jsx(TextInput, {
144
- ref: ref,
145
- style: [styles.webInput, {
146
- color: colors?.text || '#000000',
147
- borderColor: 'transparent',
148
- backgroundColor: getBackgroundColor(),
149
- paddingLeft: TEXT_LEFT,
150
- paddingRight: 60,
151
- // Space for right components
152
- paddingTop: label ? 24 : 20,
153
- // Make room for floated label
154
- paddingBottom: 8,
155
- borderWidth: 0,
156
- ...Platform.select({
157
- web: {
158
- border: 'none',
159
- outline: 'none',
160
- boxShadow: 'none'
161
- },
162
- default: {}
163
- })
164
- }, inputStyle],
165
- onFocus: handleFocus,
166
- onBlur: handleBlur,
167
- onChangeText: handleChangeText,
168
- secureTextEntry: secureTextEntry && !showPassword,
169
- placeholderTextColor: "transparent",
170
- testID: testID,
171
- autoComplete: secureTextEntry ? 'current-password' : 'off',
172
- spellCheck: false,
173
- value: value,
174
- ...textInputProps
175
- }), /*#__PURE__*/_jsx(Svg, {
176
- width: inputWidth,
177
- height: inputHeight,
178
- style: {
179
- position: 'absolute',
180
- top: 0,
181
- left: 0,
182
- zIndex: 0
183
- },
184
- pointerEvents: "none",
185
- children: /*#__PURE__*/_jsx(Path, {
186
- d: (() => {
187
- const y = borderWidth / 2;
188
- const x1 = borderRadius + borderWidth / 2;
189
- const x2 = inputWidth - borderRadius - borderWidth / 2;
190
- const labelGapStart = isLabelFloated ? labelLeft - 4 : x1;
191
- const labelGapEnd = isLabelFloated ? labelLeft + labelWidth + 4 : x2;
192
- // Start at left arc
193
- return `M${x1},${y}` + ` A${borderRadius},${borderRadius} 0 0 1 ${borderWidth / 2},${y + borderRadius}` + ` L${borderWidth / 2},${inputHeight - borderRadius - borderWidth / 2}` + ` A${borderRadius},${borderRadius} 0 0 1 ${x1},${inputHeight - borderWidth / 2}` + ` L${x2},${inputHeight - borderWidth / 2}` + ` A${borderRadius},${borderRadius} 0 0 1 ${inputWidth - borderWidth / 2},${inputHeight - borderRadius - borderWidth / 2}` + ` L${inputWidth - borderWidth / 2},${y + borderRadius}` + ` A${borderRadius},${borderRadius} 0 0 1 ${x2},${y}` + ` L${labelGapStart},${y}` + ` M${labelGapEnd},${y}` + ` L${x2},${y}`;
194
- })(),
195
- stroke: getBorderColor(),
196
- strokeWidth: borderWidth,
197
- fill: "none"
198
- })
199
- }), label && /*#__PURE__*/_jsx(Animated.Text, {
200
- onLayout: e => {
201
- setLabelWidth(e.nativeEvent.layout.width);
202
- setLabelLeft(e.nativeEvent.layout.x);
203
- },
204
- style: [styles.webFloatingLabel, {
205
- color: getLabelColor(),
206
- left: labelAnim.interpolate({
207
- inputRange: [0, 1],
208
- outputRange: [TEXT_LEFT, FLOAT_LEFT_OFFSET]
209
- }),
210
- top: labelAnim.interpolate({
563
+ return /*#__PURE__*/_jsxs(View, {
564
+ style: [styles.container, style],
565
+ ...(Platform.OS === 'web' && {
566
+ className: 'oxy-textfield-container'
567
+ }),
568
+ children: [/*#__PURE__*/_jsxs(View, {
569
+ style: [styles.inputContainer, inputContainerStyle],
570
+ ...(Platform.OS === 'web' && {
571
+ onMouseEnter: handleMouseEnter,
572
+ onMouseLeave: handleMouseLeave
573
+ }),
574
+ children: [leadingNode && /*#__PURE__*/_jsx(View, {
575
+ style: [styles.leading, leadingContainerStyle],
576
+ children: leadingNode
577
+ }), /*#__PURE__*/_jsx(TextInput, {
578
+ ref: r => {
579
+ if (typeof ref === 'function') {
580
+ ref(r);
581
+ } else if (ref && typeof ref === 'object') {
582
+ // @ts-ignore - React ref assignment
583
+ ref.current = r;
584
+ }
585
+ // @ts-ignore - Internal ref assignment
586
+ inputRef.current = r;
587
+ },
588
+ style: [styles.input, inputStyle],
589
+ placeholder: label ? focused ? placeholder : undefined : placeholder,
590
+ placeholderTextColor: surfaceScale(0.4),
591
+ selectionColor: effectivePalette.main,
592
+ onFocus: handleFocus,
593
+ onBlur: handleBlur,
594
+ onChangeText: handleChangeText,
595
+ secureTextEntry: secureTextEntry && !showPassword,
596
+ value: internalValue,
597
+ editable: !disabled,
598
+ maxLength: maxLength,
599
+ ...(Platform.OS === 'web' && {
600
+ className: 'oxy-textfield-input'
601
+ }),
602
+ ...rest
603
+ }), /*#__PURE__*/_jsxs(View, {
604
+ style: [styles.trailing, trailingContainerStyle],
605
+ children: [trailingNode, renderTrailingElements()]
606
+ }), (variant === 'filled' || variant === 'standard') && /*#__PURE__*/_jsxs(_Fragment, {
607
+ children: [/*#__PURE__*/_jsx(View, {
608
+ style: styles.underline,
609
+ pointerEvents: "none"
610
+ }), /*#__PURE__*/_jsx(Animated.View, {
611
+ style: [styles.underlineFocused, {
612
+ transform: [{
613
+ scaleX: focusAnimation
614
+ }]
615
+ }],
616
+ pointerEvents: "none"
617
+ })]
618
+ }), label && /*#__PURE__*/_jsx(View, {
619
+ style: styles.labelContainer,
620
+ pointerEvents: "none",
621
+ children: /*#__PURE__*/_jsx(Animated.Text, {
622
+ style: [styles.label, {
623
+ color: focusAnimation.interpolate({
211
624
  inputRange: [0, 1],
212
- outputRange: [20, -14]
625
+ outputRange: [error ? effectivePalette.main : surfaceScale(0.87), effectivePalette.main]
213
626
  }),
214
- fontSize: labelAnim.interpolate({
627
+ fontSize: activeAnimation.interpolate({
215
628
  inputRange: [0, 1],
216
629
  outputRange: [16, 12]
217
630
  }),
218
- backgroundColor: 'transparent',
219
- paddingHorizontal: 4,
220
- zIndex: 2
221
- }, labelStyle],
222
- children: label
223
- }), icon && !leftComponent && /*#__PURE__*/_jsx(View, {
224
- style: styles.webLeftIcon,
225
- children: /*#__PURE__*/_jsx(Ionicons, {
226
- name: icon,
227
- size: 22,
228
- color: getIconColor()
229
- })
230
- }), leftComponent && /*#__PURE__*/_jsx(View, {
231
- style: styles.webLeftComponent,
232
- children: leftComponent
233
- }), /*#__PURE__*/_jsxs(View, {
234
- style: styles.webRightComponents,
235
- children: [loading && /*#__PURE__*/_jsx(ActivityIndicator, {
236
- size: "small",
237
- color: colors?.primary || '#d169e5',
238
- style: styles.validationIndicator
239
- }), success && !loading && /*#__PURE__*/_jsx(Ionicons, {
240
- name: "checkmark-circle",
241
- size: 22,
242
- color: colors?.success || '#2E7D32',
243
- style: styles.validationIndicator
244
- }), error && !loading && !success && /*#__PURE__*/_jsx(Ionicons, {
245
- name: "close-circle",
246
- size: 22,
247
- color: colors?.error || '#D32F2F',
248
- style: styles.validationIndicator
249
- }), secureTextEntry && /*#__PURE__*/_jsx(TouchableOpacity, {
250
- style: styles.passwordToggle,
251
- onPress: togglePasswordVisibility,
252
- hitSlop: {
253
- top: 10,
254
- bottom: 10,
255
- left: 10,
256
- right: 10
257
- },
258
- ...(Platform.OS === 'web' && {
259
- role: 'button',
260
- tabIndex: 0,
261
- onKeyPress: e => {
262
- if (e.key === 'Enter' || e.key === ' ') {
263
- e.preventDefault();
264
- togglePasswordVisibility();
265
- }
266
- }
267
- }),
268
- children: /*#__PURE__*/_jsx(Ionicons, {
269
- name: showPassword ? "eye-off" : "eye",
270
- size: 22,
271
- color: colors?.secondaryText || '#666666'
272
- })
273
- }), rightComponent]
274
- })]
275
- }), error && /*#__PURE__*/_jsxs(View, {
276
- style: [styles.errorContainer, errorStyle],
277
- children: [/*#__PURE__*/_jsx(Ionicons, {
278
- name: "alert-circle",
279
- size: 16,
280
- color: colors?.error || '#D32F2F'
281
- }), /*#__PURE__*/_jsx(Text, {
282
- style: [styles.errorText, {
283
- color: colors?.error || '#D32F2F'
631
+ transform: [{
632
+ translateY: activeAnimation.interpolate({
633
+ inputRange: [0, 1],
634
+ outputRange: [0, variant === 'filled' ? -12 : variant === 'outlined' ? -28 : -24]
635
+ })
636
+ }]
284
637
  }],
285
- children: error
286
- })]
638
+ children: label
639
+ })
287
640
  })]
288
- });
289
- }
290
-
291
- // For mobile platforms, use Material Design structure
292
- return /*#__PURE__*/_jsxs(View, {
293
- style: [styles.container, containerStyle],
294
- children: [/*#__PURE__*/_jsxs(View, {
295
- style: [styles.inputWrapper, {
296
- borderColor: 'transparent',
297
- backgroundColor: getBackgroundColor(),
298
- borderWidth: 0,
299
- borderBottomWidth: variant === 'filled' ? 2 : variant === 'outlined' ? 2 : 0
641
+ }), helperTextContent && /*#__PURE__*/_jsx(Text, {
642
+ style: [styles.helperText, error && {
643
+ color: effectivePalette.main
300
644
  }],
301
- onLayout: e => {
302
- setInputWidth(e.nativeEvent.layout.width);
303
- setInputHeight(e.nativeEvent.layout.height);
304
- },
305
- children: [icon && !leftComponent && /*#__PURE__*/_jsx(Ionicons, {
306
- name: icon,
307
- size: 22,
308
- color: getIconColor(),
309
- style: styles.inputIcon
310
- }), leftComponent, /*#__PURE__*/_jsxs(View, {
311
- style: styles.inputContent,
312
- children: [label && /*#__PURE__*/_jsxs(_Fragment, {
313
- children: [/*#__PURE__*/_jsx(Svg, {
314
- width: inputWidth,
315
- height: inputHeight,
316
- style: {
317
- position: 'absolute',
318
- top: 0,
319
- left: 0,
320
- zIndex: 0
321
- },
322
- pointerEvents: "none",
323
- children: /*#__PURE__*/_jsx(Path, {
324
- d: (() => {
325
- const y = borderWidth / 2;
326
- const x1 = borderRadius + borderWidth / 2;
327
- const x2 = inputWidth - borderRadius - borderWidth / 2;
328
- const labelGapStart = isLabelFloated ? labelLeft - 4 : x1;
329
- const labelGapEnd = isLabelFloated ? labelLeft + labelWidth + 4 : x2;
330
- // Start at left arc
331
- return `M${x1},${y}` + ` A${borderRadius},${borderRadius} 0 0 1 ${borderWidth / 2},${y + borderRadius}` + ` L${borderWidth / 2},${inputHeight - borderRadius - borderWidth / 2}` + ` A${borderRadius},${borderRadius} 0 0 1 ${x1},${inputHeight - borderWidth / 2}` + ` L${x2},${inputHeight - borderWidth / 2}` + ` A${borderRadius},${borderRadius} 0 0 1 ${inputWidth - borderWidth / 2},${inputHeight - borderRadius - borderWidth / 2}` + ` L${inputWidth - borderWidth / 2},${y + borderRadius}` + ` A${borderRadius},${borderRadius} 0 0 1 ${x2},${y}` + ` L${labelGapStart},${y}` + ` M${labelGapEnd},${y}` + ` L${x2},${y}`;
332
- })(),
333
- stroke: getBorderColor(),
334
- strokeWidth: borderWidth,
335
- fill: "none"
336
- })
337
- }), /*#__PURE__*/_jsx(Animated.Text, {
338
- onLayout: e => {
339
- setLabelWidth(e.nativeEvent.layout.width);
340
- setLabelLeft(e.nativeEvent.layout.x);
341
- },
342
- style: [styles.floatingLabel, {
343
- color: getLabelColor(),
344
- left: labelAnim.interpolate({
345
- inputRange: [0, 1],
346
- outputRange: [TEXT_LEFT, FLOAT_LEFT_OFFSET]
347
- }),
348
- top: labelAnim.interpolate({
349
- inputRange: [0, 1],
350
- outputRange: [20, -14]
351
- }),
352
- fontSize: labelAnim.interpolate({
353
- inputRange: [0, 1],
354
- outputRange: [16, 12]
355
- }),
356
- zIndex: 2,
357
- paddingHorizontal: 4,
358
- backgroundColor: 'transparent'
359
- }, labelStyle],
360
- children: label
361
- })]
362
- }), /*#__PURE__*/_jsx(TextInput, {
363
- ref: ref,
364
- style: [styles.input, {
365
- color: colors?.text || '#000000',
366
- backgroundColor: getBackgroundColor(),
367
- paddingLeft: TEXT_LEFT,
368
- paddingRight: 60,
369
- // Space for right components
370
- paddingTop: label ? 24 : 20,
371
- // Make room for floated label
372
- paddingBottom: 8,
373
- borderWidth: 0,
374
- borderColor: 'transparent',
375
- ...Platform.select({
376
- web: {
377
- border: 'none',
378
- outline: 'none',
379
- boxShadow: 'none'
380
- },
381
- default: {}
382
- })
383
- }, inputStyle],
384
- onFocus: handleFocus,
385
- onBlur: handleBlur,
386
- onChangeText: handleChangeText,
387
- secureTextEntry: secureTextEntry && !showPassword,
388
- placeholderTextColor: "transparent",
389
- testID: testID,
390
- value: value,
391
- ...textInputProps
392
- })]
393
- }), /*#__PURE__*/_jsxs(View, {
394
- style: styles.rightComponents,
395
- children: [loading && /*#__PURE__*/_jsx(ActivityIndicator, {
396
- size: "small",
397
- color: colors?.primary || '#d169e5',
398
- style: styles.validationIndicator
399
- }), success && !loading && /*#__PURE__*/_jsx(Ionicons, {
400
- name: "checkmark-circle",
401
- size: 22,
402
- color: colors?.success || '#2E7D32',
403
- style: styles.validationIndicator
404
- }), error && !loading && !success && /*#__PURE__*/_jsx(Ionicons, {
405
- name: "close-circle",
406
- size: 22,
407
- color: colors?.error || '#D32F2F',
408
- style: styles.validationIndicator
409
- }), secureTextEntry && /*#__PURE__*/_jsx(TouchableOpacity, {
410
- style: styles.passwordToggle,
411
- onPress: togglePasswordVisibility,
412
- hitSlop: {
413
- top: 10,
414
- bottom: 10,
415
- left: 10,
416
- right: 10
417
- },
418
- children: /*#__PURE__*/_jsx(Ionicons, {
419
- name: showPassword ? "eye-off" : "eye",
420
- size: 22,
421
- color: colors?.secondaryText || '#666666'
422
- })
423
- }), rightComponent]
424
- })]
425
- }), error && /*#__PURE__*/_jsxs(View, {
426
- style: [styles.errorContainer, errorStyle],
427
- children: [/*#__PURE__*/_jsx(Ionicons, {
428
- name: "alert-circle",
429
- size: 16,
430
- color: colors?.error || '#D32F2F'
431
- }), /*#__PURE__*/_jsx(Text, {
432
- style: [styles.errorText, {
433
- color: colors?.error || '#D32F2F'
434
- }],
435
- children: error
436
- })]
437
- }), !error && validMessage && /*#__PURE__*/_jsxs(View, {
438
- style: {
439
- flexDirection: 'row',
440
- alignItems: 'center',
441
- marginTop: 4,
442
- gap: 6
443
- },
444
- children: [/*#__PURE__*/_jsx(Ionicons, {
445
- name: "checkmark-circle",
446
- size: 16,
447
- color: colors?.success || '#2E7D32'
448
- }), /*#__PURE__*/_jsx(Text, {
449
- style: {
450
- fontSize: 13,
451
- fontWeight: '500',
452
- color: colors?.success || '#2E7D32'
453
- },
454
- children: validMessage
455
- })]
456
- })]
645
+ children: helperTextContent
646
+ }), renderPasswordStrength(), renderCharacterCount()]
457
647
  });
458
648
  });
459
- const createStyles = (colors, variant) => StyleSheet.create({
460
- container: {
461
- width: '100%',
462
- marginBottom: 24
463
- },
464
- inputWrapper: {
465
- flexDirection: 'row',
466
- alignItems: 'center',
467
- height: 64,
468
- borderTopLeftRadius: 16,
469
- borderTopRightRadius: 16,
470
- borderBottomLeftRadius: 0,
471
- borderBottomRightRadius: 0,
472
- paddingHorizontal: 20,
473
- backgroundColor: variant === 'filled' ? colors?.inputBackground || '#F5F5F5' : 'transparent',
474
- position: 'relative',
475
- borderWidth: 0,
476
- borderColor: 'transparent'
477
- },
478
- inputIcon: {
479
- marginRight: 12,
480
- width: 22,
481
- height: 22,
482
- justifyContent: 'center',
483
- alignItems: 'center'
484
- },
485
- inputContent: {
486
- flex: 1,
487
- justifyContent: 'center',
488
- position: 'relative',
489
- height: 64
490
- },
491
- floatingLabel: {
492
- position: 'absolute',
493
- fontWeight: '500',
494
- lineHeight: 24,
495
- backgroundColor: 'transparent',
496
- paddingHorizontal: 4
497
- },
498
- input: {
499
- flex: 1,
500
- fontSize: 16,
501
- height: 24,
502
- paddingVertical: 0,
503
- marginTop: 8,
504
- // Space for floating label
505
- borderWidth: 0,
506
- borderColor: 'transparent'
507
- },
508
- // Web-specific styles
509
- webInputContainer: {
510
- position: 'relative',
511
- height: 64
512
- },
513
- webInput: {
514
- width: '100%',
515
- height: 64,
516
- fontSize: 16,
517
- paddingHorizontal: 20,
518
- borderTopLeftRadius: 16,
519
- borderTopRightRadius: 16,
520
- borderBottomLeftRadius: 0,
521
- borderBottomRightRadius: 0,
522
- borderWidth: 0,
523
- borderColor: 'transparent',
524
- borderStyle: 'solid'
525
- },
526
- webFloatingLabel: {
527
- position: 'absolute',
528
- fontWeight: '500',
529
- lineHeight: 24,
530
- backgroundColor: 'transparent',
531
- paddingHorizontal: 4
532
- },
533
- webLeftIcon: {
534
- position: 'absolute',
535
- left: 20,
536
- top: 21,
537
- // (64 - 22) / 2
538
- zIndex: 1,
539
- width: 22,
540
- height: 22,
541
- justifyContent: 'center',
542
- alignItems: 'center'
543
- },
544
- webLeftComponent: {
545
- position: 'absolute',
546
- left: 20,
547
- top: 0,
548
- height: 64,
549
- justifyContent: 'center',
550
- zIndex: 1
551
- },
552
- webRightComponents: {
553
- position: 'absolute',
554
- right: 20,
555
- top: 0,
556
- height: 64,
557
- flexDirection: 'row',
558
- alignItems: 'center',
559
- zIndex: 1
560
- },
561
- rightComponents: {
562
- flexDirection: 'row',
563
- alignItems: 'center'
564
- },
565
- validationIndicator: {
566
- marginLeft: 8
567
- },
568
- passwordToggle: {
569
- padding: 4,
570
- marginLeft: 8
571
- },
572
- errorContainer: {
573
- flexDirection: 'row',
574
- alignItems: 'center',
575
- padding: 12,
576
- borderRadius: 12,
577
- marginTop: 8,
578
- gap: 8,
579
- backgroundColor: (colors?.error || '#D32F2F') + '10',
580
- borderWidth: 1,
581
- borderColor: (colors?.error || '#D32F2F') + '30'
582
- },
583
- errorText: {
584
- fontSize: 12,
585
- fontWeight: '500',
586
- flex: 1
587
- }
588
- });
589
649
  TextField.displayName = 'TextField';
590
650
  export default TextField;
591
651
  //# sourceMappingURL=TextField.js.map