@momo-kits/native-kits 0.168.1-beta.1 → 0.168.1-beta.10-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 +7 -5
- package/build.gradle.kts +11 -0
- package/compose/build.gradle.kts +198 -0
- package/compose/build.gradle.kts.backup +198 -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 +56 -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 +205 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +399 -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 +35 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +228 -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 +105 -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 +399 -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/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/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 +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/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 +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 +138 -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 +7 -6
- package/ios/Application/DesignSystemKits.swift +100 -0
- package/ios/Application/DesignSystemKitsPayload.swift +68 -0
- package/ios/Badge/BadgeRibbon.swift +8 -1
- package/ios/Extensions/Color++.swift +2 -4
- package/ios/Input/Input.swift +13 -3
- package/ios/Input/InputPhoneNumber.swift +1 -3
- package/ios/Input/InputSearch.swift +1 -2
- package/ios/Input/InputTextArea.swift +5 -3
- package/ios/InputMoney/InputMoney.swift +6 -7
- package/ios/Layout/ScreenLayout.swift +46 -11
- package/ios/Navigation/BottomTab/DuoBarCompression.swift +2 -3
- package/ios/Navigation/BottomTab/DuoTabBar.swift +94 -69
- package/ios/Navigation/Component/HeaderBackground.swift +22 -1
- package/ios/Navigation/StackScreen.swift +3 -7
- package/ios/Popup/PopupLayout.swift +0 -2
- package/ios/Stepper/Stepper.swift +1 -2
- package/ios/Template/TrustBanner/TrustBanner.swift +4 -2
- package/ios/Typography/FontScale.swift +14 -11
- package/ios/Typography/Text.swift +86 -12
- package/ios/Typography/Typography.swift +2 -2
- package/ios/native-kits.podspec +1 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/README.md +120 -0
- package/package.json +1 -1
- package/settings.gradle.kts +64 -0
- package/ios/Typography/FontScaleStore.swift +0 -58
- package/ios/Typography/KitDesignSystem.swift +0 -17
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// File: ios/Application/DesignSystemKits.swift
|
|
2
|
+
// Created At: 2026-10-02 14:17:29 +07:00
|
|
3
|
+
// Created By: AI
|
|
4
|
+
// AI Agent: Claude Code
|
|
5
|
+
// Model: claude-opus-5-5
|
|
6
|
+
|
|
7
|
+
// AI-GENERATED START: app-wide design-system holder mirroring Compose's DesignSystemKits
|
|
8
|
+
import SwiftUI
|
|
9
|
+
|
|
10
|
+
/// Host-owned design-system inputs; mirrors Compose's `DesignSystemKitsConfigs` field for field.
|
|
11
|
+
public struct DesignSystemKitsConfigs: Equatable {
|
|
12
|
+
public var userScaleRate: CGFloat
|
|
13
|
+
public var useOSScaleRate: Bool
|
|
14
|
+
public var isWhiteList: Bool
|
|
15
|
+
public var designSystemConfig: KitConfig?
|
|
16
|
+
|
|
17
|
+
public init(
|
|
18
|
+
userScaleRate: CGFloat = 1,
|
|
19
|
+
useOSScaleRate: Bool = true,
|
|
20
|
+
isWhiteList: Bool = false,
|
|
21
|
+
designSystemConfig: KitConfig? = nil
|
|
22
|
+
) {
|
|
23
|
+
self.userScaleRate = userScaleRate
|
|
24
|
+
self.useOSScaleRate = useOSScaleRate
|
|
25
|
+
self.isWhiteList = isWhiteList
|
|
26
|
+
self.designSystemConfig = designSystemConfig
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// App-wide holder for the SwiftUI kit, mirroring Compose's `DesignSystemKits`. The kit is sandboxed from the
|
|
31
|
+
/// host, so the host pushes `setConfigs(_:)` at startup and on every change; the OS Dynamic Type factor is
|
|
32
|
+
/// tracked here. Views re-render on change as long as the subtree got `.momoFontScale()` (`NavigationContainer`
|
|
33
|
+
/// applies it). Swift reserves `init`, so there is no `init(source:)` twin: the host observes and pushes.
|
|
34
|
+
public final class DesignSystemKits: ObservableObject {
|
|
35
|
+
public static let shared = DesignSystemKits()
|
|
36
|
+
|
|
37
|
+
@Published private var configs = DesignSystemKitsConfigs()
|
|
38
|
+
@Published public private(set) var osFontScale: CGFloat = DesignSystemKits.currentOsFontScale()
|
|
39
|
+
|
|
40
|
+
private init() {
|
|
41
|
+
NotificationCenter.default.addObserver(
|
|
42
|
+
self,
|
|
43
|
+
selector: #selector(contentSizeCategoryDidChange),
|
|
44
|
+
name: UIContentSizeCategory.didChangeNotification,
|
|
45
|
+
object: nil
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public func setConfigs(_ configs: DesignSystemKitsConfigs) {
|
|
50
|
+
onMain {
|
|
51
|
+
// Re-assigning an unchanged value would still publish and invalidate every kit subtree.
|
|
52
|
+
guard self.configs != configs else { return }
|
|
53
|
+
self.configs = configs
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public func getConfigs() -> DesignSystemKitsConfigs {
|
|
58
|
+
configs
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public var fontScale: FontScale {
|
|
62
|
+
FontScale(
|
|
63
|
+
userScaleRate: configs.userScaleRate,
|
|
64
|
+
useOSScaleRate: configs.useOSScaleRate,
|
|
65
|
+
osFontScale: osFontScale
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@objc private func contentSizeCategoryDidChange() {
|
|
70
|
+
let next = Self.currentOsFontScale()
|
|
71
|
+
guard next != osFontScale else { return }
|
|
72
|
+
osFontScale = next
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// Same factor Compose's iOS `ProvideOsFontScale` derives from
|
|
76
|
+
/// `UIFont.preferredFont(forTextStyle: .body).pointSize / 17`, so both iOS surfaces scale alike.
|
|
77
|
+
static func currentOsFontScale() -> CGFloat {
|
|
78
|
+
let base = UIFont.systemFont(ofSize: UIFont.labelFontSize)
|
|
79
|
+
return UIFontMetrics.default.scaledFont(for: base).pointSize / base.pointSize
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// Synchronous when already on main — an async hop would defer the change by a frame.
|
|
83
|
+
private func onMain(_ work: @escaping () -> Void) {
|
|
84
|
+
if Thread.isMainThread { work() } else { DispatchQueue.main.async(execute: work) }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private struct DesignSystemKitsConfigsKey: EnvironmentKey {
|
|
89
|
+
/// Computed, never stored: an un-injected subtree still reads the live holder, just not reactively.
|
|
90
|
+
static var defaultValue: DesignSystemKitsConfigs { DesignSystemKits.shared.getConfigs() }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public extension EnvironmentValues {
|
|
94
|
+
/// Injected by `.momoFontScale()`; never set it directly (same trap as `\.momoFontScale`).
|
|
95
|
+
var designSystemKitsConfigs: DesignSystemKitsConfigs {
|
|
96
|
+
get { self[DesignSystemKitsConfigsKey.self] }
|
|
97
|
+
set { self[DesignSystemKitsConfigsKey.self] = newValue }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// AI-GENERATED END: app-wide design-system holder mirroring Compose's DesignSystemKits
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// File: ios/Application/DesignSystemKitsPayload.swift
|
|
2
|
+
// Created At: 2026-10-02 15:40:00 +07:00
|
|
3
|
+
// Created By: AI
|
|
4
|
+
// AI Agent: Claude Code
|
|
5
|
+
// Model: claude-opus-5-5
|
|
6
|
+
|
|
7
|
+
// AI-GENERATED START: JSON entry point for hosts that can't share Swift types with the kit
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
public extension DesignSystemKits {
|
|
11
|
+
/// Applies configs pushed as JSON by a host that can't share Swift types with the kit (e.g. the KMP bridge).
|
|
12
|
+
/// Invalid JSON is ignored and the current configs are kept.
|
|
13
|
+
func setConfigs(json: String) {
|
|
14
|
+
guard let data = json.data(using: .utf8),
|
|
15
|
+
let payload = try? JSONDecoder().decode(DesignSystemKitsPayload.self, from: data) else { return }
|
|
16
|
+
setConfigs(payload.configs)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Wire format; field names match the host's serializer (momo-app HostDesignSystemKits / DesignSystemData.kt).
|
|
21
|
+
private struct DesignSystemKitsPayload: Decodable {
|
|
22
|
+
let userScaleRate: CGFloat
|
|
23
|
+
let useOSScaleRate: Bool
|
|
24
|
+
let isWhiteList: Bool
|
|
25
|
+
let designSystemConfig: KitConfigPayload?
|
|
26
|
+
|
|
27
|
+
var configs: DesignSystemKitsConfigs {
|
|
28
|
+
DesignSystemKitsConfigs(
|
|
29
|
+
userScaleRate: userScaleRate,
|
|
30
|
+
useOSScaleRate: useOSScaleRate,
|
|
31
|
+
isWhiteList: isWhiteList,
|
|
32
|
+
designSystemConfig: designSystemConfig?.kitConfig
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private struct KitConfigPayload: Decodable {
|
|
38
|
+
let trustBanner: TrustBannerPayload?
|
|
39
|
+
let headerBar: String?
|
|
40
|
+
let headerGradient: String?
|
|
41
|
+
|
|
42
|
+
var kitConfig: KitConfig {
|
|
43
|
+
KitConfig(trustBanner: trustBanner?.trustBannerData, headerBar: headerBar, headerGradient: headerGradient)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private struct TrustBannerPayload: Decodable {
|
|
48
|
+
let content: [String: String]
|
|
49
|
+
let subContent: [String: String]
|
|
50
|
+
let pciImage: String?
|
|
51
|
+
let sslImage: String?
|
|
52
|
+
let urlConfig: String?
|
|
53
|
+
let icons: [String]?
|
|
54
|
+
let momoImage: String?
|
|
55
|
+
|
|
56
|
+
var trustBannerData: TrustBannerData {
|
|
57
|
+
TrustBannerData(
|
|
58
|
+
content: content,
|
|
59
|
+
subContent: subContent,
|
|
60
|
+
pciImage: pciImage,
|
|
61
|
+
momoImage: momoImage,
|
|
62
|
+
sslImage: sslImage,
|
|
63
|
+
icons: icons,
|
|
64
|
+
urlConfig: urlConfig ?? ""
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// AI-GENERATED END: JSON entry point for hosts that can't share Swift types with the kit
|
|
@@ -203,7 +203,9 @@ public struct BadgeRibbon: View {
|
|
|
203
203
|
/// rotation) — the label gets the same scale applied a second time so it stays right-reading
|
|
204
204
|
/// while only the ribbon shape flips.
|
|
205
205
|
public struct RoundedBadgeRibbon: View {
|
|
206
|
+
@Environment(\.appTheme) private var theme
|
|
206
207
|
@Environment(\.momoFontScale) private var fontScale
|
|
208
|
+
@Environment(\.designSystemKitsConfigs) private var designSystemKitsConfigs
|
|
207
209
|
let text: String
|
|
208
210
|
let position: RibbonPosition
|
|
209
211
|
|
|
@@ -223,7 +225,12 @@ public struct RoundedBadgeRibbon: View {
|
|
|
223
225
|
|
|
224
226
|
private var measuredTextSize: CGSize {
|
|
225
227
|
let fontSize = fontScale(TypographyStyle.labelXsMedium.fontSize)
|
|
226
|
-
let font =
|
|
228
|
+
let font = momoUIFont(
|
|
229
|
+
size: fontSize,
|
|
230
|
+
weight: .medium,
|
|
231
|
+
family: theme.font,
|
|
232
|
+
isWhiteList: designSystemKitsConfigs.isWhiteList
|
|
233
|
+
)
|
|
227
234
|
return text.size(withAttributes: [.font: font])
|
|
228
235
|
}
|
|
229
236
|
|
|
@@ -4,16 +4,14 @@ import Foundation
|
|
|
4
4
|
private final class MoMoUIKitsBundleToken {}
|
|
5
5
|
|
|
6
6
|
enum MoMoUIKitsResources {
|
|
7
|
+
|
|
7
8
|
static let bundle: Bundle = {
|
|
8
9
|
let bundleName = "MoMoUIKitsResources"
|
|
9
10
|
let tokenBundle = Bundle(for: MoMoUIKitsBundleToken.self)
|
|
10
|
-
let frameworkBundle = Bundle(identifier: "org.cocoapods.MoMoUIKits")
|
|
11
11
|
let candidates = [
|
|
12
12
|
Bundle.main.resourceURL,
|
|
13
13
|
tokenBundle.resourceURL,
|
|
14
|
-
tokenBundle.bundleURL
|
|
15
|
-
frameworkBundle?.resourceURL,
|
|
16
|
-
frameworkBundle?.bundleURL
|
|
14
|
+
tokenBundle.bundleURL
|
|
17
15
|
]
|
|
18
16
|
|
|
19
17
|
for candidate in candidates {
|
package/ios/Input/Input.swift
CHANGED
|
@@ -153,7 +153,7 @@ public struct Input: View {
|
|
|
153
153
|
handleFocusChange(focused)
|
|
154
154
|
})
|
|
155
155
|
.keyboardType(keyboardType)
|
|
156
|
-
.
|
|
156
|
+
.kitFont(fontWeight == .bold ? .actionSBold : .bodyDefaultRegular)
|
|
157
157
|
.ignoreBoldTextSetting()
|
|
158
158
|
.foregroundColor(getTextColor())
|
|
159
159
|
.disabled(disabled || readOnly)
|
|
@@ -345,7 +345,12 @@ private struct SecureInputField: UIViewRepresentable {
|
|
|
345
345
|
textField.spellCheckingType = .no
|
|
346
346
|
textField.delegate = context.coordinator
|
|
347
347
|
textField.keyboardType = keyboardType
|
|
348
|
-
textField.font =
|
|
348
|
+
textField.font = momoUIFont(
|
|
349
|
+
size: fontSize,
|
|
350
|
+
weight: fontWeight,
|
|
351
|
+
family: context.environment.appTheme.font,
|
|
352
|
+
isWhiteList: context.environment.designSystemKitsConfigs.isWhiteList
|
|
353
|
+
)
|
|
349
354
|
textField.textColor = textColor
|
|
350
355
|
textField.isEnabled = !isDisabled
|
|
351
356
|
textField.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
@@ -370,7 +375,12 @@ private struct SecureInputField: UIViewRepresentable {
|
|
|
370
375
|
if textField.text != text {
|
|
371
376
|
textField.text = text
|
|
372
377
|
}
|
|
373
|
-
let font =
|
|
378
|
+
let font = momoUIFont(
|
|
379
|
+
size: fontSize,
|
|
380
|
+
weight: fontWeight,
|
|
381
|
+
family: context.environment.appTheme.font,
|
|
382
|
+
isWhiteList: context.environment.designSystemKitsConfigs.isWhiteList
|
|
383
|
+
)
|
|
374
384
|
if textField.font != font {
|
|
375
385
|
textField.font = font
|
|
376
386
|
}
|
|
@@ -104,9 +104,7 @@ public struct InputPhoneNumber: View {
|
|
|
104
104
|
handleFocusChange(focused)
|
|
105
105
|
})
|
|
106
106
|
.keyboardType(.numberPad)
|
|
107
|
-
.
|
|
108
|
-
? fontScale.font(size: 14, weight: .semibold)
|
|
109
|
-
: fontScale.font(size: 18, weight: .bold))
|
|
107
|
+
.kitFont(size == .small ? .headerSSemibold : .headerMBold)
|
|
110
108
|
.ignoreBoldTextSetting()
|
|
111
109
|
.foregroundColor(theme.colors.text.default)
|
|
112
110
|
.applyCursorColor(theme.colors.primary)
|
|
@@ -120,7 +120,6 @@ public struct InputSearchProps {
|
|
|
120
120
|
|
|
121
121
|
public struct InputSearch: View {
|
|
122
122
|
@Environment(\.appTheme) private var theme
|
|
123
|
-
@Environment(\.momoFontScale) private var fontScale
|
|
124
123
|
@Binding var text: String
|
|
125
124
|
var buttonText: String = ""
|
|
126
125
|
var showButtonText: Bool = false
|
|
@@ -263,7 +262,7 @@ public struct InputSearch: View {
|
|
|
263
262
|
}, onCommit: onSubmit)
|
|
264
263
|
.disabled(disabled)
|
|
265
264
|
.foregroundColor(disabled ? theme.colors.text.disable : theme.colors.text.default)
|
|
266
|
-
.
|
|
265
|
+
.kitFont(size: 14, weight: fontWeight)
|
|
267
266
|
.ignoreBoldTextSetting()
|
|
268
267
|
.keyboardType(keyboardType)
|
|
269
268
|
.applyCursorColor(theme.colors.primary)
|
|
@@ -41,9 +41,11 @@ struct UITextViewWrapper: UIViewRepresentable {
|
|
|
41
41
|
uiView.tintColor = UIColor(cursorColor)
|
|
42
42
|
// Read through `context.environment` rather than the global `scaleSize`: that is what makes
|
|
43
43
|
// SwiftUI re-run updateUIView when the font scale changes.
|
|
44
|
-
uiView.font =
|
|
45
|
-
|
|
46
|
-
weight: fontWeight == .bold ? .bold : .regular
|
|
44
|
+
uiView.font = momoUIFont(
|
|
45
|
+
size: context.environment.momoFontScale(16),
|
|
46
|
+
weight: fontWeight == .bold ? .bold : .regular,
|
|
47
|
+
family: context.environment.appTheme.font,
|
|
48
|
+
isWhiteList: context.environment.designSystemKitsConfigs.isWhiteList
|
|
47
49
|
)
|
|
48
50
|
uiView.isEditable = !disabled
|
|
49
51
|
if uiView.keyboardType != keyboardType {
|
|
@@ -356,14 +356,13 @@ private struct MoneyTextField: UIViewRepresentable {
|
|
|
356
356
|
return numeric.isEmpty ? "" : formatNumberToMoney(numeric)
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
-
// Theme family as Compose `getFontFamily`: Momo Trust Sans only for whitelisted hosts, else system.
|
|
360
359
|
private func font(_ context: Context) -> UIFont {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
360
|
+
momoUIFont(
|
|
361
|
+
size: context.environment.momoFontScale(20),
|
|
362
|
+
weight: fontWeight,
|
|
363
|
+
family: fontFamily,
|
|
364
|
+
isWhiteList: context.environment.designSystemKitsConfigs.isWhiteList
|
|
365
|
+
)
|
|
367
366
|
}
|
|
368
367
|
|
|
369
368
|
final class Coordinator: NSObject, UITextFieldDelegate {
|
|
@@ -32,6 +32,8 @@ public struct ScreenLayout: Equatable {
|
|
|
32
32
|
public let width: CGFloat
|
|
33
33
|
public let height: CGFloat
|
|
34
34
|
public let isIPhoneDuo: Bool
|
|
35
|
+
/// The device has a hinge, folded or not, whatever the window size; drives popups, modal and bottom sheet.
|
|
36
|
+
public let isDuoDevice: Bool
|
|
35
37
|
public let insets: EdgeInsets
|
|
36
38
|
public let insetLeft: CGFloat
|
|
37
39
|
public let insetRight: CGFloat
|
|
@@ -68,8 +70,11 @@ enum DuoMetrics {
|
|
|
68
70
|
// (x 550–643 of 669, clock digits centered on y 47); its reserved region is taller than the glyphs.
|
|
69
71
|
static let innerStatusBarCenterY: CGFloat = 47
|
|
70
72
|
static let innerStatusBarWidth: CGFloat = 120
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
+
// Host app's iOS 26 tab bar on the inner display in portrait: a 62pt glass pill sized to its tabs,
|
|
74
|
+
// QR as a 62pt platter beside it, ≥ 20pt from the sides, 13pt into the home indicator inset.
|
|
75
|
+
static let floatingTabBarHeight: CGFloat = 62
|
|
76
|
+
static let floatingTabBarMargin: CGFloat = 20
|
|
77
|
+
static let floatingTabBarInsetOverlap: CGFloat = 13
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
enum HeaderMetrics {
|
|
@@ -104,15 +109,17 @@ extension ScreenLayout {
|
|
|
104
109
|
size: CGSize,
|
|
105
110
|
insets: EdgeInsets,
|
|
106
111
|
reservedRegions: [CGRect] = ReservedRegions.current,
|
|
107
|
-
isPhone: Bool = isPhoneIdiom
|
|
112
|
+
isPhone: Bool = isPhoneIdiom,
|
|
113
|
+
isFoldable: Bool = FoldableDevice.isFoldable(measuredIn: keyWindow)
|
|
108
114
|
) -> ScreenLayout {
|
|
109
115
|
let width = size.width
|
|
110
116
|
let height = size.height
|
|
117
|
+
let isDuoDevice = isPhone && isFoldable
|
|
111
118
|
var (headerHeight, headerStatusBarHeight) = headerGeometry(topInset: insets.top, isPhone: isPhone)
|
|
112
119
|
let isDuo = isPhone && isIPhoneDuoWindow(width: width, height: height, insets: insets)
|
|
113
120
|
guard isDuo else {
|
|
114
121
|
return ScreenLayout(
|
|
115
|
-
width: width, height: height, isIPhoneDuo: false, insets: insets,
|
|
122
|
+
width: width, height: height, isIPhoneDuo: false, isDuoDevice: isDuoDevice, insets: insets,
|
|
116
123
|
insetLeft: 0, insetRight: 0, contentWidth: width,
|
|
117
124
|
headerHeight: headerHeight, headerStatusBarHeight: headerStatusBarHeight, sideBar: nil,
|
|
118
125
|
statusBarClearance: nil
|
|
@@ -172,7 +179,7 @@ extension ScreenLayout {
|
|
|
172
179
|
}
|
|
173
180
|
|
|
174
181
|
return ScreenLayout(
|
|
175
|
-
width: width, height: height, isIPhoneDuo: true, insets: insets,
|
|
182
|
+
width: width, height: height, isIPhoneDuo: true, isDuoDevice: isDuoDevice, insets: insets,
|
|
176
183
|
insetLeft: insets.leading, insetRight: insets.trailing,
|
|
177
184
|
contentWidth: width - insets.leading - insets.trailing,
|
|
178
185
|
headerHeight: headerHeight, headerStatusBarHeight: headerStatusBarHeight, sideBar: sideBar,
|
|
@@ -251,13 +258,40 @@ extension ScreenLayout {
|
|
|
251
258
|
}
|
|
252
259
|
}
|
|
253
260
|
|
|
254
|
-
///
|
|
255
|
-
///
|
|
261
|
+
/// Snapshot for call sites without `\.screenLayout`; not reactive. `windowSize` is ignored,
|
|
262
|
+
/// kept for host call sites.
|
|
256
263
|
public func isIPhoneDuoDevice(windowSize: CGSize? = nil) -> Bool {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
264
|
+
isPhoneIdiom && FoldableDevice.isFoldable(measuredIn: keyWindow)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/// Apple's foldable check (as the host's `SideTabRailView.isFoldableDevice`): only a device with a
|
|
268
|
+
/// hinge reports a `.division` region, inactive while flat but still there. Screen size cannot tell
|
|
269
|
+
/// it apart from a large iPhone in landscape or a narrowed window.
|
|
270
|
+
enum FoldableDevice {
|
|
271
|
+
// a hinge never goes away, so a positive answer is kept
|
|
272
|
+
private static var isKnownFoldable = false
|
|
273
|
+
|
|
274
|
+
static func isFoldable(measuredIn view: UIView?) -> Bool {
|
|
275
|
+
if isKnownFoldable { return true }
|
|
276
|
+
// compiled out on SDKs older than iOS 27.1 (UIKit 9127.0.85.28)
|
|
277
|
+
#if canImport(UIKit, _version: 9127.0.85.28)
|
|
278
|
+
guard #available(iOS 27.1, *), let view, view.window != nil || view is UIWindow else { return false }
|
|
279
|
+
isKnownFoldable = !view.reservedRegions(kind: .division, options: .includeInactive).isEmpty
|
|
280
|
+
#endif
|
|
281
|
+
return isKnownFoldable
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/// Catches the hinge when the regions were read too early (before the window had them).
|
|
285
|
+
static func observe(in view: UIView) {
|
|
286
|
+
#if canImport(UIKit, _version: 9127.0.85.28)
|
|
287
|
+
guard #available(iOS 27.1, *) else { return }
|
|
288
|
+
view.addInteraction(UIHingeInteraction { _, update in
|
|
289
|
+
guard update.hinge != nil, !isKnownFoldable else { return }
|
|
290
|
+
isKnownFoldable = true
|
|
291
|
+
NotificationCenter.default.post(name: ReservedRegions.didChange, object: nil)
|
|
292
|
+
})
|
|
293
|
+
#endif
|
|
294
|
+
}
|
|
261
295
|
}
|
|
262
296
|
|
|
263
297
|
// MARK: - Reserved regions
|
|
@@ -282,6 +316,7 @@ private final class ReservedRegionsProbe: UIView {
|
|
|
282
316
|
override init(frame: CGRect) {
|
|
283
317
|
super.init(frame: frame)
|
|
284
318
|
isUserInteractionEnabled = false
|
|
319
|
+
FoldableDevice.observe(in: self)
|
|
285
320
|
}
|
|
286
321
|
|
|
287
322
|
required init?(coder: NSCoder) { nil }
|
|
@@ -35,8 +35,6 @@ struct DuoBarCompression {
|
|
|
35
35
|
static let defaultToolbarHeight = DuoMetrics.controlSize + Spacing.M + 2 * DuoMetrics.itemPitch
|
|
36
36
|
/// Back, then the single button the toolbar folds into.
|
|
37
37
|
private static let compressedToolbarHeight = 2 * DuoMetrics.controlSize + Spacing.M
|
|
38
|
-
/// Back alone, while a rail opened from the compressed state takes the toolbar slot.
|
|
39
|
-
private static let backOnlyHeight = DuoMetrics.controlSize
|
|
40
38
|
|
|
41
39
|
/// - both fit: both stay whole
|
|
42
40
|
/// - one fits whole beside the other compressed: one is compressed, tapping it swaps them
|
|
@@ -72,7 +70,8 @@ struct DuoBarCompression {
|
|
|
72
70
|
|
|
73
71
|
let expandToolbar = { opened.wrappedValue = .toolbar }
|
|
74
72
|
let expandTabBar = { opened.wrappedValue = .tabBar }
|
|
75
|
-
|
|
73
|
+
// an opened rail always shows, even when only the group gap is short: it closes once a tab is picked
|
|
74
|
+
if opened.wrappedValue == .tabBar {
|
|
76
75
|
return DuoBarCompression(
|
|
77
76
|
toolbarCompressed: true,
|
|
78
77
|
tabBarCompressed: false,
|