@momo-kits/native-kits 0.162.30-debug → 0.162.31-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 +19 -0
- package/compose/build.gradle.kts +1 -1
- package/compose/compose.podspec +1 -1
- package/gradle.properties +1 -1
- package/ios/Application/ApplicationEnvironment.swift +14 -6
- package/ios/Application/LanguageSource.swift +93 -0
- package/ios/Application/Localize.swift +73 -4
- package/ios/Application/LocalizeModifier.swift +30 -0
- package/ios/Application/Navigation/NavigationContainer.swift +28 -18
- package/ios/Application/Navigation/Navigator.swift +121 -53
- package/ios/Application/Navigation/NavigatorManager.swift +49 -0
- package/ios/Application/Navigation/component/NavigationFooter.swift +90 -0
- package/ios/Application/StackScreen.swift +19 -28
- package/ios/Badge/Badge.swift +2 -1
- package/ios/Badge/BadgeRibbon.swift +6 -2
- package/ios/Button/Button.swift +41 -61
- package/ios/Chip/Chip.swift +6 -5
- package/ios/Icon/Icon.swift +13 -4
- package/ios/Input/ErrorView.swift +2 -1
- package/ios/Input/Input.swift +9 -2
- package/ios/Input/InputPhoneNumber.swift +2 -1
- package/ios/Input/InputSearch.swift +3 -2
- package/ios/Input/InputTextArea.swift +6 -1
- package/ios/InputMoney/InputMoney.swift +5 -2
- package/ios/InputOTP/InputOTP.swift +2 -1
- package/ios/Layout/GridContext.swift +30 -0
- package/ios/Layout/Item.swift +42 -0
- package/ios/Layout/Section.swift +134 -0
- package/ios/Popup/PopupNotify.swift +2 -1
- package/ios/ProgressInfo/ProgressInfo.swift +4 -3
- package/ios/Rating/Rating.swift +2 -1
- package/ios/Stepper/Stepper.swift +2 -1
- package/ios/SuggestAction/SuggestAction.swift +3 -2
- package/ios/Tag/Tag.swift +2 -1
- package/ios/Template/TrustBanner/TrustBanner.swift +4 -2
- package/ios/Title/Title.swift +3 -2
- package/ios/Tooltip/Tooltip.swift +2 -1
- package/ios/Typography/FontScale.swift +103 -0
- package/ios/Typography/FontScaleStore.swift +52 -13
- package/ios/Typography/Text.swift +12 -29
- package/ios/Typography/Typography.swift +66 -86
- package/ios/native-kits.podspec +2 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +87 -73
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +55 -249
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +90 -82
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +74 -104
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +47 -50
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +63 -7
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +90 -81
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +73 -97
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DemoScreen.swift +420 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +46 -4
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +54 -57
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +80 -45
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconDemo.swift +81 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +49 -62
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +66 -116
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +104 -98
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +76 -118
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +73 -86
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +69 -54
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +74 -54
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +73 -20
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +68 -38
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +55 -52
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LocalizeDemo.swift +51 -4
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +14 -2
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +47 -51
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PlaygroundData.swift +76 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +67 -93
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +34 -43
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +83 -71
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +55 -23
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +58 -46
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SectionDemo.swift +83 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +41 -12
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +67 -127
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +54 -110
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +69 -79
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +78 -58
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +76 -65
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +82 -127
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +44 -19
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +92 -168
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +79 -27
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +0 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +87 -92
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +94 -163
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +70 -70
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +68 -114
- package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +3 -1
- package/package.json +2 -2
|
@@ -84,6 +84,7 @@ private struct StepperButton: View {
|
|
|
84
84
|
|
|
85
85
|
private struct StepperNumberView: View {
|
|
86
86
|
@Environment(\.appTheme) private var theme
|
|
87
|
+
@Environment(\.momoFontScale) private var fontScale
|
|
87
88
|
|
|
88
89
|
@Binding var text: String
|
|
89
90
|
var size: StepperSize
|
|
@@ -105,7 +106,7 @@ private struct StepperNumberView: View {
|
|
|
105
106
|
MomoText(text, typography: .descriptionDefaultRegular, color: textColor)
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
|
-
.font(.system(size:
|
|
109
|
+
.font(.system(size: fontScale(12)))
|
|
109
110
|
.ignoreBoldTextSetting()
|
|
110
111
|
.foregroundColor(textColor)
|
|
111
112
|
.frame(minWidth: specs.numberMinWidth)
|
|
@@ -3,6 +3,7 @@ import Foundation
|
|
|
3
3
|
|
|
4
4
|
public struct SuggestAction: View {
|
|
5
5
|
@Environment(\.appTheme) private var theme
|
|
6
|
+
@Environment(\.momoFontScale) private var fontScale
|
|
6
7
|
|
|
7
8
|
private let message: String
|
|
8
9
|
private let buttonTitle: String
|
|
@@ -32,7 +33,7 @@ public struct SuggestAction: View {
|
|
|
32
33
|
|
|
33
34
|
// Builds an attributed string with `boldMessage` (case-insensitive) emphasized.
|
|
34
35
|
private var annotatedMessage: AttributedString {
|
|
35
|
-
let baseFont = Font.system(size:
|
|
36
|
+
let baseFont = Font.system(size: fontScale(TypographyStyle.bodyDefaultRegular.fontSize), weight: .regular)
|
|
36
37
|
var attributed = AttributedString(message)
|
|
37
38
|
attributed.font = baseFont
|
|
38
39
|
|
|
@@ -44,7 +45,7 @@ public struct SuggestAction: View {
|
|
|
44
45
|
|
|
45
46
|
if let boldRange = Range(range, in: attributed) {
|
|
46
47
|
attributed[boldRange].font = Font.system(
|
|
47
|
-
size:
|
|
48
|
+
size: fontScale(TypographyStyle.bodyDefaultRegular.fontSize),
|
|
48
49
|
weight: .bold
|
|
49
50
|
)
|
|
50
51
|
}
|
package/ios/Tag/Tag.swift
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import SwiftUI
|
|
2
2
|
|
|
3
3
|
public struct Tag: View {
|
|
4
|
+
@Environment(\.momoFontScale) private var fontScale
|
|
4
5
|
public enum TagSize {
|
|
5
6
|
case large
|
|
6
7
|
case medium
|
|
@@ -71,7 +72,7 @@ public struct Tag: View {
|
|
|
71
72
|
}
|
|
72
73
|
MomoText(label, typography: .labelSMedium, color: labelColor)
|
|
73
74
|
}
|
|
74
|
-
.frame(height:
|
|
75
|
+
.frame(height: fontScale(size.height))
|
|
75
76
|
.padding(.horizontal, Spacing.S)
|
|
76
77
|
.background(
|
|
77
78
|
RoundedRectangle(cornerRadius: Radius.S, style: .continuous)
|
|
@@ -74,7 +74,8 @@ public struct TrustBanner: View {
|
|
|
74
74
|
.scaledToFit()
|
|
75
75
|
.frame(width: 64, height: 64)
|
|
76
76
|
VStack(alignment: .leading){
|
|
77
|
-
Text(applicationEnvironment.config?.trustBanner?.content[localize.currentLanguage]
|
|
77
|
+
Text(applicationEnvironment.config?.trustBanner?.content[localize.currentLanguage]
|
|
78
|
+
?? localize.translateData(defaultBanner.content))
|
|
78
79
|
.foregroundColor(Color(hex: "484848"))
|
|
79
80
|
.lineLimit(2)
|
|
80
81
|
.font(.system(size: 13, weight: Font.Weight.regular))
|
|
@@ -83,7 +84,8 @@ public struct TrustBanner: View {
|
|
|
83
84
|
.padding(.bottom, 8)
|
|
84
85
|
HStack {
|
|
85
86
|
HStack(spacing: 0) {
|
|
86
|
-
Text(applicationEnvironment.config?.trustBanner?.subContent[localize.currentLanguage]
|
|
87
|
+
Text(applicationEnvironment.config?.trustBanner?.subContent[localize.currentLanguage]
|
|
88
|
+
?? localize.translateData(defaultBanner.subContent))
|
|
87
89
|
.foregroundColor(Colors.pink03)
|
|
88
90
|
.font(.system(size: 13, weight: .bold))
|
|
89
91
|
.ignoreBoldTextSetting()
|
package/ios/Title/Title.swift
CHANGED
|
@@ -28,6 +28,7 @@ public enum TitleButtonSize {
|
|
|
28
28
|
|
|
29
29
|
public struct Title: View {
|
|
30
30
|
@Environment(\.appTheme) private var theme
|
|
31
|
+
@Environment(\.momoFontScale) private var fontScale
|
|
31
32
|
|
|
32
33
|
private let title: String
|
|
33
34
|
private let type: TitleType
|
|
@@ -152,10 +153,10 @@ public struct Title: View {
|
|
|
152
153
|
|
|
153
154
|
private var titleText: some View {
|
|
154
155
|
SwiftUI.Text(title)
|
|
155
|
-
.font(.system(size:
|
|
156
|
+
.font(.system(size: fontScale(titleFontSize), weight: titleFontWeight))
|
|
156
157
|
.ignoreBoldTextSetting()
|
|
157
158
|
.foregroundColor(theme.colors.text.default)
|
|
158
|
-
.lineSpacing(
|
|
159
|
+
.lineSpacing(fontScale(titleLineHeight) - fontScale(titleFontSize))
|
|
159
160
|
.lineLimit(titleMaxLines)
|
|
160
161
|
.truncationMode(.tail)
|
|
161
162
|
}
|
|
@@ -469,11 +469,12 @@ private struct TooltipSecondaryButton: View {
|
|
|
469
469
|
}
|
|
470
470
|
|
|
471
471
|
private struct TooltipIconButton: View {
|
|
472
|
+
@Environment(\.momoFontScale) private var fontScale
|
|
472
473
|
let icon: String
|
|
473
474
|
let onPress: () -> Void
|
|
474
475
|
|
|
475
476
|
var body: some View {
|
|
476
|
-
let iconButtonSize =
|
|
477
|
+
let iconButtonSize = fontScale(36)
|
|
477
478
|
SwiftUI.Button(action: onPress) {
|
|
478
479
|
Icon(source: icon, size: 24)
|
|
479
480
|
.frame(width: iconButtonSize, height: iconButtonSize)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// File: ios/Typography/FontScale.swift
|
|
2
|
+
// Created At: 2026-08-06 10:00:00 +07:00
|
|
3
|
+
// Created By: AI
|
|
4
|
+
// AI Agent: Claude Code
|
|
5
|
+
// Model: claude-opus-5
|
|
6
|
+
|
|
7
|
+
// AI-GENERATED START: reactive font-scale snapshot injected through the environment
|
|
8
|
+
import SwiftUI
|
|
9
|
+
|
|
10
|
+
/// Immutable snapshot of the kit's font-scale inputs. The formula is a 1:1 port of Compose's
|
|
11
|
+
/// `scaleSize()` (`const/Typography.kt`): the device-width scale and the OS Dynamic Type scale are
|
|
12
|
+
/// computed independently and the larger one wins.
|
|
13
|
+
public struct FontScale: Equatable {
|
|
14
|
+
private static let defaultScreenSize: CGFloat = 375
|
|
15
|
+
private static let maxFontScale: CGFloat = 1.5
|
|
16
|
+
private static let maxDeviceScale: CGFloat = 5
|
|
17
|
+
|
|
18
|
+
public let userScaleRate: CGFloat
|
|
19
|
+
public let useOSScaleRate: Bool
|
|
20
|
+
/// OS Dynamic Type factor at the time the snapshot was taken.
|
|
21
|
+
public let osFontScale: CGFloat
|
|
22
|
+
|
|
23
|
+
public init(userScaleRate: CGFloat = 1, useOSScaleRate: Bool = true, osFontScale: CGFloat = 1) {
|
|
24
|
+
self.userScaleRate = userScaleRate
|
|
25
|
+
self.useOSScaleRate = useOSScaleRate
|
|
26
|
+
self.osFontScale = osFontScale
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// `callAsFunction` so call sites keep reading like Compose's `scaleSize(16)`.
|
|
30
|
+
public func callAsFunction(_ size: CGFloat) -> CGFloat {
|
|
31
|
+
guard useOSScaleRate else { return size * userScaleRate }
|
|
32
|
+
|
|
33
|
+
// Device width is read live rather than snapshotted, matching Compose's
|
|
34
|
+
// getScreenDimensions() read inside scaleSize(), so a rotation or split-view resize counts.
|
|
35
|
+
let deviceScale = UIScreen.main.bounds.width / Self.defaultScreenSize
|
|
36
|
+
|
|
37
|
+
var deviceScaled = size
|
|
38
|
+
var osScaled = size
|
|
39
|
+
if deviceScale > 1 {
|
|
40
|
+
deviceScaled = min(size * deviceScale, size + Self.maxDeviceScale)
|
|
41
|
+
}
|
|
42
|
+
if osFontScale > 1 {
|
|
43
|
+
osScaled = min(size * osFontScale, size * Self.maxFontScale)
|
|
44
|
+
}
|
|
45
|
+
return max(deviceScaled, osScaled)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// `.system(size:)` is a fixed-size font — SwiftUI does not Dynamic-Type it — so the scale has to
|
|
49
|
+
/// be baked into the point size here, and there is no double-scaling to undo.
|
|
50
|
+
public func font(size: CGFloat, weight: Font.Weight = .regular) -> Font {
|
|
51
|
+
.system(size: self(size), weight: weight)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// A host-pushed mini-app context wins per field, matching Compose's
|
|
55
|
+
/// `context?.useOSScaleRate ?: KitFontScale.useOSScaleRate`.
|
|
56
|
+
public func overriding(userScaleRate: CGFloat?, useOSScaleRate: Bool?) -> FontScale {
|
|
57
|
+
guard userScaleRate != nil || useOSScaleRate != nil else { return self }
|
|
58
|
+
return FontScale(
|
|
59
|
+
userScaleRate: userScaleRate ?? self.userScaleRate,
|
|
60
|
+
useOSScaleRate: useOSScaleRate ?? self.useOSScaleRate,
|
|
61
|
+
osFontScale: osFontScale
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private struct FontScaleKey: EnvironmentKey {
|
|
67
|
+
/// Computed, never stored: a bare `UIHostingController` root that never got `.momoFontScale()`
|
|
68
|
+
/// still reads the live store instead of a value frozen at first access.
|
|
69
|
+
static var defaultValue: FontScale { FontScaleStore.shared.snapshot }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public extension EnvironmentValues {
|
|
73
|
+
var momoFontScale: FontScale {
|
|
74
|
+
get { self[FontScaleKey.self] }
|
|
75
|
+
set { self[FontScaleKey.self] = newValue }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public extension View {
|
|
80
|
+
/// Injects a Dynamic-Type-observing font scale and re-renders this subtree on change.
|
|
81
|
+
/// Use at every `UIHostingController` root that is not inside a `NavigationContainer`.
|
|
82
|
+
func momoFontScale(_ store: FontScaleStore = .shared, context: MiniAppContext? = nil) -> some View {
|
|
83
|
+
modifier(MomoFontScaleModifier(store: store, context: context))
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// `@ObservedObject` is load-bearing, same as `MomoLocalizeModifier`: `@Environment(\.momoFontScale)`
|
|
88
|
+
/// only invalidates when the boxed snapshot changes, which requires *this* body to re-run on publish.
|
|
89
|
+
private struct MomoFontScaleModifier: ViewModifier {
|
|
90
|
+
@ObservedObject var store: FontScaleStore
|
|
91
|
+
let context: MiniAppContext?
|
|
92
|
+
|
|
93
|
+
func body(content: Content) -> some View {
|
|
94
|
+
content.environment(
|
|
95
|
+
\.momoFontScale,
|
|
96
|
+
store.snapshot.overriding(
|
|
97
|
+
userScaleRate: context?.userScaleRate,
|
|
98
|
+
useOSScaleRate: context?.useOSScaleRate
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// AI-GENERATED END: reactive font-scale snapshot injected through the environment
|
|
@@ -1,19 +1,58 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
/// App-wide font-scale
|
|
4
|
-
///
|
|
5
|
-
///
|
|
6
|
-
///
|
|
7
|
-
///
|
|
8
|
-
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
/// App-wide font-scale inputs for the SwiftUI kit, and the single place they publish from.
|
|
4
|
+
/// The host writes `update(...)` at startup (from the cache-backed `FontScaleProvider`) and again
|
|
5
|
+
/// whenever the in-app font-size preference changes; the OS Dynamic Type factor is tracked here.
|
|
6
|
+
/// Mirrors Compose's `KitFontScale` + `ProvideOsFontScale` pair, including its live behaviour —
|
|
7
|
+
/// views re-render on change as long as the subtree got `.momoFontScale()` (`NavigationContainer`
|
|
8
|
+
/// applies it). When `useOSScaleRate` is false the kit applies `userScaleRate` instead of the OS factor.
|
|
9
|
+
public final class FontScaleStore: ObservableObject {
|
|
9
10
|
public static let shared = FontScaleStore()
|
|
10
|
-
private init() {}
|
|
11
11
|
|
|
12
|
-
public private(set) var userScaleRate: CGFloat = 1
|
|
13
|
-
public private(set) var useOSScaleRate: Bool = true
|
|
12
|
+
@Published public private(set) var userScaleRate: CGFloat = 1
|
|
13
|
+
@Published public private(set) var useOSScaleRate: Bool = true
|
|
14
|
+
@Published public private(set) var osFontScale: CGFloat = FontScaleStore.currentOsFontScale()
|
|
15
|
+
|
|
16
|
+
private init() {
|
|
17
|
+
NotificationCenter.default.addObserver(
|
|
18
|
+
self,
|
|
19
|
+
selector: #selector(contentSizeCategoryDidChange),
|
|
20
|
+
name: UIContentSizeCategory.didChangeNotification,
|
|
21
|
+
object: nil
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public var snapshot: FontScale {
|
|
26
|
+
FontScale(
|
|
27
|
+
userScaleRate: userScaleRate,
|
|
28
|
+
useOSScaleRate: useOSScaleRate,
|
|
29
|
+
osFontScale: osFontScale
|
|
30
|
+
)
|
|
31
|
+
}
|
|
14
32
|
|
|
15
33
|
public func update(userScaleRate: CGFloat, useOSScaleRate: Bool) {
|
|
16
|
-
|
|
17
|
-
|
|
34
|
+
onMain {
|
|
35
|
+
// Re-assigning an unchanged value would still publish and invalidate every kit subtree.
|
|
36
|
+
if self.userScaleRate != userScaleRate { self.userScaleRate = userScaleRate }
|
|
37
|
+
if self.useOSScaleRate != useOSScaleRate { self.useOSScaleRate = useOSScaleRate }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@objc private func contentSizeCategoryDidChange() {
|
|
42
|
+
let next = Self.currentOsFontScale()
|
|
43
|
+
guard next != osFontScale else { return }
|
|
44
|
+
osFontScale = next
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Same factor Compose's iOS `ProvideOsFontScale` derives from
|
|
48
|
+
/// `UIFont.preferredFont(forTextStyle: .body).pointSize / 17`, so both iOS surfaces scale alike.
|
|
49
|
+
static func currentOsFontScale() -> CGFloat {
|
|
50
|
+
let base = UIFont.systemFont(ofSize: UIFont.labelFontSize)
|
|
51
|
+
return UIFontMetrics.default.scaledFont(for: base).pointSize / base.pointSize
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Synchronous when already on main — an async hop would defer the change by a frame.
|
|
55
|
+
private func onMain(_ work: @escaping () -> Void) {
|
|
56
|
+
if Thread.isMainThread { work() } else { DispatchQueue.main.async(execute: work) }
|
|
18
57
|
}
|
|
19
58
|
}
|
|
@@ -1,33 +1,15 @@
|
|
|
1
1
|
import SwiftUI
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
let maxDeviceScale: CGFloat = 5
|
|
10
|
-
|
|
11
|
-
let deviceWidth = UIScreen.main.bounds.width
|
|
12
|
-
let deviceScale = deviceWidth / defaultScreenSize
|
|
13
|
-
|
|
14
|
-
let defaultFont = UIFont.systemFont(ofSize: UIFont.labelFontSize)
|
|
15
|
-
let scaledFont = UIFontMetrics.default.scaledFont(for: defaultFont)
|
|
16
|
-
let fontScale = scaledFont.pointSize / defaultFont.pointSize
|
|
17
|
-
|
|
18
|
-
var fontSizeDeviceScale = size
|
|
19
|
-
var fontSizeOSScale = size
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if deviceScale > 1 {
|
|
23
|
-
fontSizeDeviceScale = min(fontSizeDeviceScale * deviceScale, fontSizeDeviceScale + maxDeviceScale)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if fontScale > 1 {
|
|
27
|
-
fontSizeOSScale = min(fontSizeOSScale * fontScale, fontSizeOSScale * maxFontScale)
|
|
28
|
-
}
|
|
3
|
+
/// Non-reactive entry point, for call sites that cannot read the environment (UIKit bridges,
|
|
4
|
+
/// free functions). Inside a SwiftUI view prefer `@Environment(\.momoFontScale)` so the view
|
|
5
|
+
/// re-renders when Dynamic Type or the host preference changes.
|
|
6
|
+
public func scaleSize(_ size: CGFloat) -> CGFloat {
|
|
7
|
+
FontScaleStore.shared.snapshot(size)
|
|
8
|
+
}
|
|
29
9
|
|
|
30
|
-
|
|
10
|
+
@available(*, deprecated, message: "Max font scale is fixed at 1.5; this override is no longer applied. Drop the second argument.")
|
|
11
|
+
public func scaleSize(_ size: CGFloat, _ scaleRate: CGFloat?) -> CGFloat {
|
|
12
|
+
scaleSize(size)
|
|
31
13
|
}
|
|
32
14
|
|
|
33
15
|
public enum TypographyStyle {
|
|
@@ -119,6 +101,7 @@ public extension TypographyStyle {
|
|
|
119
101
|
|
|
120
102
|
public struct MomoText: View {
|
|
121
103
|
@Environment(\.appTheme) private var theme
|
|
104
|
+
@Environment(\.momoFontScale) private var fontScale
|
|
122
105
|
private let content: String
|
|
123
106
|
private let typography: TypographyStyle
|
|
124
107
|
private let color: Color?
|
|
@@ -176,7 +159,7 @@ public struct MomoText: View {
|
|
|
176
159
|
let isStrikethrough = strikethrough || typography.isStrikethrough
|
|
177
160
|
|
|
178
161
|
var text = SwiftUI.Text(content)
|
|
179
|
-
.font(.
|
|
162
|
+
.font(fontScale.font(size: typography.fontSize, weight: typography.fontWeight))
|
|
180
163
|
.foregroundColor(textColor)
|
|
181
164
|
.strikethrough(isStrikethrough, color: textColor)
|
|
182
165
|
.underline(underline, color: textColor)
|
|
@@ -187,7 +170,7 @@ public struct MomoText: View {
|
|
|
187
170
|
|
|
188
171
|
return text
|
|
189
172
|
.multilineTextAlignment(textAlign)
|
|
190
|
-
.lineSpacing(
|
|
173
|
+
.lineSpacing(fontScale(typography.lineHeight) - fontScale(typography.fontSize))
|
|
191
174
|
.modifier(LineLimitModifier(range: lineLimitRange))
|
|
192
175
|
.truncationMode(truncationMode)
|
|
193
176
|
.fixedSize(horizontal: !softWrap, vertical: false)
|
|
@@ -10,98 +10,78 @@ public extension View {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
public extension Font {
|
|
13
|
+
/// Non-reactive: resolves against the current `FontScaleStore` snapshot at each access. The
|
|
14
|
+
/// styles below are computed properties for that reason — as stored `static let`s they would
|
|
15
|
+
/// freeze the scale at first access and never pick up a Dynamic Type change.
|
|
16
|
+
/// Inside a view, `@Environment(\.momoFontScale)` + `fontScale.font(size:weight:)` also re-renders.
|
|
13
17
|
static func appFont(size: CGFloat) -> Font {
|
|
14
|
-
|
|
15
|
-
return Font.system(size: size * FontScaleStore.shared.userScaleRate)
|
|
16
|
-
}
|
|
17
|
-
let defaultScreenSize: CGFloat = 375
|
|
18
|
-
let maxFontScale: CGFloat = 1.5
|
|
19
|
-
let maxDeviceScale: CGFloat = 5
|
|
20
|
-
|
|
21
|
-
let deviceWidth = UIScreen.main.bounds.width
|
|
22
|
-
let deviceScale = deviceWidth / defaultScreenSize
|
|
23
|
-
|
|
24
|
-
let defaultFont = UIFont.systemFont(ofSize: UIFont.labelFontSize)
|
|
25
|
-
let scaledFont = UIFontMetrics.default.scaledFont(for: defaultFont)
|
|
26
|
-
let fontScale = scaledFont.pointSize / defaultFont.pointSize
|
|
27
|
-
|
|
28
|
-
var fontSize = size
|
|
29
|
-
|
|
30
|
-
if deviceScale > 1 {
|
|
31
|
-
fontSize = min(fontSize * deviceScale, fontSize + maxDeviceScale)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if fontScale > 1 {
|
|
35
|
-
fontSize = min(fontSize * fontScale, fontSize * maxFontScale)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return Font.system(size: fontSize)
|
|
18
|
+
FontScaleStore.shared.snapshot.font(size: size)
|
|
39
19
|
}
|
|
40
20
|
|
|
41
21
|
// New supported typography styles
|
|
42
|
-
static
|
|
43
|
-
static
|
|
44
|
-
static
|
|
45
|
-
static
|
|
46
|
-
static
|
|
47
|
-
static
|
|
48
|
-
static
|
|
49
|
-
static
|
|
50
|
-
static
|
|
51
|
-
static
|
|
52
|
-
static
|
|
53
|
-
static
|
|
54
|
-
static
|
|
55
|
-
static
|
|
56
|
-
static
|
|
57
|
-
static
|
|
22
|
+
static var headline_default_bold: Font { appFont(size: 24).weight(.bold) }
|
|
23
|
+
static var header_m_bold: Font { appFont(size: 18).weight(.bold) }
|
|
24
|
+
static var header_default_bold: Font { appFont(size: 16).weight(.bold) }
|
|
25
|
+
static var header_default_semibold: Font { appFont(size: 16).weight(.semibold) }
|
|
26
|
+
static var header_s_semibold: Font { appFont(size: 14).weight(.semibold) }
|
|
27
|
+
static var header_xs_semibold: Font { appFont(size: 12).weight(.semibold) }
|
|
28
|
+
static var body_default_regular: Font { appFont(size: 14).weight(.regular) }
|
|
29
|
+
static var description_default_regular: Font { appFont(size: 12).weight(.regular) }
|
|
30
|
+
static var description_xs_regular: Font { appFont(size: 10).weight(.regular) }
|
|
31
|
+
static var label_default_medium: Font { appFont(size: 14).weight(.medium) }
|
|
32
|
+
static var label_s_medium: Font { appFont(size: 12).weight(.medium) }
|
|
33
|
+
static var label_xs_medium: Font { appFont(size: 10).weight(.medium) }
|
|
34
|
+
static var action_default_bold: Font { appFont(size: 16).weight(.bold) }
|
|
35
|
+
static var action_s_bold: Font { appFont(size: 14).weight(.bold) }
|
|
36
|
+
static var action_xs_bold: Font { appFont(size: 12).weight(.bold) }
|
|
37
|
+
static var action_xxs_bold: Font { appFont(size: 10).weight(.bold) }
|
|
58
38
|
|
|
59
39
|
// Legacy styles
|
|
60
|
-
static
|
|
61
|
-
static
|
|
62
|
-
static
|
|
63
|
-
static
|
|
64
|
-
static
|
|
65
|
-
static
|
|
66
|
-
static
|
|
67
|
-
static
|
|
68
|
-
static
|
|
69
|
-
static
|
|
70
|
-
static
|
|
71
|
-
static
|
|
72
|
-
static
|
|
73
|
-
static
|
|
74
|
-
static
|
|
75
|
-
static
|
|
76
|
-
static
|
|
77
|
-
static
|
|
78
|
-
static
|
|
79
|
-
static
|
|
80
|
-
static
|
|
81
|
-
static
|
|
82
|
-
static
|
|
40
|
+
static var headline_default: Font { appFont(size: 28).weight(.semibold) }
|
|
41
|
+
static var headline_s: Font { appFont(size: 24).weight(.semibold) }
|
|
42
|
+
static var headline_l: Font { appFont(size: 32).weight(.semibold) }
|
|
43
|
+
static var headline_xl: Font { appFont(size: 36).weight(.semibold) }
|
|
44
|
+
static var title_default: Font { appFont(size: 20).weight(.bold) }
|
|
45
|
+
static var title_xs: Font { appFont(size: 16).weight(.bold) }
|
|
46
|
+
static var title_s: Font { appFont(size: 18).weight(.bold) }
|
|
47
|
+
static var header_default: Font { appFont(size: 15).weight(.semibold) }
|
|
48
|
+
static var header_xs: Font { appFont(size: 12).weight(.semibold) }
|
|
49
|
+
static var header_s: Font { appFont(size: 14).weight(.semibold) }
|
|
50
|
+
static var paragraph_default: Font { appFont(size: 15).weight(.regular) }
|
|
51
|
+
static var paragraph_bold: Font { appFont(size: 15).weight(.bold) }
|
|
52
|
+
static var description_default: Font { appFont(size: 14).weight(.regular) }
|
|
53
|
+
static var description_xs: Font { appFont(size: 10).weight(.regular) }
|
|
54
|
+
static var description_s: Font { appFont(size: 12).weight(.regular) }
|
|
55
|
+
static var label_default: Font { appFont(size: 14).weight(.medium) }
|
|
56
|
+
static var label_xxs: Font { appFont(size: 8).weight(.medium) }
|
|
57
|
+
static var label_xs: Font { appFont(size: 10).weight(.medium) }
|
|
58
|
+
static var label_s: Font { appFont(size: 12).weight(.medium) }
|
|
59
|
+
static var action_default: Font { appFont(size: 16).weight(.bold) }
|
|
60
|
+
static var action_xxs: Font { appFont(size: 10).weight(.bold) }
|
|
61
|
+
static var action_xs: Font { appFont(size: 12).weight(.bold) }
|
|
62
|
+
static var action_s: Font { appFont(size: 15).weight(.bold) }
|
|
83
63
|
|
|
84
64
|
// deprecated
|
|
85
|
-
static
|
|
86
|
-
static
|
|
87
|
-
static
|
|
88
|
-
static
|
|
89
|
-
static
|
|
90
|
-
static
|
|
91
|
-
static
|
|
92
|
-
static
|
|
93
|
-
static
|
|
94
|
-
static
|
|
95
|
-
static
|
|
96
|
-
static
|
|
97
|
-
static
|
|
98
|
-
static
|
|
99
|
-
static
|
|
100
|
-
static
|
|
101
|
-
static
|
|
102
|
-
static
|
|
103
|
-
static
|
|
104
|
-
static
|
|
105
|
-
static
|
|
106
|
-
static
|
|
65
|
+
static var h1: Font { appFont(size: 36).weight(.semibold) } //headline_xl
|
|
66
|
+
static var h2: Font { appFont(size: 32).weight(.semibold) }
|
|
67
|
+
static var h3: Font { appFont(size: 28).weight(.semibold) }
|
|
68
|
+
static var h4: Font { appFont(size: 24).weight(.semibold) }
|
|
69
|
+
static var title1: Font { appFont(size: 20).weight(.bold) }
|
|
70
|
+
static var title2: Font { appFont(size: 18).weight(.bold) }
|
|
71
|
+
static var title3: Font { appFont(size: 16).weight(.bold) }
|
|
72
|
+
static var headerText1: Font { appFont(size: 15).weight(.semibold) } //header_default
|
|
73
|
+
static var headerText2: Font { appFont(size: 14).weight(.semibold) }
|
|
74
|
+
static var headerText3: Font { appFont(size: 12).weight(.semibold) }
|
|
75
|
+
static var paragraph: Font { appFont(size: 15).weight(.regular) } //paragraph_default
|
|
76
|
+
static var description1: Font { appFont(size: 14).weight(.regular) }
|
|
77
|
+
static var description2: Font { appFont(size: 12).weight(.regular) }
|
|
78
|
+
static var description3: Font { appFont(size: 10).weight(.regular) }
|
|
79
|
+
static var label1: Font { appFont(size: 14).weight(.medium) }
|
|
80
|
+
static var label2: Font { appFont(size: 12).weight(.medium) }
|
|
81
|
+
static var label3: Font { appFont(size: 10).weight(.medium) }
|
|
82
|
+
static var label4: Font { appFont(size: 8).weight(.medium) }
|
|
83
|
+
static var action1: Font { appFont(size: 16).weight(.bold) }
|
|
84
|
+
static var action2: Font { appFont(size: 15).weight(.bold) }
|
|
85
|
+
static var action3: Font { appFont(size: 12).weight(.bold) }
|
|
86
|
+
static var action4: Font { appFont(size: 10).weight(.bold) }
|
|
107
87
|
}
|
package/ios/native-kits.podspec
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Pod::Spec.new do |s|
|
|
2
2
|
s.name = 'MoMoUIKits'
|
|
3
|
-
s.version = '0.163.1-
|
|
3
|
+
s.version = '0.163.1-sp.10'
|
|
4
4
|
s.summary = 'MoMoUIKits for iOS'
|
|
5
5
|
s.homepage = 'https://momo.vn'
|
|
6
6
|
s.license = { :type => 'MIT' }
|
|
@@ -34,6 +34,7 @@ Pod::Spec.new do |s|
|
|
|
34
34
|
"InputDropDown/**/*.{swift,m,h}",
|
|
35
35
|
"InputMoney/**/*.{swift,m,h}",
|
|
36
36
|
"InputOTP/**/*.{swift,m,h}",
|
|
37
|
+
"Layout/**/*.{swift,m,h}",
|
|
37
38
|
"Loader/**/*.{swift,m,h}",
|
|
38
39
|
"Lottie/**/*.{swift,m,h}",
|
|
39
40
|
"OTPKeyboard/**/*.{swift,m,h}",
|