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