@momo-kits/native-kits 0.168.1-beta.1-debug → 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.
Files changed (49) hide show
  1. package/CLAUDE.md +7 -5
  2. package/compose/build.gradle.kts +1 -1
  3. package/compose/compose.podspec +1 -1
  4. package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +4 -3
  5. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +1 -1
  6. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -14
  7. package/compose/src/commonMain/kotlin/vn/momo/kits/application/DesignSystemKits.kt +62 -0
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +2 -1
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +18 -14
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +9 -12
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +2 -2
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +2 -3
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +124 -59
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +1 -1
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +3 -1
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +2 -2
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +10 -3
  18. package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +3 -1
  19. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +54 -10
  20. package/gradle.properties +1 -1
  21. package/ios/Application/ApplicationEnvironment.swift +7 -6
  22. package/ios/Application/DesignSystemKits.swift +100 -0
  23. package/ios/Application/DesignSystemKitsPayload.swift +68 -0
  24. package/ios/Badge/BadgeRibbon.swift +8 -1
  25. package/ios/Extensions/Color++.swift +2 -4
  26. package/ios/Input/Input.swift +13 -3
  27. package/ios/Input/InputPhoneNumber.swift +1 -3
  28. package/ios/Input/InputSearch.swift +1 -2
  29. package/ios/Input/InputTextArea.swift +5 -3
  30. package/ios/InputMoney/InputMoney.swift +6 -7
  31. package/ios/Layout/ScreenLayout.swift +46 -11
  32. package/ios/Navigation/BottomTab/DuoBarCompression.swift +2 -3
  33. package/ios/Navigation/BottomTab/DuoTabBar.swift +94 -69
  34. package/ios/Navigation/Component/HeaderBackground.swift +22 -1
  35. package/ios/Navigation/StackScreen.swift +3 -7
  36. package/ios/Popup/PopupLayout.swift +0 -2
  37. package/ios/Stepper/Stepper.swift +1 -2
  38. package/ios/Template/TrustBanner/TrustBanner.swift +4 -2
  39. package/ios/Typography/FontScale.swift +14 -11
  40. package/ios/Typography/Text.swift +86 -12
  41. package/ios/Typography/Typography.swift +2 -2
  42. package/ios/native-kits.podspec +1 -1
  43. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  44. package/package.json +1 -1
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  47. package/ios/Typography/FontScaleStore.swift +0 -58
  48. package/ios/Typography/KitDesignSystem.swift +0 -17
  49. package/local.properties +0 -8
@@ -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
- // glass shows once the banner has scrolled under the header
407
- Color.clear.modifier(DuoBarMaterial(
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
- Color.clear
461
- .modifier(DuoBarMaterial(shape: Rectangle(), visible: scrollOffset > 0))
457
+ DuoHeaderBar(scrollOffset: scrollOffset)
462
458
  .edgesIgnoringSafeArea([.top, .horizontal])
463
459
  )
464
460
  .offset(y: -headerHide)
@@ -5,8 +5,6 @@ import SwiftUI
5
5
  private let duoPopupMaxWidth: CGFloat = 440
6
6
 
7
7
  extension ScreenLayout {
8
- var isDuoDevice: Bool { isIPhoneDuoDevice(windowSize: CGSize(width: width, height: height)) }
9
-
10
8
  /// Card width cap (Compose `popupMaxWidth`); unbounded off iPhone Duo, where the popup's own padding sets the width.
11
9
  var popupMaxWidth: CGFloat {
12
10
  isDuoDevice ? duoPopupMaxWidth : .infinity
@@ -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
- .font(.system(size: fontScale(12)))
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
- applicationEnvironment.config?.trustBanner ?? defaultBanner
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() {
@@ -52,7 +52,7 @@ public struct FontScale: Equatable {
52
52
  }
53
53
 
54
54
  /// A host-pushed mini-app context wins per field, matching Compose's
55
- /// `context?.useOSScaleRate ?: KitFontScale.useOSScaleRate`.
55
+ /// `context?.useOSScaleRate ?: DesignSystemKits.getConfigs().useOSScaleRate`.
56
56
  public func overriding(userScaleRate: CGFloat?, useOSScaleRate: Bool?) -> FontScale {
57
57
  guard userScaleRate != nil || useOSScaleRate != nil else { return self }
58
58
  return FontScale(
@@ -66,7 +66,7 @@ public struct FontScale: Equatable {
66
66
  private struct FontScaleKey: EnvironmentKey {
67
67
  /// Computed, never stored: a bare `UIHostingController` root that never got `.momoFontScale()`
68
68
  /// still reads the live store instead of a value frozen at first access.
69
- static var defaultValue: FontScale { FontScaleStore.shared.snapshot }
69
+ static var defaultValue: FontScale { DesignSystemKits.shared.fontScale }
70
70
  }
71
71
 
72
72
  public extension EnvironmentValues {
@@ -77,9 +77,10 @@ public extension EnvironmentValues {
77
77
  }
78
78
 
79
79
  public extension View {
80
- /// Injects a Dynamic-Type-observing font scale and re-renders this subtree on change.
80
+ /// Injects a Dynamic-Type-observing font scale plus `\.designSystemKitsConfigs` (whitelist, remote config)
81
+ /// and re-renders this subtree on change.
81
82
  /// Use at every `UIHostingController` root that is not inside a `NavigationContainer`.
82
- func momoFontScale(_ store: FontScaleStore = .shared, context: MiniAppContext? = nil) -> some View {
83
+ func momoFontScale(_ store: DesignSystemKits = .shared, context: MiniAppContext? = nil) -> some View {
83
84
  modifier(MomoFontScaleModifier(store: store, context: context))
84
85
  }
85
86
  }
@@ -87,17 +88,19 @@ public extension View {
87
88
  /// `@ObservedObject` is load-bearing, same as `MomoLocalizeModifier`: `@Environment(\.momoFontScale)`
88
89
  /// only invalidates when the boxed snapshot changes, which requires *this* body to re-run on publish.
89
90
  private struct MomoFontScaleModifier: ViewModifier {
90
- @ObservedObject var store: FontScaleStore
91
+ @ObservedObject var store: DesignSystemKits
91
92
  let context: MiniAppContext?
92
93
 
93
94
  func body(content: Content) -> some View {
94
- content.environment(
95
- \.momoFontScale,
96
- store.snapshot.overriding(
97
- userScaleRate: context?.userScaleRate,
98
- useOSScaleRate: context?.useOSScaleRate
95
+ content
96
+ .environment(
97
+ \.momoFontScale,
98
+ store.fontScale.overriding(
99
+ userScaleRate: context?.userScaleRate,
100
+ useOSScaleRate: context?.useOSScaleRate
101
+ )
99
102
  )
100
- )
103
+ .environment(\.designSystemKitsConfigs, store.getConfigs())
101
104
  }
102
105
  }
103
106
  // 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
- FontScaleStore.shared.snapshot(size)
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 && !KitDesignSystem.shared.isWhiteList) { return .system }
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(size: CGFloat, weight: Font.Weight, family: String, fallbackFamily: String? = nil) -> Font {
131
- switch momoFontFace(family: family, weight: weight) {
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(size: fontScale(fontSize), weight: TypographyStyle.headerXsSemibold.fontWeight, family: fontFamily ?? theme.font, fallbackFamily: theme.font)
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(size: fontScale(fontSize), family: fontFamily ?? theme.font, fallbackFamily: theme.font))
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 `FontScaleStore` snapshot at each access. The
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
- FontScaleStore.shared.snapshot.font(size: size)
18
+ DesignSystemKits.shared.fontScale.font(size: size)
19
19
  }
20
20
 
21
21
  // New supported typography styles
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'MoMoUIKits'
3
- s.version = '0.168.1-beta.1'
3
+ s.version = '0.167.1-beta.22'
4
4
  s.summary = 'MoMoUIKits for iOS'
5
5
  s.homepage = 'https://momo.vn'
6
6
  s.license = { :type => 'MIT' }
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'MoMoUIKitsDemo'
3
- s.version = '0.168.1-beta.1'
3
+ s.version = '0.167.1-beta.22'
4
4
  s.summary = 'Demo browser for MoMoUIKits SwiftUI components'
5
5
  s.homepage = 'https://momo.vn'
6
6
  s.license = { :type => 'MIT' }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.168.1-beta.1-debug",
3
+ "version": "0.168.1-beta.10-debug",
4
4
  "private": false,
5
5
  "dependeNavigationncies": {},
6
6
  "devDependencies": {},
@@ -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
- }
package/local.properties DELETED
@@ -1,8 +0,0 @@
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