@momo-kits/native-kits 0.167.1-duo.8 → 0.167.1-duo.9-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 +4 -1
- package/build.gradle.kts +11 -0
- package/compose/build.gradle.kts +204 -0
- package/compose/build.gradle.kts.backup +204 -0
- package/compose/compose.podspec +47 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +19 -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/AnimationSearchInput.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +124 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +183 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +207 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +39 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +293 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +831 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +110 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +378 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +56 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -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 +76 -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 +16 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +18 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +22 -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 +188 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +297 -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 +232 -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 +106 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +205 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +393 -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 +708 -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 +35 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +209 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +231 -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 +98 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +312 -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 +408 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +92 -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 +366 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +37 -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/SideBar.kt +185 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +133 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +127 -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 +13 -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/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +40 -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 +32 -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 +86 -0
- package/gradle/libs.versions.toml +69 -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 +11 -0
- package/ios/Application/ComposeApi.swift +2 -1
- package/ios/Application/{Components.swift → LegacyHeader.swift} +6 -62
- package/ios/Application/Screen.swift +3 -2
- 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 +0 -1
- package/ios/Button/Button.swift +15 -12
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +12 -20
- 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 +9 -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 +18 -0
- package/ios/Icon/Icon.swift +7 -1
- package/ios/IconButton/IconButton.swift +15 -7
- package/ios/Image/Image.swift +7 -34
- package/ios/Information/Information.swift +27 -9
- package/ios/Input/ErrorView.swift +2 -2
- package/ios/Input/Input.swift +160 -147
- package/ios/Input/InputPhoneNumber.swift +12 -9
- package/ios/Input/InputSearch.swift +102 -75
- package/ios/Input/InputTextArea.swift +128 -145
- package/ios/InputDropDown/InputDropDown.swift +10 -7
- package/ios/InputMoney/InputMoney.swift +33 -11
- package/ios/InputOTP/InputOTP.swift +22 -12
- package/ios/Layout/Card.swift +50 -0
- package/ios/Layout/GridContext.swift +123 -3
- package/ios/Layout/Item.swift +5 -4
- package/ios/Layout/Section.swift +4 -100
- package/ios/Loader/Loader.swift +5 -9
- package/ios/Lottie/LottieView.swift +10 -0
- package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +61 -9
- package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTab.swift +60 -34
- package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +11 -6
- package/ios/{Application → Navigation/Component}/FloatingButton.swift +14 -3
- package/ios/Navigation/Component/Header.swift +493 -0
- package/ios/Navigation/Component/HeaderBackground.swift +134 -0
- package/ios/Navigation/Component/HeaderJourney.swift +73 -0
- package/ios/Navigation/Component/HeaderLocation.swift +48 -0
- package/ios/Navigation/Component/HeaderRight.swift +386 -0
- package/ios/Navigation/Component/HeaderTitle.swift +36 -0
- package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +20 -23
- package/ios/{Application/Navigation/component → Navigation/Component}/SideBar.swift +49 -28
- package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +5 -5
- package/ios/Navigation/HostBackGestureBridge.swift +57 -0
- package/ios/Navigation/Navigation.swift +190 -0
- package/ios/{Application/Navigation → Navigation}/NavigationContainer.swift +46 -41
- package/ios/{Application/Navigation → Navigation}/Navigator.swift +65 -9
- package/ios/{Application → Navigation}/StackScreen.swift +236 -103
- 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 +79 -159
- package/ios/Popup/PopupInput.swift +10 -13
- package/ios/Popup/PopupLayout.swift +97 -2
- package/ios/Popup/PopupNotify.swift +27 -55
- package/ios/Popup/PopupPromotion.swift +22 -50
- 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/ScrollIndicator/ScrollIndicator.swift +11 -10
- package/ios/Skeleton/Skeleton.swift +22 -27
- package/ios/Slider/Slider.swift +16 -18
- package/ios/Stepper/Stepper.swift +6 -1
- 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 +41 -27
- package/ios/Title/Title.swift +6 -17
- package/ios/Tooltip/Tooltip.swift +29 -14
- package/ios/Typography/Text.swift +61 -19
- package/ios/Uploader/Uploader.swift +3 -1
- package/ios/native-kits.podspec +1 -0
- package/ios-demo/README.md +120 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
- package/package.json +1 -1
- package/settings.gradle.kts +64 -0
- package/ios/Application/HeaderRight.swift +0 -324
- package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
- package/ios/Application/Navigation/HeaderTitle.swift +0 -57
- package/ios/Application/Navigation/NavigationOptions.swift +0 -109
- package/ios/Application/Navigation/NavigatorManager.swift +0 -29
- package/ios/Application/Navigation/component/DuoScrollGradient.swift +0 -54
- package/ios/Application/Navigation/component/Header.swift +0 -183
- package/ios/Application/Navigation/component/HeaderBackground.swift +0 -84
- 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 -280
- /package/ios/{Application/Layout → Layout}/ScreenLayout.swift +0 -0
- /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
- /package/ios/{Application/Navigation → Navigation}/BottomTab/DuoBarCompression.swift +0 -0
- /package/ios/{Application/Navigation → Navigation}/BottomTab/DuoTabBar.swift +0 -0
- /package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +0 -0
|
@@ -2,13 +2,13 @@ import SwiftUI
|
|
|
2
2
|
import SDWebImageSwiftUI
|
|
3
3
|
|
|
4
4
|
public struct PopupPromotion: View {
|
|
5
|
-
@Environment(\.appTheme) private var theme
|
|
6
5
|
@Environment(\.screenLayout) private var layout
|
|
7
6
|
|
|
8
7
|
var imageUrl: String
|
|
9
8
|
var onIconClose: (() -> Void)?
|
|
10
9
|
var onAction: (() -> Void)?
|
|
11
10
|
|
|
11
|
+
@State private var imageFailed = false
|
|
12
12
|
|
|
13
13
|
public init(
|
|
14
14
|
imageUrl: String,
|
|
@@ -21,66 +21,38 @@ public struct PopupPromotion: View {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
private static let aspectRatio: CGFloat = 0.72
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
private static let closeTapSize: CGFloat = 40
|
|
25
|
+
// close button row under the image: top padding + 40pt tap target
|
|
26
|
+
private static let closeRowHeight: CGFloat = Spacing.S + closeTapSize
|
|
26
27
|
|
|
27
28
|
// fit the portrait image by height when iPhone Duo leaves too little room
|
|
28
29
|
private var imageWidth: CGFloat {
|
|
29
|
-
let width = min(layout.width - Spacing.
|
|
30
|
+
let width = min(layout.width - Spacing.M * 2, layout.popupMaxWidth)
|
|
30
31
|
guard let maxHeight = layout.popupMaxHeight else { return width }
|
|
31
32
|
return min(width, (maxHeight - Self.closeRowHeight) * Self.aspectRatio)
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
public var body: some View {
|
|
35
|
-
VStack(spacing:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.aspectRatio(Self.aspectRatio, contentMode: .fit)
|
|
49
|
-
.frame(maxWidth: imageWidth, alignment: .center)
|
|
50
|
-
.clipped()
|
|
51
|
-
.onTapGesture {
|
|
52
|
-
onAction?()
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
buildCloseIcon()
|
|
56
|
-
.accessibility(identifier: "ic_popup_close")
|
|
57
|
-
}
|
|
58
|
-
.accessibilityElement(children: .ignore)
|
|
59
|
-
.accessibility(identifier: "popup_promotion")
|
|
60
|
-
}
|
|
36
|
+
VStack(spacing: 0) {
|
|
37
|
+
// stretched like Compose's ContentScale.FillBounds; an empty url shows the error state, as in Compose
|
|
38
|
+
WebImage(url: URL(string: imageUrl))
|
|
39
|
+
.onSuccess { _, _, _ in imageFailed = false }
|
|
40
|
+
.onFailure { _ in imageFailed = true }
|
|
41
|
+
.resizable()
|
|
42
|
+
.placeholder { PopupImagePlaceholder(failed: imageFailed || URL(string: imageUrl) == nil) }
|
|
43
|
+
.aspectRatio(Self.aspectRatio, contentMode: .fit)
|
|
44
|
+
.frame(maxWidth: imageWidth)
|
|
45
|
+
.contentShape(Rectangle())
|
|
46
|
+
.onTapGesture {
|
|
47
|
+
onAction?()
|
|
48
|
+
}
|
|
61
49
|
|
|
62
|
-
|
|
63
|
-
private func buildCloseIcon() -> some View {
|
|
64
|
-
HStack(alignment: .center, spacing: 0) {
|
|
65
|
-
SwiftUI.Button(action: {
|
|
50
|
+
PopupCloseButton(tapSize: Self.closeTapSize) {
|
|
66
51
|
onIconClose?()
|
|
67
|
-
}) {
|
|
68
|
-
ZStack {
|
|
69
|
-
Circle()
|
|
70
|
-
.strokeBorder(theme.colors.background.surface, lineWidth: 2)
|
|
71
|
-
.background(Circle().fill(theme.colors.text.default))
|
|
72
|
-
.frame(width: 22, height: 22)
|
|
73
|
-
|
|
74
|
-
Icon(source: "navigation_close", size: 16, color: theme.colors.background.surface)
|
|
75
|
-
}
|
|
76
|
-
.frame(width: 40, height: 40)
|
|
77
|
-
.padding(8)
|
|
78
|
-
.zIndex(1)
|
|
79
52
|
}
|
|
53
|
+
.padding(.top, Spacing.S)
|
|
80
54
|
}
|
|
55
|
+
.accessibilityElement(children: .contain)
|
|
56
|
+
.accessibilityIdentifier("popup_promotion")
|
|
81
57
|
}
|
|
82
|
-
|
|
83
58
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
@@ -51,12 +51,9 @@ public struct FullscreenPopup<Item: Equatable, PopupContent: View>: ViewModifier
|
|
|
51
51
|
@State private var opacity = 0.0
|
|
52
52
|
|
|
53
53
|
// MARK: - Autohide
|
|
54
|
-
///
|
|
55
|
-
|
|
56
|
-
private var
|
|
57
|
-
|
|
58
|
-
/// holder for autohiding dispatch work (to be able to cancel it when needed)
|
|
59
|
-
private var dispatchWorkHolder = DispatchWorkHolder()
|
|
54
|
+
/// holder for autohiding dispatch work (to be able to cancel it when needed);
|
|
55
|
+
/// state so it outlives re-renders, which would otherwise orphan the pending work
|
|
56
|
+
@State private var dispatchWorkHolder = DispatchWorkHolder()
|
|
60
57
|
|
|
61
58
|
// MARK: - Internal
|
|
62
59
|
/// Set dismiss souce to pass to dismiss callback
|
|
@@ -85,9 +82,6 @@ public struct FullscreenPopup<Item: Equatable, PopupContent: View>: ViewModifier
|
|
|
85
82
|
self.backgroundColor = backgroundColor
|
|
86
83
|
self.dismissCallback = dismissCallback
|
|
87
84
|
self.view = view
|
|
88
|
-
|
|
89
|
-
self.isPresentedRef = ClassReference(self.$isPresented)
|
|
90
|
-
self.itemRef = ClassReference(self.$item)
|
|
91
85
|
}
|
|
92
86
|
|
|
93
87
|
init(item: Binding<Item?>,
|
|
@@ -113,9 +107,6 @@ public struct FullscreenPopup<Item: Equatable, PopupContent: View>: ViewModifier
|
|
|
113
107
|
self.backgroundColor = backgroundColor
|
|
114
108
|
self.dismissCallback = dismissCallback
|
|
115
109
|
self.view = view
|
|
116
|
-
|
|
117
|
-
self.isPresentedRef = ClassReference(self.$isPresented)
|
|
118
|
-
self.itemRef = ClassReference(self.$item)
|
|
119
110
|
}
|
|
120
111
|
|
|
121
112
|
public func body(content: Content) -> some View {
|
|
@@ -214,7 +205,8 @@ public struct FullscreenPopup<Item: Equatable, PopupContent: View>: ViewModifier
|
|
|
214
205
|
}
|
|
215
206
|
|
|
216
207
|
func onAnimationCompleted() -> () {
|
|
217
|
-
|
|
208
|
+
// only proceed on hiding, once: both the offset animation and the fallback timer land here
|
|
209
|
+
if shouldShowContent || !showContent {
|
|
218
210
|
return
|
|
219
211
|
}
|
|
220
212
|
showContent = false // unload popup body after hiding animation is done
|
|
@@ -229,12 +221,14 @@ public struct FullscreenPopup<Item: Equatable, PopupContent: View>: ViewModifier
|
|
|
229
221
|
if let autohideIn = autohideIn {
|
|
230
222
|
dispatchWorkHolder.work?.cancel()
|
|
231
223
|
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
dismissSource = .autohide
|
|
224
|
+
// bindings only: capturing the struct would retain the work holder in a cycle
|
|
225
|
+
let isPresented = $isPresented
|
|
226
|
+
let item = $item
|
|
227
|
+
let dismissSource = $dismissSource
|
|
228
|
+
dispatchWorkHolder.work = DispatchWorkItem(block: {
|
|
229
|
+
dismissSource.wrappedValue = .autohide
|
|
230
|
+
isPresented.wrappedValue = false
|
|
231
|
+
item.wrappedValue = nil
|
|
238
232
|
})
|
|
239
233
|
if sheetPresented, let work = dispatchWorkHolder.work {
|
|
240
234
|
DispatchQueue.main.asyncAfter(deadline: .now() + autohideIn, execute: work)
|
|
@@ -239,7 +239,7 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
|
|
|
239
239
|
.onAnimationCompleted(for: currentOffset) {
|
|
240
240
|
animationCompletedCallback()
|
|
241
241
|
}
|
|
242
|
-
.animation(animation)
|
|
242
|
+
.animation(animation, value: currentOffset)
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
#if !os(tvOS)
|
|
@@ -112,7 +112,9 @@ private struct ProgressInfoHorizontal: View {
|
|
|
112
112
|
|
|
113
113
|
var body: some View {
|
|
114
114
|
HStack(alignment: .top, spacing: 0) {
|
|
115
|
-
|
|
115
|
+
// Keyed by position: `ProgressInfoItem.id` is a fresh UUID per init, so it churns
|
|
116
|
+
// identity whenever the caller rebuilds `steps`.
|
|
117
|
+
ForEach(Array(steps.enumerated()), id: \.offset) { index, item in
|
|
116
118
|
let isFirst = index == 0
|
|
117
119
|
let isLast = index == steps.count - 1
|
|
118
120
|
|
|
@@ -207,7 +209,7 @@ private struct ProgressInfoVertical: View {
|
|
|
207
209
|
|
|
208
210
|
var body: some View {
|
|
209
211
|
VStack(alignment: .leading, spacing: 0) {
|
|
210
|
-
ForEach(Array(steps.enumerated()), id: \.
|
|
212
|
+
ForEach(Array(steps.enumerated()), id: \.offset) { index, item in
|
|
211
213
|
let isLast = index == steps.count - 1
|
|
212
214
|
|
|
213
215
|
HStack(alignment: .top, spacing: 0) {
|
package/ios/Radio/Radio.swift
CHANGED
|
@@ -36,8 +36,9 @@ public struct Radio: View {
|
|
|
36
36
|
public var body: some View {
|
|
37
37
|
SwiftUI.Button(action: onTap) {
|
|
38
38
|
HStack(spacing: 0) {
|
|
39
|
+
// strokeBorder keeps the ring inside 20pt, like Compose `border`.
|
|
39
40
|
RoundedRectangle(cornerRadius: Radius.M)
|
|
40
|
-
.
|
|
41
|
+
.strokeBorder(borderColor, lineWidth: borderWidth)
|
|
41
42
|
.frame(width: 20, height: 20)
|
|
42
43
|
.padding(.trailing, label.isEmpty ? 0 : Spacing.XS)
|
|
43
44
|
|
package/ios/Rating/Rating.swift
CHANGED
|
@@ -35,7 +35,7 @@ public struct Rating: View {
|
|
|
35
35
|
|
|
36
36
|
public var body: some View {
|
|
37
37
|
HStack(spacing: 2) {
|
|
38
|
-
ForEach(0..<numOfStars, id: \.self) { index in
|
|
38
|
+
ForEach(0..<max(numOfStars, 0), id: \.self) { index in
|
|
39
39
|
star(at: index)
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -59,7 +59,14 @@ public struct Rating: View {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
// Kotlin's toInt() maps NaN to 0 and saturates; Swift's Int(_:) traps on both.
|
|
63
|
+
private var safeRating: Float {
|
|
64
|
+
guard rating.isFinite else { return 0 }
|
|
65
|
+
return min(max(rating, -Float(Int32.max)), Float(Int32.max))
|
|
66
|
+
}
|
|
67
|
+
|
|
62
68
|
private func starIcon(at index: Int) -> String {
|
|
69
|
+
let rating = safeRating
|
|
63
70
|
let intRating = Int(rating)
|
|
64
71
|
if index < intRating {
|
|
65
72
|
return "reaction_star_full"
|
|
@@ -71,6 +78,7 @@ public struct Rating: View {
|
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
private func starColor(at index: Int) -> Color {
|
|
81
|
+
let rating = safeRating
|
|
74
82
|
let intRating = Int(rating)
|
|
75
83
|
if index < intRating {
|
|
76
84
|
return Colors.yellow03
|
|
@@ -58,19 +58,20 @@ public struct ScrollViewWithScrollBar<Content: View>: View {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
.onPreferenceChange(ViewOffsetKey.self) { offset in
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
// Clamped: content narrower than the viewport, or bounce past either end,
|
|
62
|
+
// would otherwise push the indicator out of its track (or to inf/NaN).
|
|
63
|
+
let maxOffset = contentSize - scrollViewWidth
|
|
64
|
+
let progress = maxOffset > 0 ? min(max(offset / maxOffset, 0), 1) : 0
|
|
65
|
+
self.position = CGPoint(
|
|
66
|
+
x: progress * (indicatorContainerWidth - indicatorWidth) + indicatorWidth / 2,
|
|
67
|
+
y: INDICATOR_HEIGHT / 2
|
|
68
|
+
)
|
|
68
69
|
}
|
|
69
70
|
}.background(
|
|
70
71
|
GeometryReader { proxy in
|
|
71
|
-
Color.clear
|
|
72
|
-
self.scrollViewWidth = proxy.size.width
|
|
73
|
-
|
|
72
|
+
Color.clear
|
|
73
|
+
.onAppear { self.scrollViewWidth = proxy.size.width }
|
|
74
|
+
.onChange(of: proxy.size.width) { self.scrollViewWidth = $0 }
|
|
74
75
|
}
|
|
75
76
|
).coordinateSpace(name: "scroll")
|
|
76
77
|
|
|
@@ -44,8 +44,6 @@ private struct ShimmerLoading: View {
|
|
|
44
44
|
let durationMillis: Int
|
|
45
45
|
let angleOfAxisY: CGFloat
|
|
46
46
|
|
|
47
|
-
@State private var phase: CGFloat = 0
|
|
48
|
-
|
|
49
47
|
// Matches Compose `SHADOW_BRUSH_PERCENTAGE = 0.6f`.
|
|
50
48
|
private static let shadowBrushPercentage: CGFloat = 0.6
|
|
51
49
|
|
|
@@ -67,33 +65,30 @@ private struct ShimmerLoading: View {
|
|
|
67
65
|
size.width + widthOfShadowBrush
|
|
68
66
|
}
|
|
69
67
|
|
|
68
|
+
// Clock-driven rather than an onAppear animation: gradient points aren't reliably animatable,
|
|
69
|
+
// and a view first laid out at zero width (or resized later) would never (re)start.
|
|
70
70
|
var body: some View {
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
TimelineView(.animation) { timeline in
|
|
72
|
+
Rectangle().fill(gradient(at: timeline.date))
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private func gradient(at date: Date) -> LinearGradient {
|
|
77
|
+
let duration = Double(max(durationMillis, 1)) / 1000
|
|
78
|
+
let progress = date.timeIntervalSinceReferenceDate.truncatingRemainder(dividingBy: duration) / duration
|
|
79
|
+
let endX = CGFloat(progress) * animationTarget
|
|
80
|
+
let startX = endX - widthOfShadowBrush
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
x: size.width > 0 ? endX / size.width : 0,
|
|
84
|
-
y: size.height > 0 ? angleOfAxisY / size.height : 0
|
|
85
|
-
)
|
|
86
|
-
)
|
|
82
|
+
return LinearGradient(
|
|
83
|
+
gradient: Gradient(colors: Self.shimmerColors),
|
|
84
|
+
startPoint: .init(
|
|
85
|
+
x: size.width > 0 ? startX / size.width : 0,
|
|
86
|
+
y: 0
|
|
87
|
+
),
|
|
88
|
+
endPoint: .init(
|
|
89
|
+
x: size.width > 0 ? endX / size.width : 0,
|
|
90
|
+
y: size.height > 0 ? angleOfAxisY / size.height : 0
|
|
87
91
|
)
|
|
88
|
-
|
|
89
|
-
guard size.width > 0 else { return }
|
|
90
|
-
phase = 0
|
|
91
|
-
withAnimation(
|
|
92
|
-
.linear(duration: Double(durationMillis) / 1000.0)
|
|
93
|
-
.repeatForever(autoreverses: false)
|
|
94
|
-
) {
|
|
95
|
-
phase = animationTarget
|
|
96
|
-
}
|
|
97
|
-
}
|
|
92
|
+
)
|
|
98
93
|
}
|
|
99
94
|
}
|
package/ios/Slider/Slider.swift
CHANGED
|
@@ -24,12 +24,14 @@ private func getValueForPosition(
|
|
|
24
24
|
step: CGFloat
|
|
25
25
|
) -> CGFloat {
|
|
26
26
|
let availableSpace = containerWidth - thumbWidth
|
|
27
|
-
if availableSpace <= 0 { return minV }
|
|
28
|
-
let relStepUnit = step / (maxV - minV)
|
|
27
|
+
if availableSpace <= 0 || maxV <= minV { return minV }
|
|
29
28
|
var relPosition = (positionInView - thumbWidth / 2) / availableSpace
|
|
29
|
+
// A zero step would make the snap math NaN, which later traps in `Int(_:)`.
|
|
30
|
+
if step <= 0 { return clampF(minV + relPosition * (maxV - minV), minV, maxV) }
|
|
31
|
+
let relStepUnit = step / (maxV - minV)
|
|
30
32
|
let relOffset = relPosition.truncatingRemainder(dividingBy: relStepUnit)
|
|
31
33
|
relPosition -= relOffset
|
|
32
|
-
if
|
|
34
|
+
if relOffset / relStepUnit >= 0.5 { relPosition += relStepUnit }
|
|
33
35
|
let steps = (relPosition / relStepUnit).rounded()
|
|
34
36
|
return clampF(minV + steps * step, minV, maxV)
|
|
35
37
|
}
|
|
@@ -49,7 +51,8 @@ private func formatLabel(_ value: CGFloat) -> String {
|
|
|
49
51
|
if value == value.rounded() {
|
|
50
52
|
return String(Int(value))
|
|
51
53
|
}
|
|
52
|
-
|
|
54
|
+
// Truncates to one decimal like Compose's `(value * 10).toLong() / 10.0`.
|
|
55
|
+
return String(Double((value * 10).rounded(.towardZero)) / 10)
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
// ── Component ─────────────────────────────────────────────────────────────────
|
|
@@ -64,8 +67,6 @@ public struct MomoSlider: View {
|
|
|
64
67
|
private let minV: CGFloat
|
|
65
68
|
private let maxV: CGFloat
|
|
66
69
|
private let step: CGFloat
|
|
67
|
-
private let initLow: CGFloat
|
|
68
|
-
private let initHigh: CGFloat
|
|
69
70
|
private let minRange: CGFloat
|
|
70
71
|
private let disableRange: Bool
|
|
71
72
|
private let floatingLabel: Bool
|
|
@@ -74,11 +75,10 @@ public struct MomoSlider: View {
|
|
|
74
75
|
private let onSliderTouchStart: (CGFloat, CGFloat) -> Void
|
|
75
76
|
private let onSliderTouchEnd: (CGFloat, CGFloat) -> Void
|
|
76
77
|
|
|
77
|
-
@State private var lowValue: CGFloat
|
|
78
|
-
@State private var highValue: CGFloat
|
|
78
|
+
@State private var lowValue: CGFloat
|
|
79
|
+
@State private var highValue: CGFloat
|
|
79
80
|
@State private var draggingLow: Bool = true
|
|
80
81
|
@State private var isDragging: Bool = false
|
|
81
|
-
@State private var didInit: Bool = false
|
|
82
82
|
|
|
83
83
|
public init(
|
|
84
84
|
min: CGFloat,
|
|
@@ -105,8 +105,10 @@ public struct MomoSlider: View {
|
|
|
105
105
|
self.onSliderTouchStart = onSliderTouchStart
|
|
106
106
|
self.onSliderTouchEnd = onSliderTouchEnd
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
let startLow = clampF(low ?? min, min, max)
|
|
109
|
+
let startHigh = disableRange ? max : clampF(high ?? max, min, max)
|
|
110
|
+
_lowValue = State(initialValue: startLow)
|
|
111
|
+
_highValue = State(initialValue: startHigh)
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
private var thumbColor: Color { disabled ? theme.colors.text.disable : theme.colors.primary }
|
|
@@ -165,17 +167,13 @@ public struct MomoSlider: View {
|
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
.frame(width: trackWidth, height: topPad + THUMB_SIZE, alignment: .topLeading)
|
|
170
|
+
// Compose's spring(stiffness = 1200) is critically damped.
|
|
171
|
+
.animation(.interpolatingSpring(stiffness: 1200, damping: 2 * sqrt(1200)), value: lowValue)
|
|
172
|
+
.animation(.interpolatingSpring(stiffness: 1200, damping: 2 * sqrt(1200)), value: highValue)
|
|
168
173
|
.contentShape(Rectangle())
|
|
169
174
|
.gesture(dragGesture(trackWidth: trackWidth))
|
|
170
175
|
}
|
|
171
176
|
.frame(height: (floatingLabel ? (THUMB_SIZE + LABEL_GAP) : 0) + THUMB_SIZE)
|
|
172
|
-
.onAppear {
|
|
173
|
-
if !didInit {
|
|
174
|
-
lowValue = initLow
|
|
175
|
-
highValue = initHigh
|
|
176
|
-
didInit = true
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
177
|
}
|
|
180
178
|
|
|
181
179
|
private var thumb: some View {
|
|
@@ -109,9 +109,10 @@ private struct StepperNumberView: View {
|
|
|
109
109
|
.font(.system(size: fontScale(12)))
|
|
110
110
|
.ignoreBoldTextSetting()
|
|
111
111
|
.foregroundColor(textColor)
|
|
112
|
+
// Compose's min width includes the horizontal padding.
|
|
113
|
+
.padding(.horizontal, Spacing.XS)
|
|
112
114
|
.frame(minWidth: specs.numberMinWidth)
|
|
113
115
|
.frame(height: specs.numberHeight)
|
|
114
|
-
.padding(.horizontal, Spacing.XS)
|
|
115
116
|
.overlay(
|
|
116
117
|
RoundedRectangle(cornerRadius: Radius.S)
|
|
117
118
|
.stroke(borderColor, lineWidth: 1)
|
|
@@ -217,5 +218,9 @@ public struct MomoStepper: View {
|
|
|
217
218
|
if clamped != value { value = clamped }
|
|
218
219
|
editingText = String(clamped)
|
|
219
220
|
}
|
|
221
|
+
// Keeps the editable field in step with values set by the owner of the binding.
|
|
222
|
+
.onChange(of: value) { newValue in
|
|
223
|
+
editingText = String(newValue)
|
|
224
|
+
}
|
|
220
225
|
}
|
|
221
226
|
}
|
package/ios/Steps/Steps.swift
CHANGED
|
@@ -162,8 +162,9 @@ private struct StepsHorizontal: View {
|
|
|
162
162
|
|
|
163
163
|
var body: some View {
|
|
164
164
|
HStack(alignment: .top, spacing: 0) {
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
// Keyed by position like Compose: `StepItem.id` is a fresh UUID per instance.
|
|
166
|
+
ForEach(steps.indices, id: \.self) { index in
|
|
167
|
+
column(index: index, item: steps[index])
|
|
167
168
|
.frame(maxWidth: .infinity)
|
|
168
169
|
}
|
|
169
170
|
}
|
|
@@ -234,7 +235,7 @@ private struct StepsHorizontal: View {
|
|
|
234
235
|
.clipShape(RoundedCorner(radius: Radius.XS, corners: [.topLeft, .bottomLeft]))
|
|
235
236
|
}
|
|
236
237
|
}
|
|
237
|
-
.frame(maxWidth: .infinity)
|
|
238
|
+
.frame(maxWidth: .infinity, alignment: .leading)
|
|
238
239
|
|
|
239
240
|
if !item.title.isEmpty {
|
|
240
241
|
MomoText(item.title, typography: titleStyle, color: titleColor)
|
|
@@ -316,8 +317,8 @@ private struct StepsVertical: View {
|
|
|
316
317
|
|
|
317
318
|
var body: some View {
|
|
318
319
|
VStack(spacing: 0) {
|
|
319
|
-
ForEach(
|
|
320
|
-
row(index: index, item:
|
|
320
|
+
ForEach(steps.indices, id: \.self) { index in
|
|
321
|
+
row(index: index, item: steps[index])
|
|
321
322
|
}
|
|
322
323
|
}
|
|
323
324
|
.frame(maxWidth: .infinity)
|
|
@@ -358,8 +359,9 @@ private struct StepsVertical: View {
|
|
|
358
359
|
customIcon: customIcon
|
|
359
360
|
)
|
|
360
361
|
if !isLast {
|
|
362
|
+
// Compose pads inside the fixed height, so the drawn line is 8pt shorter.
|
|
361
363
|
lineColor
|
|
362
|
-
.frame(width: 2, height: connectorHeight)
|
|
364
|
+
.frame(width: 2, height: connectorHeight - Spacing.XS * 2)
|
|
363
365
|
.padding(.vertical, Spacing.XS)
|
|
364
366
|
}
|
|
365
367
|
}
|
|
@@ -65,6 +65,11 @@ public struct SuggestAction: View {
|
|
|
65
65
|
|
|
66
66
|
Text(annotatedMessage)
|
|
67
67
|
.foregroundColor(theme.colors.text.default)
|
|
68
|
+
.lineSpacing(
|
|
69
|
+
fontScale(TypographyStyle.bodyDefaultRegular.lineHeight)
|
|
70
|
+
- fontScale(TypographyStyle.bodyDefaultRegular.fontSize)
|
|
71
|
+
)
|
|
72
|
+
.ignoreBoldTextSetting()
|
|
68
73
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
69
74
|
.padding(.trailing, Spacing.M)
|
|
70
75
|
|
|
@@ -20,6 +20,7 @@ public struct Swipe<Content: View>: View {
|
|
|
20
20
|
|
|
21
21
|
@State private var offsetX: CGFloat = 0
|
|
22
22
|
@State private var dragStartOffsetX: CGFloat?
|
|
23
|
+
@GestureState private var isDragging = false
|
|
23
24
|
|
|
24
25
|
public init(
|
|
25
26
|
leftActions: [SwipeAction] = [],
|
|
@@ -87,14 +88,17 @@ public struct Swipe<Content: View>: View {
|
|
|
87
88
|
.offset(x: offsetX)
|
|
88
89
|
.gesture(
|
|
89
90
|
DragGesture(minimumDistance: 8, coordinateSpace: .local)
|
|
91
|
+
.updating($isDragging) { _, dragging, _ in dragging = true }
|
|
90
92
|
.onChanged { value in
|
|
91
93
|
dragOnChanged(value: value, leftWidth: leftWidth, rightWidth: rightWidth)
|
|
92
94
|
}
|
|
93
|
-
.onEnded { _ in
|
|
94
|
-
dragStartOffsetX = nil
|
|
95
|
-
snapToClosest(leftWidth: leftWidth, rightWidth: rightWidth)
|
|
96
|
-
}
|
|
97
95
|
)
|
|
96
|
+
// Gesture state also resets on cancel, so a stolen drag still snaps (Compose `onDragCancel`).
|
|
97
|
+
.onChange(of: isDragging) { dragging in
|
|
98
|
+
guard !dragging else { return }
|
|
99
|
+
dragStartOffsetX = nil
|
|
100
|
+
snapToClosest(leftWidth: leftWidth, rightWidth: rightWidth)
|
|
101
|
+
}
|
|
98
102
|
}
|
|
99
103
|
.frame(maxWidth: .infinity)
|
|
100
104
|
.frame(height: height)
|
|
@@ -166,7 +170,7 @@ private struct SwipeActionButton: View {
|
|
|
166
170
|
|
|
167
171
|
var body: some View {
|
|
168
172
|
SwiftUI.Button(action: action.onPress) {
|
|
169
|
-
VStack(spacing:
|
|
173
|
+
VStack(spacing: 0) {
|
|
170
174
|
if !action.icon.isEmpty {
|
|
171
175
|
Icon(source: action.icon, size: 20, color: Colors.black01)
|
|
172
176
|
}
|
|
@@ -184,7 +188,7 @@ private struct SwipeActionButton: View {
|
|
|
184
188
|
.background(action.backgroundColor ?? defaultBackgroundColor)
|
|
185
189
|
.clipShape(RoundedRectangle(cornerRadius: itemRadius))
|
|
186
190
|
}
|
|
187
|
-
.buttonStyle(
|
|
191
|
+
.buttonStyle(ActiveOpacityButtonStyle())
|
|
188
192
|
}
|
|
189
193
|
}
|
|
190
194
|
|
package/ios/Switch/Switch.swift
CHANGED
|
@@ -41,29 +41,31 @@ public struct Switch: View {
|
|
|
41
41
|
accessibilityId?.isEmpty == false ? accessibilityId! : "toggle_\(title?.isEmpty == false ? title! : "unknown")_\(value)"
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
private func toggle() {
|
|
45
|
+
// Not re-read from the binding: a `.constant` / one-way binding would report the stale value.
|
|
46
|
+
let newValue = !value
|
|
47
|
+
value = newValue
|
|
48
|
+
onChange?(newValue)
|
|
49
|
+
}
|
|
50
|
+
|
|
44
51
|
public var body: some View {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
.background(circleColor)
|
|
59
|
-
.clipShape(Circle())
|
|
60
|
-
.padding(.leading, Spacing.XS)
|
|
61
|
-
.padding(.trailing, Spacing.XS)
|
|
52
|
+
// The whole track is the label so all of it is tappable, as in Compose.
|
|
53
|
+
SwiftUI.Button(action: toggle) {
|
|
54
|
+
Circle()
|
|
55
|
+
.fill(backgroundColor)
|
|
56
|
+
.frame(width: 6, height: 6)
|
|
57
|
+
.frame(width: 14, height: 14)
|
|
58
|
+
.background(Circle().fill(circleColor))
|
|
59
|
+
.padding(.horizontal, Spacing.XS)
|
|
60
|
+
.frame(width: 38, height: 20, alignment: value ? .trailing : .leading)
|
|
61
|
+
.background(backgroundColor)
|
|
62
|
+
.clipShape(Capsule())
|
|
63
|
+
.contentShape(Capsule())
|
|
64
|
+
.animation(.default, value: value)
|
|
62
65
|
}
|
|
66
|
+
// Custom style so `.disabled` doesn't add a system dim on top of the disabled colors.
|
|
67
|
+
.buttonStyle(ActiveOpacityButtonStyle())
|
|
63
68
|
.disabled(disabled)
|
|
64
|
-
.frame(width: 38, height: 20, alignment: value ? .trailing : .leading)
|
|
65
|
-
.background(backgroundColor)
|
|
66
|
-
.clipShape(Capsule())
|
|
67
69
|
.accessibilityIdentifier(automationId)
|
|
68
70
|
}
|
|
69
71
|
}
|