@momo-kits/native-kits 0.162.2-test.5-debug → 0.162.2-test.6-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/compose/build.gradle.kts +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +4 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +4 -4
- package/gradle.properties +1 -1
- package/ios/Application/ApplicationEnvironment.swift +2 -0
- package/ios/Application/FontScaleStore.swift +77 -0
- package/ios/Typography/Text.swift +12 -3
- package/ios/Typography/Typography.swift +11 -5
- package/package.json +1 -1
package/compose/build.gradle.kts
CHANGED
|
@@ -112,5 +112,5 @@ data class FontScaleConfig(
|
|
|
112
112
|
val userScaleRate: Float? = null,
|
|
113
113
|
)
|
|
114
114
|
|
|
115
|
-
// Single source of truth:
|
|
115
|
+
// Single source of truth: holds both the OS flag and the user-adjusted rate.
|
|
116
116
|
val LocalFontScaleConfig = staticCompositionLocalOf { FontScaleConfig() }
|
|
@@ -38,10 +38,10 @@ const val MAX_DEVICE_SCALE = 5
|
|
|
38
38
|
|
|
39
39
|
@Composable
|
|
40
40
|
fun scaleSize(size: Float, userScaleRate: Float? = null): Float {
|
|
41
|
-
// single source of truth: useOSFontScale + userScaleRate
|
|
41
|
+
// single source of truth: useOSFontScale + userScaleRate both read from LocalFontScaleConfig
|
|
42
42
|
val fontScaleConfig = LocalFontScaleConfig.current
|
|
43
43
|
val useOSFontScale = fontScaleConfig.useOSFontScale
|
|
44
|
-
// param userScaleRate
|
|
44
|
+
// param userScaleRate overrides config; if absent -> config.userScaleRate -> 1f (no scaling)
|
|
45
45
|
val customRate = userScaleRate ?: fontScaleConfig.userScaleRate ?: 1f
|
|
46
46
|
|
|
47
47
|
val deviceWidth = getScreenDimensions().width
|
|
@@ -58,8 +58,8 @@ fun scaleSize(size: Float, userScaleRate: Float? = null): Float {
|
|
|
58
58
|
min(deviceScale * fontSizeScaleDevice, fontSizeScaleDevice + MAX_DEVICE_SCALE)
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
// useOSFontScale
|
|
62
|
-
//
|
|
61
|
+
// useOSFontScale on -> apply the OS scale, ignore the user's custom rate.
|
|
62
|
+
// off -> use the user's custom rate. Both hard-capped at MAX_FONT_SCALE (150%).
|
|
63
63
|
val appliedRate = if (useOSFontScale) fontScale else customRate
|
|
64
64
|
if (appliedRate > 1f) {
|
|
65
65
|
fontSizeScaleOS = min(appliedRate * fontSizeScaleOS, fontSizeScaleOS * MAX_FONT_SCALE)
|
|
@@ -38,11 +38,11 @@ fun NavigationContainer(
|
|
|
38
38
|
val parentContext = ApplicationContext.current
|
|
39
39
|
val mergedContext = MiniAppContext.merge(parentContext, applicationContext)
|
|
40
40
|
|
|
41
|
-
// FontScaleConfig host
|
|
41
|
+
// FontScaleConfig the host writes to observer storage (single source of truth when present).
|
|
42
42
|
var observerFontScaleConfig by remember { mutableStateOf<FontScaleConfig?>(null) }
|
|
43
43
|
LaunchedEffect(maxApi) {
|
|
44
44
|
val api = maxApi ?: return@LaunchedEffect
|
|
45
|
-
// getDataObserver = snapshot
|
|
45
|
+
// getDataObserver = snapshot at mount; observer = subscribe to later host writes.
|
|
46
46
|
runCatching {
|
|
47
47
|
api.getDataObserver(FONT_SCALE_OBSERVER_KEY) { data ->
|
|
48
48
|
parseFontScaleConfig(data)?.let { observerFontScaleConfig = it }
|
|
@@ -162,10 +162,10 @@ val LocalMaxApi = staticCompositionLocalOf<IMaxApi?> {
|
|
|
162
162
|
error("No MaxApi provided")
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
//
|
|
165
|
+
// Observer storage key the host app writes FontScaleConfig to. Lowercased natively.
|
|
166
166
|
private const val FONT_SCALE_OBSERVER_KEY = "font_scale_config"
|
|
167
167
|
|
|
168
|
-
// Host
|
|
168
|
+
// Host returns object {useOSFontScale, userScaleRate}; guard against envelope wrapping / type mismatch from marshaling.
|
|
169
169
|
private fun parseFontScaleConfig(raw: Map<String, Any?>?): FontScaleConfig? {
|
|
170
170
|
if (raw.isNullOrEmpty()) return null
|
|
171
171
|
@Suppress("UNCHECKED_CAST")
|
package/gradle.properties
CHANGED
|
@@ -46,5 +46,7 @@ public class ApplicationEnvironment: ObservableObject {
|
|
|
46
46
|
self.applicationContext = applicationContext
|
|
47
47
|
self.composeApi = composeApi
|
|
48
48
|
self.config = config
|
|
49
|
+
// Load + observe the host font-scale config into the global store read by scaleSize/appFont.
|
|
50
|
+
FontScaleStore.shared.bind(composeApi)
|
|
49
51
|
}
|
|
50
52
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FontScaleStore.swift
|
|
3
|
+
// Momo Design System (SwiftUI)
|
|
4
|
+
//
|
|
5
|
+
// Global font-scale config read by scaleSize/appFont. SwiftUI counterpart of the
|
|
6
|
+
// Compose LocalFontScaleConfig and the RN ScaleSizeContext. Populated from the host's
|
|
7
|
+
// `font_scale_config` observer-storage key via the KitComposeApi bridge.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
import SwiftUI
|
|
11
|
+
|
|
12
|
+
public struct FontScaleConfig {
|
|
13
|
+
public var useOSFontScale: Bool
|
|
14
|
+
public var userScaleRate: CGFloat?
|
|
15
|
+
|
|
16
|
+
public init(useOSFontScale: Bool = false, userScaleRate: CGFloat? = nil) {
|
|
17
|
+
self.useOSFontScale = useOSFontScale
|
|
18
|
+
self.userScaleRate = userScaleRate
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// Single source of truth for the font scale. `scaleSize`/`appFont` read `shared.config`.
|
|
23
|
+
/// Views that want live updates observe this object (e.g. MomoText).
|
|
24
|
+
public final class FontScaleStore: ObservableObject {
|
|
25
|
+
public static let shared = FontScaleStore()
|
|
26
|
+
|
|
27
|
+
@Published public private(set) var config = FontScaleConfig()
|
|
28
|
+
|
|
29
|
+
private var didBind = false
|
|
30
|
+
private var observerId: String?
|
|
31
|
+
|
|
32
|
+
private init() {}
|
|
33
|
+
|
|
34
|
+
public func update(_ newConfig: FontScaleConfig) {
|
|
35
|
+
if Thread.isMainThread {
|
|
36
|
+
config = newConfig
|
|
37
|
+
} else {
|
|
38
|
+
DispatchQueue.main.async { [weak self] in self?.config = newConfig }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Load the current config + subscribe to live changes through the host MaxApi bridge.
|
|
43
|
+
/// Safe no-op when no bridge is provided (config stays at default = no scaling).
|
|
44
|
+
public func bind(_ api: KitComposeApi?) {
|
|
45
|
+
guard let api = api, !didBind else { return }
|
|
46
|
+
didBind = true
|
|
47
|
+
|
|
48
|
+
// snapshot at mount
|
|
49
|
+
api.requestCallback(funcName: "getFontScaleConfig", params: nil) { [weak self] response in
|
|
50
|
+
self?.apply(response)
|
|
51
|
+
}
|
|
52
|
+
// live: re-read on every host write to the key
|
|
53
|
+
observerId = api.request(funcName: "observer", params: ["font_scale_config"]) { [weak self] response in
|
|
54
|
+
self?.apply(response)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private func apply(_ response: String) {
|
|
59
|
+
guard let parsed = FontScaleStore.parse(response) else { return }
|
|
60
|
+
update(parsed)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// Tolerates the MaxApi envelope {"status":..,"response":{..}} as well as a bare config object.
|
|
64
|
+
static func parse(_ response: String) -> FontScaleConfig? {
|
|
65
|
+
guard
|
|
66
|
+
let data = response.data(using: .utf8),
|
|
67
|
+
let json = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
|
|
68
|
+
else { return nil }
|
|
69
|
+
|
|
70
|
+
let obj = (json["response"] as? [String: Any]) ?? (json["data"] as? [String: Any]) ?? json
|
|
71
|
+
guard obj["useOSFontScale"] != nil || obj["userScaleRate"] != nil else { return nil }
|
|
72
|
+
|
|
73
|
+
let useOS = (obj["useOSFontScale"] as? Bool) ?? false
|
|
74
|
+
let rate = (obj["userScaleRate"] as? NSNumber).map { CGFloat($0.doubleValue) }
|
|
75
|
+
return FontScaleConfig(useOSFontScale: useOS, userScaleRate: rate)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import SwiftUI
|
|
2
2
|
|
|
3
3
|
public func scaleSize(_ size: CGFloat, _ scaleRate: CGFloat? = nil) -> CGFloat {
|
|
4
|
+
// Single source of truth: useOSFontScale + userScaleRate from the host config.
|
|
5
|
+
// `scaleRate` param keeps its existing meaning: a hard cap override (default 1.5).
|
|
6
|
+
let config = FontScaleStore.shared.config
|
|
4
7
|
let defaultScreenSize: CGFloat = 375
|
|
5
8
|
let maxFontScale: CGFloat = scaleRate ?? 1.5
|
|
6
9
|
let maxDeviceScale: CGFloat = 5
|
|
@@ -10,7 +13,7 @@ public func scaleSize(_ size: CGFloat, _ scaleRate: CGFloat? = nil) -> CGFloat {
|
|
|
10
13
|
|
|
11
14
|
let defaultFont = UIFont.systemFont(ofSize: UIFont.labelFontSize)
|
|
12
15
|
let scaledFont = UIFontMetrics.default.scaledFont(for: defaultFont)
|
|
13
|
-
let
|
|
16
|
+
let osFontScale = scaledFont.pointSize / defaultFont.pointSize
|
|
14
17
|
|
|
15
18
|
var fontSizeDeviceScale = size
|
|
16
19
|
var fontSizeOSScale = size
|
|
@@ -20,8 +23,11 @@ public func scaleSize(_ size: CGFloat, _ scaleRate: CGFloat? = nil) -> CGFloat {
|
|
|
20
23
|
fontSizeDeviceScale = min(fontSizeDeviceScale * deviceScale, fontSizeDeviceScale + maxDeviceScale)
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
// useOSFontScale on -> follow OS Dynamic Type; off -> use the user rate (default 1 = no scaling).
|
|
27
|
+
// Both capped at maxFontScale. Mirrors Compose scaleSize / RN useScaleSize.
|
|
28
|
+
let appliedRate = config.useOSFontScale ? osFontScale : (config.userScaleRate ?? 1)
|
|
29
|
+
if appliedRate > 1 {
|
|
30
|
+
fontSizeOSScale = min(fontSizeOSScale * appliedRate, fontSizeOSScale * maxFontScale)
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
return max(fontSizeDeviceScale, fontSizeOSScale)
|
|
@@ -118,6 +124,8 @@ public struct MomoText: View {
|
|
|
118
124
|
private let content: String
|
|
119
125
|
private let typography: TypographyStyle
|
|
120
126
|
private let color: Color
|
|
127
|
+
// Observe the global store so the text re-renders live when the host changes the config.
|
|
128
|
+
@ObservedObject private var fontScale = FontScaleStore.shared
|
|
121
129
|
|
|
122
130
|
public init(
|
|
123
131
|
_ content: String,
|
|
@@ -130,6 +138,7 @@ public struct MomoText: View {
|
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
public var body: some View {
|
|
141
|
+
_ = fontScale.config
|
|
133
142
|
let text = SwiftUI.Text(content)
|
|
134
143
|
.font(.system(size: scaleSize(typography.fontSize), weight: typography.fontWeight))
|
|
135
144
|
.foregroundColor(color)
|
|
@@ -2,16 +2,21 @@ import SwiftUI
|
|
|
2
2
|
|
|
3
3
|
public extension Font {
|
|
4
4
|
static func appFont(size: CGFloat) -> Font {
|
|
5
|
+
// Single source of truth: useOSFontScale + userScaleRate from the host config.
|
|
6
|
+
// NOTE: the static Font.xxx constants below are evaluated once at type load, so they
|
|
7
|
+
// capture the config at first access. Live updates apply to scaleSize()/MomoText and
|
|
8
|
+
// any fresh appFont(size:) call, not to the cached static constants.
|
|
9
|
+
let config = FontScaleStore.shared.config
|
|
5
10
|
let defaultScreenSize: CGFloat = 375
|
|
6
11
|
let maxFontScale: CGFloat = 1.5
|
|
7
12
|
let maxDeviceScale: CGFloat = 5
|
|
8
|
-
|
|
13
|
+
|
|
9
14
|
let deviceWidth = UIScreen.main.bounds.width
|
|
10
15
|
let deviceScale = deviceWidth / defaultScreenSize
|
|
11
|
-
|
|
16
|
+
|
|
12
17
|
let defaultFont = UIFont.systemFont(ofSize: UIFont.labelFontSize)
|
|
13
18
|
let scaledFont = UIFontMetrics.default.scaledFont(for: defaultFont)
|
|
14
|
-
let
|
|
19
|
+
let osFontScale = scaledFont.pointSize / defaultFont.pointSize
|
|
15
20
|
|
|
16
21
|
var fontSize = size
|
|
17
22
|
|
|
@@ -19,8 +24,9 @@ public extension Font {
|
|
|
19
24
|
fontSize = min(fontSize * deviceScale, fontSize + maxDeviceScale)
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
let appliedRate = config.useOSFontScale ? osFontScale : (config.userScaleRate ?? 1)
|
|
28
|
+
if appliedRate > 1 {
|
|
29
|
+
fontSize = min(fontSize * appliedRate, fontSize * maxFontScale)
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
return Font.system(size: fontSize)
|