@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
|
@@ -10,9 +10,8 @@ private let wheelPickerBorderWidth: CGFloat = 1
|
|
|
10
10
|
|
|
11
11
|
/// A reusable single-column wheel picker, mirroring Compose `WheelPicker.kt`.
|
|
12
12
|
///
|
|
13
|
-
///
|
|
14
|
-
///
|
|
15
|
-
/// `onChange(name, value)`.
|
|
13
|
+
/// Five rows tall with the selection in the middle row. A drag always settles on a row
|
|
14
|
+
/// (Compose snaps once the scroll stops) and then reports it via `onChange(name, value)`.
|
|
16
15
|
public struct WheelPicker: View {
|
|
17
16
|
@Environment(\.appTheme) private var theme
|
|
18
17
|
|
|
@@ -28,68 +27,50 @@ public struct WheelPicker: View {
|
|
|
28
27
|
self.data = data
|
|
29
28
|
self.selectedData = selectedData
|
|
30
29
|
self.onChange = onChange
|
|
30
|
+
_restIndex = State(initialValue: Self.index(of: selectedData, in: data))
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// MARK: Public
|
|
34
34
|
|
|
35
35
|
public var body: some View {
|
|
36
|
-
let
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
.id(index)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
.background(
|
|
56
|
-
GeometryReader { inner in
|
|
57
|
-
Color.clear.preference(
|
|
58
|
-
key: WheelOffsetKey.self,
|
|
59
|
-
value: inner.frame(in: .named(scrollSpace)).minY
|
|
60
|
-
)
|
|
61
|
-
}
|
|
62
|
-
)
|
|
63
|
-
}
|
|
64
|
-
.coordinateSpace(name: scrollSpace)
|
|
65
|
-
.onPreferenceChange(WheelOffsetKey.self) { value in
|
|
66
|
-
scrollOffset = value
|
|
67
|
-
updateSelection(paddedData: paddedData, viewportHeight: height)
|
|
68
|
-
}
|
|
69
|
-
.onAppear {
|
|
70
|
-
let initial = max(paddedData.firstIndex(of: selectedData) ?? 2, 2)
|
|
71
|
-
selectedIndex = initial
|
|
72
|
-
DispatchQueue.main.async {
|
|
73
|
-
proxy.scrollTo(initial, anchor: .center)
|
|
36
|
+
let position = self.position
|
|
37
|
+
let centerSlot = CGFloat(wheelVisibleCount / 2)
|
|
38
|
+
|
|
39
|
+
return Rectangle()
|
|
40
|
+
.fill(theme.colors.background.surface)
|
|
41
|
+
.overlay(
|
|
42
|
+
// Compose draws the selection band behind the rows.
|
|
43
|
+
Rectangle()
|
|
44
|
+
.fill(Colors.blue10)
|
|
45
|
+
.opacity(0.2)
|
|
46
|
+
.frame(height: wheelItemHeight)
|
|
47
|
+
)
|
|
48
|
+
.overlay(
|
|
49
|
+
VStack(spacing: 0) {
|
|
50
|
+
ForEach(Array(data.enumerated()), id: \.offset) { index, text in
|
|
51
|
+
WheelPickerItem(text: text, distance: abs(CGFloat(index) - position))
|
|
74
52
|
}
|
|
75
53
|
}
|
|
54
|
+
.offset(y: (centerSlot - position) * wheelItemHeight),
|
|
55
|
+
alignment: .top
|
|
56
|
+
)
|
|
57
|
+
.frame(maxWidth: .infinity)
|
|
58
|
+
.frame(height: wheelItemHeight * CGFloat(wheelVisibleCount))
|
|
59
|
+
.clipShape(RoundedRectangle(cornerRadius: Radius.S))
|
|
60
|
+
.overlay(
|
|
61
|
+
RoundedRectangle(cornerRadius: Radius.S)
|
|
62
|
+
.strokeBorder(theme.colors.border.default, lineWidth: wheelPickerBorderWidth)
|
|
63
|
+
)
|
|
64
|
+
.contentShape(Rectangle())
|
|
65
|
+
.gesture(dragGesture)
|
|
66
|
+
.onAppear { reportIfUnlisted() }
|
|
67
|
+
.onChange(of: selectedData) { _ in syncToSelection() }
|
|
68
|
+
.onChange(of: data) { _ in syncToSelection() }
|
|
69
|
+
.accessibilityElement()
|
|
70
|
+
.accessibilityValue(data.indices.contains(restIndex) ? data[restIndex] : "")
|
|
71
|
+
.accessibilityAdjustableAction { direction in
|
|
72
|
+
settle(at: restIndex + (direction == .increment ? 1 : -1))
|
|
76
73
|
}
|
|
77
|
-
}
|
|
78
|
-
.frame(height: wheelItemHeight * CGFloat(wheelVisibleCount))
|
|
79
|
-
.overlay(
|
|
80
|
-
RoundedRectangle(cornerRadius: Radius.S)
|
|
81
|
-
.stroke(theme.colors.border.default, lineWidth: wheelPickerBorderWidth)
|
|
82
|
-
)
|
|
83
|
-
.background(theme.colors.background.surface.clipShape(RoundedRectangle(cornerRadius: Radius.S)))
|
|
84
|
-
.overlay(
|
|
85
|
-
// Centered translucent selection band.
|
|
86
|
-
Rectangle()
|
|
87
|
-
.fill(Colors.blue10)
|
|
88
|
-
.opacity(0.2)
|
|
89
|
-
.frame(height: wheelItemHeight)
|
|
90
|
-
.allowsHitTesting(false)
|
|
91
|
-
)
|
|
92
|
-
.clipShape(RoundedRectangle(cornerRadius: Radius.S))
|
|
93
74
|
}
|
|
94
75
|
|
|
95
76
|
// MARK: Internal
|
|
@@ -99,24 +80,60 @@ public struct WheelPicker: View {
|
|
|
99
80
|
let selectedData: String
|
|
100
81
|
let onChange: (String, String) -> Void
|
|
101
82
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
private
|
|
106
|
-
|
|
107
|
-
///
|
|
108
|
-
private
|
|
109
|
-
|
|
110
|
-
let
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
83
|
+
// MARK: Private
|
|
84
|
+
|
|
85
|
+
@State private var restIndex: Int
|
|
86
|
+
@State private var dragTranslation: CGFloat = 0
|
|
87
|
+
|
|
88
|
+
/// Fractional row index under the band; past either end the drag meets resistance.
|
|
89
|
+
private var position: CGFloat {
|
|
90
|
+
let raw = CGFloat(restIndex) - dragTranslation / wheelItemHeight
|
|
91
|
+
let last = CGFloat(max(0, data.count - 1))
|
|
92
|
+
if raw < 0 { return raw * 0.3 }
|
|
93
|
+
if raw > last { return last + (raw - last) * 0.3 }
|
|
94
|
+
return raw
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private var dragGesture: some Gesture {
|
|
98
|
+
DragGesture(minimumDistance: 4)
|
|
99
|
+
.onChanged { value in
|
|
100
|
+
dragTranslation = value.translation.height
|
|
101
|
+
}
|
|
102
|
+
.onEnded { value in
|
|
103
|
+
let predicted = CGFloat(restIndex) - value.predictedEndTranslation.height / wheelItemHeight
|
|
104
|
+
settle(at: Int(predicted.rounded()))
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/// Like Compose `paddedData.indexOf(selectedData).coerceAtLeast(2)`: an unknown value shows the first row.
|
|
109
|
+
private static func index(of value: String, in data: [String]) -> Int {
|
|
110
|
+
data.firstIndex(of: value) ?? 0
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private func settle(at index: Int) {
|
|
114
|
+
let target = min(max(index, 0), max(0, data.count - 1))
|
|
115
|
+
let rows = Double(abs(CGFloat(target) - position))
|
|
116
|
+
withAnimation(.easeOut(duration: min(0.5, 0.2 + 0.03 * rows))) {
|
|
117
|
+
restIndex = target
|
|
118
|
+
dragTranslation = 0
|
|
119
119
|
}
|
|
120
|
+
guard data.indices.contains(target), data[target] != selectedData else { return }
|
|
121
|
+
onChange(name, data[target])
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// The parent may coerce the value (e.g. day 31 → 28) or swap the list; jump without animation
|
|
125
|
+
/// like Compose `scrollToItem`.
|
|
126
|
+
private func syncToSelection() {
|
|
127
|
+
restIndex = Self.index(of: selectedData, in: data)
|
|
128
|
+
dragTranslation = 0
|
|
129
|
+
reportIfUnlisted()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// Compose reports the row the wheel lands on, so a value missing from `data` (minute 07 with a
|
|
133
|
+
/// 5-minute interval, an unset AM/PM) takes the shown row's value instead of drifting from it.
|
|
134
|
+
private func reportIfUnlisted() {
|
|
135
|
+
guard data.indices.contains(restIndex), !data.contains(selectedData) else { return }
|
|
136
|
+
onChange(name, data[restIndex])
|
|
120
137
|
}
|
|
121
138
|
}
|
|
122
139
|
|
|
@@ -126,40 +143,21 @@ private struct WheelPickerItem: View {
|
|
|
126
143
|
@Environment(\.appTheme) private var theme
|
|
127
144
|
|
|
128
145
|
let text: String
|
|
129
|
-
|
|
130
|
-
let
|
|
131
|
-
let scrollOffset: CGFloat
|
|
132
|
-
let viewportHeight: CGFloat
|
|
146
|
+
/// Rows between this one and the band.
|
|
147
|
+
let distance: CGFloat
|
|
133
148
|
|
|
134
149
|
var body: some View {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
switch distanceFromCenter {
|
|
141
|
-
case ..<1: alpha = 1.0
|
|
142
|
-
case ..<2: alpha = 0.8
|
|
143
|
-
default: alpha = 0.4
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
let scale: CGFloat = isSelected ? 1.0 : 0.87
|
|
147
|
-
|
|
148
|
-
return ZStack {
|
|
149
|
-
MomoText(text, typography: .actionSBold, color: theme.colors.text.default)
|
|
150
|
-
.scaleEffect(scale)
|
|
151
|
-
}
|
|
152
|
-
.frame(maxWidth: .infinity)
|
|
153
|
-
.frame(height: wheelItemHeight)
|
|
154
|
-
.opacity(alpha)
|
|
150
|
+
MomoText(text, typography: .actionSBold, color: theme.colors.text.default)
|
|
151
|
+
.scaleEffect(1 - 0.13 * min(distance, 1))
|
|
152
|
+
.frame(maxWidth: .infinity)
|
|
153
|
+
.frame(height: wheelItemHeight)
|
|
154
|
+
.opacity(Self.opacity(distance))
|
|
155
155
|
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// MARK: - WheelOffsetKey
|
|
159
156
|
|
|
160
|
-
|
|
161
|
-
static
|
|
162
|
-
|
|
163
|
-
|
|
157
|
+
// RN parity: 1 on the band, 0.8 one row away, 0.4 from two rows on.
|
|
158
|
+
private static func opacity(_ distance: CGFloat) -> Double {
|
|
159
|
+
if distance <= 1 { return Double(1 - 0.2 * distance) }
|
|
160
|
+
if distance <= 2 { return Double(0.8 - 0.4 * (distance - 1)) }
|
|
161
|
+
return 0.4
|
|
164
162
|
}
|
|
165
163
|
}
|
|
@@ -1,16 +1,49 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import SwiftUI
|
|
3
3
|
|
|
4
|
+
public enum DividerType {
|
|
5
|
+
case `default`
|
|
6
|
+
case dash
|
|
7
|
+
}
|
|
8
|
+
|
|
4
9
|
public struct MomoDivider: View {
|
|
5
10
|
@Environment(\.appTheme) private var theme
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
private let useMargin: Bool
|
|
13
|
+
private let type: DividerType
|
|
14
|
+
|
|
15
|
+
public init(useMargin: Bool = false, type: DividerType = .default) {
|
|
16
|
+
self.useMargin = useMargin
|
|
17
|
+
self.type = type
|
|
18
|
+
}
|
|
8
19
|
|
|
9
20
|
public var body: some View {
|
|
10
|
-
|
|
11
|
-
.fill(theme.colors.border.default)
|
|
21
|
+
line
|
|
12
22
|
.frame(maxWidth: .infinity)
|
|
13
23
|
.frame(height: 1)
|
|
14
|
-
.padding(.vertical, Spacing.XS)
|
|
24
|
+
.padding(.vertical, useMargin ? Spacing.XS : 0)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@ViewBuilder
|
|
28
|
+
private var line: some View {
|
|
29
|
+
switch type {
|
|
30
|
+
case .default:
|
|
31
|
+
Rectangle().fill(theme.colors.border.default)
|
|
32
|
+
case .dash:
|
|
33
|
+
DividerDashLine()
|
|
34
|
+
.stroke(
|
|
35
|
+
theme.colors.border.default,
|
|
36
|
+
style: StrokeStyle(lineWidth: 1, lineCap: .round, dash: [4, 4])
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private struct DividerDashLine: Shape {
|
|
43
|
+
func path(in rect: CGRect) -> Path {
|
|
44
|
+
var path = Path()
|
|
45
|
+
path.move(to: CGPoint(x: rect.minX, y: rect.midY))
|
|
46
|
+
path.addLine(to: CGPoint(x: rect.maxX, y: rect.midY))
|
|
47
|
+
return path
|
|
15
48
|
}
|
|
16
49
|
}
|
|
@@ -43,15 +43,44 @@ public extension Color {
|
|
|
43
43
|
)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/// CSS-style `RGB`, `RGBA`, `RRGGBB` or `RRGGBBAA`, `#` optional; anything else is black.
|
|
46
47
|
init(hex: String) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
var digits = hex.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
49
|
+
if digits.hasPrefix("#") { digits.removeFirst() }
|
|
50
|
+
if digits.count == 3 || digits.count == 4 {
|
|
51
|
+
digits = String(digits.flatMap { [$0, $0] })
|
|
52
|
+
}
|
|
53
|
+
guard digits.count == 6 || digits.count == 8,
|
|
54
|
+
digits.allSatisfy(\.isHexDigit),
|
|
55
|
+
let value = UInt64(digits, radix: 16) else {
|
|
56
|
+
self.init(red: 0, green: 0, blue: 0)
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
let rgba = digits.count == 8 ? value : value << 8 | 0xFF
|
|
60
|
+
self.init(
|
|
61
|
+
.sRGB,
|
|
62
|
+
red: Double((rgba >> 24) & 0xFF) / 255,
|
|
63
|
+
green: Double((rgba >> 16) & 0xFF) / 255,
|
|
64
|
+
blue: Double((rgba >> 8) & 0xFF) / 255,
|
|
65
|
+
opacity: Double(rgba & 0xFF) / 255
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
extension Color {
|
|
71
|
+
/// Compose's `lerp(start, stop, fraction)`, in sRGB; `Color.mix` needs iOS 18.
|
|
72
|
+
func lerp(to other: Color, _ fraction: CGFloat) -> Color {
|
|
73
|
+
let t = min(max(fraction, 0), 1)
|
|
74
|
+
var (r1, g1, b1, a1): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
|
|
75
|
+
var (r2, g2, b2, a2): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
|
|
76
|
+
UIColor(self).getRed(&r1, green: &g1, blue: &b1, alpha: &a1)
|
|
77
|
+
UIColor(other).getRed(&r2, green: &g2, blue: &b2, alpha: &a2)
|
|
78
|
+
return Color(
|
|
79
|
+
.sRGB,
|
|
80
|
+
red: r1 + (r2 - r1) * t,
|
|
81
|
+
green: g1 + (g2 - g1) * t,
|
|
82
|
+
blue: b1 + (b2 - b1) * t,
|
|
83
|
+
opacity: a1 + (a2 - a1) * t
|
|
84
|
+
)
|
|
56
85
|
}
|
|
57
86
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
/// `@EnvironmentObject` that reads nil instead of trapping when nothing was injected.
|
|
4
|
+
/// `_store` is the backing field of the `@frozen` `EnvironmentObject`, so its layout is ABI-stable.
|
|
5
|
+
@propertyWrapper
|
|
6
|
+
struct OptionalEnvironmentObject<Object: ObservableObject>: DynamicProperty {
|
|
7
|
+
@EnvironmentObject private var object: Object
|
|
8
|
+
|
|
9
|
+
var wrappedValue: Object? {
|
|
10
|
+
Mirror(reflecting: _object).descendant("_store") as? Object
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// File: ios/Extensions/SkeletonUICompat.swift
|
|
2
|
+
// Created At: 2026-10-07 12:03:28 +07:00
|
|
3
|
+
// Created By: AI
|
|
4
|
+
// AI Agent: Claude Code
|
|
5
|
+
// Model: claude-opus-5-5
|
|
6
|
+
|
|
7
|
+
// AI-GENERATED START: keep SkeletonUI extensions visible to MoMoUIKits clients
|
|
8
|
+
// Unused here on purpose: Swift 5 exposes extension members of every module a dependency imports, so clients
|
|
9
|
+
// that only `import MoMoUIKits` rely on SkeletonUI's (e.g. `Text(String?)` in NativeWidget). Removing it breaks them.
|
|
10
|
+
import SkeletonUI
|
|
11
|
+
// AI-GENERATED END: keep SkeletonUI extensions visible to MoMoUIKits clients
|
package/ios/Icon/Icon.swift
CHANGED
|
@@ -1,30 +1,20 @@
|
|
|
1
|
+
import SDWebImageSwiftUI
|
|
1
2
|
import SwiftUI
|
|
2
3
|
|
|
3
4
|
struct IconSource: Codable {
|
|
4
5
|
let uri: String
|
|
5
6
|
}
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
// The compiled-in map (Icons.swift) wins; a host `icon.json` in the main bundle only adds keys
|
|
9
|
+
// the kit doesn't know, so hosts that shipped one keep their extra icons.
|
|
10
|
+
let iconSources: [String: String] = {
|
|
11
|
+
guard let url = Bundle.main.url(forResource: "icon", withExtension: "json"),
|
|
12
|
+
let data = try? Data(contentsOf: url),
|
|
13
|
+
let hostIcons = try? JSONDecoder().decode([String: IconSource].self, from: data) else {
|
|
14
|
+
return kitIcons
|
|
11
15
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
let data = try Data(contentsOf: url)
|
|
15
|
-
let decoded = try JSONDecoder().decode([String: IconSource].self, from: data)
|
|
16
|
-
return decoded
|
|
17
|
-
} catch {
|
|
18
|
-
print("Error decoding icon.json: \(error)")
|
|
19
|
-
return [:]
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Load the JSON data into a global variable
|
|
24
|
-
let iconSources: [String: IconSource] = loadIconSources()
|
|
25
|
-
|
|
26
|
-
// Icons that must never be tinted (mirror Compose `noThemeIcons`)
|
|
27
|
-
let noThemeIcons: [String] = ["ic_round_momo_tiny"]
|
|
16
|
+
return kitIcons.merging(hostIcons.mapValues(\.uri)) { kit, _ in kit }
|
|
17
|
+
}()
|
|
28
18
|
|
|
29
19
|
// Compose defaults `color` to the theme text color while `color = null` means "no tint".
|
|
30
20
|
// Swift can't tell an omitted argument from an explicit `nil`, so the default is this
|
|
@@ -53,16 +43,24 @@ public struct Icon: View {
|
|
|
53
43
|
}
|
|
54
44
|
|
|
55
45
|
public var body: some View {
|
|
56
|
-
// Mirror Compose: an https source is used as-is, anything else is a key into icon
|
|
57
|
-
if let uri = source.contains("https") ? source : iconSources[source]
|
|
46
|
+
// Mirror Compose: an https source is used as-is, anything else is a key into the icon map
|
|
47
|
+
if let uri = source.contains("https") ? source : iconSources[source] {
|
|
58
48
|
let requestedColor = color == iconThemeColor ? theme.colors.text.default : color
|
|
59
49
|
// Mirror Compose: certain icons (e.g. branded glyphs) are never tinted
|
|
60
50
|
let resolvedColor = noThemeIcons.contains(source) ? nil : requestedColor
|
|
61
|
-
|
|
51
|
+
// Bare WebImage like Compose's AsyncImage: no skeleton while loading, nothing on failure.
|
|
52
|
+
WebImage(url: URL(string: uri), isAnimating: .constant(true))
|
|
53
|
+
.resizable()
|
|
54
|
+
.renderingMode(resolvedColor != nil ? .template : .original)
|
|
55
|
+
.scaledToFit()
|
|
56
|
+
.foregroundColor(resolvedColor)
|
|
62
57
|
.frame(width: size, height: size)
|
|
63
58
|
// Mirrors Compose's auto-generated `contentDescription = "img|$iconUrl"`
|
|
64
59
|
// when no explicit label is provided.
|
|
65
60
|
.accessibilityLabel(accessibilityLabel ?? "img|\(uri)")
|
|
61
|
+
} else {
|
|
62
|
+
// Compose keeps the sized box for an unknown key, so layouts don't collapse.
|
|
63
|
+
Color.clear.frame(width: size, height: size)
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
}
|