react-native-platform-components 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (636) hide show
  1. package/PlatformComponents.podspec +1 -1
  2. package/README.md +47 -51
  3. package/android/src/main/java/com/platformcomponents/PCButtonGroupView.kt +664 -0
  4. package/android/src/main/java/com/platformcomponents/PCButtonGroupViewManager.kt +241 -0
  5. package/android/src/main/java/com/platformcomponents/PCButtonSupport.kt +159 -0
  6. package/android/src/main/java/com/platformcomponents/PCButtonView.kt +527 -0
  7. package/android/src/main/java/com/platformcomponents/PCButtonViewManager.kt +204 -0
  8. package/android/src/main/java/com/platformcomponents/PCContextMenuView.kt +41 -151
  9. package/android/src/main/java/com/platformcomponents/PCContextMenuViewManager.kt +7 -63
  10. package/android/src/main/java/com/platformcomponents/PCDatePickerView.kt +310 -115
  11. package/android/src/main/java/com/platformcomponents/PCDatePickerViewManager.kt +23 -3
  12. package/android/src/main/java/com/platformcomponents/PCExpressive.kt +112 -0
  13. package/android/src/main/java/com/platformcomponents/PCFloatingActionButtonView.kt +485 -0
  14. package/android/src/main/java/com/platformcomponents/PCFloatingActionButtonViewManager.kt +109 -0
  15. package/android/src/main/java/com/platformcomponents/PCFloatingToolbarView.kt +247 -0
  16. package/android/src/main/java/com/platformcomponents/PCFloatingToolbarViewManager.kt +73 -0
  17. package/android/src/main/java/com/platformcomponents/PCHaptics.kt +46 -0
  18. package/android/src/main/java/com/platformcomponents/PCImageLoader.kt +42 -7
  19. package/android/src/main/java/com/platformcomponents/PCLiquidGlassContainerView.kt +32 -0
  20. package/android/src/main/java/com/platformcomponents/PCLiquidGlassContainerViewManager.kt +31 -0
  21. package/android/src/main/java/com/platformcomponents/PCLiquidGlassView.kt +32 -6
  22. package/android/src/main/java/com/platformcomponents/PCLiquidGlassViewManager.kt +4 -0
  23. package/android/src/main/java/com/platformcomponents/PCMenuSupport.kt +280 -0
  24. package/android/src/main/java/com/platformcomponents/PCNavigationBarSupport.kt +332 -0
  25. package/android/src/main/java/com/platformcomponents/PCNavigationRailView.kt +448 -0
  26. package/android/src/main/java/com/platformcomponents/PCNavigationRailViewManager.kt +176 -0
  27. package/android/src/main/java/com/platformcomponents/PCSegmentedControlView.kt +35 -17
  28. package/android/src/main/java/com/platformcomponents/PCSegmentedControlViewManager.kt +20 -3
  29. package/android/src/main/java/com/platformcomponents/PCSelectionMenuView.kt +371 -39
  30. package/android/src/main/java/com/platformcomponents/PCSelectionMenuViewManager.kt +18 -4
  31. package/android/src/main/java/com/platformcomponents/PCTabBarView.kt +397 -0
  32. package/android/src/main/java/com/platformcomponents/PCTabBarViewManager.kt +182 -0
  33. package/android/src/main/java/com/platformcomponents/PCTextFieldView.kt +1393 -0
  34. package/android/src/main/java/com/platformcomponents/PCTextFieldViewManager.kt +354 -0
  35. package/android/src/main/java/com/platformcomponents/PCThemeSupport.kt +37 -0
  36. package/android/src/main/java/com/platformcomponents/PlatformComponentsPackage.kt +8 -0
  37. package/android/src/main/res/drawable/pc_ic_search.xml +12 -0
  38. package/android/src/main/res/values/attrs.xml +13 -0
  39. package/android/src/main/res/values/ids.xml +5 -0
  40. package/android/src/main/res/values/styles.xml +207 -0
  41. package/ios/PCButton.h +10 -0
  42. package/ios/PCButton.mm +311 -0
  43. package/ios/PCButtonGroup.h +10 -0
  44. package/ios/PCButtonGroup.mm +323 -0
  45. package/ios/PCButtonGroupView.swift +473 -0
  46. package/ios/PCButtonSupport.swift +312 -0
  47. package/ios/PCButtonView.swift +438 -0
  48. package/ios/PCConstants.swift +23 -2
  49. package/ios/PCContextMenu.mm +28 -110
  50. package/ios/PCContextMenu.swift +106 -167
  51. package/ios/PCDatePicker.mm +21 -9
  52. package/ios/PCDatePickerView.swift +94 -20
  53. package/ios/PCFloatingActionButton.h +10 -0
  54. package/ios/PCFloatingActionButton.mm +177 -0
  55. package/ios/PCFloatingActionButtonView.swift +426 -0
  56. package/ios/PCFloatingToolbar.h +10 -0
  57. package/ios/PCFloatingToolbar.mm +103 -0
  58. package/ios/PCFloatingToolbarView.swift +336 -0
  59. package/ios/PCHaptics.swift +91 -0
  60. package/ios/PCImageLoader.swift +76 -12
  61. package/ios/PCLiquidGlass.mm +36 -0
  62. package/ios/PCLiquidGlass.swift +221 -114
  63. package/ios/PCLiquidGlassContainer.h +10 -0
  64. package/ios/PCLiquidGlassContainer.mm +84 -0
  65. package/ios/PCLiquidGlassContainer.swift +69 -0
  66. package/ios/PCMenuItems.h +76 -0
  67. package/ios/PCMenuSupport.swift +350 -0
  68. package/ios/PCNavigationRail.h +10 -0
  69. package/ios/PCNavigationRail.mm +270 -0
  70. package/ios/PCNavigationRailView.swift +415 -0
  71. package/ios/PCSegmentedControl.mm +17 -2
  72. package/ios/PCSegmentedControl.swift +57 -7
  73. package/ios/PCSelectionMenu.mm +36 -8
  74. package/ios/PCSelectionMenu.swift +224 -48
  75. package/ios/PCTabBar.h +10 -0
  76. package/ios/PCTabBar.mm +285 -0
  77. package/ios/PCTabBarAccessory.h +10 -0
  78. package/ios/PCTabBarAccessory.mm +93 -0
  79. package/ios/PCTabBarAccessoryView.swift +96 -0
  80. package/ios/PCTabBarView.swift +671 -0
  81. package/ios/PCTextField.h +10 -0
  82. package/ios/PCTextField.mm +546 -0
  83. package/ios/PCTextFieldView.swift +1558 -0
  84. package/lib/commonjs/Button.js +149 -0
  85. package/lib/commonjs/Button.js.map +1 -0
  86. package/lib/commonjs/ButtonGroup.js +109 -0
  87. package/lib/commonjs/ButtonGroup.js.map +1 -0
  88. package/lib/commonjs/ButtonGroupNativeComponent.ts +191 -0
  89. package/lib/commonjs/ButtonNativeComponent.ts +192 -0
  90. package/lib/commonjs/ContextMenu.js +9 -48
  91. package/lib/commonjs/ContextMenu.js.map +1 -1
  92. package/lib/commonjs/ContextMenuNativeComponent.ts +47 -49
  93. package/lib/commonjs/DatePicker.js +98 -2
  94. package/lib/commonjs/DatePicker.js.map +1 -1
  95. package/lib/commonjs/DatePickerNativeComponent.ts +19 -2
  96. package/lib/commonjs/FloatingActionButton.js +60 -0
  97. package/lib/commonjs/FloatingActionButton.js.map +1 -0
  98. package/lib/commonjs/FloatingActionButtonNativeComponent.ts +58 -0
  99. package/lib/commonjs/FloatingToolbar.js +77 -0
  100. package/lib/commonjs/FloatingToolbar.js.map +1 -0
  101. package/lib/commonjs/FloatingToolbarNativeComponent.ts +47 -0
  102. package/lib/commonjs/LiquidGlass.js +7 -1
  103. package/lib/commonjs/LiquidGlass.js.map +1 -1
  104. package/lib/commonjs/LiquidGlassContainer.js +32 -0
  105. package/lib/commonjs/LiquidGlassContainer.js.map +1 -0
  106. package/lib/commonjs/LiquidGlassContainerNativeComponent.ts +17 -0
  107. package/lib/commonjs/LiquidGlassNativeComponent.ts +8 -0
  108. package/lib/commonjs/NavigationRail.js +104 -0
  109. package/lib/commonjs/NavigationRail.js.map +1 -0
  110. package/lib/commonjs/NavigationRailNativeComponent.ts +107 -0
  111. package/lib/commonjs/PlatformComponentsProvider.js +18 -0
  112. package/lib/commonjs/PlatformComponentsProvider.js.map +1 -0
  113. package/lib/commonjs/SegmentedControl.js +18 -85
  114. package/lib/commonjs/SegmentedControl.js.map +1 -1
  115. package/lib/commonjs/SegmentedControlNativeComponent.ts +14 -0
  116. package/lib/commonjs/SelectionMenu.js +27 -3
  117. package/lib/commonjs/SelectionMenu.js.map +1 -1
  118. package/lib/commonjs/SelectionMenuNativeComponent.ts +26 -4
  119. package/lib/commonjs/SplitButton.js +98 -0
  120. package/lib/commonjs/SplitButton.js.map +1 -0
  121. package/lib/commonjs/TabBar.js +203 -0
  122. package/lib/commonjs/TabBar.js.map +1 -0
  123. package/lib/commonjs/TabBarAccessoryNativeComponent.ts +19 -0
  124. package/lib/commonjs/TabBarNativeComponent.ts +145 -0
  125. package/lib/commonjs/TextField.js +358 -0
  126. package/lib/commonjs/TextField.js.map +1 -0
  127. package/lib/commonjs/TextFieldNativeComponent.ts +324 -0
  128. package/lib/commonjs/focusRegistry.js +79 -0
  129. package/lib/commonjs/focusRegistry.js.map +1 -0
  130. package/lib/commonjs/haptics.js +2 -0
  131. package/lib/commonjs/haptics.js.map +1 -0
  132. package/lib/commonjs/icons.js +106 -0
  133. package/lib/commonjs/icons.js.map +1 -0
  134. package/lib/commonjs/index.js +143 -0
  135. package/lib/commonjs/index.js.map +1 -1
  136. package/lib/commonjs/index.web.js +157 -63
  137. package/lib/commonjs/index.web.js.map +1 -1
  138. package/lib/commonjs/jest.js +910 -0
  139. package/lib/commonjs/jest.js.map +1 -0
  140. package/lib/commonjs/labelStyle.js +27 -0
  141. package/lib/commonjs/labelStyle.js.map +1 -0
  142. package/lib/commonjs/menuItems.js +68 -0
  143. package/lib/commonjs/menuItems.js.map +1 -0
  144. package/lib/commonjs/reactNativeInternals.d.js +2 -0
  145. package/lib/commonjs/reactNativeInternals.d.js.map +1 -0
  146. package/lib/commonjs/selectionIdentifiers.js +18 -0
  147. package/lib/commonjs/selectionIdentifiers.js.map +1 -0
  148. package/lib/commonjs/submitBehavior.js +19 -0
  149. package/lib/commonjs/submitBehavior.js.map +1 -0
  150. package/lib/commonjs/tabItems.js +49 -0
  151. package/lib/commonjs/tabItems.js.map +1 -0
  152. package/lib/commonjs/web/Button.js +170 -0
  153. package/lib/commonjs/web/Button.js.map +1 -0
  154. package/lib/commonjs/web/ButtonGroup.js +119 -0
  155. package/lib/commonjs/web/ButtonGroup.js.map +1 -0
  156. package/lib/commonjs/web/DatePicker.js +168 -0
  157. package/lib/commonjs/web/DatePicker.js.map +1 -0
  158. package/lib/commonjs/web/DateRangePicker.js +137 -0
  159. package/lib/commonjs/web/DateRangePicker.js.map +1 -0
  160. package/lib/commonjs/web/Dialog.js +54 -0
  161. package/lib/commonjs/web/Dialog.js.map +1 -0
  162. package/lib/commonjs/web/FloatingActionButton.js +98 -0
  163. package/lib/commonjs/web/FloatingActionButton.js.map +1 -0
  164. package/lib/commonjs/web/Icon.js +34 -0
  165. package/lib/commonjs/web/Icon.js.map +1 -0
  166. package/lib/commonjs/web/NativeTheme.js +28 -0
  167. package/lib/commonjs/web/NativeTheme.js.map +1 -0
  168. package/lib/commonjs/web/NavigationRail.js +139 -0
  169. package/lib/commonjs/web/NavigationRail.js.map +1 -0
  170. package/lib/commonjs/web/PlatformComponentsProvider.js +51 -0
  171. package/lib/commonjs/web/PlatformComponentsProvider.js.map +1 -0
  172. package/lib/commonjs/web/SegmentedControl.js +117 -0
  173. package/lib/commonjs/web/SegmentedControl.js.map +1 -0
  174. package/lib/commonjs/web/SelectionMenu.js +142 -0
  175. package/lib/commonjs/web/SelectionMenu.js.map +1 -0
  176. package/lib/commonjs/web/SplitButton.js +35 -0
  177. package/lib/commonjs/web/SplitButton.js.map +1 -0
  178. package/lib/commonjs/web/TabBar.js +131 -0
  179. package/lib/commonjs/web/TabBar.js.map +1 -0
  180. package/lib/commonjs/web/TextField.js +322 -0
  181. package/lib/commonjs/web/TextField.js.map +1 -0
  182. package/lib/commonjs/web/components.js +36 -0
  183. package/lib/commonjs/web/components.js.map +1 -0
  184. package/lib/commonjs/web/containers.js +121 -0
  185. package/lib/commonjs/web/containers.js.map +1 -0
  186. package/lib/commonjs/web/dateInput.js +116 -0
  187. package/lib/commonjs/web/dateInput.js.map +1 -0
  188. package/lib/commonjs/web/shared.js +192 -0
  189. package/lib/commonjs/web/shared.js.map +1 -0
  190. package/lib/commonjs/webComponents.js +6 -0
  191. package/lib/commonjs/webComponents.js.map +1 -0
  192. package/lib/module/Button.js +142 -0
  193. package/lib/module/Button.js.map +1 -0
  194. package/lib/module/ButtonGroup.js +102 -0
  195. package/lib/module/ButtonGroup.js.map +1 -0
  196. package/lib/module/ButtonGroupNativeComponent.ts +191 -0
  197. package/lib/module/ButtonNativeComponent.ts +192 -0
  198. package/lib/module/ContextMenu.js +9 -48
  199. package/lib/module/ContextMenu.js.map +1 -1
  200. package/lib/module/ContextMenuNativeComponent.ts +47 -49
  201. package/lib/module/DatePicker.js +98 -4
  202. package/lib/module/DatePicker.js.map +1 -1
  203. package/lib/module/DatePickerNativeComponent.ts +19 -2
  204. package/lib/module/FloatingActionButton.js +53 -0
  205. package/lib/module/FloatingActionButton.js.map +1 -0
  206. package/lib/module/FloatingActionButtonNativeComponent.ts +58 -0
  207. package/lib/module/FloatingToolbar.js +70 -0
  208. package/lib/module/FloatingToolbar.js.map +1 -0
  209. package/lib/module/FloatingToolbarNativeComponent.ts +47 -0
  210. package/lib/module/LiquidGlass.js +7 -1
  211. package/lib/module/LiquidGlass.js.map +1 -1
  212. package/lib/module/LiquidGlassContainer.js +26 -0
  213. package/lib/module/LiquidGlassContainer.js.map +1 -0
  214. package/lib/module/LiquidGlassContainerNativeComponent.ts +17 -0
  215. package/lib/module/LiquidGlassNativeComponent.ts +8 -0
  216. package/lib/module/NavigationRail.js +99 -0
  217. package/lib/module/NavigationRail.js.map +1 -0
  218. package/lib/module/NavigationRailNativeComponent.ts +107 -0
  219. package/lib/module/PlatformComponentsProvider.js +13 -0
  220. package/lib/module/PlatformComponentsProvider.js.map +1 -0
  221. package/lib/module/SegmentedControl.js +18 -85
  222. package/lib/module/SegmentedControl.js.map +1 -1
  223. package/lib/module/SegmentedControlNativeComponent.ts +14 -0
  224. package/lib/module/SelectionMenu.js +27 -3
  225. package/lib/module/SelectionMenu.js.map +1 -1
  226. package/lib/module/SelectionMenuNativeComponent.ts +26 -4
  227. package/lib/module/SplitButton.js +91 -0
  228. package/lib/module/SplitButton.js.map +1 -0
  229. package/lib/module/TabBar.js +196 -0
  230. package/lib/module/TabBar.js.map +1 -0
  231. package/lib/module/TabBarAccessoryNativeComponent.ts +19 -0
  232. package/lib/module/TabBarNativeComponent.ts +145 -0
  233. package/lib/module/TextField.js +352 -0
  234. package/lib/module/TextField.js.map +1 -0
  235. package/lib/module/TextFieldNativeComponent.ts +324 -0
  236. package/lib/module/focusRegistry.js +75 -0
  237. package/lib/module/focusRegistry.js.map +1 -0
  238. package/lib/module/haptics.js +2 -0
  239. package/lib/module/haptics.js.map +1 -0
  240. package/lib/module/icons.js +101 -0
  241. package/lib/module/icons.js.map +1 -0
  242. package/lib/module/index.js +13 -0
  243. package/lib/module/index.js.map +1 -1
  244. package/lib/module/index.web.js +12 -59
  245. package/lib/module/index.web.js.map +1 -1
  246. package/lib/module/jest.js +840 -0
  247. package/lib/module/jest.js.map +1 -0
  248. package/lib/module/labelStyle.js +23 -0
  249. package/lib/module/labelStyle.js.map +1 -0
  250. package/lib/module/menuItems.js +65 -0
  251. package/lib/module/menuItems.js.map +1 -0
  252. package/lib/module/reactNativeInternals.d.js +2 -0
  253. package/lib/module/reactNativeInternals.d.js.map +1 -0
  254. package/lib/module/selectionIdentifiers.js +14 -0
  255. package/lib/module/selectionIdentifiers.js.map +1 -0
  256. package/lib/module/submitBehavior.js +15 -0
  257. package/lib/module/submitBehavior.js.map +1 -0
  258. package/lib/module/tabItems.js +45 -0
  259. package/lib/module/tabItems.js.map +1 -0
  260. package/lib/module/web/Button.js +164 -0
  261. package/lib/module/web/Button.js.map +1 -0
  262. package/lib/module/web/ButtonGroup.js +113 -0
  263. package/lib/module/web/ButtonGroup.js.map +1 -0
  264. package/lib/module/web/DatePicker.js +162 -0
  265. package/lib/module/web/DatePicker.js.map +1 -0
  266. package/lib/module/web/DateRangePicker.js +131 -0
  267. package/lib/module/web/DateRangePicker.js.map +1 -0
  268. package/lib/module/web/Dialog.js +48 -0
  269. package/lib/module/web/Dialog.js.map +1 -0
  270. package/lib/module/web/FloatingActionButton.js +94 -0
  271. package/lib/module/web/FloatingActionButton.js.map +1 -0
  272. package/lib/module/web/Icon.js +29 -0
  273. package/lib/module/web/Icon.js.map +1 -0
  274. package/lib/module/web/NativeTheme.js +23 -0
  275. package/lib/module/web/NativeTheme.js.map +1 -0
  276. package/lib/module/web/NavigationRail.js +133 -0
  277. package/lib/module/web/NavigationRail.js.map +1 -0
  278. package/lib/module/web/PlatformComponentsProvider.js +44 -0
  279. package/lib/module/web/PlatformComponentsProvider.js.map +1 -0
  280. package/lib/module/web/SegmentedControl.js +112 -0
  281. package/lib/module/web/SegmentedControl.js.map +1 -0
  282. package/lib/module/web/SelectionMenu.js +137 -0
  283. package/lib/module/web/SelectionMenu.js.map +1 -0
  284. package/lib/module/web/SplitButton.js +30 -0
  285. package/lib/module/web/SplitButton.js.map +1 -0
  286. package/lib/module/web/TabBar.js +126 -0
  287. package/lib/module/web/TabBar.js.map +1 -0
  288. package/lib/module/web/TextField.js +316 -0
  289. package/lib/module/web/TextField.js.map +1 -0
  290. package/lib/module/web/components.js +33 -0
  291. package/lib/module/web/components.js.map +1 -0
  292. package/lib/module/web/containers.js +112 -0
  293. package/lib/module/web/containers.js.map +1 -0
  294. package/lib/module/web/dateInput.js +109 -0
  295. package/lib/module/web/dateInput.js.map +1 -0
  296. package/lib/module/web/shared.js +179 -0
  297. package/lib/module/web/shared.js.map +1 -0
  298. package/lib/module/webComponents.js +4 -0
  299. package/lib/module/webComponents.js.map +1 -0
  300. package/lib/typescript/commonjs/docs/sidebars.d.ts.map +1 -1
  301. package/lib/typescript/commonjs/src/Button.d.ts +184 -0
  302. package/lib/typescript/commonjs/src/Button.d.ts.map +1 -0
  303. package/lib/typescript/commonjs/src/ButtonGroup.d.ts +117 -0
  304. package/lib/typescript/commonjs/src/ButtonGroup.d.ts.map +1 -0
  305. package/lib/typescript/commonjs/src/ButtonGroupNativeComponent.d.ts +154 -0
  306. package/lib/typescript/commonjs/src/ButtonGroupNativeComponent.d.ts.map +1 -0
  307. package/lib/typescript/commonjs/src/ButtonNativeComponent.d.ts +148 -0
  308. package/lib/typescript/commonjs/src/ButtonNativeComponent.d.ts.map +1 -0
  309. package/lib/typescript/commonjs/src/ContextMenu.d.ts +20 -33
  310. package/lib/typescript/commonjs/src/ContextMenu.d.ts.map +1 -1
  311. package/lib/typescript/commonjs/src/ContextMenuNativeComponent.d.ts +45 -47
  312. package/lib/typescript/commonjs/src/ContextMenuNativeComponent.d.ts.map +1 -1
  313. package/lib/typescript/commonjs/src/DatePicker.d.ts +74 -1
  314. package/lib/typescript/commonjs/src/DatePicker.d.ts.map +1 -1
  315. package/lib/typescript/commonjs/src/DatePickerNativeComponent.d.ts +16 -1
  316. package/lib/typescript/commonjs/src/DatePickerNativeComponent.d.ts.map +1 -1
  317. package/lib/typescript/commonjs/src/FloatingActionButton.d.ts +67 -0
  318. package/lib/typescript/commonjs/src/FloatingActionButton.d.ts.map +1 -0
  319. package/lib/typescript/commonjs/src/FloatingActionButtonNativeComponent.d.ts +43 -0
  320. package/lib/typescript/commonjs/src/FloatingActionButtonNativeComponent.d.ts.map +1 -0
  321. package/lib/typescript/commonjs/src/FloatingToolbar.d.ts +75 -0
  322. package/lib/typescript/commonjs/src/FloatingToolbar.d.ts.map +1 -0
  323. package/lib/typescript/commonjs/src/FloatingToolbarNativeComponent.d.ts +36 -0
  324. package/lib/typescript/commonjs/src/FloatingToolbarNativeComponent.d.ts.map +1 -0
  325. package/lib/typescript/commonjs/src/LiquidGlass.d.ts +17 -0
  326. package/lib/typescript/commonjs/src/LiquidGlass.d.ts.map +1 -1
  327. package/lib/typescript/commonjs/src/LiquidGlassContainer.d.ts +27 -0
  328. package/lib/typescript/commonjs/src/LiquidGlassContainer.d.ts.map +1 -0
  329. package/lib/typescript/commonjs/src/LiquidGlassContainerNativeComponent.d.ts +13 -0
  330. package/lib/typescript/commonjs/src/LiquidGlassContainerNativeComponent.d.ts.map +1 -0
  331. package/lib/typescript/commonjs/src/LiquidGlassNativeComponent.d.ts +7 -0
  332. package/lib/typescript/commonjs/src/LiquidGlassNativeComponent.d.ts.map +1 -1
  333. package/lib/typescript/commonjs/src/NavigationRail.d.ts +86 -0
  334. package/lib/typescript/commonjs/src/NavigationRail.d.ts.map +1 -0
  335. package/lib/typescript/commonjs/src/NavigationRailNativeComponent.d.ts +81 -0
  336. package/lib/typescript/commonjs/src/NavigationRailNativeComponent.d.ts.map +1 -0
  337. package/lib/typescript/commonjs/src/PlatformComponentsProvider.d.ts +6 -0
  338. package/lib/typescript/commonjs/src/PlatformComponentsProvider.d.ts.map +1 -0
  339. package/lib/typescript/commonjs/src/SegmentedControl.d.ts +35 -47
  340. package/lib/typescript/commonjs/src/SegmentedControl.d.ts.map +1 -1
  341. package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts +11 -0
  342. package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts.map +1 -1
  343. package/lib/typescript/commonjs/src/SelectionMenu.d.ts +44 -2
  344. package/lib/typescript/commonjs/src/SelectionMenu.d.ts.map +1 -1
  345. package/lib/typescript/commonjs/src/SelectionMenuNativeComponent.d.ts +20 -4
  346. package/lib/typescript/commonjs/src/SelectionMenuNativeComponent.d.ts.map +1 -1
  347. package/lib/typescript/commonjs/src/SplitButton.d.ts +83 -0
  348. package/lib/typescript/commonjs/src/SplitButton.d.ts.map +1 -0
  349. package/lib/typescript/commonjs/src/TabBar.d.ts +144 -0
  350. package/lib/typescript/commonjs/src/TabBar.d.ts.map +1 -0
  351. package/lib/typescript/commonjs/src/TabBarAccessoryNativeComponent.d.ts +13 -0
  352. package/lib/typescript/commonjs/src/TabBarAccessoryNativeComponent.d.ts.map +1 -0
  353. package/lib/typescript/commonjs/src/TabBarNativeComponent.d.ts +110 -0
  354. package/lib/typescript/commonjs/src/TabBarNativeComponent.d.ts.map +1 -0
  355. package/lib/typescript/commonjs/src/TextField.d.ts +332 -0
  356. package/lib/typescript/commonjs/src/TextField.d.ts.map +1 -0
  357. package/lib/typescript/commonjs/src/TextFieldNativeComponent.d.ts +240 -0
  358. package/lib/typescript/commonjs/src/TextFieldNativeComponent.d.ts.map +1 -0
  359. package/lib/typescript/commonjs/src/focusRegistry.d.ts +13 -0
  360. package/lib/typescript/commonjs/src/focusRegistry.d.ts.map +1 -0
  361. package/lib/typescript/commonjs/src/haptics.d.ts +20 -0
  362. package/lib/typescript/commonjs/src/haptics.d.ts.map +1 -0
  363. package/lib/typescript/commonjs/src/icons.d.ts +58 -0
  364. package/lib/typescript/commonjs/src/icons.d.ts.map +1 -0
  365. package/lib/typescript/commonjs/src/index.d.ts +13 -0
  366. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  367. package/lib/typescript/commonjs/src/index.web.d.ts +25 -133
  368. package/lib/typescript/commonjs/src/index.web.d.ts.map +1 -1
  369. package/lib/typescript/commonjs/src/jest.d.ts +139 -0
  370. package/lib/typescript/commonjs/src/jest.d.ts.map +1 -0
  371. package/lib/typescript/commonjs/src/labelStyle.d.ts +20 -0
  372. package/lib/typescript/commonjs/src/labelStyle.d.ts.map +1 -0
  373. package/lib/typescript/commonjs/src/menuItems.d.ts +100 -0
  374. package/lib/typescript/commonjs/src/menuItems.d.ts.map +1 -0
  375. package/lib/typescript/commonjs/src/selectionIdentifiers.d.ts +3 -0
  376. package/lib/typescript/commonjs/src/selectionIdentifiers.d.ts.map +1 -0
  377. package/lib/typescript/commonjs/src/sharedTypes.d.ts +6 -1
  378. package/lib/typescript/commonjs/src/sharedTypes.d.ts.map +1 -1
  379. package/lib/typescript/commonjs/src/submitBehavior.d.ts +9 -0
  380. package/lib/typescript/commonjs/src/submitBehavior.d.ts.map +1 -0
  381. package/lib/typescript/commonjs/src/tabItems.d.ts +55 -0
  382. package/lib/typescript/commonjs/src/tabItems.d.ts.map +1 -0
  383. package/lib/typescript/commonjs/src/web/Button.d.ts +9 -0
  384. package/lib/typescript/commonjs/src/web/Button.d.ts.map +1 -0
  385. package/lib/typescript/commonjs/src/web/ButtonGroup.d.ts +11 -0
  386. package/lib/typescript/commonjs/src/web/ButtonGroup.d.ts.map +1 -0
  387. package/lib/typescript/commonjs/src/web/DatePicker.d.ts +14 -0
  388. package/lib/typescript/commonjs/src/web/DatePicker.d.ts.map +1 -0
  389. package/lib/typescript/commonjs/src/web/DateRangePicker.d.ts +11 -0
  390. package/lib/typescript/commonjs/src/web/DateRangePicker.d.ts.map +1 -0
  391. package/lib/typescript/commonjs/src/web/Dialog.d.ts +12 -0
  392. package/lib/typescript/commonjs/src/web/Dialog.d.ts.map +1 -0
  393. package/lib/typescript/commonjs/src/web/FloatingActionButton.d.ts +8 -0
  394. package/lib/typescript/commonjs/src/web/FloatingActionButton.d.ts.map +1 -0
  395. package/lib/typescript/commonjs/src/web/Icon.d.ts +12 -0
  396. package/lib/typescript/commonjs/src/web/Icon.d.ts.map +1 -0
  397. package/lib/typescript/commonjs/src/web/NativeTheme.d.ts +10 -0
  398. package/lib/typescript/commonjs/src/web/NativeTheme.d.ts.map +1 -0
  399. package/lib/typescript/commonjs/src/web/NavigationRail.d.ts +8 -0
  400. package/lib/typescript/commonjs/src/web/NavigationRail.d.ts.map +1 -0
  401. package/lib/typescript/commonjs/src/web/PlatformComponentsProvider.d.ts +10 -0
  402. package/lib/typescript/commonjs/src/web/PlatformComponentsProvider.d.ts.map +1 -0
  403. package/lib/typescript/commonjs/src/web/SegmentedControl.d.ts +8 -0
  404. package/lib/typescript/commonjs/src/web/SegmentedControl.d.ts.map +1 -0
  405. package/lib/typescript/commonjs/src/web/SelectionMenu.d.ts +10 -0
  406. package/lib/typescript/commonjs/src/web/SelectionMenu.d.ts.map +1 -0
  407. package/lib/typescript/commonjs/src/web/SplitButton.d.ts +9 -0
  408. package/lib/typescript/commonjs/src/web/SplitButton.d.ts.map +1 -0
  409. package/lib/typescript/commonjs/src/web/TabBar.d.ts +9 -0
  410. package/lib/typescript/commonjs/src/web/TabBar.d.ts.map +1 -0
  411. package/lib/typescript/commonjs/src/web/TextField.d.ts +9 -0
  412. package/lib/typescript/commonjs/src/web/TextField.d.ts.map +1 -0
  413. package/lib/typescript/commonjs/src/web/components.d.ts +17 -0
  414. package/lib/typescript/commonjs/src/web/components.d.ts.map +1 -0
  415. package/lib/typescript/commonjs/src/web/containers.d.ts +24 -0
  416. package/lib/typescript/commonjs/src/web/containers.d.ts.map +1 -0
  417. package/lib/typescript/commonjs/src/web/dateInput.d.ts +20 -0
  418. package/lib/typescript/commonjs/src/web/dateInput.d.ts.map +1 -0
  419. package/lib/typescript/commonjs/src/web/shared.d.ts +43 -0
  420. package/lib/typescript/commonjs/src/web/shared.d.ts.map +1 -0
  421. package/lib/typescript/commonjs/src/webComponents.d.ts +43 -0
  422. package/lib/typescript/commonjs/src/webComponents.d.ts.map +1 -0
  423. package/lib/typescript/module/docs/sidebars.d.ts.map +1 -1
  424. package/lib/typescript/module/src/Button.d.ts +184 -0
  425. package/lib/typescript/module/src/Button.d.ts.map +1 -0
  426. package/lib/typescript/module/src/ButtonGroup.d.ts +117 -0
  427. package/lib/typescript/module/src/ButtonGroup.d.ts.map +1 -0
  428. package/lib/typescript/module/src/ButtonGroupNativeComponent.d.ts +154 -0
  429. package/lib/typescript/module/src/ButtonGroupNativeComponent.d.ts.map +1 -0
  430. package/lib/typescript/module/src/ButtonNativeComponent.d.ts +148 -0
  431. package/lib/typescript/module/src/ButtonNativeComponent.d.ts.map +1 -0
  432. package/lib/typescript/module/src/ContextMenu.d.ts +20 -33
  433. package/lib/typescript/module/src/ContextMenu.d.ts.map +1 -1
  434. package/lib/typescript/module/src/ContextMenuNativeComponent.d.ts +45 -47
  435. package/lib/typescript/module/src/ContextMenuNativeComponent.d.ts.map +1 -1
  436. package/lib/typescript/module/src/DatePicker.d.ts +74 -1
  437. package/lib/typescript/module/src/DatePicker.d.ts.map +1 -1
  438. package/lib/typescript/module/src/DatePickerNativeComponent.d.ts +16 -1
  439. package/lib/typescript/module/src/DatePickerNativeComponent.d.ts.map +1 -1
  440. package/lib/typescript/module/src/FloatingActionButton.d.ts +67 -0
  441. package/lib/typescript/module/src/FloatingActionButton.d.ts.map +1 -0
  442. package/lib/typescript/module/src/FloatingActionButtonNativeComponent.d.ts +43 -0
  443. package/lib/typescript/module/src/FloatingActionButtonNativeComponent.d.ts.map +1 -0
  444. package/lib/typescript/module/src/FloatingToolbar.d.ts +75 -0
  445. package/lib/typescript/module/src/FloatingToolbar.d.ts.map +1 -0
  446. package/lib/typescript/module/src/FloatingToolbarNativeComponent.d.ts +36 -0
  447. package/lib/typescript/module/src/FloatingToolbarNativeComponent.d.ts.map +1 -0
  448. package/lib/typescript/module/src/LiquidGlass.d.ts +17 -0
  449. package/lib/typescript/module/src/LiquidGlass.d.ts.map +1 -1
  450. package/lib/typescript/module/src/LiquidGlassContainer.d.ts +27 -0
  451. package/lib/typescript/module/src/LiquidGlassContainer.d.ts.map +1 -0
  452. package/lib/typescript/module/src/LiquidGlassContainerNativeComponent.d.ts +13 -0
  453. package/lib/typescript/module/src/LiquidGlassContainerNativeComponent.d.ts.map +1 -0
  454. package/lib/typescript/module/src/LiquidGlassNativeComponent.d.ts +7 -0
  455. package/lib/typescript/module/src/LiquidGlassNativeComponent.d.ts.map +1 -1
  456. package/lib/typescript/module/src/NavigationRail.d.ts +86 -0
  457. package/lib/typescript/module/src/NavigationRail.d.ts.map +1 -0
  458. package/lib/typescript/module/src/NavigationRailNativeComponent.d.ts +81 -0
  459. package/lib/typescript/module/src/NavigationRailNativeComponent.d.ts.map +1 -0
  460. package/lib/typescript/module/src/PlatformComponentsProvider.d.ts +6 -0
  461. package/lib/typescript/module/src/PlatformComponentsProvider.d.ts.map +1 -0
  462. package/lib/typescript/module/src/SegmentedControl.d.ts +35 -47
  463. package/lib/typescript/module/src/SegmentedControl.d.ts.map +1 -1
  464. package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts +11 -0
  465. package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts.map +1 -1
  466. package/lib/typescript/module/src/SelectionMenu.d.ts +44 -2
  467. package/lib/typescript/module/src/SelectionMenu.d.ts.map +1 -1
  468. package/lib/typescript/module/src/SelectionMenuNativeComponent.d.ts +20 -4
  469. package/lib/typescript/module/src/SelectionMenuNativeComponent.d.ts.map +1 -1
  470. package/lib/typescript/module/src/SplitButton.d.ts +83 -0
  471. package/lib/typescript/module/src/SplitButton.d.ts.map +1 -0
  472. package/lib/typescript/module/src/TabBar.d.ts +144 -0
  473. package/lib/typescript/module/src/TabBar.d.ts.map +1 -0
  474. package/lib/typescript/module/src/TabBarAccessoryNativeComponent.d.ts +13 -0
  475. package/lib/typescript/module/src/TabBarAccessoryNativeComponent.d.ts.map +1 -0
  476. package/lib/typescript/module/src/TabBarNativeComponent.d.ts +110 -0
  477. package/lib/typescript/module/src/TabBarNativeComponent.d.ts.map +1 -0
  478. package/lib/typescript/module/src/TextField.d.ts +332 -0
  479. package/lib/typescript/module/src/TextField.d.ts.map +1 -0
  480. package/lib/typescript/module/src/TextFieldNativeComponent.d.ts +240 -0
  481. package/lib/typescript/module/src/TextFieldNativeComponent.d.ts.map +1 -0
  482. package/lib/typescript/module/src/focusRegistry.d.ts +13 -0
  483. package/lib/typescript/module/src/focusRegistry.d.ts.map +1 -0
  484. package/lib/typescript/module/src/haptics.d.ts +20 -0
  485. package/lib/typescript/module/src/haptics.d.ts.map +1 -0
  486. package/lib/typescript/module/src/icons.d.ts +58 -0
  487. package/lib/typescript/module/src/icons.d.ts.map +1 -0
  488. package/lib/typescript/module/src/index.d.ts +13 -0
  489. package/lib/typescript/module/src/index.d.ts.map +1 -1
  490. package/lib/typescript/module/src/index.web.d.ts +25 -133
  491. package/lib/typescript/module/src/index.web.d.ts.map +1 -1
  492. package/lib/typescript/module/src/jest.d.ts +139 -0
  493. package/lib/typescript/module/src/jest.d.ts.map +1 -0
  494. package/lib/typescript/module/src/labelStyle.d.ts +20 -0
  495. package/lib/typescript/module/src/labelStyle.d.ts.map +1 -0
  496. package/lib/typescript/module/src/menuItems.d.ts +100 -0
  497. package/lib/typescript/module/src/menuItems.d.ts.map +1 -0
  498. package/lib/typescript/module/src/selectionIdentifiers.d.ts +3 -0
  499. package/lib/typescript/module/src/selectionIdentifiers.d.ts.map +1 -0
  500. package/lib/typescript/module/src/sharedTypes.d.ts +6 -1
  501. package/lib/typescript/module/src/sharedTypes.d.ts.map +1 -1
  502. package/lib/typescript/module/src/submitBehavior.d.ts +9 -0
  503. package/lib/typescript/module/src/submitBehavior.d.ts.map +1 -0
  504. package/lib/typescript/module/src/tabItems.d.ts +55 -0
  505. package/lib/typescript/module/src/tabItems.d.ts.map +1 -0
  506. package/lib/typescript/module/src/web/Button.d.ts +9 -0
  507. package/lib/typescript/module/src/web/Button.d.ts.map +1 -0
  508. package/lib/typescript/module/src/web/ButtonGroup.d.ts +11 -0
  509. package/lib/typescript/module/src/web/ButtonGroup.d.ts.map +1 -0
  510. package/lib/typescript/module/src/web/DatePicker.d.ts +14 -0
  511. package/lib/typescript/module/src/web/DatePicker.d.ts.map +1 -0
  512. package/lib/typescript/module/src/web/DateRangePicker.d.ts +11 -0
  513. package/lib/typescript/module/src/web/DateRangePicker.d.ts.map +1 -0
  514. package/lib/typescript/module/src/web/Dialog.d.ts +12 -0
  515. package/lib/typescript/module/src/web/Dialog.d.ts.map +1 -0
  516. package/lib/typescript/module/src/web/FloatingActionButton.d.ts +8 -0
  517. package/lib/typescript/module/src/web/FloatingActionButton.d.ts.map +1 -0
  518. package/lib/typescript/module/src/web/Icon.d.ts +12 -0
  519. package/lib/typescript/module/src/web/Icon.d.ts.map +1 -0
  520. package/lib/typescript/module/src/web/NativeTheme.d.ts +10 -0
  521. package/lib/typescript/module/src/web/NativeTheme.d.ts.map +1 -0
  522. package/lib/typescript/module/src/web/NavigationRail.d.ts +8 -0
  523. package/lib/typescript/module/src/web/NavigationRail.d.ts.map +1 -0
  524. package/lib/typescript/module/src/web/PlatformComponentsProvider.d.ts +10 -0
  525. package/lib/typescript/module/src/web/PlatformComponentsProvider.d.ts.map +1 -0
  526. package/lib/typescript/module/src/web/SegmentedControl.d.ts +8 -0
  527. package/lib/typescript/module/src/web/SegmentedControl.d.ts.map +1 -0
  528. package/lib/typescript/module/src/web/SelectionMenu.d.ts +10 -0
  529. package/lib/typescript/module/src/web/SelectionMenu.d.ts.map +1 -0
  530. package/lib/typescript/module/src/web/SplitButton.d.ts +9 -0
  531. package/lib/typescript/module/src/web/SplitButton.d.ts.map +1 -0
  532. package/lib/typescript/module/src/web/TabBar.d.ts +9 -0
  533. package/lib/typescript/module/src/web/TabBar.d.ts.map +1 -0
  534. package/lib/typescript/module/src/web/TextField.d.ts +9 -0
  535. package/lib/typescript/module/src/web/TextField.d.ts.map +1 -0
  536. package/lib/typescript/module/src/web/components.d.ts +17 -0
  537. package/lib/typescript/module/src/web/components.d.ts.map +1 -0
  538. package/lib/typescript/module/src/web/containers.d.ts +24 -0
  539. package/lib/typescript/module/src/web/containers.d.ts.map +1 -0
  540. package/lib/typescript/module/src/web/dateInput.d.ts +20 -0
  541. package/lib/typescript/module/src/web/dateInput.d.ts.map +1 -0
  542. package/lib/typescript/module/src/web/shared.d.ts +43 -0
  543. package/lib/typescript/module/src/web/shared.d.ts.map +1 -0
  544. package/lib/typescript/module/src/webComponents.d.ts +43 -0
  545. package/lib/typescript/module/src/webComponents.d.ts.map +1 -0
  546. package/package.json +55 -4
  547. package/plugin/build/index.d.ts +3 -2
  548. package/plugin/build/index.d.ts.map +1 -1
  549. package/react-native.config.js +6 -0
  550. package/shared/PCButtonComponentDescriptors-custom.h +22 -0
  551. package/shared/PCButtonGroupComponentDescriptors-custom.h +22 -0
  552. package/shared/PCButtonGroupShadowNode-custom.cpp +48 -0
  553. package/shared/PCButtonGroupShadowNode-custom.h +55 -0
  554. package/shared/PCButtonGroupState-custom.h +61 -0
  555. package/shared/PCButtonShadowNode-custom.cpp +48 -0
  556. package/shared/PCButtonShadowNode-custom.h +55 -0
  557. package/shared/PCButtonState-custom.h +61 -0
  558. package/shared/PCFloatingActionButtonComponentDescriptors-custom.h +22 -0
  559. package/shared/PCFloatingActionButtonShadowNode-custom.cpp +48 -0
  560. package/shared/PCFloatingActionButtonShadowNode-custom.h +54 -0
  561. package/shared/PCFloatingActionButtonState-custom.h +61 -0
  562. package/shared/PCNavigationRailComponentDescriptors-custom.h +47 -0
  563. package/shared/PCNavigationRailShadowNode-custom.h +42 -0
  564. package/shared/PCNavigationRailState-custom.h +62 -0
  565. package/shared/PCTabBarComponentDescriptors-custom.h +22 -0
  566. package/shared/PCTabBarShadowNode-custom.cpp +54 -0
  567. package/shared/PCTabBarShadowNode-custom.h +54 -0
  568. package/shared/PCTabBarState-custom.h +62 -0
  569. package/shared/PCTextFieldComponentDescriptors-custom.h +22 -0
  570. package/shared/PCTextFieldShadowNode-custom.cpp +47 -0
  571. package/shared/PCTextFieldShadowNode-custom.h +55 -0
  572. package/shared/PCTextFieldState-custom.h +61 -0
  573. package/shared/react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h +6 -0
  574. package/src/Button.tsx +359 -0
  575. package/src/ButtonGroup.tsx +251 -0
  576. package/src/ButtonGroupNativeComponent.ts +191 -0
  577. package/src/ButtonNativeComponent.ts +192 -0
  578. package/src/ContextMenu.tsx +34 -81
  579. package/src/ContextMenuNativeComponent.ts +47 -49
  580. package/src/DatePicker.tsx +182 -4
  581. package/src/DatePickerNativeComponent.ts +19 -2
  582. package/src/FloatingActionButton.tsx +121 -0
  583. package/src/FloatingActionButtonNativeComponent.ts +58 -0
  584. package/src/FloatingToolbar.tsx +164 -0
  585. package/src/FloatingToolbarNativeComponent.ts +47 -0
  586. package/src/LiquidGlass.tsx +24 -1
  587. package/src/LiquidGlassContainer.tsx +42 -0
  588. package/src/LiquidGlassContainerNativeComponent.ts +17 -0
  589. package/src/LiquidGlassNativeComponent.ts +8 -0
  590. package/src/NavigationRail.tsx +203 -0
  591. package/src/NavigationRailNativeComponent.ts +107 -0
  592. package/src/PlatformComponentsProvider.tsx +15 -0
  593. package/src/SegmentedControl.tsx +57 -119
  594. package/src/SegmentedControlNativeComponent.ts +14 -0
  595. package/src/SelectionMenu.tsx +81 -6
  596. package/src/SelectionMenuNativeComponent.ts +26 -4
  597. package/src/SplitButton.tsx +198 -0
  598. package/src/TabBar.tsx +373 -0
  599. package/src/TabBarAccessoryNativeComponent.ts +19 -0
  600. package/src/TabBarNativeComponent.ts +145 -0
  601. package/src/TextField.tsx +908 -0
  602. package/src/TextFieldNativeComponent.ts +324 -0
  603. package/src/focusRegistry.ts +90 -0
  604. package/src/haptics.ts +31 -0
  605. package/src/icons.ts +143 -0
  606. package/src/index.tsx +13 -0
  607. package/src/index.web.tsx +53 -199
  608. package/src/jest.tsx +958 -0
  609. package/src/labelStyle.ts +36 -0
  610. package/src/menuItems.ts +152 -0
  611. package/src/reactNativeInternals.d.ts +22 -0
  612. package/src/selectionIdentifiers.ts +17 -0
  613. package/src/sharedTypes.ts +7 -1
  614. package/src/submitBehavior.ts +16 -0
  615. package/src/tabItems.ts +113 -0
  616. package/src/web/Button.tsx +201 -0
  617. package/src/web/ButtonGroup.tsx +154 -0
  618. package/src/web/DatePicker.tsx +204 -0
  619. package/src/web/DateRangePicker.tsx +135 -0
  620. package/src/web/Dialog.tsx +54 -0
  621. package/src/web/FloatingActionButton.tsx +76 -0
  622. package/src/web/Icon.tsx +33 -0
  623. package/src/web/NativeTheme.ts +23 -0
  624. package/src/web/NavigationRail.tsx +148 -0
  625. package/src/web/PlatformComponentsProvider.tsx +48 -0
  626. package/src/web/SegmentedControl.tsx +127 -0
  627. package/src/web/SelectionMenu.tsx +149 -0
  628. package/src/web/SplitButton.tsx +32 -0
  629. package/src/web/TabBar.tsx +150 -0
  630. package/src/web/TextField.tsx +361 -0
  631. package/src/web/components.tsx +71 -0
  632. package/src/web/containers.tsx +130 -0
  633. package/src/web/dateInput.ts +137 -0
  634. package/src/web/shared.ts +176 -0
  635. package/src/webComponents.ts +45 -0
  636. package/android/src/main/java/com/platformcomponents/DateConstraints.kt +0 -83
@@ -0,0 +1,1393 @@
1
+ package com.platformcomponents
2
+
3
+ import android.content.Context
4
+ import android.graphics.Rect
5
+ import android.graphics.drawable.BitmapDrawable
6
+ import android.graphics.drawable.Drawable
7
+ import android.os.Build
8
+ import android.text.Editable
9
+ import android.text.InputFilter
10
+ import android.text.InputType
11
+ import android.text.TextWatcher
12
+ import android.view.ContextThemeWrapper
13
+ import android.view.Gravity
14
+ import android.view.KeyEvent
15
+ import android.view.View
16
+ import android.view.ViewGroup
17
+ import android.view.inputmethod.EditorInfo
18
+ import android.view.inputmethod.InputConnection
19
+ import android.view.inputmethod.InputMethodManager
20
+ import android.widget.FrameLayout
21
+ import android.widget.LinearLayout
22
+ import android.widget.TextView
23
+ import android.content.res.ColorStateList
24
+ import android.view.accessibility.AccessibilityNodeInfo
25
+ import android.widget.Button
26
+ import android.util.TypedValue
27
+ import android.view.MotionEvent
28
+ import androidx.core.widget.TextViewCompat
29
+ import androidx.appcompat.R as AppCompatR
30
+ import com.facebook.react.bridge.WritableNativeMap
31
+ import com.facebook.react.uimanager.PixelUtil
32
+ import com.facebook.react.uimanager.ReactCompoundViewGroup
33
+ import com.facebook.react.uimanager.StateWrapper
34
+ import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper
35
+ import com.facebook.react.views.scroll.ReactScrollViewHelper
36
+ import com.google.android.material.textfield.TextInputEditText
37
+ import com.google.android.material.textfield.TextInputLayout
38
+ import com.google.android.material.R as MaterialR
39
+
40
+ /**
41
+ * A Material 3 text field: a TextInputLayout (floating label, box, icons,
42
+ * supporting text, counter) around a TextInputEditText. With
43
+ * `android.material: 'system'` it is the platform EditText instead (the hint
44
+ * as placeholder, compound-drawable icons, supporting text below), as the
45
+ * other components' system mode gives the AppCompat widget.
46
+ *
47
+ * The widgets are rebuilt for the props they read at construction (the box
48
+ * style, the native theme); everything else is applied to the live widgets.
49
+ * The text and its edit counter live here, so a rebuild keeps them.
50
+ *
51
+ * Text flows one way at a time: user edits report an event count with the
52
+ * text, and JS pushes a controlled value back with the last count it saw.
53
+ * A push that is older than the latest edit is dropped, so a slow JS
54
+ * round-trip can't erase what was typed meanwhile.
55
+ */
56
+ class PCTextFieldView(context: Context) :
57
+ FrameLayout(context),
58
+ ReactScrollViewHelper.HasStateWrapper,
59
+ ReactCompoundViewGroup {
60
+
61
+ companion object {
62
+ private const val TAG = "PCTextField"
63
+ }
64
+
65
+ // --- State Wrapper for Fabric state updates ---
66
+ override var stateWrapper: StateWrapper? = null
67
+
68
+ private var lastReportedHeight: Float = -1f
69
+
70
+ // --- Props ---
71
+ var label: String = ""
72
+ var placeholder: String = ""
73
+ var supportingText: String = ""
74
+ var errorState: String = "none" // "none" | "error"
75
+ var errorText: String = ""
76
+ var prefix: String = ""
77
+ var suffix: String = ""
78
+ var leadingIcon: PCButtonSupport.Icon = PCButtonSupport.NO_ICON
79
+ var trailingIcon: PCButtonSupport.Icon = PCButtonSupport.NO_ICON
80
+ var clearButtonMode: String = "never"
81
+ var passwordToggle: Boolean = false
82
+ var characterCount: Boolean = false
83
+ var maxLength: Int = 0
84
+ var keyboardType: String = "default"
85
+ var returnKeyType: String = "default"
86
+ var autoCapitalize: String = "sentences"
87
+ var autoCorrect: Boolean = true
88
+ var secure: Boolean = false
89
+ var multiline: Boolean = false
90
+ var submitBehavior: String = "" // "" | "submit" | "blurAndSubmit" | "newline"
91
+ var fieldEnabled: Boolean = true
92
+ var autoFocus: Boolean = false
93
+ var selectTextOnFocus: Boolean = false
94
+ var autoComplete: String = ""
95
+ var spokenLabel: String = ""
96
+ var fontFamily: String = ""
97
+ var fontSize: Float = 0f
98
+ var fontWeight: String = ""
99
+ var fontStyle: String = ""
100
+ var variant: String = "outlined" // "outlined" | "filled" | "plain"
101
+ var dense: Boolean = false
102
+ var materialMode: String = "m3" // "m3" | "system"
103
+ var inputTestID: String = ""
104
+ var leadingIconTestID: String = ""
105
+ var leadingIconSpokenLabel: String = ""
106
+ var trailingIconTestID: String = ""
107
+ var trailingIconSpokenLabel: String = ""
108
+ var activeColor: Int? = null
109
+ var outlineColor: Int? = null
110
+ var errorColor: Int? = null
111
+ var containerColor: Int? = null
112
+ var textColor: Int? = null
113
+ var placeholderTextColor: Int? = null
114
+ var maxFontSizeMultiplier: Float = 0f
115
+ var textAlign: String = "" // "" | "left" | "center" | "right"
116
+ var minLines: Int = 0
117
+ var maxLines: Int = 0
118
+ var pressable: Boolean = false
119
+
120
+ // --- Events ---
121
+ var onChange: ((text: String, eventCount: Int) -> Unit)? = null
122
+ var onFocusChange: ((focused: Boolean, text: String) -> Unit)? = null
123
+ var onSubmit: ((text: String) -> Unit)? = null
124
+ var onSelectionChange: ((start: Int, end: Int) -> Unit)? = null
125
+ var onTrailingIconPress: (() -> Unit)? = null
126
+ var onPress: (() -> Unit)? = null
127
+
128
+ // --- Text state (survives rebuilds) ---
129
+ private var text: String = ""
130
+ private var nativeEventCount = 0
131
+ private var settingTextInternally = false
132
+ private var initialTextApplied = false
133
+ private var autoFocusDone = false
134
+
135
+ // The selection JS last heard of, so a change is reported once. Text and
136
+ // selection set from JS update it without an event. Declared before init:
137
+ // the edit text reports selections while rebuildUI() builds it.
138
+ private var reportedSelectionStart = -1
139
+ private var reportedSelectionEnd = -1
140
+ private var applyingSelection = false
141
+
142
+ // --- UI ---
143
+ /** The top-level widget: the TextInputLayout, or the system field's column. */
144
+ private var widget: View? = null
145
+ private var layout: TextInputLayout? = null
146
+ private var editText: FieldEditText? = null
147
+ private var helperView: TextView? = null
148
+
149
+ /** The Material style's error icon, shown in the end slot while there is an error. */
150
+ private var defaultErrorIcon: Drawable? = null
151
+ private var defaultStartIconTint: ColorStateList? = null
152
+ private var defaultEndIconTint: ColorStateList? = null
153
+
154
+ /** Bumped on every rebuild and icon change so late image loads can't touch stale widgets. */
155
+ private var startIconGeneration = 0
156
+ private var endIconGeneration = 0
157
+
158
+ // --- Native theme ---
159
+ // Widgets read theme colors when they are created, so they are rebuilt when the
160
+ // native theme changes (brand color, light/dark switch).
161
+ private var builtThemeVersion = PCNativeTheme.version
162
+ private val nativeThemeListener = PCNativeTheme.Listener {
163
+ PCThemeSupport.clearColorStateListCaches(this)
164
+ rebuildUI()
165
+ }
166
+
167
+ // Declared before init: rebuildUI() attaches the watcher, and Kotlin
168
+ // initializes properties in declaration order.
169
+ private val textWatcher = object : TextWatcher {
170
+ override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
171
+ override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
172
+ override fun afterTextChanged(s: Editable) {
173
+ val next = s.toString()
174
+ if (settingTextInternally) {
175
+ text = next
176
+ return
177
+ }
178
+ if (next == text) return
179
+ text = next
180
+ nativeEventCount += 1
181
+ onChange?.invoke(next, nativeEventCount)
182
+ }
183
+ }
184
+
185
+ private var manualLayoutPending = false
186
+
187
+ init {
188
+ clipChildren = false
189
+ clipToPadding = false
190
+ rebuildUI()
191
+ }
192
+
193
+ // ---- Public apply* (called by manager) ----
194
+
195
+ /** The text the field starts with; only the first value is used (see setTextFromJS). */
196
+ fun applyInitialText(value: String) {
197
+ if (initialTextApplied) return
198
+ initialTextApplied = true
199
+ setTextInternal(value, moveCursorToEnd = true)
200
+ }
201
+
202
+ fun applyLabel(value: String) {
203
+ if (label == value) return
204
+ label = value
205
+ applyHintAndPlaceholder()
206
+ }
207
+
208
+ fun applyPlaceholder(value: String) {
209
+ if (placeholder == value) return
210
+ placeholder = value
211
+ applyHintAndPlaceholder()
212
+ }
213
+
214
+ fun applySupportingText(value: String) {
215
+ if (supportingText == value) return
216
+ supportingText = value
217
+ applyHelperAndError()
218
+ }
219
+
220
+ fun applyError(state: String, message: String) {
221
+ val nextState = if (state == "error") "error" else "none"
222
+ if (errorState == nextState && errorText == message) return
223
+ errorState = nextState
224
+ errorText = message
225
+ applyHelperAndError()
226
+ }
227
+
228
+ fun applyPrefix(value: String) {
229
+ if (prefix == value) return
230
+ prefix = value
231
+ layout?.prefixText = value.ifEmpty { null }
232
+ requestLayout()
233
+ }
234
+
235
+ fun applySuffix(value: String) {
236
+ if (suffix == value) return
237
+ suffix = value
238
+ layout?.suffixText = value.ifEmpty { null }
239
+ requestLayout()
240
+ }
241
+
242
+ fun applyLeadingIcon(value: PCButtonSupport.Icon) {
243
+ if (leadingIcon == value) return
244
+ leadingIcon = value
245
+ applyStartIcon()
246
+ }
247
+
248
+ fun applyTrailingIcon(value: PCButtonSupport.Icon) {
249
+ if (trailingIcon == value) return
250
+ trailingIcon = value
251
+ applyEndIcon()
252
+ }
253
+
254
+ fun applyClearButtonMode(value: String) {
255
+ if (clearButtonMode == value) return
256
+ clearButtonMode = value
257
+ applyEndIcon()
258
+ }
259
+
260
+ fun applyPasswordToggle(value: Boolean) {
261
+ if (passwordToggle == value) return
262
+ passwordToggle = value
263
+ applyEndIcon()
264
+ }
265
+
266
+ fun applyCharacterCount(value: Boolean) {
267
+ if (characterCount == value) return
268
+ characterCount = value
269
+ applyCounterAndLength()
270
+ }
271
+
272
+ fun applyMaxLength(value: Int) {
273
+ if (maxLength == value) return
274
+ maxLength = value
275
+ applyCounterAndLength()
276
+ }
277
+
278
+ fun applyKeyboardType(value: String) {
279
+ if (keyboardType == value) return
280
+ keyboardType = value
281
+ applyInputType()
282
+ }
283
+
284
+ fun applyReturnKeyType(value: String) {
285
+ if (returnKeyType == value) return
286
+ returnKeyType = value
287
+ applyInputType()
288
+ }
289
+
290
+ fun applyAutoCapitalize(value: String) {
291
+ if (autoCapitalize == value) return
292
+ autoCapitalize = value
293
+ applyInputType()
294
+ }
295
+
296
+ fun applyAutoCorrect(value: Boolean) {
297
+ if (autoCorrect == value) return
298
+ autoCorrect = value
299
+ applyInputType()
300
+ }
301
+
302
+ fun applySecure(value: Boolean) {
303
+ if (secure == value) return
304
+ secure = value
305
+ applyInputType()
306
+ }
307
+
308
+ fun applyMultiline(value: Boolean) {
309
+ if (multiline == value) return
310
+ multiline = value
311
+ applyInputType()
312
+ }
313
+
314
+ fun applySubmitBehavior(value: String) {
315
+ if (submitBehavior == value) return
316
+ submitBehavior = value
317
+ // The IME asks for the enter key's action when input starts
318
+ val edit = editText ?: return
319
+ if (multiline && edit.isFocused) {
320
+ val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
321
+ imm?.restartInput(edit)
322
+ }
323
+ }
324
+
325
+ fun applyInteractivity(value: String?) {
326
+ val next = value != "disabled"
327
+ if (fieldEnabled == next) return
328
+ fieldEnabled = next
329
+ applyPressMode()
330
+ }
331
+
332
+ fun applyAutoFocus(value: Boolean) {
333
+ autoFocus = value
334
+ if (value && isAttachedToWindow) performAutoFocus()
335
+ }
336
+
337
+ fun applySelectTextOnFocus(value: Boolean) {
338
+ if (selectTextOnFocus == value) return
339
+ selectTextOnFocus = value
340
+ editText?.setSelectAllOnFocus(value)
341
+ }
342
+
343
+ fun applyAutoComplete(value: String) {
344
+ if (autoComplete == value) return
345
+ autoComplete = value
346
+ applyAutofill()
347
+ }
348
+
349
+ fun applySpokenLabel(value: String) {
350
+ if (spokenLabel == value) return
351
+ spokenLabel = value
352
+ editText?.contentDescription = value.ifEmpty { null }
353
+ }
354
+
355
+ fun applyTextStyle(family: String, size: Float, weight: String, style: String) {
356
+ if (fontFamily == family && fontSize == size && fontWeight == weight && fontStyle == style) return
357
+ fontFamily = family
358
+ fontSize = size
359
+ fontWeight = weight
360
+ fontStyle = style
361
+ editText?.let { applyFont(it) }
362
+ requestLayout()
363
+ }
364
+
365
+ fun applyVariant(value: String?) {
366
+ val next = when (value) {
367
+ "filled", "plain" -> value
368
+ else -> "outlined"
369
+ }
370
+ if (variant == next) return
371
+ variant = next
372
+ rebuildUI()
373
+ }
374
+
375
+ fun applyDensity(value: String?) {
376
+ val next = value == "dense"
377
+ if (dense == next) return
378
+ dense = next
379
+ rebuildUI()
380
+ }
381
+
382
+ fun applyMaterial(value: String?) {
383
+ val next = if (value == "system") "system" else "m3"
384
+ if (materialMode == next) return
385
+ materialMode = next
386
+ rebuildUI()
387
+ }
388
+
389
+ fun applyInputTestID(value: String) {
390
+ if (inputTestID == value) return
391
+ inputTestID = value
392
+ applyTestIDs()
393
+ }
394
+
395
+ fun applyIconAccessibility(leadingID: String, leadingLabel: String, trailingID: String, trailingLabel: String) {
396
+ if (leadingIconTestID == leadingID && leadingIconSpokenLabel == leadingLabel &&
397
+ trailingIconTestID == trailingID && trailingIconSpokenLabel == trailingLabel
398
+ ) return
399
+ leadingIconTestID = leadingID
400
+ leadingIconSpokenLabel = leadingLabel
401
+ trailingIconTestID = trailingID
402
+ trailingIconSpokenLabel = trailingLabel
403
+ applyTestIDs()
404
+ }
405
+
406
+ fun applyColors(active: Int?, outline: Int?, error: Int?, container: Int?, textValue: Int?, placeholderValue: Int?) {
407
+ if (activeColor == active && outlineColor == outline && errorColor == error &&
408
+ containerColor == container && textColor == textValue && placeholderTextColor == placeholderValue
409
+ ) return
410
+ // A color going back to the default needs fresh widgets; the rest applies live
411
+ val cleared = (activeColor != null && active == null) || (outlineColor != null && outline == null) ||
412
+ (errorColor != null && error == null) || (containerColor != null && container == null) ||
413
+ (textColor != null && textValue == null) || (placeholderTextColor != null && placeholderValue == null)
414
+ activeColor = active
415
+ outlineColor = outline
416
+ errorColor = error
417
+ containerColor = container
418
+ textColor = textValue
419
+ placeholderTextColor = placeholderValue
420
+ if (cleared) rebuildUI() else applyColors()
421
+ }
422
+
423
+ fun applyMaxFontSizeMultiplier(value: Float) {
424
+ if (maxFontSizeMultiplier == value) return
425
+ maxFontSizeMultiplier = value
426
+ // Fresh widgets start from their uncapped sizes
427
+ rebuildUI()
428
+ }
429
+
430
+ fun applyTextAlign(value: String) {
431
+ if (textAlign == value) return
432
+ textAlign = value
433
+ applyInputType()
434
+ }
435
+
436
+ fun applyLineBounds(min: Int, max: Int) {
437
+ if (minLines == min && maxLines == max) return
438
+ minLines = min
439
+ maxLines = max
440
+ applyInputType()
441
+ }
442
+
443
+ fun applyPressable(value: Boolean) {
444
+ if (pressable == value) return
445
+ pressable = value
446
+ applyPressMode()
447
+ }
448
+
449
+ // ---- Commands ----
450
+
451
+ fun focusFromJS() {
452
+ val edit = editText ?: return
453
+ edit.requestFocus()
454
+ if (edit.isInTouchMode) showKeyboard(edit)
455
+ }
456
+
457
+ fun blurFromJS() {
458
+ val edit = editText ?: return
459
+ clearFocusAndMaybeRefocus(edit)
460
+ hideKeyboard()
461
+ }
462
+
463
+ /** Clears the text as if the user had, so JS gets a change event. */
464
+ fun clearFromJS() {
465
+ val edit = editText ?: return
466
+ if (edit.length() == 0) return
467
+ edit.setText("")
468
+ }
469
+
470
+ /**
471
+ * A controlled value from JS. Dropped when the user has edited since
472
+ * [eventCount] was reported: the edit event that follows carries the text
473
+ * JS should reconcile against.
474
+ */
475
+ fun setTextFromJS(eventCount: Int, value: String) {
476
+ if (eventCount < nativeEventCount) return
477
+ if (value == text) return
478
+ setTextInternal(value, moveCursorToEnd = false)
479
+ }
480
+
481
+ /**
482
+ * A selection from JS, clamped to the text. Dropped like a stale
483
+ * [setTextFromJS] when the user has edited since [eventCount].
484
+ */
485
+ fun setSelectionFromJS(eventCount: Int, start: Int, end: Int) {
486
+ if (eventCount < nativeEventCount) return
487
+ val edit = editText ?: return
488
+ val length = edit.length()
489
+ val from = start.coerceIn(0, length)
490
+ val to = end.coerceIn(from, length)
491
+ applyingSelection = true
492
+ edit.setSelection(from, to)
493
+ applyingSelection = false
494
+ reportedSelectionStart = edit.selectionStart
495
+ reportedSelectionEnd = edit.selectionEnd
496
+ }
497
+
498
+ // ---- Touch ----
499
+
500
+ // The widgets have generated view ids, which React Native's touch handling
501
+ // would take for React tags and dispatch JS touch events to unrelated views.
502
+ // Claim the touch for this view; the field still receives it natively.
503
+ override fun interceptsTouchEvent(touchX: Float, touchY: Float): Boolean = true
504
+
505
+ override fun reactTagForTouch(touchX: Float, touchY: Float): Int = id
506
+
507
+ // ---- Lifecycle ----
508
+
509
+ override fun onAttachedToWindow() {
510
+ super.onAttachedToWindow()
511
+ PCNativeTheme.addListener(nativeThemeListener)
512
+ PCNativeTheme.attach(context)
513
+ if (builtThemeVersion != PCNativeTheme.version) {
514
+ nativeThemeListener.onNativeThemeChanged()
515
+ }
516
+ if (autoFocus) performAutoFocus()
517
+ }
518
+
519
+ override fun onDetachedFromWindow() {
520
+ PCNativeTheme.removeListener(nativeThemeListener)
521
+ super.onDetachedFromWindow()
522
+ }
523
+
524
+ private fun performAutoFocus() {
525
+ if (autoFocusDone) return
526
+ autoFocusDone = true
527
+ post { focusFromJS() }
528
+ }
529
+
530
+ // ---- UI Building ----
531
+
532
+ private fun rebuildUI() {
533
+ builtThemeVersion = PCNativeTheme.version
534
+ val wasFocused = editText?.isFocused == true
535
+ val selection = editText?.selectionEnd ?: -1
536
+ removeAllViews()
537
+ startIconGeneration += 1
538
+ endIconGeneration += 1
539
+ layout = null
540
+ helperView = null
541
+
542
+ val edit: FieldEditText
543
+ if (materialMode == "system") {
544
+ // The platform field: an AppCompat EditText with a supporting-text line
545
+ val column = LinearLayout(context).apply {
546
+ orientation = LinearLayout.VERTICAL
547
+ layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
548
+ }
549
+ edit = FieldEditText(context).apply {
550
+ id = View.generateViewId()
551
+ layoutParams = LinearLayout.LayoutParams(
552
+ ViewGroup.LayoutParams.MATCH_PARENT,
553
+ ViewGroup.LayoutParams.WRAP_CONTENT
554
+ )
555
+ compoundDrawablePadding = dp(8)
556
+ setSelectAllOnFocus(selectTextOnFocus)
557
+ contentDescription = spokenLabel.ifEmpty { null }
558
+ isEnabled = fieldEnabled
559
+ }
560
+ val helper = TextView(context).apply {
561
+ layoutParams = LinearLayout.LayoutParams(
562
+ ViewGroup.LayoutParams.MATCH_PARENT,
563
+ ViewGroup.LayoutParams.WRAP_CONTENT
564
+ ).apply { marginStart = dp(4); topMargin = dp(2) }
565
+ TextViewCompat.setTextAppearance(this, android.R.style.TextAppearance_Material_Small)
566
+ visibility = View.GONE
567
+ }
568
+ column.addView(edit)
569
+ column.addView(helper)
570
+ addView(column)
571
+ widget = column
572
+ helperView = helper
573
+ editText = edit
574
+ } else {
575
+ // Material widgets need a Material theme; fall back to Material 3 defaults instead of crashing.
576
+ val base = PCThemeSupport.materialContext(context, "TextField")
577
+ val styleOverlay = when {
578
+ variant != "outlined" && dense -> R.style.PCTextField_Filled_Dense
579
+ variant != "outlined" -> R.style.PCTextField_Filled
580
+ dense -> R.style.PCTextField_Outlined_Dense
581
+ else -> R.style.PCTextField_Outlined
582
+ }
583
+
584
+ val til = TextInputLayout(ContextThemeWrapper(base, styleOverlay)).apply {
585
+ layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
586
+ }
587
+
588
+ // The layout's context carries the style's theme overlay for its edit text
589
+ edit = FieldEditText(til.context).apply {
590
+ id = View.generateViewId()
591
+ layoutParams = LinearLayout.LayoutParams(
592
+ ViewGroup.LayoutParams.MATCH_PARENT,
593
+ ViewGroup.LayoutParams.WRAP_CONTENT
594
+ )
595
+ setSelectAllOnFocus(selectTextOnFocus)
596
+ contentDescription = spokenLabel.ifEmpty { null }
597
+ }
598
+ // Before it joins the layout, which sizes the resting label from it
599
+ applyFont(edit)
600
+ // The plain variant is the filled field without its box or underline;
601
+ // the mode must be set before the layout gives the edit text its box
602
+ if (variant == "plain") {
603
+ til.boxBackgroundMode = TextInputLayout.BOX_BACKGROUND_NONE
604
+ edit.background = null
605
+ }
606
+
607
+ til.addView(edit)
608
+ addView(til)
609
+ widget = til
610
+ layout = til
611
+ editText = edit
612
+ defaultErrorIcon = til.errorIconDrawable
613
+ // TextInputLayout has tint setters but no getters. Resolve the same
614
+ // styled attributes its constructor uses, preserving host theme state
615
+ // colors so changing an image's template mode need not replace the input.
616
+ // https://github.com/material-components/material-components-android/blob/1.14.0/lib/java/com/google/android/material/textfield/TextInputLayout.java
617
+ val iconTints = til.context.obtainStyledAttributes(
618
+ null,
619
+ MaterialR.styleable.TextInputLayout,
620
+ MaterialR.attr.textInputStyle,
621
+ MaterialR.style.Widget_Design_TextInputLayout
622
+ )
623
+ try {
624
+ defaultStartIconTint = iconTints.getColorStateList(MaterialR.styleable.TextInputLayout_startIconTint)
625
+ defaultEndIconTint = iconTints.getColorStateList(MaterialR.styleable.TextInputLayout_endIconTint)
626
+ } finally {
627
+ iconTints.recycle()
628
+ }
629
+ til.prefixText = prefix.ifEmpty { null }
630
+ til.suffixText = suffix.ifEmpty { null }
631
+ }
632
+
633
+ applyInputType()
634
+ applyHintAndPlaceholder()
635
+ applyHelperAndError()
636
+ applyStartIcon()
637
+ applyEndIcon()
638
+ applyCounterAndLength()
639
+ applyAutofill()
640
+ applyColors()
641
+ applyPressMode()
642
+ applyTestIDs()
643
+
644
+ // Restore the text without reporting it as an edit
645
+ settingTextInternally = true
646
+ edit.setText(text)
647
+ text = edit.text?.toString().orEmpty()
648
+ val length = edit.length()
649
+ edit.setSelection(if (selection in 0..length) selection else length)
650
+ settingTextInternally = false
651
+ reportedSelectionStart = edit.selectionStart
652
+ reportedSelectionEnd = edit.selectionEnd
653
+
654
+ edit.addTextChangedListener(textWatcher)
655
+ // Focus changes are reported from FieldEditText.onFocusChanged: the Material
656
+ // end-icon delegates (clear text, for one) install their own
657
+ // OnFocusChangeListener on the edit text, replacing any listener set here.
658
+ edit.setOnEditorActionListener { _, actionId, event -> onEditorAction(actionId, event) }
659
+
660
+ if (wasFocused) edit.requestFocus()
661
+ requestLayout()
662
+ }
663
+
664
+ /** What the return key does; a single-line field has no newline to insert. */
665
+ private val returnKeyBehavior: String
666
+ get() = when {
667
+ submitBehavior == "submit" -> "submit"
668
+ multiline && submitBehavior != "blurAndSubmit" -> "newline"
669
+ else -> "blurAndSubmit"
670
+ }
671
+
672
+ private fun onEditorAction(actionId: Int, event: KeyEvent?): Boolean {
673
+ val behavior = returnKeyBehavior
674
+ // Multi-line fields keep the return key for newlines unless they submit
675
+ if (behavior == "newline") return false
676
+ // A hardware / IME Enter reports IME_ACTION_UNSPECIFIED twice (key down and up)
677
+ if (actionId == EditorInfo.IME_ACTION_UNSPECIFIED && event != null && event.action != KeyEvent.ACTION_DOWN) {
678
+ return true
679
+ }
680
+ onSubmit?.invoke(text)
681
+ // Fields blur on submit unless submitBehavior is 'submit', as the core
682
+ // TextInput does; returning true keeps the keyboard up otherwise
683
+ if (behavior == "blurAndSubmit") blurFromJS()
684
+ return true
685
+ }
686
+
687
+ private fun selectionChanged(start: Int, end: Int) {
688
+ if (settingTextInternally || applyingSelection) return
689
+ if (start == reportedSelectionStart && end == reportedSelectionEnd) return
690
+ reportedSelectionStart = start
691
+ reportedSelectionEnd = end
692
+ onSelectionChange?.invoke(start, end)
693
+ }
694
+
695
+ private fun setTextInternal(value: String, moveCursorToEnd: Boolean) {
696
+ val edit = editText
697
+ if (edit == null) {
698
+ text = value
699
+ return
700
+ }
701
+ settingTextInternally = true
702
+ val wasAtEnd = edit.selectionEnd == edit.length()
703
+ val cursor = edit.selectionStart
704
+ edit.setText(value)
705
+ // InputFilter (notably maxLength) can change the supplied value. Events
706
+ // must report the text the user sees, including after a widget rebuild.
707
+ text = edit.text?.toString().orEmpty()
708
+ val length = edit.length()
709
+ edit.setSelection(if (moveCursorToEnd || wasAtEnd) length else cursor.coerceIn(0, length))
710
+ settingTextInternally = false
711
+ // The cursor moved with the text; JS knows, so there is no event
712
+ reportedSelectionStart = edit.selectionStart
713
+ reportedSelectionEnd = edit.selectionEnd
714
+ }
715
+
716
+ // ---- Decorations ----
717
+
718
+ /**
719
+ * With a label the Material layout floats it and shows the placeholder
720
+ * while focused; without one the placeholder is the edit text's own hint,
721
+ * and the box shrinks to the height of a plain field.
722
+ */
723
+ private fun applyHintAndPlaceholder() {
724
+ val edit = editText ?: return
725
+ val til = layout
726
+ if (til == null) {
727
+ // The platform field has one hint: the placeholder, else the label
728
+ edit.hint = placeholder.ifEmpty { label }.ifEmpty { null }
729
+ requestLayout()
730
+ return
731
+ }
732
+ if (label.isNotEmpty()) {
733
+ edit.hint = null
734
+ til.isHintEnabled = true
735
+ til.hint = label
736
+ til.placeholderText = placeholder.ifEmpty { null }
737
+ } else {
738
+ til.placeholderText = null
739
+ til.hint = null
740
+ til.isHintEnabled = false
741
+ edit.hint = placeholder.ifEmpty { null }
742
+ }
743
+ requestLayout()
744
+ }
745
+
746
+ private fun applyHelperAndError() {
747
+ val til = layout
748
+ if (til == null) {
749
+ applySystemHelper()
750
+ return
751
+ }
752
+ til.helperText = supportingText.ifEmpty { null }
753
+ if (errorState == "error") {
754
+ // No message: the supporting text turns to the error color, as the
755
+ // Material spec shows; with nothing to show, keep the outline red only
756
+ til.isErrorEnabled = true
757
+ til.error = errorText.ifEmpty { supportingText.ifEmpty { " " } }
758
+ } else {
759
+ til.error = null
760
+ til.isErrorEnabled = false
761
+ }
762
+ requestLayout()
763
+ }
764
+
765
+ /** The system field's supporting / error line, in the theme's secondary or error color. */
766
+ private fun applySystemHelper() {
767
+ val helper = helperView ?: return
768
+ val showsError = errorState == "error"
769
+ val message = if (showsError && errorText.isNotEmpty()) errorText else supportingText
770
+ helper.text = message
771
+ helper.visibility = if (message.isEmpty()) View.GONE else View.VISIBLE
772
+ val color = if (showsError) {
773
+ errorColor ?: themeColor(AppCompatR.attr.colorError, 0xFFB3261E.toInt())
774
+ } else {
775
+ themeColor(android.R.attr.textColorSecondary, 0xFF757575.toInt())
776
+ }
777
+ helper.setTextColor(color)
778
+ requestLayout()
779
+ }
780
+
781
+ private fun themeColor(attr: Int, fallback: Int): Int {
782
+ val value = TypedValue()
783
+ if (!context.theme.resolveAttribute(attr, value, true)) return fallback
784
+ return if (value.resourceId != 0) context.getColor(value.resourceId) else value.data
785
+ }
786
+
787
+ private fun dp(value: Int): Int = (value * resources.displayMetrics.density).toInt()
788
+
789
+ /** Icons of the system field: compound drawables, the trailing one tappable. */
790
+ private var systemStart: Drawable? = null
791
+ private var systemEnd: Drawable? = null
792
+
793
+ private fun applySystemIcons() {
794
+ val edit = editText ?: return
795
+ val start = systemStart
796
+ val end = systemEnd
797
+ val tint = ColorStateList.valueOf(themeColor(android.R.attr.colorControlNormal, 0xFF757575.toInt()))
798
+ // TextView's compound tint applies to every side. Each icon has its own
799
+ // template preference, so put the tint on the drawable instead.
800
+ // https://developer.android.com/reference/android/widget/TextView#setCompoundDrawableTintList(android.content.res.ColorStateList)
801
+ start?.mutate()?.setTintList(if (leadingIcon.tinted) tint else null)
802
+ end?.mutate()?.setTintList(if (trailingIcon.tinted) tint else null)
803
+ edit.setCompoundDrawablesRelativeWithIntrinsicBounds(start, null, end, null)
804
+ if (end != null) {
805
+ edit.setOnTouchListener { v, event ->
806
+ if (event.action == MotionEvent.ACTION_UP) {
807
+ val width = end.bounds.width() + edit.compoundDrawablePadding
808
+ val isRtl = v.layoutDirection == View.LAYOUT_DIRECTION_RTL
809
+ val hit = if (isRtl) event.x <= v.paddingStart + width else event.x >= v.width - v.paddingEnd - width
810
+ if (hit) {
811
+ v.performClick()
812
+ onTrailingIconPress?.invoke()
813
+ return@setOnTouchListener true
814
+ }
815
+ }
816
+ false
817
+ }
818
+ } else {
819
+ edit.setOnTouchListener(null)
820
+ }
821
+ requestLayout()
822
+ }
823
+
824
+ private fun applyStartIcon() {
825
+ startIconGeneration += 1
826
+ val generation = startIconGeneration
827
+ val til = layout
828
+ if (til == null) {
829
+ systemStart = null
830
+ if (leadingIcon.isPresent) {
831
+ loadIcon(leadingIcon) { drawable ->
832
+ if (generation != startIconGeneration || layout != null) return@loadIcon
833
+ systemStart = drawable
834
+ applySystemIcons()
835
+ }
836
+ }
837
+ applySystemIcons()
838
+ return
839
+ }
840
+ if (!leadingIcon.isPresent) {
841
+ til.startIconDrawable = null
842
+ requestLayout()
843
+ return
844
+ }
845
+ loadIcon(leadingIcon) { drawable ->
846
+ if (generation != startIconGeneration || layout !== til) return@loadIcon
847
+ til.setStartIconTintList(if (leadingIcon.tinted) defaultStartIconTint else null)
848
+ til.startIconDrawable = drawable
849
+ applyTestIDs()
850
+ requestLayout()
851
+ }
852
+ }
853
+
854
+ /**
855
+ * The end icon slot, by priority: a custom trailing icon, the password
856
+ * toggle, the clear button (shown while the focused field has text).
857
+ */
858
+ private fun applyEndIcon() {
859
+ endIconGeneration += 1
860
+ val generation = endIconGeneration
861
+ val til = layout
862
+ if (til == null) {
863
+ // The platform field has no clear button or password toggle
864
+ systemEnd = null
865
+ if (trailingIcon.isPresent) {
866
+ loadIcon(trailingIcon) { drawable ->
867
+ if (generation != endIconGeneration || layout != null) return@loadIcon
868
+ systemEnd = drawable
869
+ applySystemIcons()
870
+ }
871
+ }
872
+ applySystemIcons()
873
+ return
874
+ }
875
+ til.setEndIconTintList(if (trailingIcon.isPresent && !trailingIcon.tinted) null else defaultEndIconTint)
876
+ when {
877
+ trailingIcon.isPresent -> {
878
+ til.endIconMode = TextInputLayout.END_ICON_CUSTOM
879
+ til.setEndIconOnClickListener { onTrailingIconPress?.invoke() }
880
+ loadIcon(trailingIcon) { drawable ->
881
+ if (generation != endIconGeneration || layout !== til) return@loadIcon
882
+ til.endIconDrawable = drawable
883
+ applyTestIDs()
884
+ requestLayout()
885
+ }
886
+ }
887
+ passwordToggle -> til.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE
888
+ clearButtonMode != "never" -> til.endIconMode = TextInputLayout.END_ICON_CLEAR_TEXT
889
+ else -> til.endIconMode = TextInputLayout.END_ICON_NONE
890
+ }
891
+ // Material replaces the end icon with its error icon while an error shows;
892
+ // a password toggle or a custom icon stays, since the user still needs it
893
+ til.errorIconDrawable = if (trailingIcon.isPresent || passwordToggle) null else defaultErrorIcon
894
+ applyTestIDs()
895
+ requestLayout()
896
+ }
897
+
898
+ // ---- Test ids, colors, font scale, press ----
899
+
900
+ /**
901
+ * testID sits on the EditText, where Espresso's typeText / clearText work
902
+ * (Detox matches the view tag); the icon ids on the Material icon buttons.
903
+ * The system field's icons are compound drawables and can't carry one.
904
+ */
905
+ private fun applyTestIDs() {
906
+ val edit = editText ?: return
907
+ edit.tag = inputTestID.ifEmpty { null }
908
+ val til = layout ?: return
909
+ til.findViewById<View>(MaterialR.id.text_input_start_icon)?.tag = leadingIconTestID.ifEmpty { null }
910
+ til.startIconContentDescription = leadingIconSpokenLabel.ifEmpty { null }
911
+ // The password toggle and clear button keep Material's own descriptions
912
+ if (trailingIcon.isPresent) {
913
+ til.findViewById<View>(MaterialR.id.text_input_end_icon)?.tag = trailingIconTestID.ifEmpty { null }
914
+ til.endIconContentDescription = trailingIconSpokenLabel.ifEmpty { null }
915
+ } else {
916
+ til.findViewById<View>(MaterialR.id.text_input_end_icon)?.tag = null
917
+ }
918
+ }
919
+
920
+ /** Applies the color props over the style's colors; unset ones keep them. */
921
+ private fun applyColors() {
922
+ val edit = editText ?: return
923
+ textColor?.let { edit.setTextColor(it) }
924
+ placeholderTextColor?.let { edit.setHintTextColor(it) }
925
+ val til = layout
926
+ if (til == null) {
927
+ // The platform field: the underline follows the focus state
928
+ if (activeColor != null || outlineColor != null) {
929
+ val normal = outlineColor ?: themeColor(android.R.attr.colorControlNormal, 0xFF757575.toInt())
930
+ val focused = activeColor ?: themeColor(android.R.attr.colorControlActivated, normal)
931
+ edit.backgroundTintList = ColorStateList(
932
+ arrayOf(intArrayOf(android.R.attr.state_focused), intArrayOf()),
933
+ intArrayOf(focused, normal)
934
+ )
935
+ }
936
+ applySystemHelper()
937
+ return
938
+ }
939
+ if (activeColor != null || outlineColor != null) {
940
+ // The Material 3 stroke colors: outline (filled: on-surface-variant),
941
+ // on-surface while hovered, on-surface at 38% while disabled
942
+ fun attr(id: Int, fallback: Int): Int {
943
+ val value = TypedValue()
944
+ if (!til.context.theme.resolveAttribute(id, value, true)) return fallback
945
+ return if (value.resourceId != 0) til.context.getColor(value.resourceId) else value.data
946
+ }
947
+ val onSurface = attr(MaterialR.attr.colorOnSurface, 0xFF1D1B20.toInt())
948
+ val baseNormal = if (variant == "outlined") {
949
+ attr(MaterialR.attr.colorOutline, 0xFF79747E.toInt())
950
+ } else {
951
+ attr(MaterialR.attr.colorOnSurfaceVariant, 0xFF49454F.toInt())
952
+ }
953
+ val normal = outlineColor ?: baseNormal
954
+ val focused = activeColor ?: til.boxStrokeColor
955
+ val hovered = outlineColor ?: onSurface
956
+ val disabled = (onSurface and 0x00FFFFFF) or (0x61 shl 24)
957
+ til.setBoxStrokeColorStateList(
958
+ ColorStateList(
959
+ arrayOf(
960
+ intArrayOf(-android.R.attr.state_enabled),
961
+ intArrayOf(android.R.attr.state_focused),
962
+ intArrayOf(android.R.attr.state_hovered),
963
+ intArrayOf()
964
+ ),
965
+ intArrayOf(disabled, focused, hovered, normal)
966
+ )
967
+ )
968
+ }
969
+ activeColor?.let {
970
+ til.hintTextColor = ColorStateList.valueOf(it)
971
+ til.cursorColor = ColorStateList.valueOf(it)
972
+ }
973
+ errorColor?.let {
974
+ val list = ColorStateList.valueOf(it)
975
+ til.setErrorTextColor(list)
976
+ til.setBoxStrokeErrorColor(list)
977
+ til.setErrorIconTintList(list)
978
+ til.cursorErrorColor = list
979
+ }
980
+ containerColor?.let { if (til.boxBackgroundMode != TextInputLayout.BOX_BACKGROUND_NONE) til.boxBackgroundColor = it }
981
+ placeholderTextColor?.let { til.placeholderTextColor = ColorStateList.valueOf(it) }
982
+ }
983
+
984
+ private val actsAsButton: Boolean
985
+ get() = pressable && !fieldEnabled
986
+
987
+ /**
988
+ * Enabled state, or a non-editable field that acts as a button: enabled
989
+ * look, no focus or keyboard, presses reported through onPress.
990
+ */
991
+ private fun applyPressMode() {
992
+ val edit = editText ?: return
993
+ val button = actsAsButton
994
+ val enabled = fieldEnabled || button
995
+ layout?.isEnabled = enabled
996
+ if (layout == null) edit.isEnabled = enabled
997
+ edit.isFocusable = !button
998
+ edit.isFocusableInTouchMode = !button
999
+ edit.isCursorVisible = !button
1000
+ edit.isLongClickable = !button
1001
+ if (button) {
1002
+ if (edit.isFocused) clearFocusAndMaybeRefocus(edit)
1003
+ edit.setOnClickListener { onPress?.invoke() }
1004
+ } else {
1005
+ edit.setOnClickListener(null)
1006
+ }
1007
+ }
1008
+
1009
+ private fun applyCounterAndLength() {
1010
+ val edit = editText ?: return
1011
+ edit.filters = if (maxLength > 0) arrayOf(InputFilter.LengthFilter(maxLength)) else emptyArray()
1012
+ val til = layout ?: return
1013
+ til.counterMaxLength = maxLength
1014
+ til.isCounterEnabled = characterCount
1015
+ requestLayout()
1016
+ }
1017
+
1018
+ private fun applyInputType() {
1019
+ val edit = editText ?: return
1020
+
1021
+ var type = when (keyboardType) {
1022
+ "numeric" -> InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL or InputType.TYPE_NUMBER_FLAG_SIGNED
1023
+ "number-pad" -> InputType.TYPE_CLASS_NUMBER
1024
+ "decimal-pad" -> InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
1025
+ "phone-pad" -> InputType.TYPE_CLASS_PHONE
1026
+ "email-address" -> InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
1027
+ "url" -> InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI
1028
+ "visible-password" -> InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
1029
+ else -> InputType.TYPE_CLASS_TEXT
1030
+ }
1031
+ val isText = (type and InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT
1032
+ val isNumber = (type and InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_NUMBER
1033
+
1034
+ if (secure) {
1035
+ if (isText) {
1036
+ type = (type and InputType.TYPE_MASK_VARIATION.inv()) or InputType.TYPE_TEXT_VARIATION_PASSWORD
1037
+ } else if (isNumber) {
1038
+ type = type or InputType.TYPE_NUMBER_VARIATION_PASSWORD
1039
+ }
1040
+ }
1041
+
1042
+ if (isText && !secure) {
1043
+ type = type or when (autoCapitalize) {
1044
+ "characters" -> InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
1045
+ "words" -> InputType.TYPE_TEXT_FLAG_CAP_WORDS
1046
+ "sentences" -> InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
1047
+ else -> 0
1048
+ }
1049
+ type = type or if (autoCorrect) {
1050
+ InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
1051
+ } else {
1052
+ InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
1053
+ }
1054
+ }
1055
+
1056
+ if (multiline && isText) {
1057
+ type = type or InputType.TYPE_TEXT_FLAG_MULTI_LINE
1058
+ }
1059
+
1060
+ if (edit.inputType != type) {
1061
+ // Setting the input type resets the typeface (password fields default
1062
+ // to monospace) and the transformation; the font is re-applied below.
1063
+ edit.inputType = type
1064
+ }
1065
+
1066
+ edit.imeOptions = when (returnKeyType) {
1067
+ "done" -> EditorInfo.IME_ACTION_DONE
1068
+ "go" -> EditorInfo.IME_ACTION_GO
1069
+ "next" -> EditorInfo.IME_ACTION_NEXT
1070
+ "search" -> EditorInfo.IME_ACTION_SEARCH
1071
+ "send" -> EditorInfo.IME_ACTION_SEND
1072
+ "previous" -> EditorInfo.IME_ACTION_PREVIOUS
1073
+ "none" -> EditorInfo.IME_ACTION_NONE
1074
+ else -> EditorInfo.IME_ACTION_UNSPECIFIED
1075
+ }
1076
+
1077
+ val horizontal = when (textAlign) {
1078
+ "left" -> Gravity.LEFT
1079
+ "center" -> Gravity.CENTER_HORIZONTAL
1080
+ "right" -> Gravity.RIGHT
1081
+ else -> Gravity.START
1082
+ }
1083
+ if (multiline) {
1084
+ edit.gravity = Gravity.TOP or horizontal
1085
+ edit.minLines = if (minLines > 0) minLines else 1
1086
+ edit.maxLines = if (maxLines > 0) maxLines else Int.MAX_VALUE
1087
+ } else {
1088
+ edit.gravity = Gravity.CENTER_VERTICAL or horizontal
1089
+ }
1090
+
1091
+ applyFont(edit)
1092
+ requestLayout()
1093
+ }
1094
+
1095
+ private fun applyFont(edit: TextInputEditText) {
1096
+ PCButtonSupport.applyFont(edit, fontFamily, fontSize, fontWeight, fontStyle)
1097
+ edit.setTag(R.id.pc_capped_text_size, null)
1098
+ PCThemeSupport.capTextSize(edit, maxFontSizeMultiplier)
1099
+ }
1100
+
1101
+ private fun applyAutofill() {
1102
+ val edit = editText ?: return
1103
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
1104
+ when (autoComplete) {
1105
+ "" -> {
1106
+ edit.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_AUTO
1107
+ edit.setAutofillHints()
1108
+ }
1109
+ "off" -> {
1110
+ edit.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_NO
1111
+ edit.setAutofillHints()
1112
+ }
1113
+ else -> {
1114
+ val hint = autofillHint(autoComplete)
1115
+ edit.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_YES
1116
+ if (hint != null) edit.setAutofillHints(hint) else edit.setAutofillHints()
1117
+ }
1118
+ }
1119
+ }
1120
+
1121
+ /**
1122
+ * React Native autoComplete values onto the Android autofill hints: the
1123
+ * core TextInput's map, plus the HTML names it leaves out. The values are
1124
+ * androidx.autofill HintConstants, spelled out because the React Native
1125
+ * floor ships an older androidx.autofill. Unknown values give no hint.
1126
+ */
1127
+ private fun autofillHint(value: String): String? = when (value) {
1128
+ "username" -> "username"
1129
+ "username-new" -> "newUsername"
1130
+ "password", "current-password" -> "password"
1131
+ "new-password", "password-new" -> "newPassword"
1132
+ "one-time-code", "sms-otp" -> "smsOTPCode"
1133
+ "email-otp" -> "emailOTPCode"
1134
+ "2fa-app-otp" -> "2faAppOTPCode"
1135
+ "email" -> "emailAddress"
1136
+ "name" -> "personName"
1137
+ "given-name", "name-given" -> "personGivenName"
1138
+ "family-name", "name-family" -> "personFamilyName"
1139
+ "additional-name", "name-middle" -> "personMiddleName"
1140
+ "name-middle-initial" -> "personMiddleInitial"
1141
+ "honorific-prefix", "name-prefix" -> "personNamePrefix"
1142
+ "honorific-suffix", "name-suffix" -> "personNameSuffix"
1143
+ "tel" -> "phoneNumber"
1144
+ "tel-country-code" -> "phoneCountryCode"
1145
+ "tel-national" -> "phoneNational"
1146
+ "tel-device" -> "phoneNumberDevice"
1147
+ "street-address", "address-line1" -> "streetAddress"
1148
+ "address-line2", "postal-address-extended" -> "extendedAddress"
1149
+ "postal-address" -> "postalAddress"
1150
+ "postal-address-country", "country" -> "addressCountry"
1151
+ "postal-address-extended-postal-code" -> "extendedPostalCode"
1152
+ "postal-address-locality" -> "addressLocality"
1153
+ "postal-address-region" -> "addressRegion"
1154
+ "postal-address-dependent-locality" -> "dependentLocality"
1155
+ "postal-address-unit" -> "aptNumber"
1156
+ "postal-code" -> "postalCode"
1157
+ "birthdate-full" -> "birthDateFull"
1158
+ "birthdate-day" -> "birthDateDay"
1159
+ "birthdate-month" -> "birthDateMonth"
1160
+ "birthdate-year" -> "birthDateYear"
1161
+ "gender" -> "gender"
1162
+ "cc-number" -> "creditCardNumber"
1163
+ "cc-exp" -> "creditCardExpirationDate"
1164
+ "cc-exp-day" -> "creditCardExpirationDay"
1165
+ "cc-exp-month" -> "creditCardExpirationMonth"
1166
+ "cc-exp-year" -> "creditCardExpirationYear"
1167
+ "cc-csc" -> "creditCardSecurityCode"
1168
+ "flight-number" -> "flightNumber"
1169
+ "flight-confirmation-code" -> "flightConfirmationCode"
1170
+ "gift-card-number" -> "giftCardNumber"
1171
+ "gift-card-pin" -> "giftCardPIN"
1172
+ "loyalty-account-number" -> "loyaltyAccountNumber"
1173
+ "promo-code" -> "promoCode"
1174
+ "upi-vpa" -> "upiVirtualPaymentAddress"
1175
+ "wifi-password" -> "wifiPassword"
1176
+ else -> null
1177
+ }
1178
+
1179
+ /**
1180
+ * Resolves an icon. Drawable names and release-bundled assets (which React
1181
+ * Native ships as drawable resources) resolve synchronously; other image
1182
+ * URIs load in the background and are delivered when they arrive.
1183
+ */
1184
+ private fun loadIcon(icon: PCButtonSupport.Icon, onLoaded: (Drawable?) -> Unit) {
1185
+ when (icon.type) {
1186
+ "drawable" -> onLoaded(ResourceDrawableIdHelper.instance.getResourceDrawable(context, icon.name))
1187
+ "image" -> {
1188
+ val uri = icon.uri
1189
+ if (!uri.contains(':')) {
1190
+ onLoaded(ResourceDrawableIdHelper.instance.getResourceDrawable(context, uri))
1191
+ } else {
1192
+ PCImageLoader.load(context, uri, icon.scale, icon.request) { bitmap ->
1193
+ onLoaded(bitmap?.let { BitmapDrawable(context.resources, it) })
1194
+ }
1195
+ }
1196
+ }
1197
+ else -> onLoaded(null)
1198
+ }
1199
+ }
1200
+
1201
+ // ---- Keyboard and focus ----
1202
+
1203
+ private fun showKeyboard(view: View) {
1204
+ val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
1205
+ imm?.showSoftInput(view, 0)
1206
+ }
1207
+
1208
+ private fun hideKeyboard() {
1209
+ val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
1210
+ imm?.hideSoftInputFromWindow(windowToken, 0)
1211
+ }
1212
+
1213
+ /**
1214
+ * Drops focus. Android 9 and below would hand it to the next focusable
1215
+ * view (often another text field) when the root view refocuses; block
1216
+ * that, as the core TextInput does.
1217
+ */
1218
+ private fun clearFocusAndMaybeRefocus(edit: View) {
1219
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P || !edit.isInTouchMode) {
1220
+ edit.clearFocus()
1221
+ return
1222
+ }
1223
+ val root = rootView as? ViewGroup
1224
+ if (root == null) {
1225
+ edit.clearFocus()
1226
+ return
1227
+ }
1228
+ val previous = root.descendantFocusability
1229
+ root.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS
1230
+ edit.clearFocus()
1231
+ root.descendantFocusability = previous
1232
+ }
1233
+
1234
+ /**
1235
+ * The edit text. The keyboard's back key hides the keyboard; the field
1236
+ * drops focus with it so JS gets onBlur, as the core TextInput does not,
1237
+ * but a Material field with a focused label and no keyboard looks stuck.
1238
+ */
1239
+ private inner class FieldEditText(context: Context) : TextInputEditText(context) {
1240
+ override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
1241
+ super.onFocusChanged(focused, direction, previouslyFocusedRect)
1242
+ onFocusChange?.invoke(focused, this@PCTextFieldView.text)
1243
+ }
1244
+
1245
+ override fun onSelectionChanged(selStart: Int, selEnd: Int) {
1246
+ super.onSelectionChanged(selStart, selEnd)
1247
+ selectionChanged(selStart, selEnd)
1248
+ }
1249
+
1250
+ /**
1251
+ * A multi-line field that submits on return asks the IME for its action
1252
+ * key (Send, Done) in place of Enter, which TextView turns off for
1253
+ * multi-line input.
1254
+ */
1255
+ override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection? {
1256
+ val connection = super.onCreateInputConnection(outAttrs)
1257
+ if (multiline && returnKeyBehavior != "newline") {
1258
+ outAttrs.imeOptions = outAttrs.imeOptions and EditorInfo.IME_FLAG_NO_ENTER_ACTION.inv()
1259
+ }
1260
+ return connection
1261
+ }
1262
+
1263
+ override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {
1264
+ super.onInitializeAccessibilityNodeInfo(info)
1265
+ if (actsAsButton) {
1266
+ info.className = Button::class.java.name
1267
+ info.isEditable = false
1268
+ }
1269
+ }
1270
+
1271
+ /** A multi-line field capped at maxLines scrolls itself, not the ScrollView around it. */
1272
+ override fun onTouchEvent(event: MotionEvent): Boolean {
1273
+ if (multiline && maxLines > 0 && (canScrollVertically(1) || canScrollVertically(-1))) {
1274
+ when (event.actionMasked) {
1275
+ MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> parent?.requestDisallowInterceptTouchEvent(true)
1276
+ }
1277
+ }
1278
+ return super.onTouchEvent(event)
1279
+ }
1280
+
1281
+ override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
1282
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP && isFocused) {
1283
+ clearFocusAndMaybeRefocus(this)
1284
+ }
1285
+ return super.onKeyPreIme(keyCode, event)
1286
+ }
1287
+ }
1288
+
1289
+ // ---- Layout ----
1290
+
1291
+ /**
1292
+ * React Native's root view ignores requestLayout after the initial pass, so
1293
+ * a native view whose subtree changes later (a longer multi-line text, an
1294
+ * error message appearing) has to measure and lay itself out. Coalesced to
1295
+ * one pass per frame.
1296
+ */
1297
+ override fun requestLayout() {
1298
+ super.requestLayout()
1299
+ if (!manualLayoutPending) {
1300
+ manualLayoutPending = true
1301
+ post { runManualLayout() }
1302
+ }
1303
+ }
1304
+
1305
+ private fun runManualLayout() {
1306
+ manualLayoutPending = false
1307
+ // Nothing to do until Yoga has placed us; the regular pass covers that.
1308
+ if (width == 0 || height == 0) return
1309
+ measure(
1310
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
1311
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
1312
+ )
1313
+ layout(left, top, right, bottom)
1314
+ }
1315
+
1316
+ // ---- Measurement ----
1317
+
1318
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
1319
+ val child = widget
1320
+ if (child == null) {
1321
+ setMeasuredDimension(0, 0)
1322
+ return
1323
+ }
1324
+
1325
+ // The field fills the width it is given and takes the height its
1326
+ // content needs; Yoga catches up through the state update.
1327
+ val width = resolveWidth(widthMeasureSpec)
1328
+ measureWidget(child, width)
1329
+ val naturalHeight = child.measuredHeight
1330
+ val heightSize = MeasureSpec.getSize(heightMeasureSpec)
1331
+ val height = when (MeasureSpec.getMode(heightMeasureSpec)) {
1332
+ // Fabric may give us 0 before the state is applied; keep the natural size then.
1333
+ MeasureSpec.EXACTLY -> if (heightSize > 0) heightSize else naturalHeight
1334
+ MeasureSpec.AT_MOST -> minOf(naturalHeight, heightSize)
1335
+ else -> naturalHeight
1336
+ }
1337
+ setMeasuredDimension(width, height)
1338
+ reportHeight(naturalHeight)
1339
+ }
1340
+
1341
+ /** Measures the widget at [width], as tall as its content needs. */
1342
+ private fun measureWidget(child: View, width: Int) {
1343
+ child.measure(
1344
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
1345
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
1346
+ )
1347
+ }
1348
+
1349
+ private fun resolveWidth(spec: Int): Int {
1350
+ val size = MeasureSpec.getSize(spec)
1351
+ return if (size > 0) size else (PCConstants.FALLBACK_WIDTH_DP * resources.displayMetrics.density).toInt()
1352
+ }
1353
+
1354
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
1355
+ val child = widget ?: return
1356
+ // Keep the widget at its natural height even when Yoga hasn't caught up
1357
+ child.layout(0, 0, right - left, child.measuredHeight)
1358
+ // Material creates the helper, error, counter and placeholder views as
1359
+ // they are needed; cap the new ones (a changed size asks for a new pass)
1360
+ PCThemeSupport.capTextSizes(child, maxFontSizeMultiplier)
1361
+ // TextInputLayout makes room for its start icon and prefix (invisible
1362
+ // compound drawables on the edit text) from a ViewTreeObserver global
1363
+ // layout callback, which only a window traversal delivers. React Native's
1364
+ // root doesn't run one for a native subtree that laid itself out, so run
1365
+ // it here; when it changes something it asks for another pass.
1366
+ layout?.onGlobalLayout()
1367
+ }
1368
+
1369
+ /** Reports the intrinsic height to Fabric; called as soon as the state wrapper is available. */
1370
+ fun reportIntrinsicSize() {
1371
+ val child = widget ?: return
1372
+ val width = if (width > 0) width else (PCConstants.FALLBACK_WIDTH_DP * resources.displayMetrics.density).toInt()
1373
+ measureWidget(child, width)
1374
+ reportHeight(child.measuredHeight)
1375
+ }
1376
+
1377
+ /**
1378
+ * Update Fabric state with the field's natural height. The width stays 0:
1379
+ * the shadow node fills the available width.
1380
+ */
1381
+ private fun reportHeight(heightPx: Int) {
1382
+ val wrapper = stateWrapper ?: return
1383
+ val heightDp = PixelUtil.toDIPFromPixel(heightPx.toFloat())
1384
+ if (heightDp == lastReportedHeight) return
1385
+ lastReportedHeight = heightDp
1386
+
1387
+ val stateData = WritableNativeMap().apply {
1388
+ putDouble("width", 0.0)
1389
+ putDouble("height", heightDp.toDouble())
1390
+ }
1391
+ wrapper.updateState(stateData)
1392
+ }
1393
+ }