@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
|
@@ -328,8 +328,13 @@ private struct ScrollableTabBar: View {
|
|
|
328
328
|
Rectangle()
|
|
329
329
|
.stroke(theme.colors.border.default, lineWidth: 0.5)
|
|
330
330
|
)
|
|
331
|
+
// Compose scrolls the selected tab to the leading edge, initial page included.
|
|
331
332
|
.onChange(of: selectedIndex) { index in
|
|
332
|
-
withAnimation { proxy.scrollTo(index, anchor: .
|
|
333
|
+
withAnimation { proxy.scrollTo(index, anchor: .leading) }
|
|
334
|
+
}
|
|
335
|
+
.onAppear {
|
|
336
|
+
guard selectedIndex > 0 else { return }
|
|
337
|
+
DispatchQueue.main.async { proxy.scrollTo(selectedIndex, anchor: .leading) }
|
|
333
338
|
}
|
|
334
339
|
}
|
|
335
340
|
}
|
|
@@ -346,6 +351,11 @@ private struct TabFramePreferenceKey: PreferenceKey {
|
|
|
346
351
|
// Card tab bar
|
|
347
352
|
// ---------------------------------------------------------------------------
|
|
348
353
|
|
|
354
|
+
private let cardTabHeight: CGFloat = 40
|
|
355
|
+
private let cardTabActiveHeight: CGFloat = 44
|
|
356
|
+
|
|
357
|
+
/// Two layers, as in Compose / react-kits: a flat strip where every tab looks unselected,
|
|
358
|
+
/// and one raised card for the active tab, 4pt taller and joined to the strip by curved ears.
|
|
349
359
|
private struct CardTabBar: View {
|
|
350
360
|
@Environment(\.appTheme) private var theme
|
|
351
361
|
|
|
@@ -357,47 +367,116 @@ private struct CardTabBar: View {
|
|
|
357
367
|
let onPress: (Int) -> Void
|
|
358
368
|
|
|
359
369
|
var body: some View {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
370
|
+
GeometryReader { geo in
|
|
371
|
+
let itemWidth = geo.size.width / CGFloat(Swift.max(tabs.count, 1))
|
|
372
|
+
|
|
373
|
+
ZStack(alignment: .bottomLeading) {
|
|
374
|
+
HStack(spacing: 0) {
|
|
375
|
+
ForEach(tabs.indices, id: \.self) { index in
|
|
376
|
+
SwiftUI.Button(action: { onPress(index) }) {
|
|
377
|
+
cardItem(tab: tabs[index], isActive: false)
|
|
378
|
+
.padding(.horizontal, Spacing.S)
|
|
379
|
+
.frame(width: itemWidth, height: cardTabHeight)
|
|
380
|
+
.background(Colors.black02)
|
|
381
|
+
.clipShape(UnstyledRoundedRectangle(topLeft: Radius.M, topRight: Radius.M))
|
|
382
|
+
.contentShape(Rectangle())
|
|
383
|
+
}
|
|
384
|
+
.buttonStyle(ActiveOpacityButtonStyle())
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if let active = tabs[safe: selectedIndex] {
|
|
389
|
+
activeCard(active, itemWidth: itemWidth)
|
|
373
390
|
}
|
|
374
|
-
.buttonStyle(ActiveOpacityButtonStyle())
|
|
375
391
|
}
|
|
392
|
+
.frame(width: geo.size.width, height: cardTabActiveHeight, alignment: .bottomLeading)
|
|
376
393
|
}
|
|
377
|
-
.
|
|
378
|
-
.
|
|
394
|
+
// The raised card overflows the strip into the top `Spacing.XS` margin.
|
|
395
|
+
.frame(height: cardTabActiveHeight)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
private func activeCard(_ tab: TabViewItem, itemWidth: CGFloat) -> some View {
|
|
399
|
+
let surface = theme.colors.background.surface
|
|
400
|
+
let pathWidth: CGFloat = tabs.count > 2 ? 22 : 28
|
|
401
|
+
let isFirst = selectedIndex == 0
|
|
402
|
+
let isLast = selectedIndex == tabs.count - 1
|
|
403
|
+
|
|
404
|
+
return HStack(alignment: .bottom, spacing: 0) {
|
|
405
|
+
// Each ear is nudged 8pt under the card so the join has no seam.
|
|
406
|
+
if !isFirst { cardEar(mirrored: true, width: pathWidth, color: surface).offset(x: 8) }
|
|
407
|
+
SwiftUI.Button(action: { onPress(selectedIndex) }) {
|
|
408
|
+
cardItem(tab: tab, isActive: true)
|
|
409
|
+
.padding(.horizontal, tabs.count == 3 ? Spacing.S : Spacing.M)
|
|
410
|
+
.frame(width: itemWidth, height: cardTabActiveHeight)
|
|
411
|
+
.background(surface)
|
|
412
|
+
.clipShape(UnstyledRoundedRectangle(topLeft: Radius.M, topRight: Radius.M))
|
|
413
|
+
.contentShape(Rectangle())
|
|
414
|
+
}
|
|
415
|
+
.buttonStyle(ActiveOpacityButtonStyle())
|
|
416
|
+
if !isLast { cardEar(mirrored: false, width: pathWidth, color: surface).offset(x: -8) }
|
|
417
|
+
}
|
|
418
|
+
.offset(x: itemWidth * CGFloat(selectedIndex) - (isFirst ? 0 : pathWidth))
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private func cardEar(mirrored: Bool, width: CGFloat, color: Color) -> some View {
|
|
422
|
+
CardTabEarShape(mirrored: mirrored)
|
|
423
|
+
.fill(color)
|
|
424
|
+
.frame(width: width, height: cardTabActiveHeight)
|
|
425
|
+
.clipped()
|
|
426
|
+
.allowsHitTesting(false)
|
|
379
427
|
}
|
|
380
428
|
|
|
381
429
|
@ViewBuilder
|
|
382
|
-
private func cardItem(tab: TabViewItem, isActive: Bool
|
|
430
|
+
private func cardItem(tab: TabViewItem, isActive: Bool) -> some View {
|
|
431
|
+
let color = isActive ? selectedColor : unselectedColor
|
|
383
432
|
HStack(spacing: 0) {
|
|
384
433
|
if let renderIcon = tab.renderIcon {
|
|
385
434
|
renderIcon(isActive)
|
|
386
435
|
Spacer().frame(width: Spacing.XS)
|
|
387
436
|
} else if let icon = tab.icon {
|
|
388
|
-
Icon(source: icon, size: 18, color:
|
|
437
|
+
Icon(source: icon, size: 18, color: color)
|
|
389
438
|
Spacer().frame(width: Spacing.XS)
|
|
390
439
|
}
|
|
391
440
|
MomoText(
|
|
392
441
|
tab.title,
|
|
393
442
|
typography: isActive ? .headerSSemibold : .bodyDefaultRegular,
|
|
394
|
-
color:
|
|
443
|
+
color: color
|
|
395
444
|
)
|
|
396
445
|
.lineLimit(1)
|
|
397
446
|
|
|
398
447
|
tabBadge(tab)
|
|
399
448
|
}
|
|
400
|
-
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/// react-kits' 28x44 ear path, in points; not scaled to the frame, which clips it at a 22pt width.
|
|
453
|
+
private struct CardTabEarShape: Shape {
|
|
454
|
+
let mirrored: Bool
|
|
455
|
+
|
|
456
|
+
func path(in rect: CGRect) -> Path {
|
|
457
|
+
var path = Path()
|
|
458
|
+
path.move(to: CGPoint(x: 0, y: 0))
|
|
459
|
+
path.addLine(to: CGPoint(x: 0.60765, y: 0))
|
|
460
|
+
path.addCurve(
|
|
461
|
+
to: CGPoint(x: 12.4602, y: 10.0244),
|
|
462
|
+
control1: CGPoint(x: 6.47582, y: 0),
|
|
463
|
+
control2: CGPoint(x: 11.4819, y: 4.23831)
|
|
464
|
+
)
|
|
465
|
+
path.addCurve(
|
|
466
|
+
to: CGPoint(x: 16.6925, y: 34),
|
|
467
|
+
control1: CGPoint(x: 13.9248, y: 18.6872),
|
|
468
|
+
control2: CGPoint(x: 15.9294, y: 30.37)
|
|
469
|
+
)
|
|
470
|
+
path.addCurve(
|
|
471
|
+
to: CGPoint(x: 27.9996, y: 44),
|
|
472
|
+
control1: CGPoint(x: 17.9536, y: 40),
|
|
473
|
+
control2: CGPoint(x: 22.259, y: 44)
|
|
474
|
+
)
|
|
475
|
+
path.addLine(to: CGPoint(x: 0, y: 44))
|
|
476
|
+
path.closeSubpath()
|
|
477
|
+
return mirrored
|
|
478
|
+
? path.applying(CGAffineTransform(a: -1, b: 0, c: 0, d: 1, tx: rect.width, ty: 0))
|
|
479
|
+
: path
|
|
401
480
|
}
|
|
402
481
|
}
|
|
403
482
|
|
|
@@ -426,6 +505,8 @@ private struct TabItemView: View {
|
|
|
426
505
|
rowBody
|
|
427
506
|
}
|
|
428
507
|
}
|
|
508
|
+
// Equal-width bars hit-test the whole cell, like Compose's weighted clickable.
|
|
509
|
+
.frame(maxWidth: scrollable ? nil : .infinity)
|
|
429
510
|
.contentShape(Rectangle())
|
|
430
511
|
}
|
|
431
512
|
.buttonStyle(ActiveOpacityButtonStyle())
|
package/ios/Tag/Tag.swift
CHANGED
|
@@ -45,26 +45,37 @@ public struct Tag: View {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// RN only accepts the `_03` palette as a custom tag color.
|
|
49
|
+
private static let customColors: [Color] = [
|
|
50
|
+
Colors.black03, Colors.pink03, Colors.violet03, Colors.indigo03,
|
|
51
|
+
Colors.blue03, Colors.mint03, Colors.green03, Colors.lime03,
|
|
52
|
+
Colors.yellow03, Colors.gold03, Colors.orange03, Colors.red03
|
|
53
|
+
]
|
|
54
|
+
|
|
48
55
|
private let label: String
|
|
49
56
|
private let icon: String?
|
|
50
57
|
private let color: TagColor
|
|
51
58
|
private let size: TagSize
|
|
59
|
+
private let customColor: Color?
|
|
52
60
|
|
|
53
61
|
public init(
|
|
54
62
|
label: String = "Label",
|
|
55
63
|
icon: String? = nil,
|
|
56
64
|
color: TagColor = .default,
|
|
57
|
-
size: TagSize = .large
|
|
65
|
+
size: TagSize = .large,
|
|
66
|
+
customColor: Color? = nil
|
|
58
67
|
) {
|
|
59
68
|
self.label = label
|
|
60
69
|
self.icon = icon
|
|
61
70
|
self.color = color
|
|
62
71
|
self.size = size
|
|
72
|
+
self.customColor = customColor
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
public var body: some View {
|
|
66
|
-
let
|
|
67
|
-
let
|
|
76
|
+
let custom = customColor.flatMap { Self.customColors.contains($0) ? $0 : nil }
|
|
77
|
+
let tagColor = custom ?? color.backgroundColor
|
|
78
|
+
let labelColor = custom != nil ? Colors.black01 : color.textColor
|
|
68
79
|
|
|
69
80
|
HStack(spacing: 0) {
|
|
70
81
|
if let icon {
|
|
@@ -75,7 +86,7 @@ public struct Tag: View {
|
|
|
75
86
|
.frame(height: fontScale(size.height))
|
|
76
87
|
.padding(.horizontal, Spacing.S)
|
|
77
88
|
.background(
|
|
78
|
-
RoundedRectangle(cornerRadius: Radius.S
|
|
89
|
+
RoundedRectangle(cornerRadius: Radius.S)
|
|
79
90
|
.fill(tagColor)
|
|
80
91
|
)
|
|
81
92
|
}
|
|
@@ -57,7 +57,8 @@ private extension View {
|
|
|
57
57
|
func containerBackground (_ type: String, _ useBackground: Bool, _ backgroundColor: Color) -> some View {
|
|
58
58
|
Group {
|
|
59
59
|
if (useBackground && type != "icon") {
|
|
60
|
-
|
|
60
|
+
// Same shape as the border, or the square corners (and the shadow) show around it.
|
|
61
|
+
self.background(RoundedRectangle(cornerRadius: 8).fill(backgroundColor))
|
|
61
62
|
} else {
|
|
62
63
|
self
|
|
63
64
|
}
|
|
@@ -70,7 +70,7 @@ let defaultBanner = TrustBannerData(
|
|
|
70
70
|
"vi": "Tìm hiểu thêm",
|
|
71
71
|
"en": "Learn more"
|
|
72
72
|
],
|
|
73
|
-
pciImage: "https://static.momocdn.net/app/img/kits/trustBanner/
|
|
73
|
+
pciImage: "https://static.momocdn.net/app/img/kits/trustBanner/pci_dss.png",
|
|
74
74
|
momoImage: "https://static.momocdn.net/app/img/kits/trustBanner/ic_secu.png",
|
|
75
75
|
sslImage: "https://static.momocdn.net/app/img/kits/trustBanner/ssl.png",
|
|
76
76
|
icons: [
|
|
@@ -87,9 +87,20 @@ public struct TrustBanner: View {
|
|
|
87
87
|
@Environment(\.localize) private var localize
|
|
88
88
|
|
|
89
89
|
var onPress: ((String)->Void)?
|
|
90
|
+
private let serviceName: String
|
|
91
|
+
private let screenName: String
|
|
92
|
+
private let trackEvent: ((String, [String: String]) -> Void)?
|
|
90
93
|
|
|
91
|
-
public init(
|
|
94
|
+
public init(
|
|
95
|
+
onPress: ((String)->Void)?,
|
|
96
|
+
serviceName: String = "",
|
|
97
|
+
screenName: String = "",
|
|
98
|
+
trackEvent: ((String, [String: String]) -> Void)? = nil
|
|
99
|
+
) {
|
|
92
100
|
self.onPress = onPress
|
|
101
|
+
self.serviceName = serviceName
|
|
102
|
+
self.screenName = screenName
|
|
103
|
+
self.trackEvent = trackEvent
|
|
93
104
|
}
|
|
94
105
|
|
|
95
106
|
private var banner: TrustBannerData {
|
|
@@ -97,46 +108,50 @@ public struct TrustBanner: View {
|
|
|
97
108
|
}
|
|
98
109
|
|
|
99
110
|
func onPressSecurity() {
|
|
111
|
+
trackEvent?("service_component_clicked", [
|
|
112
|
+
"service_name": serviceName,
|
|
113
|
+
"screen_name": screenName,
|
|
114
|
+
"component_name": "logo_trust",
|
|
115
|
+
"url_config": banner.urlConfig
|
|
116
|
+
])
|
|
100
117
|
onPress?(banner.urlConfig)
|
|
101
118
|
}
|
|
102
119
|
|
|
103
|
-
|
|
104
120
|
public var body: some View {
|
|
105
|
-
HStack(alignment: .
|
|
121
|
+
HStack(alignment: .top, spacing: 10) {
|
|
106
122
|
if let momoImage = banner.momoImage {
|
|
107
123
|
WebImage(url: URL(string: momoImage))
|
|
108
124
|
.resizable()
|
|
109
125
|
.scaledToFit()
|
|
110
126
|
.frame(width: 64, height: 64)
|
|
111
127
|
}
|
|
112
|
-
VStack(alignment: .leading){
|
|
113
|
-
|
|
114
|
-
.
|
|
115
|
-
.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
HStack {
|
|
128
|
+
VStack(alignment: .leading, spacing: 0) {
|
|
129
|
+
MomoText(
|
|
130
|
+
localize.translateData(banner.content),
|
|
131
|
+
typography: .descriptionDefaultRegular,
|
|
132
|
+
color: Color(hex: "484848"),
|
|
133
|
+
maxLines: 2
|
|
134
|
+
)
|
|
135
|
+
.padding(.bottom, 8)
|
|
136
|
+
HStack(alignment: .top, spacing: 0) {
|
|
121
137
|
HStack(spacing: 0) {
|
|
122
|
-
|
|
123
|
-
.
|
|
124
|
-
|
|
125
|
-
.
|
|
126
|
-
|
|
127
|
-
|
|
138
|
+
MomoText(
|
|
139
|
+
localize.translateData(banner.subContent),
|
|
140
|
+
typography: .actionXsBold,
|
|
141
|
+
color: Colors.pink03,
|
|
142
|
+
maxLines: 2
|
|
143
|
+
)
|
|
128
144
|
Icon(source: "arrow_chevron_right_small", color: Colors.pink03)
|
|
129
|
-
}.onTapGesture {
|
|
130
|
-
onPressSecurity()
|
|
131
145
|
}
|
|
132
|
-
|
|
133
|
-
Spacer()
|
|
134
|
-
HStack{
|
|
146
|
+
|
|
147
|
+
Spacer(minLength: 0)
|
|
148
|
+
HStack(spacing: 0) {
|
|
135
149
|
if let pciImage = banner.pciImage {
|
|
136
150
|
WebImage(url: URL(string: pciImage))
|
|
137
151
|
.resizable()
|
|
138
152
|
.scaledToFit()
|
|
139
|
-
.frame(width:
|
|
153
|
+
.frame(width: 27, height: 20)
|
|
154
|
+
.padding(.trailing, 4)
|
|
140
155
|
}
|
|
141
156
|
if let sslImage = banner.sslImage {
|
|
142
157
|
WebImage(url: URL(string: sslImage))
|
|
@@ -146,13 +161,12 @@ public struct TrustBanner: View {
|
|
|
146
161
|
}
|
|
147
162
|
}
|
|
148
163
|
}
|
|
149
|
-
|
|
150
164
|
}
|
|
151
|
-
|
|
152
165
|
}
|
|
153
166
|
.padding(.all, 12)
|
|
154
167
|
.background(Color(hex: "F2F8FF"))
|
|
155
168
|
.cornerRadius(12)
|
|
169
|
+
.contentShape(Rectangle())
|
|
156
170
|
.onTapGesture {
|
|
157
171
|
onPressSecurity()
|
|
158
172
|
}
|
package/ios/Title/Title.swift
CHANGED
|
@@ -28,7 +28,6 @@ public enum TitleButtonSize {
|
|
|
28
28
|
|
|
29
29
|
public struct Title: View {
|
|
30
30
|
@Environment(\.appTheme) private var theme
|
|
31
|
-
@Environment(\.momoFontScale) private var fontScale
|
|
32
31
|
|
|
33
32
|
private let title: String
|
|
34
33
|
private let type: TitleType
|
|
@@ -116,15 +115,9 @@ public struct Title: View {
|
|
|
116
115
|
}
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
private var titleFontWeight: Font.Weight {
|
|
120
|
-
switch type {
|
|
121
|
-
case .card: return .regular
|
|
122
|
-
case .section: return .bold
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
118
|
private var titleMaxLines: Int {
|
|
127
|
-
|
|
119
|
+
if textOnly { return Int.max }
|
|
120
|
+
return (showTrailingAction || badgeLabel != nil) ? 1 : 2
|
|
128
121
|
}
|
|
129
122
|
|
|
130
123
|
private var iconVerticalAlignment: VerticalAlignment {
|
|
@@ -151,14 +144,10 @@ public struct Title: View {
|
|
|
151
144
|
}
|
|
152
145
|
}
|
|
153
146
|
|
|
147
|
+
// Every Title variant is bold in the theme font; `headerDefaultBold` only supplies the weight.
|
|
154
148
|
private var titleText: some View {
|
|
155
|
-
|
|
156
|
-
.
|
|
157
|
-
.ignoreBoldTextSetting()
|
|
158
|
-
.foregroundColor(theme.colors.text.default)
|
|
159
|
-
.lineSpacing(fontScale(titleLineHeight) - fontScale(titleFontSize))
|
|
160
|
-
.lineLimit(titleMaxLines)
|
|
161
|
-
.truncationMode(.tail)
|
|
149
|
+
MomoText(title, typography: .headerDefaultBold, maxLines: titleMaxLines)
|
|
150
|
+
.fontSize(titleFontSize, lineHeight: titleLineHeight)
|
|
162
151
|
}
|
|
163
152
|
|
|
164
153
|
private var fullLayout: some View {
|
|
@@ -237,7 +226,7 @@ public struct Title: View {
|
|
|
237
226
|
}
|
|
238
227
|
}
|
|
239
228
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
240
|
-
.padding(
|
|
229
|
+
.padding(.top, type == .section ? Spacing.S : 0)
|
|
241
230
|
}
|
|
242
231
|
}
|
|
243
232
|
|
|
@@ -3,7 +3,7 @@ import SwiftUI
|
|
|
3
3
|
// MARK: - Types
|
|
4
4
|
|
|
5
5
|
/// Tooltip placement relative to the anchor element.
|
|
6
|
-
public enum TooltipPlacement {
|
|
6
|
+
public enum TooltipPlacement: Sendable {
|
|
7
7
|
case top
|
|
8
8
|
case bottom
|
|
9
9
|
case left
|
|
@@ -78,24 +78,37 @@ struct TooltipArrowShape: Shape {
|
|
|
78
78
|
func path(in rect: CGRect) -> Path {
|
|
79
79
|
let w = rect.width
|
|
80
80
|
let h = rect.height
|
|
81
|
-
|
|
81
|
+
let points: [CGPoint]
|
|
82
82
|
switch placement {
|
|
83
83
|
case .top:
|
|
84
|
-
|
|
85
|
-
path.addLine(to: CGPoint(x: w, y: 0))
|
|
86
|
-
path.addLine(to: CGPoint(x: w / 2, y: h))
|
|
84
|
+
points = [CGPoint(x: 0, y: 0), CGPoint(x: w, y: 0), CGPoint(x: w / 2, y: h)]
|
|
87
85
|
case .bottom:
|
|
88
|
-
|
|
89
|
-
path.addLine(to: CGPoint(x: w, y: h))
|
|
90
|
-
path.addLine(to: CGPoint(x: w / 2, y: 0))
|
|
86
|
+
points = [CGPoint(x: 0, y: h), CGPoint(x: w, y: h), CGPoint(x: w / 2, y: 0)]
|
|
91
87
|
case .left:
|
|
92
|
-
|
|
93
|
-
path.addLine(to: CGPoint(x: 0, y: h))
|
|
94
|
-
path.addLine(to: CGPoint(x: w, y: h / 2))
|
|
88
|
+
points = [CGPoint(x: 0, y: 0), CGPoint(x: 0, y: h), CGPoint(x: w, y: h / 2)]
|
|
95
89
|
case .right:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
90
|
+
points = [CGPoint(x: w, y: 0), CGPoint(x: w, y: h), CGPoint(x: 0, y: h / 2)]
|
|
91
|
+
}
|
|
92
|
+
// rounded like Compose's cornerPathEffect: each corner is cut `radius` along both edges
|
|
93
|
+
let radius = TooltipConst.arrowCornerRadius
|
|
94
|
+
func toward(_ from: CGPoint, _ to: CGPoint) -> CGPoint {
|
|
95
|
+
let dx = to.x - from.x
|
|
96
|
+
let dy = to.y - from.y
|
|
97
|
+
let length = (dx * dx + dy * dy).squareRoot()
|
|
98
|
+
guard length > 0 else { return from }
|
|
99
|
+
let distance = min(radius, length / 2)
|
|
100
|
+
return CGPoint(x: from.x + dx / length * distance, y: from.y + dy / length * distance)
|
|
101
|
+
}
|
|
102
|
+
var path = Path()
|
|
103
|
+
for index in 0..<points.count {
|
|
104
|
+
let corner = points[index]
|
|
105
|
+
let start = toward(corner, points[(index + points.count - 1) % points.count])
|
|
106
|
+
if index == 0 {
|
|
107
|
+
path.move(to: start)
|
|
108
|
+
} else {
|
|
109
|
+
path.addLine(to: start)
|
|
110
|
+
}
|
|
111
|
+
path.addQuadCurve(to: toward(corner, points[(index + 1) % points.count]), control: corner)
|
|
99
112
|
}
|
|
100
113
|
path.closeSubpath()
|
|
101
114
|
return path
|
|
@@ -209,6 +222,8 @@ private struct TooltipPositionModifier: ViewModifier {
|
|
|
209
222
|
}
|
|
210
223
|
)
|
|
211
224
|
.offset(x: originX, y: originY)
|
|
225
|
+
// the first pass has no size yet and would flash at the wrong spot
|
|
226
|
+
.opacity(popupSize == .zero ? 0 : 1)
|
|
212
227
|
}
|
|
213
228
|
|
|
214
229
|
private var originX: CGFloat {
|
|
@@ -99,28 +99,47 @@ public extension TypographyStyle {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
private
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
private enum MomoFontFace {
|
|
103
|
+
case system
|
|
104
|
+
case named(String)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/// Port of Compose `getFontFamily`; SF Pro Text is the iOS system font, so the legacy family is `.system`.
|
|
108
|
+
private func momoFontFace(family: String, weight: Font.Weight) -> MomoFontFace? {
|
|
109
|
+
let isNewFamily = family == "MomoTrustSans" || family == headerFontFamily
|
|
110
|
+
// Outside the whitelist the new DS families drop to the legacy font, as before DS-791.
|
|
111
|
+
if family == "SFProText" || (isNewFamily && !KitDesignSystem.shared.isWhiteList) { return .system }
|
|
112
|
+
// single-file families (Compose maps every weight to the same .otf)
|
|
113
|
+
if family == headerFontFamily || family == "MoMoSignature" { return .named(family) }
|
|
105
114
|
guard family == "MomoTrustSans" else { return nil }
|
|
106
115
|
switch weight {
|
|
107
|
-
case .ultraLight, .thin: return "MomoTrustSans-ExtraLight"
|
|
108
|
-
case .light: return "MomoTrustSans-Light"
|
|
109
|
-
case .regular: return "MomoTrustSans-Regular"
|
|
110
|
-
case .medium: return "MomoTrustSans-Medium"
|
|
111
|
-
case .semibold: return "MomoTrustSans-SemiBold"
|
|
112
|
-
case .bold: return "MomoTrustSans-Bold"
|
|
113
|
-
case .heavy, .black: return "MomoTrustSans-ExtraBold"
|
|
114
|
-
default: return "MomoTrustSans-Regular"
|
|
116
|
+
case .ultraLight, .thin: return .named("MomoTrustSans-ExtraLight")
|
|
117
|
+
case .light: return .named("MomoTrustSans-Light")
|
|
118
|
+
case .regular: return .named("MomoTrustSans-Regular")
|
|
119
|
+
case .medium: return .named("MomoTrustSans-Medium")
|
|
120
|
+
case .semibold: return .named("MomoTrustSans-SemiBold")
|
|
121
|
+
case .bold: return .named("MomoTrustSans-Bold")
|
|
122
|
+
case .heavy, .black: return .named("MomoTrustSans-ExtraBold")
|
|
123
|
+
default: return .named("MomoTrustSans-Regular")
|
|
115
124
|
}
|
|
116
125
|
}
|
|
117
126
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
/// Header title family (Compose `HEADER_FONT_FAMILY`), whitelist only like the default font.
|
|
128
|
+
let headerFontFamily = "MoMoTrustDisplay"
|
|
129
|
+
|
|
130
|
+
private func momoFont(size: CGFloat, weight: Font.Weight, family: String, fallbackFamily: String? = nil) -> Font {
|
|
131
|
+
switch momoFontFace(family: family, weight: weight) {
|
|
132
|
+
case .system:
|
|
133
|
+
return .system(size: size, weight: weight)
|
|
134
|
+
case .named(let name) where UIFont(name: name, size: size) != nil:
|
|
135
|
+
return .custom(name, fixedSize: size)
|
|
136
|
+
default:
|
|
137
|
+
// Compose falls back to the default font file when a family is missing
|
|
138
|
+
if let fallbackFamily, fallbackFamily != family {
|
|
139
|
+
return momoFont(size: size, weight: weight, family: fallbackFamily)
|
|
140
|
+
}
|
|
121
141
|
return .system(size: size, weight: weight)
|
|
122
142
|
}
|
|
123
|
-
return .custom(name, fixedSize: size)
|
|
124
143
|
}
|
|
125
144
|
|
|
126
145
|
public struct MomoText: View {
|
|
@@ -139,7 +158,12 @@ public struct MomoText: View {
|
|
|
139
158
|
private let letterSpacing: CGFloat?
|
|
140
159
|
private let softWrap: Bool
|
|
141
160
|
private let accessibilityId: String?
|
|
161
|
+
private let fontFamily: String?
|
|
142
162
|
private var highlightText: [String] = []
|
|
163
|
+
private var sizeOverride: (fontSize: CGFloat, lineHeight: CGFloat)?
|
|
164
|
+
|
|
165
|
+
private var fontSize: CGFloat { sizeOverride?.fontSize ?? typography.fontSize }
|
|
166
|
+
private var lineHeight: CGFloat { sizeOverride?.lineHeight ?? typography.lineHeight }
|
|
143
167
|
|
|
144
168
|
public init(
|
|
145
169
|
_ content: String,
|
|
@@ -153,7 +177,8 @@ public struct MomoText: View {
|
|
|
153
177
|
underline: Bool = false,
|
|
154
178
|
letterSpacing: CGFloat? = nil,
|
|
155
179
|
softWrap: Bool = true,
|
|
156
|
-
accessibilityId: String? = nil
|
|
180
|
+
accessibilityId: String? = nil,
|
|
181
|
+
fontFamily: String? = nil
|
|
157
182
|
) {
|
|
158
183
|
self.content = content
|
|
159
184
|
self.typography = typography
|
|
@@ -175,6 +200,7 @@ public struct MomoText: View {
|
|
|
175
200
|
self.letterSpacing = letterSpacing
|
|
176
201
|
self.softWrap = softWrap
|
|
177
202
|
self.accessibilityId = accessibilityId
|
|
203
|
+
self.fontFamily = fontFamily
|
|
178
204
|
}
|
|
179
205
|
|
|
180
206
|
init(_ content: String, typography: TypographyStyle, highlightText: [String]) {
|
|
@@ -182,11 +208,27 @@ public struct MomoText: View {
|
|
|
182
208
|
self.highlightText = highlightText
|
|
183
209
|
}
|
|
184
210
|
|
|
211
|
+
/// Compose's `style.copy(fontSize, lineHeight)`; the weight still comes from `typography`.
|
|
212
|
+
func fontSize(_ fontSize: CGFloat, lineHeight: CGFloat) -> MomoText {
|
|
213
|
+
var text = self
|
|
214
|
+
text.sizeOverride = (fontSize, lineHeight)
|
|
215
|
+
return text
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private var font: Font {
|
|
219
|
+
momoFont(
|
|
220
|
+
size: fontScale(fontSize),
|
|
221
|
+
weight: typography.fontWeight,
|
|
222
|
+
family: fontFamily ?? theme.font,
|
|
223
|
+
fallbackFamily: theme.font
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
185
227
|
// Whole-word matches of `highlightText` render semibold; the longest keyword wins (RN parity).
|
|
186
228
|
private var baseText: SwiftUI.Text {
|
|
187
229
|
let keywords = highlightText.filter { !$0.isEmpty }.sorted { $0.count > $1.count }
|
|
188
230
|
guard !keywords.isEmpty else { return SwiftUI.Text(content) }
|
|
189
|
-
let highlightFont = momoFont(size: fontScale(
|
|
231
|
+
let highlightFont = momoFont(size: fontScale(fontSize), weight: TypographyStyle.headerXsSemibold.fontWeight, family: fontFamily ?? theme.font, fallbackFamily: theme.font)
|
|
190
232
|
func isWordChar(_ character: Character?) -> Bool {
|
|
191
233
|
guard let character else { return false }
|
|
192
234
|
return character.isLetter || character.isNumber || character == "_"
|
|
@@ -223,7 +265,7 @@ public struct MomoText: View {
|
|
|
223
265
|
let isStrikethrough = strikethrough || typography.isStrikethrough
|
|
224
266
|
|
|
225
267
|
var text = baseText
|
|
226
|
-
.font(
|
|
268
|
+
.font(font)
|
|
227
269
|
.foregroundColor(textColor)
|
|
228
270
|
.strikethrough(isStrikethrough, color: textColor)
|
|
229
271
|
.underline(underline, color: textColor)
|
|
@@ -234,7 +276,7 @@ public struct MomoText: View {
|
|
|
234
276
|
|
|
235
277
|
return text
|
|
236
278
|
.multilineTextAlignment(textAlign)
|
|
237
|
-
.lineSpacing(fontScale(
|
|
279
|
+
.lineSpacing(fontScale(lineHeight) - fontScale(fontSize))
|
|
238
280
|
.modifier(LineLimitModifier(range: lineLimitRange))
|
|
239
281
|
.truncationMode(truncationMode)
|
|
240
282
|
.fixedSize(horizontal: !softWrap, vertical: false)
|
|
@@ -80,7 +80,9 @@ public struct Uploader: View {
|
|
|
80
80
|
|
|
81
81
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
82
82
|
HStack(alignment: .top, spacing: Spacing.S) {
|
|
83
|
-
|
|
83
|
+
// Positional like Compose: `UploadImage.id` is a fresh UUID per init, so hosts that
|
|
84
|
+
// rebuild the array each render would otherwise re-create (and reload) every thumbnail.
|
|
85
|
+
ForEach(Array(images.enumerated()), id: \.offset) { index, image in
|
|
84
86
|
UploaderImageItem(
|
|
85
87
|
image: image,
|
|
86
88
|
width: width,
|
package/ios/native-kits.podspec
CHANGED