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.
- package/PlatformComponents.podspec +1 -1
- package/README.md +47 -51
- package/android/src/main/java/com/platformcomponents/PCButtonGroupView.kt +664 -0
- package/android/src/main/java/com/platformcomponents/PCButtonGroupViewManager.kt +241 -0
- package/android/src/main/java/com/platformcomponents/PCButtonSupport.kt +159 -0
- package/android/src/main/java/com/platformcomponents/PCButtonView.kt +527 -0
- package/android/src/main/java/com/platformcomponents/PCButtonViewManager.kt +204 -0
- package/android/src/main/java/com/platformcomponents/PCContextMenuView.kt +41 -151
- package/android/src/main/java/com/platformcomponents/PCContextMenuViewManager.kt +7 -63
- package/android/src/main/java/com/platformcomponents/PCDatePickerView.kt +310 -115
- package/android/src/main/java/com/platformcomponents/PCDatePickerViewManager.kt +23 -3
- package/android/src/main/java/com/platformcomponents/PCExpressive.kt +112 -0
- package/android/src/main/java/com/platformcomponents/PCFloatingActionButtonView.kt +485 -0
- package/android/src/main/java/com/platformcomponents/PCFloatingActionButtonViewManager.kt +109 -0
- package/android/src/main/java/com/platformcomponents/PCFloatingToolbarView.kt +247 -0
- package/android/src/main/java/com/platformcomponents/PCFloatingToolbarViewManager.kt +73 -0
- package/android/src/main/java/com/platformcomponents/PCHaptics.kt +46 -0
- package/android/src/main/java/com/platformcomponents/PCImageLoader.kt +42 -7
- package/android/src/main/java/com/platformcomponents/PCLiquidGlassContainerView.kt +32 -0
- package/android/src/main/java/com/platformcomponents/PCLiquidGlassContainerViewManager.kt +31 -0
- package/android/src/main/java/com/platformcomponents/PCLiquidGlassView.kt +32 -6
- package/android/src/main/java/com/platformcomponents/PCLiquidGlassViewManager.kt +4 -0
- package/android/src/main/java/com/platformcomponents/PCMenuSupport.kt +280 -0
- package/android/src/main/java/com/platformcomponents/PCNavigationBarSupport.kt +332 -0
- package/android/src/main/java/com/platformcomponents/PCNavigationRailView.kt +448 -0
- package/android/src/main/java/com/platformcomponents/PCNavigationRailViewManager.kt +176 -0
- package/android/src/main/java/com/platformcomponents/PCSegmentedControlView.kt +35 -17
- package/android/src/main/java/com/platformcomponents/PCSegmentedControlViewManager.kt +20 -3
- package/android/src/main/java/com/platformcomponents/PCSelectionMenuView.kt +371 -39
- package/android/src/main/java/com/platformcomponents/PCSelectionMenuViewManager.kt +18 -4
- package/android/src/main/java/com/platformcomponents/PCTabBarView.kt +397 -0
- package/android/src/main/java/com/platformcomponents/PCTabBarViewManager.kt +182 -0
- package/android/src/main/java/com/platformcomponents/PCTextFieldView.kt +1393 -0
- package/android/src/main/java/com/platformcomponents/PCTextFieldViewManager.kt +354 -0
- package/android/src/main/java/com/platformcomponents/PCThemeSupport.kt +37 -0
- package/android/src/main/java/com/platformcomponents/PlatformComponentsPackage.kt +8 -0
- package/android/src/main/res/drawable/pc_ic_search.xml +12 -0
- package/android/src/main/res/values/attrs.xml +13 -0
- package/android/src/main/res/values/ids.xml +5 -0
- package/android/src/main/res/values/styles.xml +207 -0
- package/ios/PCButton.h +10 -0
- package/ios/PCButton.mm +311 -0
- package/ios/PCButtonGroup.h +10 -0
- package/ios/PCButtonGroup.mm +323 -0
- package/ios/PCButtonGroupView.swift +473 -0
- package/ios/PCButtonSupport.swift +312 -0
- package/ios/PCButtonView.swift +438 -0
- package/ios/PCConstants.swift +23 -2
- package/ios/PCContextMenu.mm +28 -110
- package/ios/PCContextMenu.swift +106 -167
- package/ios/PCDatePicker.mm +21 -9
- package/ios/PCDatePickerView.swift +94 -20
- package/ios/PCFloatingActionButton.h +10 -0
- package/ios/PCFloatingActionButton.mm +177 -0
- package/ios/PCFloatingActionButtonView.swift +426 -0
- package/ios/PCFloatingToolbar.h +10 -0
- package/ios/PCFloatingToolbar.mm +103 -0
- package/ios/PCFloatingToolbarView.swift +336 -0
- package/ios/PCHaptics.swift +91 -0
- package/ios/PCImageLoader.swift +76 -12
- package/ios/PCLiquidGlass.mm +36 -0
- package/ios/PCLiquidGlass.swift +221 -114
- package/ios/PCLiquidGlassContainer.h +10 -0
- package/ios/PCLiquidGlassContainer.mm +84 -0
- package/ios/PCLiquidGlassContainer.swift +69 -0
- package/ios/PCMenuItems.h +76 -0
- package/ios/PCMenuSupport.swift +350 -0
- package/ios/PCNavigationRail.h +10 -0
- package/ios/PCNavigationRail.mm +270 -0
- package/ios/PCNavigationRailView.swift +415 -0
- package/ios/PCSegmentedControl.mm +17 -2
- package/ios/PCSegmentedControl.swift +57 -7
- package/ios/PCSelectionMenu.mm +36 -8
- package/ios/PCSelectionMenu.swift +224 -48
- package/ios/PCTabBar.h +10 -0
- package/ios/PCTabBar.mm +285 -0
- package/ios/PCTabBarAccessory.h +10 -0
- package/ios/PCTabBarAccessory.mm +93 -0
- package/ios/PCTabBarAccessoryView.swift +96 -0
- package/ios/PCTabBarView.swift +671 -0
- package/ios/PCTextField.h +10 -0
- package/ios/PCTextField.mm +546 -0
- package/ios/PCTextFieldView.swift +1558 -0
- package/lib/commonjs/Button.js +149 -0
- package/lib/commonjs/Button.js.map +1 -0
- package/lib/commonjs/ButtonGroup.js +109 -0
- package/lib/commonjs/ButtonGroup.js.map +1 -0
- package/lib/commonjs/ButtonGroupNativeComponent.ts +191 -0
- package/lib/commonjs/ButtonNativeComponent.ts +192 -0
- package/lib/commonjs/ContextMenu.js +9 -48
- package/lib/commonjs/ContextMenu.js.map +1 -1
- package/lib/commonjs/ContextMenuNativeComponent.ts +47 -49
- package/lib/commonjs/DatePicker.js +98 -2
- package/lib/commonjs/DatePicker.js.map +1 -1
- package/lib/commonjs/DatePickerNativeComponent.ts +19 -2
- package/lib/commonjs/FloatingActionButton.js +60 -0
- package/lib/commonjs/FloatingActionButton.js.map +1 -0
- package/lib/commonjs/FloatingActionButtonNativeComponent.ts +58 -0
- package/lib/commonjs/FloatingToolbar.js +77 -0
- package/lib/commonjs/FloatingToolbar.js.map +1 -0
- package/lib/commonjs/FloatingToolbarNativeComponent.ts +47 -0
- package/lib/commonjs/LiquidGlass.js +7 -1
- package/lib/commonjs/LiquidGlass.js.map +1 -1
- package/lib/commonjs/LiquidGlassContainer.js +32 -0
- package/lib/commonjs/LiquidGlassContainer.js.map +1 -0
- package/lib/commonjs/LiquidGlassContainerNativeComponent.ts +17 -0
- package/lib/commonjs/LiquidGlassNativeComponent.ts +8 -0
- package/lib/commonjs/NavigationRail.js +104 -0
- package/lib/commonjs/NavigationRail.js.map +1 -0
- package/lib/commonjs/NavigationRailNativeComponent.ts +107 -0
- package/lib/commonjs/PlatformComponentsProvider.js +18 -0
- package/lib/commonjs/PlatformComponentsProvider.js.map +1 -0
- package/lib/commonjs/SegmentedControl.js +18 -85
- package/lib/commonjs/SegmentedControl.js.map +1 -1
- package/lib/commonjs/SegmentedControlNativeComponent.ts +14 -0
- package/lib/commonjs/SelectionMenu.js +27 -3
- package/lib/commonjs/SelectionMenu.js.map +1 -1
- package/lib/commonjs/SelectionMenuNativeComponent.ts +26 -4
- package/lib/commonjs/SplitButton.js +98 -0
- package/lib/commonjs/SplitButton.js.map +1 -0
- package/lib/commonjs/TabBar.js +203 -0
- package/lib/commonjs/TabBar.js.map +1 -0
- package/lib/commonjs/TabBarAccessoryNativeComponent.ts +19 -0
- package/lib/commonjs/TabBarNativeComponent.ts +145 -0
- package/lib/commonjs/TextField.js +358 -0
- package/lib/commonjs/TextField.js.map +1 -0
- package/lib/commonjs/TextFieldNativeComponent.ts +324 -0
- package/lib/commonjs/focusRegistry.js +79 -0
- package/lib/commonjs/focusRegistry.js.map +1 -0
- package/lib/commonjs/haptics.js +2 -0
- package/lib/commonjs/haptics.js.map +1 -0
- package/lib/commonjs/icons.js +106 -0
- package/lib/commonjs/icons.js.map +1 -0
- package/lib/commonjs/index.js +143 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +157 -63
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/jest.js +910 -0
- package/lib/commonjs/jest.js.map +1 -0
- package/lib/commonjs/labelStyle.js +27 -0
- package/lib/commonjs/labelStyle.js.map +1 -0
- package/lib/commonjs/menuItems.js +68 -0
- package/lib/commonjs/menuItems.js.map +1 -0
- package/lib/commonjs/reactNativeInternals.d.js +2 -0
- package/lib/commonjs/reactNativeInternals.d.js.map +1 -0
- package/lib/commonjs/selectionIdentifiers.js +18 -0
- package/lib/commonjs/selectionIdentifiers.js.map +1 -0
- package/lib/commonjs/submitBehavior.js +19 -0
- package/lib/commonjs/submitBehavior.js.map +1 -0
- package/lib/commonjs/tabItems.js +49 -0
- package/lib/commonjs/tabItems.js.map +1 -0
- package/lib/commonjs/web/Button.js +170 -0
- package/lib/commonjs/web/Button.js.map +1 -0
- package/lib/commonjs/web/ButtonGroup.js +119 -0
- package/lib/commonjs/web/ButtonGroup.js.map +1 -0
- package/lib/commonjs/web/DatePicker.js +168 -0
- package/lib/commonjs/web/DatePicker.js.map +1 -0
- package/lib/commonjs/web/DateRangePicker.js +137 -0
- package/lib/commonjs/web/DateRangePicker.js.map +1 -0
- package/lib/commonjs/web/Dialog.js +54 -0
- package/lib/commonjs/web/Dialog.js.map +1 -0
- package/lib/commonjs/web/FloatingActionButton.js +98 -0
- package/lib/commonjs/web/FloatingActionButton.js.map +1 -0
- package/lib/commonjs/web/Icon.js +34 -0
- package/lib/commonjs/web/Icon.js.map +1 -0
- package/lib/commonjs/web/NativeTheme.js +28 -0
- package/lib/commonjs/web/NativeTheme.js.map +1 -0
- package/lib/commonjs/web/NavigationRail.js +139 -0
- package/lib/commonjs/web/NavigationRail.js.map +1 -0
- package/lib/commonjs/web/PlatformComponentsProvider.js +51 -0
- package/lib/commonjs/web/PlatformComponentsProvider.js.map +1 -0
- package/lib/commonjs/web/SegmentedControl.js +117 -0
- package/lib/commonjs/web/SegmentedControl.js.map +1 -0
- package/lib/commonjs/web/SelectionMenu.js +142 -0
- package/lib/commonjs/web/SelectionMenu.js.map +1 -0
- package/lib/commonjs/web/SplitButton.js +35 -0
- package/lib/commonjs/web/SplitButton.js.map +1 -0
- package/lib/commonjs/web/TabBar.js +131 -0
- package/lib/commonjs/web/TabBar.js.map +1 -0
- package/lib/commonjs/web/TextField.js +322 -0
- package/lib/commonjs/web/TextField.js.map +1 -0
- package/lib/commonjs/web/components.js +36 -0
- package/lib/commonjs/web/components.js.map +1 -0
- package/lib/commonjs/web/containers.js +121 -0
- package/lib/commonjs/web/containers.js.map +1 -0
- package/lib/commonjs/web/dateInput.js +116 -0
- package/lib/commonjs/web/dateInput.js.map +1 -0
- package/lib/commonjs/web/shared.js +192 -0
- package/lib/commonjs/web/shared.js.map +1 -0
- package/lib/commonjs/webComponents.js +6 -0
- package/lib/commonjs/webComponents.js.map +1 -0
- package/lib/module/Button.js +142 -0
- package/lib/module/Button.js.map +1 -0
- package/lib/module/ButtonGroup.js +102 -0
- package/lib/module/ButtonGroup.js.map +1 -0
- package/lib/module/ButtonGroupNativeComponent.ts +191 -0
- package/lib/module/ButtonNativeComponent.ts +192 -0
- package/lib/module/ContextMenu.js +9 -48
- package/lib/module/ContextMenu.js.map +1 -1
- package/lib/module/ContextMenuNativeComponent.ts +47 -49
- package/lib/module/DatePicker.js +98 -4
- package/lib/module/DatePicker.js.map +1 -1
- package/lib/module/DatePickerNativeComponent.ts +19 -2
- package/lib/module/FloatingActionButton.js +53 -0
- package/lib/module/FloatingActionButton.js.map +1 -0
- package/lib/module/FloatingActionButtonNativeComponent.ts +58 -0
- package/lib/module/FloatingToolbar.js +70 -0
- package/lib/module/FloatingToolbar.js.map +1 -0
- package/lib/module/FloatingToolbarNativeComponent.ts +47 -0
- package/lib/module/LiquidGlass.js +7 -1
- package/lib/module/LiquidGlass.js.map +1 -1
- package/lib/module/LiquidGlassContainer.js +26 -0
- package/lib/module/LiquidGlassContainer.js.map +1 -0
- package/lib/module/LiquidGlassContainerNativeComponent.ts +17 -0
- package/lib/module/LiquidGlassNativeComponent.ts +8 -0
- package/lib/module/NavigationRail.js +99 -0
- package/lib/module/NavigationRail.js.map +1 -0
- package/lib/module/NavigationRailNativeComponent.ts +107 -0
- package/lib/module/PlatformComponentsProvider.js +13 -0
- package/lib/module/PlatformComponentsProvider.js.map +1 -0
- package/lib/module/SegmentedControl.js +18 -85
- package/lib/module/SegmentedControl.js.map +1 -1
- package/lib/module/SegmentedControlNativeComponent.ts +14 -0
- package/lib/module/SelectionMenu.js +27 -3
- package/lib/module/SelectionMenu.js.map +1 -1
- package/lib/module/SelectionMenuNativeComponent.ts +26 -4
- package/lib/module/SplitButton.js +91 -0
- package/lib/module/SplitButton.js.map +1 -0
- package/lib/module/TabBar.js +196 -0
- package/lib/module/TabBar.js.map +1 -0
- package/lib/module/TabBarAccessoryNativeComponent.ts +19 -0
- package/lib/module/TabBarNativeComponent.ts +145 -0
- package/lib/module/TextField.js +352 -0
- package/lib/module/TextField.js.map +1 -0
- package/lib/module/TextFieldNativeComponent.ts +324 -0
- package/lib/module/focusRegistry.js +75 -0
- package/lib/module/focusRegistry.js.map +1 -0
- package/lib/module/haptics.js +2 -0
- package/lib/module/haptics.js.map +1 -0
- package/lib/module/icons.js +101 -0
- package/lib/module/icons.js.map +1 -0
- package/lib/module/index.js +13 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +12 -59
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/jest.js +840 -0
- package/lib/module/jest.js.map +1 -0
- package/lib/module/labelStyle.js +23 -0
- package/lib/module/labelStyle.js.map +1 -0
- package/lib/module/menuItems.js +65 -0
- package/lib/module/menuItems.js.map +1 -0
- package/lib/module/reactNativeInternals.d.js +2 -0
- package/lib/module/reactNativeInternals.d.js.map +1 -0
- package/lib/module/selectionIdentifiers.js +14 -0
- package/lib/module/selectionIdentifiers.js.map +1 -0
- package/lib/module/submitBehavior.js +15 -0
- package/lib/module/submitBehavior.js.map +1 -0
- package/lib/module/tabItems.js +45 -0
- package/lib/module/tabItems.js.map +1 -0
- package/lib/module/web/Button.js +164 -0
- package/lib/module/web/Button.js.map +1 -0
- package/lib/module/web/ButtonGroup.js +113 -0
- package/lib/module/web/ButtonGroup.js.map +1 -0
- package/lib/module/web/DatePicker.js +162 -0
- package/lib/module/web/DatePicker.js.map +1 -0
- package/lib/module/web/DateRangePicker.js +131 -0
- package/lib/module/web/DateRangePicker.js.map +1 -0
- package/lib/module/web/Dialog.js +48 -0
- package/lib/module/web/Dialog.js.map +1 -0
- package/lib/module/web/FloatingActionButton.js +94 -0
- package/lib/module/web/FloatingActionButton.js.map +1 -0
- package/lib/module/web/Icon.js +29 -0
- package/lib/module/web/Icon.js.map +1 -0
- package/lib/module/web/NativeTheme.js +23 -0
- package/lib/module/web/NativeTheme.js.map +1 -0
- package/lib/module/web/NavigationRail.js +133 -0
- package/lib/module/web/NavigationRail.js.map +1 -0
- package/lib/module/web/PlatformComponentsProvider.js +44 -0
- package/lib/module/web/PlatformComponentsProvider.js.map +1 -0
- package/lib/module/web/SegmentedControl.js +112 -0
- package/lib/module/web/SegmentedControl.js.map +1 -0
- package/lib/module/web/SelectionMenu.js +137 -0
- package/lib/module/web/SelectionMenu.js.map +1 -0
- package/lib/module/web/SplitButton.js +30 -0
- package/lib/module/web/SplitButton.js.map +1 -0
- package/lib/module/web/TabBar.js +126 -0
- package/lib/module/web/TabBar.js.map +1 -0
- package/lib/module/web/TextField.js +316 -0
- package/lib/module/web/TextField.js.map +1 -0
- package/lib/module/web/components.js +33 -0
- package/lib/module/web/components.js.map +1 -0
- package/lib/module/web/containers.js +112 -0
- package/lib/module/web/containers.js.map +1 -0
- package/lib/module/web/dateInput.js +109 -0
- package/lib/module/web/dateInput.js.map +1 -0
- package/lib/module/web/shared.js +179 -0
- package/lib/module/web/shared.js.map +1 -0
- package/lib/module/webComponents.js +4 -0
- package/lib/module/webComponents.js.map +1 -0
- package/lib/typescript/commonjs/docs/sidebars.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/Button.d.ts +184 -0
- package/lib/typescript/commonjs/src/Button.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/ButtonGroup.d.ts +117 -0
- package/lib/typescript/commonjs/src/ButtonGroup.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/ButtonGroupNativeComponent.d.ts +154 -0
- package/lib/typescript/commonjs/src/ButtonGroupNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/ButtonNativeComponent.d.ts +148 -0
- package/lib/typescript/commonjs/src/ButtonNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/ContextMenu.d.ts +20 -33
- package/lib/typescript/commonjs/src/ContextMenu.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/ContextMenuNativeComponent.d.ts +45 -47
- package/lib/typescript/commonjs/src/ContextMenuNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/DatePicker.d.ts +74 -1
- package/lib/typescript/commonjs/src/DatePicker.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/DatePickerNativeComponent.d.ts +16 -1
- package/lib/typescript/commonjs/src/DatePickerNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/FloatingActionButton.d.ts +67 -0
- package/lib/typescript/commonjs/src/FloatingActionButton.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/FloatingActionButtonNativeComponent.d.ts +43 -0
- package/lib/typescript/commonjs/src/FloatingActionButtonNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/FloatingToolbar.d.ts +75 -0
- package/lib/typescript/commonjs/src/FloatingToolbar.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/FloatingToolbarNativeComponent.d.ts +36 -0
- package/lib/typescript/commonjs/src/FloatingToolbarNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/LiquidGlass.d.ts +17 -0
- package/lib/typescript/commonjs/src/LiquidGlass.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/LiquidGlassContainer.d.ts +27 -0
- package/lib/typescript/commonjs/src/LiquidGlassContainer.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/LiquidGlassContainerNativeComponent.d.ts +13 -0
- package/lib/typescript/commonjs/src/LiquidGlassContainerNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/LiquidGlassNativeComponent.d.ts +7 -0
- package/lib/typescript/commonjs/src/LiquidGlassNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/NavigationRail.d.ts +86 -0
- package/lib/typescript/commonjs/src/NavigationRail.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/NavigationRailNativeComponent.d.ts +81 -0
- package/lib/typescript/commonjs/src/NavigationRailNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/PlatformComponentsProvider.d.ts +6 -0
- package/lib/typescript/commonjs/src/PlatformComponentsProvider.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/SegmentedControl.d.ts +35 -47
- package/lib/typescript/commonjs/src/SegmentedControl.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts +11 -0
- package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/SelectionMenu.d.ts +44 -2
- package/lib/typescript/commonjs/src/SelectionMenu.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/SelectionMenuNativeComponent.d.ts +20 -4
- package/lib/typescript/commonjs/src/SelectionMenuNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/SplitButton.d.ts +83 -0
- package/lib/typescript/commonjs/src/SplitButton.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/TabBar.d.ts +144 -0
- package/lib/typescript/commonjs/src/TabBar.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/TabBarAccessoryNativeComponent.d.ts +13 -0
- package/lib/typescript/commonjs/src/TabBarAccessoryNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/TabBarNativeComponent.d.ts +110 -0
- package/lib/typescript/commonjs/src/TabBarNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/TextField.d.ts +332 -0
- package/lib/typescript/commonjs/src/TextField.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/TextFieldNativeComponent.d.ts +240 -0
- package/lib/typescript/commonjs/src/TextFieldNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/focusRegistry.d.ts +13 -0
- package/lib/typescript/commonjs/src/focusRegistry.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/haptics.d.ts +20 -0
- package/lib/typescript/commonjs/src/haptics.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/icons.d.ts +58 -0
- package/lib/typescript/commonjs/src/icons.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +13 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.web.d.ts +25 -133
- package/lib/typescript/commonjs/src/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/jest.d.ts +139 -0
- package/lib/typescript/commonjs/src/jest.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/labelStyle.d.ts +20 -0
- package/lib/typescript/commonjs/src/labelStyle.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/menuItems.d.ts +100 -0
- package/lib/typescript/commonjs/src/menuItems.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/selectionIdentifiers.d.ts +3 -0
- package/lib/typescript/commonjs/src/selectionIdentifiers.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/sharedTypes.d.ts +6 -1
- package/lib/typescript/commonjs/src/sharedTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/submitBehavior.d.ts +9 -0
- package/lib/typescript/commonjs/src/submitBehavior.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/tabItems.d.ts +55 -0
- package/lib/typescript/commonjs/src/tabItems.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/Button.d.ts +9 -0
- package/lib/typescript/commonjs/src/web/Button.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/ButtonGroup.d.ts +11 -0
- package/lib/typescript/commonjs/src/web/ButtonGroup.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/DatePicker.d.ts +14 -0
- package/lib/typescript/commonjs/src/web/DatePicker.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/DateRangePicker.d.ts +11 -0
- package/lib/typescript/commonjs/src/web/DateRangePicker.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/Dialog.d.ts +12 -0
- package/lib/typescript/commonjs/src/web/Dialog.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/FloatingActionButton.d.ts +8 -0
- package/lib/typescript/commonjs/src/web/FloatingActionButton.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/Icon.d.ts +12 -0
- package/lib/typescript/commonjs/src/web/Icon.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/NativeTheme.d.ts +10 -0
- package/lib/typescript/commonjs/src/web/NativeTheme.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/NavigationRail.d.ts +8 -0
- package/lib/typescript/commonjs/src/web/NavigationRail.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/PlatformComponentsProvider.d.ts +10 -0
- package/lib/typescript/commonjs/src/web/PlatformComponentsProvider.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/SegmentedControl.d.ts +8 -0
- package/lib/typescript/commonjs/src/web/SegmentedControl.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/SelectionMenu.d.ts +10 -0
- package/lib/typescript/commonjs/src/web/SelectionMenu.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/SplitButton.d.ts +9 -0
- package/lib/typescript/commonjs/src/web/SplitButton.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/TabBar.d.ts +9 -0
- package/lib/typescript/commonjs/src/web/TabBar.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/TextField.d.ts +9 -0
- package/lib/typescript/commonjs/src/web/TextField.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/components.d.ts +17 -0
- package/lib/typescript/commonjs/src/web/components.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/containers.d.ts +24 -0
- package/lib/typescript/commonjs/src/web/containers.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/dateInput.d.ts +20 -0
- package/lib/typescript/commonjs/src/web/dateInput.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/shared.d.ts +43 -0
- package/lib/typescript/commonjs/src/web/shared.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/webComponents.d.ts +43 -0
- package/lib/typescript/commonjs/src/webComponents.d.ts.map +1 -0
- package/lib/typescript/module/docs/sidebars.d.ts.map +1 -1
- package/lib/typescript/module/src/Button.d.ts +184 -0
- package/lib/typescript/module/src/Button.d.ts.map +1 -0
- package/lib/typescript/module/src/ButtonGroup.d.ts +117 -0
- package/lib/typescript/module/src/ButtonGroup.d.ts.map +1 -0
- package/lib/typescript/module/src/ButtonGroupNativeComponent.d.ts +154 -0
- package/lib/typescript/module/src/ButtonGroupNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/ButtonNativeComponent.d.ts +148 -0
- package/lib/typescript/module/src/ButtonNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/ContextMenu.d.ts +20 -33
- package/lib/typescript/module/src/ContextMenu.d.ts.map +1 -1
- package/lib/typescript/module/src/ContextMenuNativeComponent.d.ts +45 -47
- package/lib/typescript/module/src/ContextMenuNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/DatePicker.d.ts +74 -1
- package/lib/typescript/module/src/DatePicker.d.ts.map +1 -1
- package/lib/typescript/module/src/DatePickerNativeComponent.d.ts +16 -1
- package/lib/typescript/module/src/DatePickerNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/FloatingActionButton.d.ts +67 -0
- package/lib/typescript/module/src/FloatingActionButton.d.ts.map +1 -0
- package/lib/typescript/module/src/FloatingActionButtonNativeComponent.d.ts +43 -0
- package/lib/typescript/module/src/FloatingActionButtonNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/FloatingToolbar.d.ts +75 -0
- package/lib/typescript/module/src/FloatingToolbar.d.ts.map +1 -0
- package/lib/typescript/module/src/FloatingToolbarNativeComponent.d.ts +36 -0
- package/lib/typescript/module/src/FloatingToolbarNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/LiquidGlass.d.ts +17 -0
- package/lib/typescript/module/src/LiquidGlass.d.ts.map +1 -1
- package/lib/typescript/module/src/LiquidGlassContainer.d.ts +27 -0
- package/lib/typescript/module/src/LiquidGlassContainer.d.ts.map +1 -0
- package/lib/typescript/module/src/LiquidGlassContainerNativeComponent.d.ts +13 -0
- package/lib/typescript/module/src/LiquidGlassContainerNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/LiquidGlassNativeComponent.d.ts +7 -0
- package/lib/typescript/module/src/LiquidGlassNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/NavigationRail.d.ts +86 -0
- package/lib/typescript/module/src/NavigationRail.d.ts.map +1 -0
- package/lib/typescript/module/src/NavigationRailNativeComponent.d.ts +81 -0
- package/lib/typescript/module/src/NavigationRailNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/PlatformComponentsProvider.d.ts +6 -0
- package/lib/typescript/module/src/PlatformComponentsProvider.d.ts.map +1 -0
- package/lib/typescript/module/src/SegmentedControl.d.ts +35 -47
- package/lib/typescript/module/src/SegmentedControl.d.ts.map +1 -1
- package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts +11 -0
- package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/SelectionMenu.d.ts +44 -2
- package/lib/typescript/module/src/SelectionMenu.d.ts.map +1 -1
- package/lib/typescript/module/src/SelectionMenuNativeComponent.d.ts +20 -4
- package/lib/typescript/module/src/SelectionMenuNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/SplitButton.d.ts +83 -0
- package/lib/typescript/module/src/SplitButton.d.ts.map +1 -0
- package/lib/typescript/module/src/TabBar.d.ts +144 -0
- package/lib/typescript/module/src/TabBar.d.ts.map +1 -0
- package/lib/typescript/module/src/TabBarAccessoryNativeComponent.d.ts +13 -0
- package/lib/typescript/module/src/TabBarAccessoryNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/TabBarNativeComponent.d.ts +110 -0
- package/lib/typescript/module/src/TabBarNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/TextField.d.ts +332 -0
- package/lib/typescript/module/src/TextField.d.ts.map +1 -0
- package/lib/typescript/module/src/TextFieldNativeComponent.d.ts +240 -0
- package/lib/typescript/module/src/TextFieldNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/focusRegistry.d.ts +13 -0
- package/lib/typescript/module/src/focusRegistry.d.ts.map +1 -0
- package/lib/typescript/module/src/haptics.d.ts +20 -0
- package/lib/typescript/module/src/haptics.d.ts.map +1 -0
- package/lib/typescript/module/src/icons.d.ts +58 -0
- package/lib/typescript/module/src/icons.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +13 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/index.web.d.ts +25 -133
- package/lib/typescript/module/src/index.web.d.ts.map +1 -1
- package/lib/typescript/module/src/jest.d.ts +139 -0
- package/lib/typescript/module/src/jest.d.ts.map +1 -0
- package/lib/typescript/module/src/labelStyle.d.ts +20 -0
- package/lib/typescript/module/src/labelStyle.d.ts.map +1 -0
- package/lib/typescript/module/src/menuItems.d.ts +100 -0
- package/lib/typescript/module/src/menuItems.d.ts.map +1 -0
- package/lib/typescript/module/src/selectionIdentifiers.d.ts +3 -0
- package/lib/typescript/module/src/selectionIdentifiers.d.ts.map +1 -0
- package/lib/typescript/module/src/sharedTypes.d.ts +6 -1
- package/lib/typescript/module/src/sharedTypes.d.ts.map +1 -1
- package/lib/typescript/module/src/submitBehavior.d.ts +9 -0
- package/lib/typescript/module/src/submitBehavior.d.ts.map +1 -0
- package/lib/typescript/module/src/tabItems.d.ts +55 -0
- package/lib/typescript/module/src/tabItems.d.ts.map +1 -0
- package/lib/typescript/module/src/web/Button.d.ts +9 -0
- package/lib/typescript/module/src/web/Button.d.ts.map +1 -0
- package/lib/typescript/module/src/web/ButtonGroup.d.ts +11 -0
- package/lib/typescript/module/src/web/ButtonGroup.d.ts.map +1 -0
- package/lib/typescript/module/src/web/DatePicker.d.ts +14 -0
- package/lib/typescript/module/src/web/DatePicker.d.ts.map +1 -0
- package/lib/typescript/module/src/web/DateRangePicker.d.ts +11 -0
- package/lib/typescript/module/src/web/DateRangePicker.d.ts.map +1 -0
- package/lib/typescript/module/src/web/Dialog.d.ts +12 -0
- package/lib/typescript/module/src/web/Dialog.d.ts.map +1 -0
- package/lib/typescript/module/src/web/FloatingActionButton.d.ts +8 -0
- package/lib/typescript/module/src/web/FloatingActionButton.d.ts.map +1 -0
- package/lib/typescript/module/src/web/Icon.d.ts +12 -0
- package/lib/typescript/module/src/web/Icon.d.ts.map +1 -0
- package/lib/typescript/module/src/web/NativeTheme.d.ts +10 -0
- package/lib/typescript/module/src/web/NativeTheme.d.ts.map +1 -0
- package/lib/typescript/module/src/web/NavigationRail.d.ts +8 -0
- package/lib/typescript/module/src/web/NavigationRail.d.ts.map +1 -0
- package/lib/typescript/module/src/web/PlatformComponentsProvider.d.ts +10 -0
- package/lib/typescript/module/src/web/PlatformComponentsProvider.d.ts.map +1 -0
- package/lib/typescript/module/src/web/SegmentedControl.d.ts +8 -0
- package/lib/typescript/module/src/web/SegmentedControl.d.ts.map +1 -0
- package/lib/typescript/module/src/web/SelectionMenu.d.ts +10 -0
- package/lib/typescript/module/src/web/SelectionMenu.d.ts.map +1 -0
- package/lib/typescript/module/src/web/SplitButton.d.ts +9 -0
- package/lib/typescript/module/src/web/SplitButton.d.ts.map +1 -0
- package/lib/typescript/module/src/web/TabBar.d.ts +9 -0
- package/lib/typescript/module/src/web/TabBar.d.ts.map +1 -0
- package/lib/typescript/module/src/web/TextField.d.ts +9 -0
- package/lib/typescript/module/src/web/TextField.d.ts.map +1 -0
- package/lib/typescript/module/src/web/components.d.ts +17 -0
- package/lib/typescript/module/src/web/components.d.ts.map +1 -0
- package/lib/typescript/module/src/web/containers.d.ts +24 -0
- package/lib/typescript/module/src/web/containers.d.ts.map +1 -0
- package/lib/typescript/module/src/web/dateInput.d.ts +20 -0
- package/lib/typescript/module/src/web/dateInput.d.ts.map +1 -0
- package/lib/typescript/module/src/web/shared.d.ts +43 -0
- package/lib/typescript/module/src/web/shared.d.ts.map +1 -0
- package/lib/typescript/module/src/webComponents.d.ts +43 -0
- package/lib/typescript/module/src/webComponents.d.ts.map +1 -0
- package/package.json +55 -4
- package/plugin/build/index.d.ts +3 -2
- package/plugin/build/index.d.ts.map +1 -1
- package/react-native.config.js +6 -0
- package/shared/PCButtonComponentDescriptors-custom.h +22 -0
- package/shared/PCButtonGroupComponentDescriptors-custom.h +22 -0
- package/shared/PCButtonGroupShadowNode-custom.cpp +48 -0
- package/shared/PCButtonGroupShadowNode-custom.h +55 -0
- package/shared/PCButtonGroupState-custom.h +61 -0
- package/shared/PCButtonShadowNode-custom.cpp +48 -0
- package/shared/PCButtonShadowNode-custom.h +55 -0
- package/shared/PCButtonState-custom.h +61 -0
- package/shared/PCFloatingActionButtonComponentDescriptors-custom.h +22 -0
- package/shared/PCFloatingActionButtonShadowNode-custom.cpp +48 -0
- package/shared/PCFloatingActionButtonShadowNode-custom.h +54 -0
- package/shared/PCFloatingActionButtonState-custom.h +61 -0
- package/shared/PCNavigationRailComponentDescriptors-custom.h +47 -0
- package/shared/PCNavigationRailShadowNode-custom.h +42 -0
- package/shared/PCNavigationRailState-custom.h +62 -0
- package/shared/PCTabBarComponentDescriptors-custom.h +22 -0
- package/shared/PCTabBarShadowNode-custom.cpp +54 -0
- package/shared/PCTabBarShadowNode-custom.h +54 -0
- package/shared/PCTabBarState-custom.h +62 -0
- package/shared/PCTextFieldComponentDescriptors-custom.h +22 -0
- package/shared/PCTextFieldShadowNode-custom.cpp +47 -0
- package/shared/PCTextFieldShadowNode-custom.h +55 -0
- package/shared/PCTextFieldState-custom.h +61 -0
- package/shared/react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h +6 -0
- package/src/Button.tsx +359 -0
- package/src/ButtonGroup.tsx +251 -0
- package/src/ButtonGroupNativeComponent.ts +191 -0
- package/src/ButtonNativeComponent.ts +192 -0
- package/src/ContextMenu.tsx +34 -81
- package/src/ContextMenuNativeComponent.ts +47 -49
- package/src/DatePicker.tsx +182 -4
- package/src/DatePickerNativeComponent.ts +19 -2
- package/src/FloatingActionButton.tsx +121 -0
- package/src/FloatingActionButtonNativeComponent.ts +58 -0
- package/src/FloatingToolbar.tsx +164 -0
- package/src/FloatingToolbarNativeComponent.ts +47 -0
- package/src/LiquidGlass.tsx +24 -1
- package/src/LiquidGlassContainer.tsx +42 -0
- package/src/LiquidGlassContainerNativeComponent.ts +17 -0
- package/src/LiquidGlassNativeComponent.ts +8 -0
- package/src/NavigationRail.tsx +203 -0
- package/src/NavigationRailNativeComponent.ts +107 -0
- package/src/PlatformComponentsProvider.tsx +15 -0
- package/src/SegmentedControl.tsx +57 -119
- package/src/SegmentedControlNativeComponent.ts +14 -0
- package/src/SelectionMenu.tsx +81 -6
- package/src/SelectionMenuNativeComponent.ts +26 -4
- package/src/SplitButton.tsx +198 -0
- package/src/TabBar.tsx +373 -0
- package/src/TabBarAccessoryNativeComponent.ts +19 -0
- package/src/TabBarNativeComponent.ts +145 -0
- package/src/TextField.tsx +908 -0
- package/src/TextFieldNativeComponent.ts +324 -0
- package/src/focusRegistry.ts +90 -0
- package/src/haptics.ts +31 -0
- package/src/icons.ts +143 -0
- package/src/index.tsx +13 -0
- package/src/index.web.tsx +53 -199
- package/src/jest.tsx +958 -0
- package/src/labelStyle.ts +36 -0
- package/src/menuItems.ts +152 -0
- package/src/reactNativeInternals.d.ts +22 -0
- package/src/selectionIdentifiers.ts +17 -0
- package/src/sharedTypes.ts +7 -1
- package/src/submitBehavior.ts +16 -0
- package/src/tabItems.ts +113 -0
- package/src/web/Button.tsx +201 -0
- package/src/web/ButtonGroup.tsx +154 -0
- package/src/web/DatePicker.tsx +204 -0
- package/src/web/DateRangePicker.tsx +135 -0
- package/src/web/Dialog.tsx +54 -0
- package/src/web/FloatingActionButton.tsx +76 -0
- package/src/web/Icon.tsx +33 -0
- package/src/web/NativeTheme.ts +23 -0
- package/src/web/NavigationRail.tsx +148 -0
- package/src/web/PlatformComponentsProvider.tsx +48 -0
- package/src/web/SegmentedControl.tsx +127 -0
- package/src/web/SelectionMenu.tsx +149 -0
- package/src/web/SplitButton.tsx +32 -0
- package/src/web/TabBar.tsx +150 -0
- package/src/web/TextField.tsx +361 -0
- package/src/web/components.tsx +71 -0
- package/src/web/containers.tsx +130 -0
- package/src/web/dateInput.ts +137 -0
- package/src/web/shared.ts +176 -0
- package/src/webComponents.ts +45 -0
- 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
|
+
}
|