@momo-kits/native-kits 0.168.1-beta.2-debug → 0.168.1-beta.24-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 +10 -6
- package/compose/build.gradle.kts +1 -1
- package/compose/compose.podspec +1 -1
- package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +4 -3
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/DesignSystemKits.kt +62 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +25 -16
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +3 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +18 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +25 -13
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +206 -45
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +14 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +53 -30
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +2 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +124 -59
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +3 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +10 -3
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +3 -1
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +9 -8
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +54 -10
- package/gradle.properties +1 -1
- package/ios/Application/ApplicationEnvironment.swift +7 -6
- package/ios/Application/DesignSystemKits.swift +163 -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 +65 -10
- package/ios/Navigation/BottomSheet.swift +153 -23
- 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/Component/SnackBar.swift +4 -0
- package/ios/Navigation/ModalScreen.swift +7 -3
- package/ios/Navigation/StackScreen.swift +3 -7
- package/ios/Popup/PopupDisplay.swift +2 -2
- package/ios/Popup/PopupInput.swift +2 -2
- package/ios/Popup/PopupLayout.swift +15 -10
- package/ios/Popup/PopupNotify.swift +2 -2
- package/ios/Stepper/Stepper.swift +1 -2
- package/ios/Template/TrustBanner/TrustBanner.swift +4 -2
- package/ios/Typography/FontScale.swift +29 -15
- 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/local.properties +8 -0
- package/package.json +1 -1
- package/.claude/settings.local.json +0 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
- package/ios/Typography/FontScaleStore.swift +0 -58
- package/ios/Typography/KitDesignSystem.swift +0 -17
|
@@ -403,11 +403,8 @@ public struct StackScreen<Content: View>: View {
|
|
|
403
403
|
.background(alignment: .top) {
|
|
404
404
|
Group {
|
|
405
405
|
if layout.sideBar != nil {
|
|
406
|
-
//
|
|
407
|
-
|
|
408
|
-
shape: Rectangle(),
|
|
409
|
-
visible: scrollOffset > heroGlassStart(animatedHeader)
|
|
410
|
-
))
|
|
406
|
+
// the bar shows once the banner has scrolled under the header
|
|
407
|
+
DuoHeaderBar(scrollOffset: scrollOffset, fadeStart: heroGlassStart(animatedHeader))
|
|
411
408
|
} else {
|
|
412
409
|
HeaderBarBackground(opacity: opacity)
|
|
413
410
|
}
|
|
@@ -457,8 +454,7 @@ public struct StackScreen<Content: View>: View {
|
|
|
457
454
|
|
|
458
455
|
header
|
|
459
456
|
.background(
|
|
460
|
-
|
|
461
|
-
.modifier(DuoBarMaterial(shape: Rectangle(), visible: scrollOffset > 0))
|
|
457
|
+
DuoHeaderBar(scrollOffset: scrollOffset)
|
|
462
458
|
.edgesIgnoringSafeArea([.top, .horizontal])
|
|
463
459
|
)
|
|
464
460
|
.offset(y: -headerHide)
|
|
@@ -107,7 +107,7 @@ public struct PopupDisplay: View {
|
|
|
107
107
|
}
|
|
108
108
|
.fixedSize(horizontal: false, vertical: true)
|
|
109
109
|
.background(theme.colors.background.surface)
|
|
110
|
-
.clipShape(RoundedRectangle(cornerRadius:
|
|
110
|
+
.clipShape(RoundedRectangle(cornerRadius: layout.popupRadius()))
|
|
111
111
|
|
|
112
112
|
if isShowCloseIcon {
|
|
113
113
|
PopupCloseButton(action: onClose)
|
|
@@ -115,7 +115,7 @@ public struct PopupDisplay: View {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
.frame(maxWidth: layout.popupMaxWidth)
|
|
118
|
-
.padding(.horizontal, 12)
|
|
118
|
+
.padding(.horizontal, layout.popupHorizontalPadding(12))
|
|
119
119
|
.accessibilityElement(children: .contain)
|
|
120
120
|
.accessibilityIdentifier("popup_notify")
|
|
121
121
|
}
|
|
@@ -59,11 +59,11 @@ public struct PopupInput: View {
|
|
|
59
59
|
.offset(x: 11, y: -11)
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
.background(theme.colors.background.surface.cornerRadius(8))
|
|
62
|
+
.background(theme.colors.background.surface.cornerRadius(layout.popupRadius(8)))
|
|
63
63
|
.shadow(color: .black.opacity(0.08), radius: 2, x: 0, y: 0)
|
|
64
64
|
.shadow(color: .black.opacity(0.16), radius: 24, x: 0, y: 0)
|
|
65
65
|
.frame(maxWidth: layout.popupMaxWidth)
|
|
66
|
-
.padding(.horizontal, 40)
|
|
66
|
+
.padding(.horizontal, layout.popupHorizontalPadding(40))
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// MARK: Internal
|
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
import SDWebImageSwiftUI
|
|
2
2
|
import SwiftUI
|
|
3
3
|
|
|
4
|
-
// Width of a system alert on the iPhone Duo inner display, in both orientations (RN Popup/utils.ts).
|
|
5
|
-
private let duoPopupMaxWidth: CGFloat = 440
|
|
6
|
-
|
|
7
4
|
extension ScreenLayout {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
/// Card width cap (Compose `popupMaxWidth`); unbounded off iPhone Duo, where the popup's own padding sets the width.
|
|
5
|
+
/// Card width cap on iPhone Duo (the modal bounds, Compose `popupMaxWidth`); unbounded elsewhere,
|
|
6
|
+
/// where the popup's own padding sets the width.
|
|
11
7
|
var popupMaxWidth: CGFloat {
|
|
12
|
-
isDuoDevice ?
|
|
8
|
+
isDuoDevice ? duoModalMaxWidth : .infinity
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/// The modal bounds already keep iPhone Duo popups off the edges, so their own side padding goes.
|
|
12
|
+
func popupHorizontalPadding(_ padding: CGFloat) -> CGFloat {
|
|
13
|
+
isDuoDevice ? 0 : padding
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
///
|
|
16
|
+
/// Tallest a popup gets on iPhone Duo, landscape and the inner display included; nil elsewhere.
|
|
16
17
|
var popupMaxHeight: CGFloat? {
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
isDuoDevice ? duoModalMaxHeight : nil
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Rounder on iPhone Duo, closer to its system alerts.
|
|
22
|
+
func popupRadius(_ regular: CGFloat = Radius.L) -> CGFloat {
|
|
23
|
+
isDuoDevice ? Radius.XL : regular
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
26
|
|
|
@@ -105,7 +105,7 @@ public struct PopupNotify: View {
|
|
|
105
105
|
.popupReadHeight($actionsHeight)
|
|
106
106
|
}
|
|
107
107
|
.background(theme.colors.background.surface)
|
|
108
|
-
.clipShape(RoundedRectangle(cornerRadius:
|
|
108
|
+
.clipShape(RoundedRectangle(cornerRadius: layout.popupRadius()))
|
|
109
109
|
|
|
110
110
|
if props.isShowCloseIcon {
|
|
111
111
|
PopupCloseButton(action: props.onIconClose)
|
|
@@ -113,7 +113,7 @@ public struct PopupNotify: View {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
.frame(maxWidth: layout.popupMaxWidth)
|
|
116
|
-
.padding(.horizontal, Spacing.M)
|
|
116
|
+
.padding(.horizontal, layout.popupHorizontalPadding(Spacing.M))
|
|
117
117
|
.accessibilityElement(children: .contain)
|
|
118
118
|
.accessibilityIdentifier("popup_notify")
|
|
119
119
|
}
|
|
@@ -84,7 +84,6 @@ 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
|
|
88
87
|
|
|
89
88
|
@Binding var text: String
|
|
90
89
|
var size: StepperSize
|
|
@@ -106,7 +105,7 @@ private struct StepperNumberView: View {
|
|
|
106
105
|
MomoText(text, typography: .descriptionDefaultRegular, color: textColor)
|
|
107
106
|
}
|
|
108
107
|
}
|
|
109
|
-
.
|
|
108
|
+
.kitFont(size: 12)
|
|
110
109
|
.ignoreBoldTextSetting()
|
|
111
110
|
.foregroundColor(textColor)
|
|
112
111
|
// Compose's min width includes the horizontal padding.
|
|
@@ -11,7 +11,7 @@ import SDWebImageSwiftUI
|
|
|
11
11
|
|
|
12
12
|
let jsonUrl = "https://static.momocdn.net/app/json/component-kits/design_system.json"
|
|
13
13
|
|
|
14
|
-
public struct TrustBannerData: Decodable {
|
|
14
|
+
public struct TrustBannerData: Decodable, Equatable {
|
|
15
15
|
public let content: [String: String]
|
|
16
16
|
public let subContent: [String: String]
|
|
17
17
|
public let pciImage: String?
|
|
@@ -85,6 +85,7 @@ let defaultBanner = TrustBannerData(
|
|
|
85
85
|
public struct TrustBanner: View {
|
|
86
86
|
@Environment(\.applicationEnvironment) var applicationEnvironment
|
|
87
87
|
@Environment(\.localize) private var localize
|
|
88
|
+
@Environment(\.designSystemKitsConfigs) private var designSystemKitsConfigs
|
|
88
89
|
|
|
89
90
|
var onPress: ((String)->Void)?
|
|
90
91
|
private let serviceName: String
|
|
@@ -104,7 +105,8 @@ public struct TrustBanner: View {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
private var banner: TrustBannerData {
|
|
107
|
-
|
|
108
|
+
// Container config wins, else the host-pushed DesignSystemKits config (Compose parity).
|
|
109
|
+
(applicationEnvironment.config ?? designSystemKitsConfigs.designSystemConfig)?.trustBanner ?? defaultBanner
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
func onPressSecurity() {
|
|
@@ -19,11 +19,19 @@ public struct FontScale: Equatable {
|
|
|
19
19
|
public let useOSScaleRate: Bool
|
|
20
20
|
/// OS Dynamic Type factor at the time the snapshot was taken.
|
|
21
21
|
public let osFontScale: CGFloat
|
|
22
|
+
// AI-GENERATED START: device scale from the app window, not UIScreen.main (fixed to one display on iPhone Duo)
|
|
23
|
+
public let windowShortSide: CGFloat
|
|
22
24
|
|
|
23
|
-
public init(
|
|
25
|
+
public init(
|
|
26
|
+
userScaleRate: CGFloat = 1,
|
|
27
|
+
useOSScaleRate: Bool = true,
|
|
28
|
+
osFontScale: CGFloat = 1,
|
|
29
|
+
windowShortSide: CGFloat? = nil
|
|
30
|
+
) {
|
|
24
31
|
self.userScaleRate = userScaleRate
|
|
25
32
|
self.useOSScaleRate = useOSScaleRate
|
|
26
33
|
self.osFontScale = osFontScale
|
|
34
|
+
self.windowShortSide = windowShortSide ?? DesignSystemKits.currentWindowShortSide()
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
/// `callAsFunction` so call sites keep reading like Compose's `scaleSize(16)`.
|
|
@@ -31,8 +39,8 @@ public struct FontScale: Equatable {
|
|
|
31
39
|
guard useOSScaleRate else { return size * userScaleRate }
|
|
32
40
|
|
|
33
41
|
// Short side, not width: landscape must not inflate every font (and so every popup's height).
|
|
34
|
-
let
|
|
35
|
-
|
|
42
|
+
let deviceScale = windowShortSide / Self.defaultScreenSize
|
|
43
|
+
// AI-GENERATED END: device scale from the app window, not UIScreen.main (fixed to one display on iPhone Duo)
|
|
36
44
|
|
|
37
45
|
var deviceScaled = size
|
|
38
46
|
var osScaled = size
|
|
@@ -52,13 +60,16 @@ public struct FontScale: Equatable {
|
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
/// A host-pushed mini-app context wins per field, matching Compose's
|
|
55
|
-
/// `context?.useOSScaleRate ?:
|
|
63
|
+
/// `context?.useOSScaleRate ?: DesignSystemKits.getConfigs().useOSScaleRate`.
|
|
56
64
|
public func overriding(userScaleRate: CGFloat?, useOSScaleRate: Bool?) -> FontScale {
|
|
57
65
|
guard userScaleRate != nil || useOSScaleRate != nil else { return self }
|
|
58
66
|
return FontScale(
|
|
59
67
|
userScaleRate: userScaleRate ?? self.userScaleRate,
|
|
60
68
|
useOSScaleRate: useOSScaleRate ?? self.useOSScaleRate,
|
|
61
|
-
osFontScale: osFontScale
|
|
69
|
+
osFontScale: osFontScale,
|
|
70
|
+
// AI-GENERATED START: keep the window size through context overrides
|
|
71
|
+
windowShortSide: windowShortSide
|
|
72
|
+
// AI-GENERATED END: keep the window size through context overrides
|
|
62
73
|
)
|
|
63
74
|
}
|
|
64
75
|
}
|
|
@@ -66,7 +77,7 @@ public struct FontScale: Equatable {
|
|
|
66
77
|
private struct FontScaleKey: EnvironmentKey {
|
|
67
78
|
/// Computed, never stored: a bare `UIHostingController` root that never got `.momoFontScale()`
|
|
68
79
|
/// still reads the live store instead of a value frozen at first access.
|
|
69
|
-
static var defaultValue: FontScale {
|
|
80
|
+
static var defaultValue: FontScale { DesignSystemKits.shared.fontScale }
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
public extension EnvironmentValues {
|
|
@@ -77,9 +88,10 @@ public extension EnvironmentValues {
|
|
|
77
88
|
}
|
|
78
89
|
|
|
79
90
|
public extension View {
|
|
80
|
-
/// Injects a Dynamic-Type-observing font scale
|
|
91
|
+
/// Injects a Dynamic-Type-observing font scale plus `\.designSystemKitsConfigs` (whitelist, remote config)
|
|
92
|
+
/// and re-renders this subtree on change.
|
|
81
93
|
/// Use at every `UIHostingController` root that is not inside a `NavigationContainer`.
|
|
82
|
-
func momoFontScale(_ store:
|
|
94
|
+
func momoFontScale(_ store: DesignSystemKits = .shared, context: MiniAppContext? = nil) -> some View {
|
|
83
95
|
modifier(MomoFontScaleModifier(store: store, context: context))
|
|
84
96
|
}
|
|
85
97
|
}
|
|
@@ -87,17 +99,19 @@ public extension View {
|
|
|
87
99
|
/// `@ObservedObject` is load-bearing, same as `MomoLocalizeModifier`: `@Environment(\.momoFontScale)`
|
|
88
100
|
/// only invalidates when the boxed snapshot changes, which requires *this* body to re-run on publish.
|
|
89
101
|
private struct MomoFontScaleModifier: ViewModifier {
|
|
90
|
-
@ObservedObject var store:
|
|
102
|
+
@ObservedObject var store: DesignSystemKits
|
|
91
103
|
let context: MiniAppContext?
|
|
92
104
|
|
|
93
105
|
func body(content: Content) -> some View {
|
|
94
|
-
content
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
content
|
|
107
|
+
.environment(
|
|
108
|
+
\.momoFontScale,
|
|
109
|
+
store.fontScale.overriding(
|
|
110
|
+
userScaleRate: context?.userScaleRate,
|
|
111
|
+
useOSScaleRate: context?.useOSScaleRate
|
|
112
|
+
)
|
|
99
113
|
)
|
|
100
|
-
|
|
114
|
+
.environment(\.designSystemKitsConfigs, store.getConfigs())
|
|
101
115
|
}
|
|
102
116
|
}
|
|
103
117
|
// AI-GENERATED END: reactive font-scale snapshot injected through the environment
|
|
@@ -4,7 +4,7 @@ import SwiftUI
|
|
|
4
4
|
/// free functions). Inside a SwiftUI view prefer `@Environment(\.momoFontScale)` so the view
|
|
5
5
|
/// re-renders when Dynamic Type or the host preference changes.
|
|
6
6
|
public func scaleSize(_ size: CGFloat) -> CGFloat {
|
|
7
|
-
|
|
7
|
+
DesignSystemKits.shared.fontScale(size)
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
@available(*, deprecated, message: "Max font scale is fixed at 1.5; this override is no longer applied. Drop the second argument.")
|
|
@@ -105,10 +105,10 @@ private enum MomoFontFace {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
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? {
|
|
108
|
+
private func momoFontFace(family: String, weight: Font.Weight, isWhiteList: Bool) -> MomoFontFace? {
|
|
109
109
|
let isNewFamily = family == "MomoTrustSans" || family == headerFontFamily
|
|
110
110
|
// Outside the whitelist the new DS families drop to the legacy font, as before DS-791.
|
|
111
|
-
if family == "SFProText" || (isNewFamily && !
|
|
111
|
+
if family == "SFProText" || (isNewFamily && !isWhiteList) { return .system }
|
|
112
112
|
// single-file families (Compose maps every weight to the same .otf)
|
|
113
113
|
if family == headerFontFamily || family == "MoMoSignature" { return .named(family) }
|
|
114
114
|
guard family == "MomoTrustSans" else { return nil }
|
|
@@ -127,8 +127,14 @@ private func momoFontFace(family: String, weight: Font.Weight) -> MomoFontFace?
|
|
|
127
127
|
/// Header title family (Compose `HEADER_FONT_FAMILY`), whitelist only like the default font.
|
|
128
128
|
let headerFontFamily = "MoMoTrustDisplay"
|
|
129
129
|
|
|
130
|
-
private func momoFont(
|
|
131
|
-
|
|
130
|
+
private func momoFont(
|
|
131
|
+
size: CGFloat,
|
|
132
|
+
weight: Font.Weight,
|
|
133
|
+
family: String,
|
|
134
|
+
fallbackFamily: String? = nil,
|
|
135
|
+
isWhiteList: Bool
|
|
136
|
+
) -> Font {
|
|
137
|
+
switch momoFontFace(family: family, weight: weight, isWhiteList: isWhiteList) {
|
|
132
138
|
case .system:
|
|
133
139
|
return .system(size: size, weight: weight)
|
|
134
140
|
case .named(let name) where UIFont(name: name, size: size) != nil:
|
|
@@ -136,15 +142,15 @@ private func momoFont(size: CGFloat, weight: Font.Weight, family: String, fallba
|
|
|
136
142
|
default:
|
|
137
143
|
// Compose falls back to the default font file when a family is missing
|
|
138
144
|
if let fallbackFamily, fallbackFamily != family {
|
|
139
|
-
return momoFont(size: size, weight: weight, family: fallbackFamily)
|
|
145
|
+
return momoFont(size: size, weight: weight, family: fallbackFamily, isWhiteList: isWhiteList)
|
|
140
146
|
}
|
|
141
147
|
return .system(size: size, weight: weight)
|
|
142
148
|
}
|
|
143
149
|
}
|
|
144
150
|
|
|
145
151
|
// lineHeight is identical across weights of a family, so the regular face is enough.
|
|
146
|
-
private func momoLineHeight(size: CGFloat, family: String, fallbackFamily: String? = nil) -> CGFloat {
|
|
147
|
-
switch momoFontFace(family: family, weight: .regular) {
|
|
152
|
+
private func momoLineHeight(size: CGFloat, family: String, fallbackFamily: String? = nil, isWhiteList: Bool) -> CGFloat {
|
|
153
|
+
switch momoFontFace(family: family, weight: .regular, isWhiteList: isWhiteList) {
|
|
148
154
|
case .system:
|
|
149
155
|
break
|
|
150
156
|
case .named(let name):
|
|
@@ -152,15 +158,71 @@ private func momoLineHeight(size: CGFloat, family: String, fallbackFamily: Strin
|
|
|
152
158
|
fallthrough
|
|
153
159
|
case nil:
|
|
154
160
|
if let fallbackFamily, fallbackFamily != family {
|
|
155
|
-
return momoLineHeight(size: size, family: fallbackFamily)
|
|
161
|
+
return momoLineHeight(size: size, family: fallbackFamily, isWhiteList: isWhiteList)
|
|
156
162
|
}
|
|
157
163
|
}
|
|
158
164
|
return UIFont.systemFont(ofSize: size).lineHeight
|
|
159
165
|
}
|
|
160
166
|
|
|
167
|
+
/// UIKit counterpart of `momoFont`, for text that is not a `MomoText` (UITextField/UITextView, text measuring).
|
|
168
|
+
func momoUIFont(size: CGFloat, weight: UIFont.Weight, family: String, isWhiteList: Bool) -> UIFont {
|
|
169
|
+
guard case .named(let name) = momoFontFace(family: family, weight: Font.Weight(weight), isWhiteList: isWhiteList),
|
|
170
|
+
let font = UIFont(name: name, size: size) else {
|
|
171
|
+
return .systemFont(ofSize: size, weight: weight)
|
|
172
|
+
}
|
|
173
|
+
return font
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private extension Font.Weight {
|
|
177
|
+
init(_ weight: UIFont.Weight) {
|
|
178
|
+
switch weight {
|
|
179
|
+
case .ultraLight: self = .ultraLight
|
|
180
|
+
case .thin: self = .thin
|
|
181
|
+
case .light: self = .light
|
|
182
|
+
case .medium: self = .medium
|
|
183
|
+
case .semibold: self = .semibold
|
|
184
|
+
case .bold: self = .bold
|
|
185
|
+
case .heavy: self = .heavy
|
|
186
|
+
case .black: self = .black
|
|
187
|
+
default: self = .regular
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Gives input text the same family as `MomoText` (Compose's `getFontFamily`), e.g. a TextField
|
|
193
|
+
/// next to a `MomoText` placeholder. `size` is unscaled; the environment font scale is applied here.
|
|
194
|
+
private struct KitFontModifier: ViewModifier {
|
|
195
|
+
@Environment(\.appTheme) private var theme
|
|
196
|
+
@Environment(\.momoFontScale) private var fontScale
|
|
197
|
+
@Environment(\.designSystemKitsConfigs) private var designSystemKitsConfigs
|
|
198
|
+
let size: CGFloat
|
|
199
|
+
let weight: Font.Weight
|
|
200
|
+
|
|
201
|
+
func body(content: Content) -> some View {
|
|
202
|
+
content.font(momoFont(
|
|
203
|
+
size: fontScale(size),
|
|
204
|
+
weight: weight,
|
|
205
|
+
family: theme.font,
|
|
206
|
+
isWhiteList: designSystemKitsConfigs.isWhiteList
|
|
207
|
+
))
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
extension View {
|
|
212
|
+
func kitFont(_ typography: TypographyStyle) -> some View {
|
|
213
|
+
modifier(KitFontModifier(size: typography.fontSize, weight: typography.fontWeight))
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
func kitFont(size: CGFloat, weight: Font.Weight = .regular) -> some View {
|
|
217
|
+
modifier(KitFontModifier(size: size, weight: weight))
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
161
221
|
public struct MomoText: View {
|
|
162
222
|
@Environment(\.appTheme) private var theme
|
|
163
223
|
@Environment(\.momoFontScale) private var fontScale
|
|
224
|
+
// From the environment, not DesignSystemKits.shared, so a whitelist flip re-renders the text.
|
|
225
|
+
@Environment(\.designSystemKitsConfigs) private var designSystemKitsConfigs
|
|
164
226
|
private let content: String
|
|
165
227
|
private let typography: TypographyStyle
|
|
166
228
|
private let color: Color?
|
|
@@ -236,7 +298,8 @@ public struct MomoText: View {
|
|
|
236
298
|
size: fontScale(fontSize),
|
|
237
299
|
weight: typography.fontWeight,
|
|
238
300
|
family: fontFamily ?? theme.font,
|
|
239
|
-
fallbackFamily: theme.font
|
|
301
|
+
fallbackFamily: theme.font,
|
|
302
|
+
isWhiteList: designSystemKitsConfigs.isWhiteList
|
|
240
303
|
)
|
|
241
304
|
}
|
|
242
305
|
|
|
@@ -244,7 +307,13 @@ public struct MomoText: View {
|
|
|
244
307
|
private var baseText: SwiftUI.Text {
|
|
245
308
|
let keywords = highlightText.filter { !$0.isEmpty }.sorted { $0.count > $1.count }
|
|
246
309
|
guard !keywords.isEmpty else { return SwiftUI.Text(content) }
|
|
247
|
-
let highlightFont = momoFont(
|
|
310
|
+
let highlightFont = momoFont(
|
|
311
|
+
size: fontScale(fontSize),
|
|
312
|
+
weight: TypographyStyle.headerXsSemibold.fontWeight,
|
|
313
|
+
family: fontFamily ?? theme.font,
|
|
314
|
+
fallbackFamily: theme.font,
|
|
315
|
+
isWhiteList: designSystemKitsConfigs.isWhiteList
|
|
316
|
+
)
|
|
248
317
|
func isWordChar(_ character: Character?) -> Bool {
|
|
249
318
|
guard let character else { return false }
|
|
250
319
|
return character.isLetter || character.isNumber || character == "_"
|
|
@@ -282,7 +351,12 @@ public struct MomoText: View {
|
|
|
282
351
|
|
|
283
352
|
// lineSpacing is added on top of the font's natural line height, not on top of fontSize.
|
|
284
353
|
let lineSpacing = max(0, fontScale(lineHeight)
|
|
285
|
-
- momoLineHeight(
|
|
354
|
+
- momoLineHeight(
|
|
355
|
+
size: fontScale(fontSize),
|
|
356
|
+
family: fontFamily ?? theme.font,
|
|
357
|
+
fallbackFamily: theme.font,
|
|
358
|
+
isWhiteList: designSystemKitsConfigs.isWhiteList
|
|
359
|
+
))
|
|
286
360
|
|
|
287
361
|
var text = baseText
|
|
288
362
|
.font(font)
|
|
@@ -10,12 +10,12 @@ public extension View {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
public extension Font {
|
|
13
|
-
/// Non-reactive: resolves against the current `
|
|
13
|
+
/// Non-reactive: resolves against the current `DesignSystemKits` font scale at each access. The
|
|
14
14
|
/// styles below are computed properties for that reason — as stored `static let`s they would
|
|
15
15
|
/// freeze the scale at first access and never pick up a Dynamic Type change.
|
|
16
16
|
/// Inside a view, `@Environment(\.momoFontScale)` + `fontScale.font(size:weight:)` also re-renders.
|
|
17
17
|
static func appFont(size: CGFloat) -> Font {
|
|
18
|
-
|
|
18
|
+
DesignSystemKits.shared.fontScale.font(size: size)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// New supported typography styles
|
package/ios/native-kits.podspec
CHANGED
package/local.properties
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
## This file must *NOT* be checked into Version Control Systems,
|
|
2
|
+
# as it contains information specific to your local configuration.
|
|
3
|
+
#
|
|
4
|
+
# Location of the SDK. This is only used by Gradle.
|
|
5
|
+
# For customization when using a Version Control System, please read the
|
|
6
|
+
# header note.
|
|
7
|
+
#Wed Aug 21 14:20:12 ICT 2024
|
|
8
|
+
sdk.dir=/Users/huynhdung/Library/Android/sdk
|
package/package.json
CHANGED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.const
|
|
2
|
-
|
|
3
|
-
import androidx.compose.runtime.getValue
|
|
4
|
-
import androidx.compose.runtime.mutableStateOf
|
|
5
|
-
import androidx.compose.runtime.setValue
|
|
6
|
-
|
|
7
|
-
// Design-system rollout holder, same shape as KitFontScale: the kit is sandboxed and can't read the
|
|
8
|
-
// host config, so the host seeds this once at startup from DesignSystemConfig. getFontFamily() reads
|
|
9
|
-
// it to decide whether the new default font applies, so every screen follows the flag with no prop
|
|
10
|
-
// threading. Backed by snapshot state: writes recompose text automatically.
|
|
11
|
-
object KitDesignSystem {
|
|
12
|
-
var isWhiteList by mutableStateOf(false)
|
|
13
|
-
private set
|
|
14
|
-
|
|
15
|
-
fun update(isWhiteList: Boolean) {
|
|
16
|
-
this.isWhiteList = isWhiteList
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
package vn.momo.kits.const
|
|
2
|
-
|
|
3
|
-
import androidx.compose.runtime.getValue
|
|
4
|
-
import androidx.compose.runtime.mutableStateOf
|
|
5
|
-
import androidx.compose.runtime.setValue
|
|
6
|
-
|
|
7
|
-
// App-wide font-scale holder. The kit is sandboxed and can't read the host cache directly, so the
|
|
8
|
-
// host (which sees both core and this kit) seeds this once at startup and pushes runtime updates into
|
|
9
|
-
// it. scaleSize() reads it as the fallback, so every screen — including direct NavigationContainer
|
|
10
|
-
// screens with no host-pushed MiniAppContext — scales with no per-render prop and no async flicker.
|
|
11
|
-
// Backed by snapshot state: writes recompose text automatically.
|
|
12
|
-
object KitFontScale {
|
|
13
|
-
var userScaleRate by mutableStateOf(1f)
|
|
14
|
-
private set
|
|
15
|
-
var useOSScaleRate by mutableStateOf(true)
|
|
16
|
-
private set
|
|
17
|
-
|
|
18
|
-
fun update(userScaleRate: Float, useOSScaleRate: Boolean) {
|
|
19
|
-
this.userScaleRate = userScaleRate
|
|
20
|
-
this.useOSScaleRate = useOSScaleRate
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
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 {
|
|
10
|
-
public static let shared = FontScaleStore()
|
|
11
|
-
|
|
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
|
-
}
|
|
32
|
-
|
|
33
|
-
public func update(userScaleRate: CGFloat, useOSScaleRate: Bool) {
|
|
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) }
|
|
57
|
-
}
|
|
58
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
|
|
3
|
-
/// Design-system rollout flag for the SwiftUI kit, mirroring Compose's `KitDesignSystem`.
|
|
4
|
-
/// The kit is sandboxed and can't read the host config, so the host writes `update(_:)` once at
|
|
5
|
-
/// startup from `DesignSystemConfig`. Startup-only and non-reactive, like `FontScaleStore` was
|
|
6
|
-
/// before it gained live updates: there is no SwiftUI navigation container to re-render from.
|
|
7
|
-
public final class KitDesignSystem {
|
|
8
|
-
public static let shared = KitDesignSystem()
|
|
9
|
-
|
|
10
|
-
public private(set) var isWhiteList: Bool = false
|
|
11
|
-
|
|
12
|
-
private init() {}
|
|
13
|
-
|
|
14
|
-
public func update(isWhiteList: Bool) {
|
|
15
|
-
self.isWhiteList = isWhiteList
|
|
16
|
-
}
|
|
17
|
-
}
|