@momo-kits/native-kits 0.300.3 → 0.300.4-debug
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/CLAUDE.md +51 -7
- package/README.md +2 -2
- package/build.gradle.kts +13 -0
- package/compose/build.gradle.kts +197 -0
- package/compose/build.gradle.kts.backup +197 -0
- package/compose/compose.podspec +47 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +20 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +12 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +153 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +6 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +110 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/DesignSystemKits.kt +62 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +1071 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +65 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +81 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +357 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +115 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +542 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +49 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +189 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +478 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +265 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +235 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +223 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +265 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +185 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +361 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +108 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +308 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +330 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +220 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +207 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +92 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +560 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +125 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +207 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +501 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +176 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +236 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +180 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +325 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +17 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +194 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +287 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +54 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +50 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +306 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +41 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +338 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +58 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +137 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +116 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +215 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +404 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +732 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +148 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +40 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +233 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +217 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +97 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +377 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +169 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +455 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +103 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +106 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +74 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +413 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +39 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +363 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HelpCenterRequest.kt +37 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +192 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +133 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/ToolkitRequests.kt +41 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +135 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +12 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +91 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +74 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +20 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +118 -0
- package/compose/src/commonTest/kotlin/vn/momo/kits/navigation/component/HelpCenterRequestTest.kt +44 -0
- package/compose/src/commonTest/kotlin/vn/momo/kits/navigation/component/ToolkitRequestsTest.kt +62 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +42 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +45 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +36 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +38 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +217 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +138 -0
- package/gradle/libs.versions.toml +68 -0
- package/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle/wrapper/gradle-wrapper.properties +8 -0
- package/gradle.properties +26 -0
- package/gradlew +252 -0
- package/gradlew.bat +94 -0
- package/ios/Application/ApplicationEnvironment.swift +24 -6
- package/ios/Application/DesignSystemKits.swift +100 -0
- package/ios/Application/DesignSystemKitsPayload.swift +68 -0
- package/ios/Application/MaxApi.swift +30 -3
- package/ios/Avatar/Avatar.swift +4 -3
- package/ios/Badge/Badge.swift +6 -4
- package/ios/Badge/BadgeDot.swift +1 -1
- package/ios/Badge/BadgeRibbon.swift +8 -2
- package/ios/Button/Button.swift +15 -12
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +64 -18
- package/ios/Carousel/Carousel.swift +29 -12
- package/ios/Checkbox/Checkbox.swift +7 -3
- package/ios/Chip/Chip.swift +5 -4
- package/ios/Collapse/Collapse.swift +70 -19
- package/ios/Colors+Radius+Spacing/Colors.swift +10 -1
- package/ios/Colors+Radius+Spacing/Radius.swift +6 -0
- package/ios/DateTimePicker/DateTimePicker.swift +54 -20
- package/ios/DateTimePicker/WheelPicker.swift +103 -105
- package/ios/Divider/Divider.swift +37 -4
- package/ios/Extensions/Color++.swift +38 -9
- package/ios/Extensions/OptionalEnvironmentObject.swift +12 -0
- package/ios/Extensions/SkeletonUICompat.swift +11 -0
- package/ios/Icon/Icon.swift +21 -23
- package/ios/Icon/Icons.swift +1329 -0
- package/ios/IconButton/IconButton.swift +15 -7
- package/ios/Image/Image.swift +7 -23
- package/ios/Information/Information.swift +27 -9
- package/ios/Input/ErrorView.swift +2 -2
- package/ios/Input/Input.swift +171 -148
- package/ios/Input/InputPhoneNumber.swift +10 -9
- package/ios/Input/InputSearch.swift +107 -78
- package/ios/Input/InputTextArea.swift +133 -148
- package/ios/InputDropDown/InputDropDown.swift +10 -7
- package/ios/InputMoney/InputMoney.swift +32 -11
- package/ios/InputOTP/InputOTP.swift +22 -12
- package/ios/Layout/Card.swift +50 -0
- package/ios/Layout/GridContext.swift +187 -3
- package/ios/Layout/Item.swift +5 -4
- package/ios/Layout/ScreenLayout.swift +415 -0
- package/ios/Layout/Section.swift +4 -100
- package/ios/Loader/Loader.swift +5 -9
- package/ios/Lottie/LottieView.swift +14 -2
- package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +169 -29
- package/ios/Navigation/BottomTab/BottomTab.swift +332 -0
- package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +18 -7
- package/ios/Navigation/BottomTab/DuoBarCompression.swift +104 -0
- package/ios/Navigation/BottomTab/DuoTabBar.swift +329 -0
- package/ios/{Application → Navigation/Component}/FloatingButton.swift +43 -54
- package/ios/Navigation/Component/Header.swift +545 -0
- package/ios/Navigation/Component/HeaderBackground.swift +154 -0
- package/ios/Navigation/Component/HeaderJourney.swift +73 -0
- package/ios/Navigation/Component/HeaderLocation.swift +48 -0
- package/ios/Navigation/Component/HeaderRight.swift +425 -0
- package/ios/Navigation/Component/HeaderTitle.swift +36 -0
- package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +21 -24
- package/ios/Navigation/Component/SideBar.swift +183 -0
- package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +8 -8
- package/ios/Navigation/HostBackGestureBridge.swift +78 -0
- package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +9 -2
- package/ios/Navigation/Navigation.swift +204 -0
- package/ios/Navigation/NavigationContainer.swift +195 -0
- package/ios/{Application/Navigation → Navigation}/Navigator.swift +207 -107
- package/ios/Navigation/StackScreen.swift +930 -0
- package/ios/Navigation/StackTransition.swift +400 -0
- package/ios/Navigation/SwipeBackHaptic.swift +33 -0
- package/ios/{Application/Navigation → Navigation}/Tracking/ScreenTracker.swift +12 -5
- package/ios/OTPKeyboard/OTPKeyboard.swift +8 -11
- package/ios/Pagination/PaginationScroll.swift +2 -2
- package/ios/Popup/PopupDisplay.swift +72 -135
- package/ios/Popup/PopupInput.swift +10 -13
- package/ios/Popup/PopupLayout.swift +138 -0
- package/ios/Popup/PopupNotify.swift +59 -69
- package/ios/Popup/PopupPromotion.swift +31 -47
- package/ios/PopupView/FullscreenPopup.swift +13 -19
- package/ios/PopupView/PopupView.swift +1 -1
- package/ios/PopupView/Utils++.swift +0 -8
- package/ios/ProgressInfo/ProgressInfo.swift +4 -2
- package/ios/Radio/Radio.swift +2 -1
- package/ios/Rating/Rating.swift +9 -1
- package/ios/Resources/dot_loading.json +558 -0
- package/ios/Resources/lottie_circle_loader.json +1 -0
- package/ios/ScrollIndicator/ScrollIndicator.swift +11 -10
- package/ios/Skeleton/Skeleton.swift +22 -27
- package/ios/Slider/Slider.swift +16 -18
- package/ios/Stepper/Stepper.swift +7 -3
- package/ios/Steps/Steps.swift +8 -6
- package/ios/SuggestAction/SuggestAction.swift +5 -0
- package/ios/Swipeable/SwipeCell.swift +10 -6
- package/ios/Switch/Switch.swift +22 -20
- package/ios/TabView/TabView.swift +102 -21
- package/ios/Tag/Tag.swift +15 -4
- package/ios/Template/Logo/Logo.swift +2 -1
- package/ios/Template/TrustBanner/TrustBanner.swift +109 -55
- package/ios/Title/Title.swift +6 -17
- package/ios/Tooltip/Tooltip.swift +29 -14
- package/ios/Typography/FontScale.swift +17 -14
- package/ios/Typography/Text.swift +156 -20
- package/ios/Typography/Typography.swift +2 -2
- package/ios/Uploader/Uploader.swift +3 -1
- package/ios/native-kits.podspec +2 -3
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -3
- package/ios-demo/README.md +120 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DuoTabDemo.swift +80 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +2 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +173 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +1 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +2 -2
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +2 -0
- package/package.json +1 -1
- package/publish.sh +19 -2
- package/scripts/gen-ios-icons.mjs +36 -0
- package/settings.gradle.kts +64 -0
- package/ios/Application/Components.swift +0 -270
- package/ios/Application/ComposeApi.swift +0 -22
- package/ios/Application/HeaderRight.swift +0 -306
- package/ios/Application/Navigation/BottomTab/BottomTab.swift +0 -104
- package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
- package/ios/Application/Navigation/HeaderTitle.swift +0 -57
- package/ios/Application/Navigation/NavigationContainer.swift +0 -213
- package/ios/Application/Navigation/NavigationOptions.swift +0 -109
- package/ios/Application/Navigation/NavigatorManager.swift +0 -22
- package/ios/Application/Navigation/component/Header.swift +0 -178
- package/ios/Application/Navigation/component/HeaderBackground.swift +0 -83
- package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
- package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
- package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
- package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -196
- package/ios/Application/Screen.swift +0 -250
- package/ios/Application/StackScreen.swift +0 -492
- package/ios/Typography/FontScaleStore.swift +0 -58
- package/ios/Typography/KitDesignSystem.swift +0 -17
- /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
|
@@ -16,22 +16,24 @@ struct UITextViewWrapper: UIViewRepresentable {
|
|
|
16
16
|
var textColor: Color
|
|
17
17
|
var cursorColor: Color
|
|
18
18
|
var fontWeight: Font.Weight
|
|
19
|
+
var keyboardType: UIKeyboardType = .default
|
|
19
20
|
var disabled: Bool
|
|
20
21
|
var onChangeText: (String) -> Void
|
|
21
|
-
var onDone: (() -> Void)?
|
|
22
22
|
var onFocus: (() -> Void)?
|
|
23
23
|
var onBlur: (() -> Void)?
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
func makeUIView(context: Context) -> UITextView {
|
|
26
26
|
let textView = UITextView()
|
|
27
27
|
textView.delegate = context.coordinator
|
|
28
28
|
textView.backgroundColor = .clear
|
|
29
29
|
textView.textContainerInset = .zero
|
|
30
30
|
textView.textContainer.lineFragmentPadding = 0
|
|
31
|
+
textView.keyboardType = keyboardType
|
|
31
32
|
return textView
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
|
|
34
35
|
func updateUIView(_ uiView: UITextView, context: Context) {
|
|
36
|
+
context.coordinator.parent = self
|
|
35
37
|
if uiView.text != text {
|
|
36
38
|
uiView.text = text
|
|
37
39
|
}
|
|
@@ -39,24 +41,30 @@ struct UITextViewWrapper: UIViewRepresentable {
|
|
|
39
41
|
uiView.tintColor = UIColor(cursorColor)
|
|
40
42
|
// Read through `context.environment` rather than the global `scaleSize`: that is what makes
|
|
41
43
|
// SwiftUI re-run updateUIView when the font scale changes.
|
|
42
|
-
uiView.font =
|
|
43
|
-
|
|
44
|
-
weight: fontWeight == .bold ? .bold : .regular
|
|
44
|
+
uiView.font = momoUIFont(
|
|
45
|
+
size: context.environment.momoFontScale(16),
|
|
46
|
+
weight: fontWeight == .bold ? .bold : .regular,
|
|
47
|
+
family: context.environment.appTheme.font,
|
|
48
|
+
isWhiteList: context.environment.designSystemKitsConfigs.isWhiteList
|
|
45
49
|
)
|
|
46
50
|
uiView.isEditable = !disabled
|
|
51
|
+
if uiView.keyboardType != keyboardType {
|
|
52
|
+
uiView.keyboardType = keyboardType
|
|
53
|
+
uiView.reloadInputViews()
|
|
54
|
+
}
|
|
47
55
|
}
|
|
48
|
-
|
|
56
|
+
|
|
49
57
|
func makeCoordinator() -> Coordinator {
|
|
50
58
|
Coordinator(self)
|
|
51
59
|
}
|
|
52
|
-
|
|
60
|
+
|
|
53
61
|
class Coordinator: NSObject, UITextViewDelegate {
|
|
54
62
|
var parent: UITextViewWrapper
|
|
55
|
-
|
|
63
|
+
|
|
56
64
|
init(_ parent: UITextViewWrapper) {
|
|
57
65
|
self.parent = parent
|
|
58
66
|
}
|
|
59
|
-
|
|
67
|
+
|
|
60
68
|
func textViewDidChange(_ textView: UITextView) {
|
|
61
69
|
let limited = String(textView.text.prefix(parent.maxLength))
|
|
62
70
|
if textView.text != limited {
|
|
@@ -65,20 +73,17 @@ struct UITextViewWrapper: UIViewRepresentable {
|
|
|
65
73
|
parent.text = limited
|
|
66
74
|
parent.onChangeText(limited)
|
|
67
75
|
}
|
|
68
|
-
|
|
76
|
+
|
|
69
77
|
func textViewDidBeginEditing(_ textView: UITextView) {
|
|
70
78
|
parent.onFocus?()
|
|
71
79
|
}
|
|
72
|
-
|
|
80
|
+
|
|
73
81
|
func textViewDidEndEditing(_ textView: UITextView) {
|
|
74
82
|
parent.onBlur?()
|
|
75
83
|
}
|
|
76
|
-
|
|
84
|
+
|
|
85
|
+
// Like Compose's `onValueChange`, an edit that would exceed maxLength is rejected whole.
|
|
77
86
|
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
|
78
|
-
if text == "\n" {
|
|
79
|
-
parent.onDone?()
|
|
80
|
-
return false
|
|
81
|
-
}
|
|
82
87
|
let current = textView.text ?? ""
|
|
83
88
|
guard let range = Range(range, in: current) else { return false }
|
|
84
89
|
let proposed = current.replacingCharacters(in: range, with: text)
|
|
@@ -90,6 +95,7 @@ struct UITextViewWrapper: UIViewRepresentable {
|
|
|
90
95
|
// Make the struct public
|
|
91
96
|
public struct InputTextArea: View {
|
|
92
97
|
@Environment(\.appTheme) private var theme
|
|
98
|
+
@Environment(\.momoFontScale) private var fontScale
|
|
93
99
|
@Binding var text: String
|
|
94
100
|
var maxLength: Int = MAX_LENGTH
|
|
95
101
|
var height: CGFloat = DEFAULT_HEIGHT
|
|
@@ -111,11 +117,9 @@ public struct InputTextArea: View {
|
|
|
111
117
|
var required: Bool = false
|
|
112
118
|
var fontWeight: Font.Weight = .regular
|
|
113
119
|
var keyboardType: UIKeyboardType = .default
|
|
114
|
-
|
|
120
|
+
|
|
115
121
|
@State private var isFocused = false
|
|
116
|
-
|
|
117
|
-
@State private var floatingLabelHeight: CGFloat = 16
|
|
118
|
-
|
|
122
|
+
|
|
119
123
|
// Make the initializer public
|
|
120
124
|
public init(
|
|
121
125
|
text: Binding<String>,
|
|
@@ -162,147 +166,128 @@ public struct InputTextArea: View {
|
|
|
162
166
|
self.fontWeight = fontWeight
|
|
163
167
|
self.keyboardType = keyboardType
|
|
164
168
|
}
|
|
165
|
-
|
|
169
|
+
|
|
166
170
|
public var body: some View {
|
|
167
171
|
VStack(alignment: .leading, spacing: 0) {
|
|
172
|
+
// Compose: `height` is the whole box (text, counter and paddings), scaled.
|
|
173
|
+
VStack(spacing: 0) {
|
|
174
|
+
HStack(alignment: .top, spacing: 0) {
|
|
168
175
|
ZStack(alignment: .topLeading) {
|
|
169
|
-
if
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
color: disabled ? theme.colors.text.disable : (floatingValueColor ?? theme.colors.text.hint)
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
if required {
|
|
178
|
-
MomoText("*", typography: .labelSMedium, color: theme.colors.error.primary)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if !floatingIcon.isEmpty {
|
|
182
|
-
Icon(
|
|
183
|
-
source: floatingIcon,
|
|
184
|
-
size: 16,
|
|
185
|
-
color: disabled ? theme.colors.text.disable : (floatingIconColor ?? theme.colors.text.default)
|
|
186
|
-
)
|
|
187
|
-
.padding(.leading, Spacing.XS)
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
.padding(.horizontal, Spacing.S)
|
|
191
|
-
.background(
|
|
192
|
-
GeometryReader { geo in
|
|
193
|
-
Color.clear.onAppear { floatingLabelHeight = geo.size.height }
|
|
194
|
-
}
|
|
176
|
+
if text.isEmpty {
|
|
177
|
+
MomoText(
|
|
178
|
+
placeholder,
|
|
179
|
+
typography: fontWeight == .bold ? .actionSBold : .bodyDefaultRegular,
|
|
180
|
+
color: disabled ? theme.colors.text.disable : theme.colors.text.hint
|
|
195
181
|
)
|
|
196
|
-
.
|
|
197
|
-
// Kotlin centers the label within the box-height-tall decorationBox
|
|
198
|
-
// by default, then applies `offsetY = -scaleHeight / 2` — net effect:
|
|
199
|
-
// the label ends up centered exactly on the box's top border. Our
|
|
200
|
-
// ZStack anchors top-leading instead, so matching that requires
|
|
201
|
-
// offsetting by half the label's OWN measured height, not half the
|
|
202
|
-
// (104pt-tall) text-area box height.
|
|
203
|
-
.offset(x: Spacing.S, y: -floatingLabelHeight / 2)
|
|
204
|
-
.zIndex(10)
|
|
182
|
+
.fontSize(16, lineHeight: 24)
|
|
205
183
|
}
|
|
206
184
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
fontWeight: fontWeight,
|
|
224
|
-
disabled: disabled,
|
|
225
|
-
onChangeText: onChangeText,
|
|
226
|
-
onDone: {
|
|
227
|
-
// Handle done action if needed
|
|
228
|
-
},
|
|
229
|
-
onFocus: {
|
|
230
|
-
isFocused = true
|
|
231
|
-
onFocus()
|
|
232
|
-
}, onBlur: {
|
|
233
|
-
isFocused = false
|
|
234
|
-
isBlurred = true
|
|
235
|
-
onBlur()
|
|
236
|
-
})
|
|
237
|
-
.frame(height: height)
|
|
238
|
-
.padding(.horizontal, 12)
|
|
239
|
-
.padding(.vertical, 8)
|
|
240
|
-
.disabled(disabled)
|
|
241
|
-
}
|
|
242
|
-
.frame(maxWidth: .infinity)
|
|
243
|
-
|
|
244
|
-
// Clear button (mirrors Compose: focused + non-empty).
|
|
245
|
-
if isFocused && !text.isEmpty {
|
|
246
|
-
SwiftUI.Button(action: {
|
|
247
|
-
text = ""
|
|
248
|
-
onChangeText("")
|
|
249
|
-
}) {
|
|
250
|
-
Icon(source: "24_navigation_close_circle_full", size: 16, color: theme.colors.text.hint)
|
|
251
|
-
}
|
|
252
|
-
.padding(.leading, Spacing.XS)
|
|
253
|
-
.padding(.top, 12)
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Loading indicator and right icon (independent, mirrors
|
|
257
|
-
// Compose's RenderRightIcon: both can render at once).
|
|
258
|
-
if loading {
|
|
259
|
-
ActivityIndicator(isAnimating: .constant(true), style: .medium)
|
|
260
|
-
.frame(width: 16, height: 16)
|
|
261
|
-
.padding(.leading, Spacing.XS)
|
|
262
|
-
.padding(.top, 12)
|
|
263
|
-
}
|
|
264
|
-
if !icon.isEmpty {
|
|
265
|
-
SwiftUI.Button(action: onRightIconPressed) {
|
|
266
|
-
Icon(source: icon, size: 24, color: disabled ? theme.colors.text.disable : (iconColor ?? theme.colors.text.default))
|
|
267
|
-
}
|
|
268
|
-
.padding(.leading, Spacing.XS)
|
|
269
|
-
.padding(.top, 12)
|
|
270
|
-
}
|
|
185
|
+
UITextViewWrapper(
|
|
186
|
+
text: $text,
|
|
187
|
+
maxLength: maxLength,
|
|
188
|
+
textColor: disabled ? theme.colors.text.disable : theme.colors.text.default,
|
|
189
|
+
cursorColor: theme.colors.primary,
|
|
190
|
+
fontWeight: fontWeight,
|
|
191
|
+
keyboardType: keyboardType,
|
|
192
|
+
disabled: disabled,
|
|
193
|
+
onChangeText: onChangeText,
|
|
194
|
+
onFocus: {
|
|
195
|
+
isFocused = true
|
|
196
|
+
onFocus()
|
|
197
|
+
},
|
|
198
|
+
onBlur: {
|
|
199
|
+
isFocused = false
|
|
200
|
+
onBlur()
|
|
271
201
|
}
|
|
202
|
+
)
|
|
203
|
+
.disabled(disabled)
|
|
204
|
+
}
|
|
205
|
+
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
|
272
206
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
207
|
+
// Clear button (mirrors Compose: focused + non-empty).
|
|
208
|
+
if isFocused && !text.isEmpty {
|
|
209
|
+
SwiftUI.Button(action: {
|
|
210
|
+
text = ""
|
|
211
|
+
onChangeText("")
|
|
212
|
+
}) {
|
|
213
|
+
Icon(source: "24_navigation_close_circle_full", size: 16, color: theme.colors.text.hint)
|
|
279
214
|
}
|
|
280
|
-
.
|
|
281
|
-
.cornerRadius(Radius.S)
|
|
282
|
-
.overlay(
|
|
283
|
-
RoundedRectangle(cornerRadius: Radius.S)
|
|
284
|
-
.stroke(borderColor, lineWidth: 1)
|
|
285
|
-
)
|
|
215
|
+
.padding(.leading, Spacing.XS)
|
|
286
216
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
217
|
+
|
|
218
|
+
// Loading indicator and right icon (independent, mirrors
|
|
219
|
+
// Compose's RenderRightIcon: both can render at once).
|
|
220
|
+
if loading {
|
|
221
|
+
ActivityIndicator(isAnimating: .constant(true), style: .medium, color: iconTintColor)
|
|
222
|
+
.frame(width: 16, height: 16)
|
|
223
|
+
.padding(.leading, Spacing.XS)
|
|
294
224
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
onBlur()
|
|
225
|
+
if !icon.isEmpty {
|
|
226
|
+
SwiftUI.Button(action: onRightIconPressed) {
|
|
227
|
+
Icon(source: icon, size: 24, color: iconTintColor)
|
|
228
|
+
}
|
|
229
|
+
.padding(.leading, Spacing.XS)
|
|
301
230
|
}
|
|
302
231
|
}
|
|
303
|
-
.
|
|
232
|
+
.padding([.horizontal, .top], Spacing.M)
|
|
233
|
+
.frame(maxHeight: .infinity, alignment: .top)
|
|
234
|
+
|
|
235
|
+
MomoText("\(text.count)/\(maxLength)", typography: .descriptionXsRegular, color: theme.colors.text.hint)
|
|
236
|
+
.frame(maxWidth: .infinity, alignment: .trailing)
|
|
237
|
+
.padding(.trailing, Spacing.M)
|
|
238
|
+
.padding(.bottom, Spacing.M)
|
|
239
|
+
}
|
|
240
|
+
.frame(height: fontScale(height))
|
|
241
|
+
.background(
|
|
242
|
+
RoundedRectangle(cornerRadius: Radius.S)
|
|
243
|
+
.fill(theme.colors.background.surface)
|
|
244
|
+
)
|
|
245
|
+
.overlay(
|
|
246
|
+
RoundedRectangle(cornerRadius: Radius.S)
|
|
247
|
+
.stroke(borderColor, lineWidth: 1)
|
|
248
|
+
)
|
|
249
|
+
.overlay(alignment: .topLeading) { floatingLabel }
|
|
250
|
+
|
|
251
|
+
ErrorView(errorMessage: error, errorSpacing: errorSpacing, hintText: "")
|
|
252
|
+
}
|
|
253
|
+
.ignoreBoldTextSetting()
|
|
304
254
|
}
|
|
305
|
-
|
|
255
|
+
|
|
256
|
+
/// Centered on the box's top border, like Compose's `FloatingLabel` (offset by -height / 2).
|
|
257
|
+
@ViewBuilder
|
|
258
|
+
private var floatingLabel: some View {
|
|
259
|
+
if !floatingValue.isEmpty || !floatingIcon.isEmpty {
|
|
260
|
+
HStack(spacing: 0) {
|
|
261
|
+
MomoText(
|
|
262
|
+
floatingValue,
|
|
263
|
+
typography: .labelSMedium,
|
|
264
|
+
color: disabled ? theme.colors.text.disable : (floatingValueColor ?? theme.colors.text.hint)
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
if required {
|
|
268
|
+
MomoText("*", typography: .labelSMedium, color: theme.colors.error.primary)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if !floatingIcon.isEmpty {
|
|
272
|
+
Icon(
|
|
273
|
+
source: floatingIcon,
|
|
274
|
+
size: 16,
|
|
275
|
+
color: disabled ? theme.colors.text.disable : (floatingIconColor ?? theme.colors.text.default)
|
|
276
|
+
)
|
|
277
|
+
.padding(.leading, Spacing.XS)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
.padding(.horizontal, Spacing.S)
|
|
281
|
+
.background(theme.colors.background.surface)
|
|
282
|
+
.alignmentGuide(.top) { $0[VerticalAlignment.center] }
|
|
283
|
+
.offset(x: Spacing.S)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private var iconTintColor: Color {
|
|
288
|
+
disabled ? theme.colors.text.disable : (iconColor ?? theme.colors.text.default)
|
|
289
|
+
}
|
|
290
|
+
|
|
306
291
|
private var borderColor: Color {
|
|
307
292
|
if disabled {
|
|
308
293
|
return theme.colors.border.disable
|
|
@@ -19,7 +19,7 @@ private struct RenderRightIcon: View {
|
|
|
19
19
|
// Mirrors Compose's two independent `if` blocks: loading spinner and icon
|
|
20
20
|
// can both render at once, not exclusively (not an if/else).
|
|
21
21
|
if loading {
|
|
22
|
-
ActivityIndicator(isAnimating: .constant(true), style: .medium)
|
|
22
|
+
ActivityIndicator(isAnimating: .constant(true), style: .medium, color: color)
|
|
23
23
|
.frame(width: 16, height: 16)
|
|
24
24
|
}
|
|
25
25
|
if !icon.isEmpty {
|
|
@@ -98,7 +98,7 @@ public struct InputDropDown: View {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
public var body: some View {
|
|
101
|
-
VStack(alignment: .leading, spacing:
|
|
101
|
+
VStack(alignment: .leading, spacing: 0) {
|
|
102
102
|
ZStack(alignment: .topLeading) {
|
|
103
103
|
// Floating label
|
|
104
104
|
if !floatingValue.isEmpty || !floatingIcon.isEmpty {
|
|
@@ -114,7 +114,7 @@ public struct InputDropDown: View {
|
|
|
114
114
|
}
|
|
115
115
|
.padding(.horizontal, Spacing.S)
|
|
116
116
|
.background(theme.colors.background.surface)
|
|
117
|
-
.offset(x: Spacing.S, y: -
|
|
117
|
+
.offset(x: Spacing.S, y: -Spacing.S)
|
|
118
118
|
.zIndex(10)
|
|
119
119
|
}
|
|
120
120
|
|
|
@@ -130,6 +130,7 @@ public struct InputDropDown: View {
|
|
|
130
130
|
typography: .bodyDefaultRegular,
|
|
131
131
|
color: value.isEmpty ? placeholderColor : textColor
|
|
132
132
|
)
|
|
133
|
+
.fontSize(16, lineHeight: 24)
|
|
133
134
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
134
135
|
|
|
135
136
|
RenderRightIcon(
|
|
@@ -146,10 +147,6 @@ public struct InputDropDown: View {
|
|
|
146
147
|
.stroke(getBorderColor(isFocused: false, error: error, disabled: disabled), lineWidth: 1)
|
|
147
148
|
)
|
|
148
149
|
}
|
|
149
|
-
.contentShape(Rectangle())
|
|
150
|
-
.onTapGesture {
|
|
151
|
-
if !disabled { onPress() }
|
|
152
|
-
}
|
|
153
150
|
|
|
154
151
|
ErrorView(
|
|
155
152
|
errorMessage: error,
|
|
@@ -157,8 +154,14 @@ public struct InputDropDown: View {
|
|
|
157
154
|
hintText: hintText
|
|
158
155
|
)
|
|
159
156
|
}
|
|
157
|
+
// Compose makes the whole column clickable, error/hint row included.
|
|
158
|
+
.contentShape(Rectangle())
|
|
159
|
+
.onTapGesture {
|
|
160
|
+
if !disabled { onPress() }
|
|
161
|
+
}
|
|
160
162
|
.accessibilityElement(children: .combine)
|
|
161
163
|
.accessibility(label: Text(floatingValue))
|
|
164
|
+
.accessibilityIdentifier(disabled ? "input_\(floatingValue)_disabled" : "input_\(floatingValue)")
|
|
162
165
|
}
|
|
163
166
|
|
|
164
167
|
// MARK: - Computed colors
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
// MoMoUIKits
|
|
4
4
|
//
|
|
5
5
|
// Native SwiftUI reimplementation of Compose `InputMoney`.
|
|
6
|
-
// The bound `text` holds RAW DIGITS only
|
|
7
|
-
//
|
|
8
|
-
// thousands-separated currency). Display formatting is applied live.
|
|
6
|
+
// The bound `text` holds RAW DIGITS only, as in Compose; the field shows
|
|
7
|
+
// the thousands-separated currency and edits apply at the caret.
|
|
9
8
|
//
|
|
10
9
|
|
|
11
10
|
import Foundation
|
|
@@ -165,17 +164,18 @@ public struct InputMoney: View {
|
|
|
165
164
|
// MARK: - Body
|
|
166
165
|
|
|
167
166
|
public var body: some View {
|
|
168
|
-
VStack(alignment: .leading, spacing:
|
|
167
|
+
VStack(alignment: .leading, spacing: 0) {
|
|
169
168
|
ZStack(alignment: .topLeading) {
|
|
170
169
|
// Floating label
|
|
171
170
|
if !floatingValue.isEmpty || !floatingIcon.isEmpty {
|
|
172
|
-
HStack(spacing:
|
|
171
|
+
HStack(spacing: 0) {
|
|
173
172
|
MomoText(floatingValue, typography: .labelSMedium, color: floatingTitleColor)
|
|
174
173
|
if required {
|
|
175
174
|
MomoText("*", typography: .labelSMedium, color: theme.colors.error.primary)
|
|
176
175
|
}
|
|
177
176
|
if !floatingIcon.isEmpty {
|
|
178
177
|
Icon(source: floatingIcon, size: 16, color: floatingIconTintColor)
|
|
178
|
+
.padding(.leading, Spacing.XS)
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
.padding(.horizontal, Spacing.S)
|
|
@@ -204,17 +204,19 @@ public struct InputMoney: View {
|
|
|
204
204
|
|
|
205
205
|
// Money field
|
|
206
206
|
ZStack(alignment: .leading) {
|
|
207
|
-
if
|
|
207
|
+
if !hasDigits {
|
|
208
208
|
MomoText(
|
|
209
209
|
placeholder,
|
|
210
210
|
typography: fontWeight == .bold ? .actionSBold : .bodyDefaultRegular,
|
|
211
211
|
color: placeholderColor
|
|
212
212
|
)
|
|
213
|
+
.fontSize(20, lineHeight: 24)
|
|
213
214
|
}
|
|
214
215
|
MoneyTextField(
|
|
215
216
|
digits: $text,
|
|
216
217
|
keyboardType: keyboardType,
|
|
217
218
|
fontWeight: fontWeight == .bold ? .bold : .regular,
|
|
219
|
+
fontFamily: theme.font,
|
|
218
220
|
textColor: UIColor(textColor),
|
|
219
221
|
cursorColor: UIColor(theme.colors.primary),
|
|
220
222
|
isDisabled: disabled,
|
|
@@ -225,7 +227,7 @@ public struct InputMoney: View {
|
|
|
225
227
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
226
228
|
|
|
227
229
|
// Clear button (focused + has value)
|
|
228
|
-
if isFocused &&
|
|
230
|
+
if isFocused && hasDigits {
|
|
229
231
|
SwiftUI.Button(action: {
|
|
230
232
|
text = ""
|
|
231
233
|
onChangeText?("")
|
|
@@ -239,7 +241,7 @@ public struct InputMoney: View {
|
|
|
239
241
|
// Loading indicator and right icon can both render (mirrors
|
|
240
242
|
// Compose's two independent `if` blocks, not if/else).
|
|
241
243
|
if loading {
|
|
242
|
-
ActivityIndicator(isAnimating: .constant(true), style: .medium)
|
|
244
|
+
ActivityIndicator(isAnimating: .constant(true), style: .medium, color: iconTintColor)
|
|
243
245
|
.frame(width: 16, height: 16)
|
|
244
246
|
.padding(.leading, Spacing.S)
|
|
245
247
|
}
|
|
@@ -264,6 +266,9 @@ public struct InputMoney: View {
|
|
|
264
266
|
|
|
265
267
|
ErrorView(errorMessage: error, errorSpacing: errorSpacing, hintText: "")
|
|
266
268
|
}
|
|
269
|
+
.accessibilityElement(children: .contain)
|
|
270
|
+
.accessibilityLabel(floatingValue)
|
|
271
|
+
.accessibilityIdentifier(disabled ? "input_\(floatingValue)_disabled" : "input_\(floatingValue)")
|
|
267
272
|
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
|
|
268
273
|
if isFocused {
|
|
269
274
|
DispatchQueue.main.async { handleFocusChange(false) }
|
|
@@ -273,6 +278,8 @@ public struct InputMoney: View {
|
|
|
273
278
|
|
|
274
279
|
// MARK: - Helpers
|
|
275
280
|
|
|
281
|
+
private var hasDigits: Bool { text.contains(where: isMoneyDigit) }
|
|
282
|
+
|
|
276
283
|
private func handleFocusChange(_ focused: Bool) {
|
|
277
284
|
guard isFocused != focused else { return }
|
|
278
285
|
isFocused = focused
|
|
@@ -303,6 +310,7 @@ private struct MoneyTextField: UIViewRepresentable {
|
|
|
303
310
|
@Binding var digits: String
|
|
304
311
|
var keyboardType: UIKeyboardType
|
|
305
312
|
var fontWeight: UIFont.Weight
|
|
313
|
+
var fontFamily: String
|
|
306
314
|
var textColor: UIColor
|
|
307
315
|
var cursorColor: UIColor
|
|
308
316
|
var isDisabled: Bool
|
|
@@ -315,7 +323,7 @@ private struct MoneyTextField: UIViewRepresentable {
|
|
|
315
323
|
let field = UITextField()
|
|
316
324
|
field.delegate = context.coordinator
|
|
317
325
|
field.keyboardType = keyboardType
|
|
318
|
-
field.font =
|
|
326
|
+
field.font = font(context)
|
|
319
327
|
field.textColor = textColor
|
|
320
328
|
field.isEnabled = !isDisabled
|
|
321
329
|
field.tintColor = cursorColor
|
|
@@ -328,10 +336,14 @@ private struct MoneyTextField: UIViewRepresentable {
|
|
|
328
336
|
func updateUIView(_ field: UITextField, context: Context) {
|
|
329
337
|
context.coordinator.parent = self
|
|
330
338
|
// Re-applied here, not just in makeUIView, so a font-scale change reaches an existing field.
|
|
331
|
-
field.font =
|
|
339
|
+
field.font = font(context)
|
|
332
340
|
field.isEnabled = !isDisabled
|
|
333
341
|
field.textColor = textColor
|
|
334
342
|
field.tintColor = cursorColor
|
|
343
|
+
if field.keyboardType != keyboardType {
|
|
344
|
+
field.keyboardType = keyboardType
|
|
345
|
+
field.reloadInputViews()
|
|
346
|
+
}
|
|
335
347
|
let expected = displayValue(digits)
|
|
336
348
|
if field.text != expected {
|
|
337
349
|
field.text = expected
|
|
@@ -340,10 +352,19 @@ private struct MoneyTextField: UIViewRepresentable {
|
|
|
340
352
|
}
|
|
341
353
|
|
|
342
354
|
fileprivate func displayValue(_ raw: String) -> String {
|
|
343
|
-
let numeric = raw.filter
|
|
355
|
+
let numeric = raw.filter(isMoneyDigit)
|
|
344
356
|
return numeric.isEmpty ? "" : formatNumberToMoney(numeric)
|
|
345
357
|
}
|
|
346
358
|
|
|
359
|
+
private func font(_ context: Context) -> UIFont {
|
|
360
|
+
momoUIFont(
|
|
361
|
+
size: context.environment.momoFontScale(20),
|
|
362
|
+
weight: fontWeight,
|
|
363
|
+
family: fontFamily,
|
|
364
|
+
isWhiteList: context.environment.designSystemKitsConfigs.isWhiteList
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
|
|
347
368
|
final class Coordinator: NSObject, UITextFieldDelegate {
|
|
348
369
|
var parent: MoneyTextField
|
|
349
370
|
|
|
@@ -48,6 +48,7 @@ public struct InputOTP: View {
|
|
|
48
48
|
|
|
49
49
|
@FocusState private var isFocused: Bool
|
|
50
50
|
@State private var hasBeenFocused: Bool = false
|
|
51
|
+
@State private var fieldText: String
|
|
51
52
|
|
|
52
53
|
private var maxLength: Int { length ?? 10 }
|
|
53
54
|
|
|
@@ -64,6 +65,7 @@ public struct InputOTP: View {
|
|
|
64
65
|
onBlur: (() -> Void)? = nil
|
|
65
66
|
) {
|
|
66
67
|
self._value = value
|
|
68
|
+
self._fieldText = State(initialValue: value.wrappedValue)
|
|
67
69
|
self.length = length
|
|
68
70
|
self.errorMessage = errorMessage
|
|
69
71
|
self.errorSpacing = errorSpacing
|
|
@@ -105,7 +107,7 @@ public struct InputOTP: View {
|
|
|
105
107
|
HStack {
|
|
106
108
|
Spacer()
|
|
107
109
|
SwiftUI.Button(action: clearText) {
|
|
108
|
-
Icon(source: "24_navigation_close_circle_full", size: 12, color: theme.colors.text.
|
|
110
|
+
Icon(source: "24_navigation_close_circle_full", size: 12, color: theme.colors.text.default)
|
|
109
111
|
.padding(.trailing, 12)
|
|
110
112
|
}
|
|
111
113
|
}
|
|
@@ -122,17 +124,24 @@ public struct InputOTP: View {
|
|
|
122
124
|
// MARK: Subviews
|
|
123
125
|
|
|
124
126
|
private var hiddenField: some View {
|
|
125
|
-
TextField("", text:
|
|
126
|
-
get: { value },
|
|
127
|
-
set: { handleChangeText($0) }
|
|
128
|
-
))
|
|
127
|
+
TextField("", text: $fieldText)
|
|
129
128
|
.keyboardType(dataType == "number" ? .numberPad : .asciiCapable)
|
|
129
|
+
.textInputAutocapitalization(.never)
|
|
130
|
+
.autocorrectionDisabled()
|
|
130
131
|
.focused($isFocused)
|
|
131
132
|
.foregroundColor(.clear)
|
|
132
133
|
.accentColor(.clear)
|
|
133
134
|
.frame(maxWidth: .infinity)
|
|
134
135
|
.frame(height: 56)
|
|
135
136
|
.opacity(0.01)
|
|
137
|
+
// Writes a rejected edit back, so the field stays in step with `value` like Compose's
|
|
138
|
+
// controlled field instead of keeping the extra character and swallowing the next key.
|
|
139
|
+
.onChange(of: fieldText) { text in
|
|
140
|
+
if text != value && !handleChangeText(text) { fieldText = value }
|
|
141
|
+
}
|
|
142
|
+
.onChange(of: value) { newValue in
|
|
143
|
+
if fieldText != newValue { fieldText = newValue }
|
|
144
|
+
}
|
|
136
145
|
.onChange(of: isFocused) { focused in
|
|
137
146
|
if focused {
|
|
138
147
|
onFocus?()
|
|
@@ -147,7 +156,7 @@ public struct InputOTP: View {
|
|
|
147
156
|
private var cells: some View {
|
|
148
157
|
if let length = length {
|
|
149
158
|
HStack(spacing: Spacing.L) {
|
|
150
|
-
ForEach(0 ..< length, id: \.self) { i in
|
|
159
|
+
ForEach(0 ..< max(length, 0), id: \.self) { i in
|
|
151
160
|
cell(at: i)
|
|
152
161
|
}
|
|
153
162
|
}
|
|
@@ -155,7 +164,7 @@ public struct InputOTP: View {
|
|
|
155
164
|
} else {
|
|
156
165
|
HStack(spacing: Spacing.L) {
|
|
157
166
|
if value.isEmpty && !isFocused && !placeholder.isEmpty {
|
|
158
|
-
MomoText(placeholder, typography: .
|
|
167
|
+
MomoText(placeholder, typography: .bodyDefaultRegular, color: theme.colors.text.hint)
|
|
159
168
|
} else {
|
|
160
169
|
ForEach(Array(value.enumerated()), id: \.offset) { _, ch in
|
|
161
170
|
MomoText(String(ch), typography: .headerDefaultBold, color: theme.colors.text.default)
|
|
@@ -186,13 +195,14 @@ public struct InputOTP: View {
|
|
|
186
195
|
|
|
187
196
|
// MARK: Logic
|
|
188
197
|
|
|
189
|
-
|
|
198
|
+
@discardableResult
|
|
199
|
+
private func handleChangeText(_ text: String) -> Bool {
|
|
190
200
|
let validData = dataType != "number" || text.allSatisfy { $0.isNumber }
|
|
191
201
|
let changed = text.count != value.count
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
202
|
+
guard text.count <= maxLength && validData && changed else { return false }
|
|
203
|
+
value = text
|
|
204
|
+
onChangeText?(text)
|
|
205
|
+
return true
|
|
196
206
|
}
|
|
197
207
|
|
|
198
208
|
private func clearText() {
|