@momo-kits/native-kits 0.162.2-sp.7-debug → 0.162.2-sp.8-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/settings.local.json +15 -1
- package/CLAUDE.md +78 -0
- package/compose/build.gradle.kts +12 -31
- package/compose/build.gradle.kts.backup +11 -30
- package/compose/compose.podspec +1 -1
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +2 -7
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +8 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +6 -24
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +4 -19
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +1 -10
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +6 -17
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +6 -101
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +8 -19
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +8 -33
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +22 -25
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +27 -29
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +3 -26
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +30 -31
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +3 -13
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -5
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +3 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +3 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +11 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +42 -65
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +69 -26
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +1 -5
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +84 -59
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +13 -71
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +10 -51
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +6 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +4 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +16 -75
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +5 -26
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +3 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +16 -40
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +3 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -28
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +2 -13
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +4 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +4 -10
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +3 -28
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +8 -47
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +4 -25
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +2 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +1 -17
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +1 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +50 -111
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +1 -5
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +1 -5
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +1 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +4 -96
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +5 -7
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +2 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +9 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +9 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +8 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +22 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +11 -20
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -5
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +94 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +3 -7
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +9 -15
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +36 -24
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +48 -33
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +67 -61
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +6 -13
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +13 -31
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +6 -24
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +11 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +9 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +3 -13
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +3 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +1 -40
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -8
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +2 -17
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +3 -3
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +2 -3
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -2
- package/gradle/libs.versions.toml +2 -0
- package/gradle.properties +1 -1
- package/ios/Application/ApplicationEnvironment.swift +4 -2
- package/ios/Application/Components.swift +16 -0
- package/ios/Application/Localize.swift +277 -0
- package/ios/Application/MaxApi.swift +18 -0
- package/ios/Application/Navigation/BottomTab/BottomTab.swift +1 -0
- package/ios/Application/Navigation/NavigationContainer.swift +27 -3
- package/ios/Application/Navigation/Navigator.swift +50 -29
- package/ios/Application/Navigation/Overplay/BottomSheet.swift +87 -19
- package/ios/Application/Navigation/Overplay/ModalScreen.swift +6 -0
- package/ios/Application/Navigation/Tracking/ScreenTracker.swift +117 -0
- package/ios/Application/Screen.swift +1 -1
- package/ios/Application/StackScreen.swift +49 -1
- package/ios/Badge/BadgeRibbon.swift +80 -0
- package/ios/Button/Button.swift +68 -12
- package/ios/Carousel/Carousel.swift +16 -1
- package/ios/Checkbox/Checkbox.swift +14 -20
- package/ios/Chip/Chip.swift +35 -26
- package/ios/Collapse/Collapse.swift +11 -1
- package/ios/Colors+Radius+Spacing/Colors.swift +9 -6
- package/ios/Colors+Radius+Spacing/Spacing.swift +3 -1
- package/ios/DateTimePicker/DateTimePicker.swift +4 -2
- package/ios/DateTimePicker/WheelPicker.swift +7 -3
- package/ios/Extensions/ActiveOpacityButtonStyle.swift +14 -0
- package/ios/Extensions/Color++.swift +5 -2
- package/ios/Icon/Icon.swift +5 -1
- package/ios/IconButton/IconButton.swift +35 -7
- package/ios/Image/Image.swift +16 -3
- package/ios/Information/Information.swift +3 -1
- package/ios/Input/ErrorView.swift +3 -1
- package/ios/Input/Input.swift +31 -8
- package/ios/Input/InputPhoneNumber.swift +14 -3
- package/ios/Input/InputSearch.swift +7 -1
- package/ios/Input/InputTextArea.swift +78 -34
- package/ios/InputDropDown/InputDropDown.swift +4 -1
- package/ios/InputMoney/InputMoney.swift +16 -4
- package/ios/PARITY_AUDIT.md +641 -0
- package/ios/Popup/PopupDisplay.swift +3 -4
- package/ios/Popup/PopupNotify.swift +3 -4
- package/ios/Popup/PopupPromotion.swift +16 -8
- package/ios/ProgressInfo/ProgressInfo.swift +4 -2
- package/ios/Radio/Radio.swift +12 -13
- package/ios/Rating/Rating.swift +1 -1
- package/ios/Stepper/Stepper.swift +2 -2
- package/ios/Steps/Steps.swift +22 -6
- package/ios/SuggestAction/SuggestAction.swift +5 -3
- package/ios/Swipeable/SwipeCell.swift +5 -3
- package/ios/Switch/Switch.swift +53 -28
- package/ios/TabView/TabView.swift +18 -14
- package/ios/Template/TrustBanner/TrustBanner.swift +6 -7
- package/ios/Tooltip/Tooltip.swift +3 -0
- package/ios/Typography/FontScaleStore.swift +19 -0
- package/ios/Typography/Text.swift +3 -0
- package/ios/Typography/Typography.swift +8 -5
- package/ios/Uploader/Uploader.swift +0 -9
- package/ios/native-kits.podspec +1 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/package.json +1 -1
- package/ios/Colors+Radius+Spacing/MoMoUIKitsResources.swift +0 -2
|
@@ -28,29 +28,23 @@ public struct Checkbox: View{
|
|
|
28
28
|
public var body: some View {
|
|
29
29
|
HStack(spacing: Spacing.S) {
|
|
30
30
|
// Checkbox
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
SwiftUI.Button(action: onCheck) {
|
|
32
|
+
ZStack {
|
|
33
|
+
RoundedRectangle(cornerRadius: Radius.XS)
|
|
34
|
+
.fill(backgroundColor)
|
|
35
|
+
.frame(width: 20, height: 20)
|
|
36
|
+
.overlay(
|
|
37
|
+
RoundedRectangle(cornerRadius: Radius.XS)
|
|
38
|
+
.stroke(borderColor, lineWidth: Spacing.XXS)
|
|
39
|
+
)
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
.scaledToFit()
|
|
44
|
-
.foregroundColor(Colors.black01)
|
|
45
|
-
.frame(width: 12, height: 12)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.onTapGesture {
|
|
50
|
-
if !disabled {
|
|
51
|
-
onCheck()
|
|
41
|
+
if checked {
|
|
42
|
+
Icon(source: indeterminate ? "navigation_minus" : "ic_checked", size: 20, color: Colors.black01)
|
|
43
|
+
}
|
|
52
44
|
}
|
|
53
45
|
}
|
|
46
|
+
.buttonStyle(ActiveOpacityButtonStyle(activeOpacity: 0.8))
|
|
47
|
+
.disabled(disabled)
|
|
54
48
|
|
|
55
49
|
// Title
|
|
56
50
|
if !title.isEmpty {
|
package/ios/Chip/Chip.swift
CHANGED
|
@@ -52,6 +52,13 @@ public struct Chip: View {
|
|
|
52
52
|
|
|
53
53
|
public var body: some View {
|
|
54
54
|
let dims = (size == .small) ? Self.Small : Self.Large
|
|
55
|
+
// Mirrors Compose's `scaleSize(...)` on every chip dimension so it responds
|
|
56
|
+
// to Dynamic Type scaling the same way the rest of the kit does.
|
|
57
|
+
let height = scaleSize(dims.height)
|
|
58
|
+
let horizontal = scaleSize(dims.horizontal)
|
|
59
|
+
let iconSize = scaleSize(dims.iconSize)
|
|
60
|
+
let iconSpacing = scaleSize(dims.iconSpacing)
|
|
61
|
+
let radius = scaleSize(Radius.L)
|
|
55
62
|
|
|
56
63
|
let bg: Color = {
|
|
57
64
|
if selected { return theme.colors.background.selected }
|
|
@@ -63,36 +70,38 @@ public struct Chip: View {
|
|
|
63
70
|
let leftTint: Color = selected ? theme.colors.primary : iconLeftTint ?? theme.colors.text.default
|
|
64
71
|
let rightTint: Color = selected ? theme.colors.primary : iconRightTint ?? theme.colors.text.default
|
|
65
72
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
SwiftUI.Button(action: onClick) {
|
|
74
|
+
HStack(spacing: iconSpacing) {
|
|
75
|
+
if let iconLeft {
|
|
76
|
+
Icon(source: iconLeft, size: iconSize, color: leftTint)
|
|
77
|
+
}
|
|
70
78
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
if let text = label, !text.isEmpty {
|
|
80
|
+
MomoText(text, typography: (size == .large) ? .labelDefaultMedium : .labelSMedium, color: textColor)
|
|
81
|
+
.lineLimit(1)
|
|
82
|
+
}
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
.padding(.horizontal, dims.horizontal)
|
|
81
|
-
.frame(height: dims.height)
|
|
82
|
-
.background(
|
|
83
|
-
RoundedRectangle(cornerRadius: Radius.L, style: .continuous)
|
|
84
|
-
.fill(bg)
|
|
85
|
-
)
|
|
86
|
-
.overlay(
|
|
87
|
-
Group {
|
|
88
|
-
if selected {
|
|
89
|
-
RoundedRectangle(cornerRadius: Radius.L, style: .continuous)
|
|
90
|
-
.stroke(theme.colors.secondary, lineWidth: 2)
|
|
84
|
+
if let iconRight {
|
|
85
|
+
Icon(source: iconRight, size: iconSize, color: rightTint)
|
|
91
86
|
}
|
|
92
87
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
88
|
+
.padding(.horizontal, horizontal)
|
|
89
|
+
.frame(height: height)
|
|
90
|
+
.background(
|
|
91
|
+
RoundedRectangle(cornerRadius: radius, style: .continuous)
|
|
92
|
+
.fill(bg)
|
|
93
|
+
)
|
|
94
|
+
.overlay(
|
|
95
|
+
Group {
|
|
96
|
+
if selected {
|
|
97
|
+
RoundedRectangle(cornerRadius: radius, style: .continuous)
|
|
98
|
+
.stroke(theme.colors.secondary, lineWidth: 2)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
.contentShape(RoundedRectangle(cornerRadius: radius, style: .continuous))
|
|
103
|
+
}
|
|
104
|
+
.buttonStyle(ActiveOpacityButtonStyle())
|
|
96
105
|
.modifier(ChipAccessibilityModifier(accessibilityLabel: accessibilityLabel))
|
|
97
106
|
}
|
|
98
107
|
}
|
|
@@ -30,6 +30,7 @@ public struct Collapse<Content: View>: View {
|
|
|
30
30
|
private let useBackgroundCollapseButton: Bool
|
|
31
31
|
private let backgroundColor: Color?
|
|
32
32
|
private let titleColor: Color?
|
|
33
|
+
private let accessibilityId: String?
|
|
33
34
|
private let onPress: ((Bool) -> Void)?
|
|
34
35
|
private let content: (() -> Content)?
|
|
35
36
|
|
|
@@ -49,6 +50,7 @@ public struct Collapse<Content: View>: View {
|
|
|
49
50
|
useBackgroundCollapseButton: Bool = false,
|
|
50
51
|
backgroundColor: Color? = nil,
|
|
51
52
|
titleColor: Color? = nil,
|
|
53
|
+
accessibilityId: String? = nil,
|
|
52
54
|
onPress: ((Bool) -> Void)? = nil,
|
|
53
55
|
@ViewBuilder content: @escaping () -> Content
|
|
54
56
|
) {
|
|
@@ -65,11 +67,17 @@ public struct Collapse<Content: View>: View {
|
|
|
65
67
|
self.useBackgroundCollapseButton = useBackgroundCollapseButton
|
|
66
68
|
self.backgroundColor = backgroundColor
|
|
67
69
|
self.titleColor = titleColor
|
|
70
|
+
self.accessibilityId = accessibilityId
|
|
68
71
|
self.onPress = onPress
|
|
69
72
|
self.content = content
|
|
70
73
|
_expanded = State(initialValue: expandDefault)
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
// Mirrors Kotlin's `accessibilityId ?: "Collapse/$title"`.
|
|
77
|
+
private var automationId: String {
|
|
78
|
+
accessibilityId ?? "Collapse/\(title)"
|
|
79
|
+
}
|
|
80
|
+
|
|
73
81
|
private var resolvedBackgroundColor: Color {
|
|
74
82
|
backgroundColor ?? theme.colors.background.surface
|
|
75
83
|
}
|
|
@@ -97,6 +105,7 @@ public struct Collapse<Content: View>: View {
|
|
|
97
105
|
.frame(maxWidth: .infinity)
|
|
98
106
|
.background(resolvedBackgroundColor)
|
|
99
107
|
.clipShape(RoundedRectangle(cornerRadius: Radius.S, style: .continuous))
|
|
108
|
+
.accessibilityIdentifier(automationId)
|
|
100
109
|
.overlay(
|
|
101
110
|
showBorder
|
|
102
111
|
? RoundedRectangle(cornerRadius: Radius.S, style: .continuous)
|
|
@@ -159,7 +168,8 @@ public struct Collapse<Content: View>: View {
|
|
|
159
168
|
.frame(maxWidth: .infinity)
|
|
160
169
|
.contentShape(Rectangle())
|
|
161
170
|
}
|
|
162
|
-
.buttonStyle(
|
|
171
|
+
.buttonStyle(ActiveOpacityButtonStyle())
|
|
172
|
+
.accessibilityIdentifier("\(automationId)|touch")
|
|
163
173
|
}
|
|
164
174
|
|
|
165
175
|
private var chevron: some View {
|
|
@@ -13,7 +13,7 @@ public enum Colors {
|
|
|
13
13
|
public static let textSecondary = Color.momoAsset("TextSecondary")
|
|
14
14
|
public static let card = Color.momoAsset("Card")
|
|
15
15
|
public static let error = Color.momoAsset("Error")
|
|
16
|
-
public static let info =
|
|
16
|
+
public static let info = blue03
|
|
17
17
|
public static let success = Color.momoAsset("Success")
|
|
18
18
|
|
|
19
19
|
public static let black20 = Color(hex: "000000")
|
|
@@ -49,15 +49,18 @@ public enum Colors {
|
|
|
49
49
|
public static let pink03 = Color(hex: "eb2f96")
|
|
50
50
|
public static let pink02 = Color(hex: "d42a87")
|
|
51
51
|
public static let pink01 = Color(hex: "bc2678")
|
|
52
|
+
public static let pinkMoMoBranding = Color(hex: "a50064")
|
|
52
53
|
|
|
53
54
|
|
|
54
|
-
public static let
|
|
55
|
-
public static let
|
|
55
|
+
public static let violet11 = Color(hex: "fcf8fe")
|
|
56
|
+
public static let violet11Stroke = Color(hex: "dfe1e5")
|
|
57
|
+
public static let violet10 = Color(hex: "faf4fe")
|
|
58
|
+
public static let violet09 = Color(hex: "f4e9fd")
|
|
56
59
|
public static let violet08 = Color(hex: "ead4fc")
|
|
57
60
|
public static let violet07 = Color(hex: "d5aaf9")
|
|
58
|
-
public static let violet06 = Color(hex: "
|
|
59
|
-
public static let violet05 = Color(hex: "
|
|
60
|
-
public static let violet04 = Color(hex: "
|
|
61
|
+
public static let violet06 = Color(hex: "c07ff6")
|
|
62
|
+
public static let violet05 = Color(hex: "ab55f3")
|
|
63
|
+
public static let violet04 = Color(hex: "a03ff1")
|
|
61
64
|
public static let violet03 = Color(hex: "962af0")
|
|
62
65
|
public static let violet02 = Color(hex: "8726d8")
|
|
63
66
|
public static let violet01 = Color(hex: "7822c0")
|
|
@@ -8,5 +8,7 @@ public enum Spacing {
|
|
|
8
8
|
public static let L: CGFloat = 16
|
|
9
9
|
public static let XL: CGFloat = 24
|
|
10
10
|
public static let XXL: CGFloat = 32
|
|
11
|
-
public static let
|
|
11
|
+
public static let Size3XL: CGFloat = 48
|
|
12
|
+
public static let Size4XL: CGFloat = 56
|
|
13
|
+
public static let Size5XL: CGFloat = 64
|
|
12
14
|
}
|
|
@@ -14,6 +14,8 @@ private let datePickerWithLabelsHeight: CGFloat = 238
|
|
|
14
14
|
/// - `"HH:mm"` shows hour + minute; a lone `"HH"` switches the hour wheel to a
|
|
15
15
|
/// 12-hour clock with an AM/PM column.
|
|
16
16
|
public struct DateTimePicker: View {
|
|
17
|
+
@Environment(\.appTheme) private var theme
|
|
18
|
+
|
|
17
19
|
// MARK: Lifecycle
|
|
18
20
|
|
|
19
21
|
public init(
|
|
@@ -84,7 +86,7 @@ public struct DateTimePicker: View {
|
|
|
84
86
|
|
|
85
87
|
VStack(spacing: 0) {
|
|
86
88
|
if hasLabel {
|
|
87
|
-
MomoText(arrayLabelTime[index], typography: .actionSBold, color:
|
|
89
|
+
MomoText(arrayLabelTime[index], typography: .actionSBold, color: theme.colors.text.default)
|
|
88
90
|
.padding(.bottom, Spacing.S)
|
|
89
91
|
}
|
|
90
92
|
|
|
@@ -100,7 +102,7 @@ public struct DateTimePicker: View {
|
|
|
100
102
|
}
|
|
101
103
|
.frame(maxWidth: .infinity)
|
|
102
104
|
.frame(height: pickerHeight)
|
|
103
|
-
.background(
|
|
105
|
+
.background(theme.colors.background.surface)
|
|
104
106
|
.padding(.horizontal, Spacing.M)
|
|
105
107
|
.onAppear { emitIfValid() }
|
|
106
108
|
}
|
|
@@ -14,6 +14,8 @@ private let wheelPickerBorderWidth: CGFloat = 1
|
|
|
14
14
|
/// real value can be centered. The currently centered row is reported back via
|
|
15
15
|
/// `onChange(name, value)`.
|
|
16
16
|
public struct WheelPicker: View {
|
|
17
|
+
@Environment(\.appTheme) private var theme
|
|
18
|
+
|
|
17
19
|
// MARK: Lifecycle
|
|
18
20
|
|
|
19
21
|
public init(
|
|
@@ -76,9 +78,9 @@ public struct WheelPicker: View {
|
|
|
76
78
|
.frame(height: wheelItemHeight * CGFloat(wheelVisibleCount))
|
|
77
79
|
.overlay(
|
|
78
80
|
RoundedRectangle(cornerRadius: Radius.S)
|
|
79
|
-
.stroke(
|
|
81
|
+
.stroke(theme.colors.border.default, lineWidth: wheelPickerBorderWidth)
|
|
80
82
|
)
|
|
81
|
-
.background(
|
|
83
|
+
.background(theme.colors.background.surface.clipShape(RoundedRectangle(cornerRadius: Radius.S)))
|
|
82
84
|
.overlay(
|
|
83
85
|
// Centered translucent selection band.
|
|
84
86
|
Rectangle()
|
|
@@ -121,6 +123,8 @@ public struct WheelPicker: View {
|
|
|
121
123
|
// MARK: - WheelPickerItem
|
|
122
124
|
|
|
123
125
|
private struct WheelPickerItem: View {
|
|
126
|
+
@Environment(\.appTheme) private var theme
|
|
127
|
+
|
|
124
128
|
let text: String
|
|
125
129
|
let isSelected: Bool
|
|
126
130
|
let index: Int
|
|
@@ -142,7 +146,7 @@ private struct WheelPickerItem: View {
|
|
|
142
146
|
let scale: CGFloat = isSelected ? 1.0 : 0.87
|
|
143
147
|
|
|
144
148
|
return ZStack {
|
|
145
|
-
MomoText(text, typography: .actionSBold, color:
|
|
149
|
+
MomoText(text, typography: .actionSBold, color: theme.colors.text.default)
|
|
146
150
|
.scaleEffect(scale)
|
|
147
151
|
}
|
|
148
152
|
.frame(maxWidth: .infinity)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
/// Mirrors Compose's `Modifier.activeOpacityClickable(activeOpacity:)` (modifier/Clickable.kt):
|
|
4
|
+
/// fades the whole tappable subtree to `activeOpacity` while pressed, animated, instead of
|
|
5
|
+
/// relying on a platform-default press indication.
|
|
6
|
+
struct ActiveOpacityButtonStyle: ButtonStyle {
|
|
7
|
+
var activeOpacity: Double = 0.2
|
|
8
|
+
|
|
9
|
+
func makeBody(configuration: Configuration) -> some View {
|
|
10
|
+
configuration.label
|
|
11
|
+
.opacity(configuration.isPressed ? activeOpacity : 1)
|
|
12
|
+
.animation(.default, value: configuration.isPressed)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -7,10 +7,13 @@ enum MoMoUIKitsResources {
|
|
|
7
7
|
static let bundle: Bundle = {
|
|
8
8
|
let bundleName = "MoMoUIKitsResources"
|
|
9
9
|
let tokenBundle = Bundle(for: MoMoUIKitsBundleToken.self)
|
|
10
|
+
let frameworkBundle = Bundle(identifier: "org.cocoapods.MoMoUIKits")
|
|
10
11
|
let candidates = [
|
|
11
12
|
Bundle.main.resourceURL,
|
|
12
13
|
tokenBundle.resourceURL,
|
|
13
|
-
tokenBundle.bundleURL
|
|
14
|
+
tokenBundle.bundleURL,
|
|
15
|
+
frameworkBundle?.resourceURL,
|
|
16
|
+
frameworkBundle?.bundleURL
|
|
14
17
|
]
|
|
15
18
|
|
|
16
19
|
for candidate in candidates {
|
|
@@ -21,7 +24,7 @@ enum MoMoUIKitsResources {
|
|
|
21
24
|
return bundle
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
return
|
|
27
|
+
return Bundle.main
|
|
25
28
|
}()
|
|
26
29
|
}
|
|
27
30
|
|
package/ios/Icon/Icon.swift
CHANGED
|
@@ -49,7 +49,11 @@ public struct Icon: View {
|
|
|
49
49
|
if let uri = iconSources[source]?.uri {
|
|
50
50
|
// Mirror Compose: certain icons (e.g. branded glyphs) are never tinted
|
|
51
51
|
let resolvedColor = noThemeIcons.contains(source) ? nil : color
|
|
52
|
-
ImageView(uri, color: resolvedColor)
|
|
52
|
+
ImageView(uri, color: resolvedColor)
|
|
53
|
+
.frame(width: size, height: size)
|
|
54
|
+
// Mirrors Compose's auto-generated `contentDescription = "img|$iconUrl"`
|
|
55
|
+
// when no explicit label is provided.
|
|
56
|
+
.accessibilityLabel(accessibilityLabel ?? "img|\(uri)")
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
59
|
}
|
|
@@ -5,13 +5,15 @@ import SwiftUI
|
|
|
5
5
|
|
|
6
6
|
public enum IconSize {
|
|
7
7
|
case large
|
|
8
|
+
case medium
|
|
8
9
|
case small
|
|
9
10
|
|
|
10
11
|
/// Container width/height of the button.
|
|
11
12
|
var containerSize: CGFloat {
|
|
12
13
|
switch self {
|
|
13
14
|
case .large: return 48
|
|
14
|
-
case .
|
|
15
|
+
case .medium: return 36
|
|
16
|
+
case .small: return 28
|
|
15
17
|
}
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -19,13 +21,16 @@ public enum IconSize {
|
|
|
19
21
|
var iconSize: CGFloat {
|
|
20
22
|
switch self {
|
|
21
23
|
case .large: return 24
|
|
22
|
-
case .small: return 16
|
|
24
|
+
case .medium, .small: return 16
|
|
23
25
|
}
|
|
24
26
|
}
|
|
27
|
+
}
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
// MARK: - IconShape
|
|
30
|
+
|
|
31
|
+
public enum IconShape {
|
|
32
|
+
case circle
|
|
33
|
+
case square
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
// MARK: - IconButton
|
|
@@ -37,6 +42,7 @@ public struct IconButton: View {
|
|
|
37
42
|
var onClick: () -> Void
|
|
38
43
|
var type: ButtonType
|
|
39
44
|
var size: IconSize
|
|
45
|
+
var shape: IconShape
|
|
40
46
|
var color: Color?
|
|
41
47
|
|
|
42
48
|
public init(
|
|
@@ -44,12 +50,14 @@ public struct IconButton: View {
|
|
|
44
50
|
onClick: @escaping () -> Void,
|
|
45
51
|
type: ButtonType = .primary,
|
|
46
52
|
size: IconSize = .small,
|
|
53
|
+
shape: IconShape = .circle,
|
|
47
54
|
color: Color? = nil
|
|
48
55
|
) {
|
|
49
56
|
self.icon = icon
|
|
50
57
|
self.onClick = onClick
|
|
51
58
|
self.type = type
|
|
52
59
|
self.size = size
|
|
60
|
+
self.shape = shape
|
|
53
61
|
self.color = color
|
|
54
62
|
}
|
|
55
63
|
|
|
@@ -72,9 +80,9 @@ public struct IconButton: View {
|
|
|
72
80
|
Icon(source: icon, size: size.iconSize, color: fg)
|
|
73
81
|
.frame(width: size.containerSize, height: size.containerSize)
|
|
74
82
|
.background(bg)
|
|
75
|
-
.clipShape(
|
|
83
|
+
.clipShape(IconButtonShape(shape))
|
|
76
84
|
.overlay(
|
|
77
|
-
|
|
85
|
+
IconButtonShape(shape)
|
|
78
86
|
.stroke(border ?? .clear, lineWidth: border != nil ? borderWidth : 0)
|
|
79
87
|
)
|
|
80
88
|
}
|
|
@@ -110,3 +118,23 @@ public struct IconButton: View {
|
|
|
110
118
|
}
|
|
111
119
|
}
|
|
112
120
|
}
|
|
121
|
+
|
|
122
|
+
/// Type-erased shape switch (`Circle` vs a fixed `Radius.S` rounded rect) — mirrors
|
|
123
|
+
/// Compose's `IconShape.toShape()`. `AnyShape` needs iOS 17+, so this hand-rolls the same
|
|
124
|
+
/// erasure at the deployment target's iOS 15 floor.
|
|
125
|
+
private struct IconButtonShape: Shape {
|
|
126
|
+
let shape: IconShape
|
|
127
|
+
|
|
128
|
+
init(_ shape: IconShape) {
|
|
129
|
+
self.shape = shape
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
func path(in rect: CGRect) -> Path {
|
|
133
|
+
switch shape {
|
|
134
|
+
case .circle:
|
|
135
|
+
return Circle().path(in: rect)
|
|
136
|
+
case .square:
|
|
137
|
+
return RoundedRectangle(cornerRadius: Radius.S).path(in: rect)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
package/ios/Image/Image.swift
CHANGED
|
@@ -5,15 +5,23 @@ import SDWebImageSwiftUI
|
|
|
5
5
|
import SkeletonUI
|
|
6
6
|
import SwiftUI
|
|
7
7
|
|
|
8
|
+
public enum ImageState {
|
|
9
|
+
case loading, success, error
|
|
10
|
+
}
|
|
11
|
+
|
|
8
12
|
public struct ImageView: View {
|
|
13
|
+
@Environment(\.appTheme) private var theme
|
|
14
|
+
|
|
9
15
|
// MARK: Lifecycle
|
|
10
16
|
|
|
11
|
-
public init(_ url: String, aspectRatio: CGFloat? = nil, contentMode: ContentMode = ContentMode.fit, placeholder: AnyView? = nil, color: Color? = nil) {
|
|
17
|
+
public init(_ url: String, aspectRatio: CGFloat? = nil, contentMode: ContentMode = ContentMode.fit, placeholder: AnyView? = nil, color: Color? = nil, loading: Bool = true, onStateChanged: ((ImageState) -> Void)? = nil) {
|
|
12
18
|
self.url = url
|
|
13
19
|
self.aspectRatio = aspectRatio
|
|
14
20
|
self.contentMode = contentMode
|
|
15
21
|
self.placeholder = placeholder
|
|
16
22
|
self.color = color
|
|
23
|
+
self.loading = loading
|
|
24
|
+
self.onStateChanged = onStateChanged
|
|
17
25
|
}
|
|
18
26
|
|
|
19
27
|
// MARK: Public
|
|
@@ -22,9 +30,11 @@ public struct ImageView: View {
|
|
|
22
30
|
return WebImage(url: URL(string: url), isAnimating: .constant(true))
|
|
23
31
|
.onFailure { _ in
|
|
24
32
|
error = true
|
|
33
|
+
onStateChanged?(.error)
|
|
25
34
|
}
|
|
26
35
|
.onSuccess { _, _, _ in
|
|
27
36
|
error = false
|
|
37
|
+
onStateChanged?(.success)
|
|
28
38
|
}
|
|
29
39
|
.resizable()
|
|
30
40
|
.renderingMode(color != nil ? .template : .original)
|
|
@@ -34,20 +44,21 @@ public struct ImageView: View {
|
|
|
34
44
|
Image("media_fail")
|
|
35
45
|
.resizable()
|
|
36
46
|
.renderingMode(.template)
|
|
37
|
-
.foregroundColor(
|
|
47
|
+
.foregroundColor(theme.colors.text.disable)
|
|
38
48
|
.frame(width: 24, height: 24)
|
|
39
49
|
} else if placeholder != nil {
|
|
40
50
|
placeholder
|
|
41
51
|
}
|
|
42
52
|
})
|
|
43
53
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
44
|
-
.skeleton(with: !error && placeholder == nil)
|
|
54
|
+
.skeleton(with: loading && !error && placeholder == nil)
|
|
45
55
|
.shape(type: .rectangle)
|
|
46
56
|
}
|
|
47
57
|
.scaledToFit()
|
|
48
58
|
.transition(.fade)
|
|
49
59
|
.aspectRatio(aspectRatio, contentMode: contentMode)
|
|
50
60
|
.foregroundColor(color)
|
|
61
|
+
.onAppear { onStateChanged?(.loading) }
|
|
51
62
|
}
|
|
52
63
|
|
|
53
64
|
// MARK: Internal
|
|
@@ -56,6 +67,8 @@ public struct ImageView: View {
|
|
|
56
67
|
var contentMode: ContentMode
|
|
57
68
|
var placeholder: AnyView?
|
|
58
69
|
var color: Color?
|
|
70
|
+
var loading: Bool
|
|
71
|
+
var onStateChanged: ((ImageState) -> Void)?
|
|
59
72
|
|
|
60
73
|
@State var error: Bool = false
|
|
61
74
|
var url: String
|
|
@@ -7,6 +7,8 @@ public enum InformationState {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
public struct Information: View {
|
|
10
|
+
@Environment(\.appTheme) private var theme
|
|
11
|
+
|
|
10
12
|
private let title: String?
|
|
11
13
|
private let description: String
|
|
12
14
|
private let state: InformationState
|
|
@@ -93,7 +95,7 @@ public struct Information: View {
|
|
|
93
95
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
94
96
|
|
|
95
97
|
if let onPressClose, showIconClose {
|
|
96
|
-
Icon(source: "navigation_close", size: 16, color:
|
|
98
|
+
Icon(source: "navigation_close", size: 16, color: theme.colors.text.default)
|
|
97
99
|
.contentShape(Rectangle())
|
|
98
100
|
.onTapGesture(perform: onPressClose)
|
|
99
101
|
}
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
import SwiftUI
|
|
9
9
|
|
|
10
10
|
public struct ErrorView: View {
|
|
11
|
+
@Environment(\.appTheme) private var theme
|
|
12
|
+
|
|
11
13
|
public var errorMessage: String
|
|
12
14
|
public var errorSpacing: Bool
|
|
13
15
|
public var hintText: String
|
|
@@ -43,7 +45,7 @@ public struct ErrorView: View {
|
|
|
43
45
|
// MARK: - Helpers
|
|
44
46
|
|
|
45
47
|
private func displayColor() -> Color {
|
|
46
|
-
!errorMessage.isEmpty ?
|
|
48
|
+
!errorMessage.isEmpty ? theme.colors.error.primary : theme.colors.text.hint
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
private func displayText() -> String {
|
package/ios/Input/Input.swift
CHANGED
|
@@ -35,6 +35,8 @@ public struct Input: View {
|
|
|
35
35
|
|
|
36
36
|
@State private var isFocused: Bool = false
|
|
37
37
|
@State private var isPasswordHidden: Bool = true
|
|
38
|
+
@FocusState private var isFieldFocused: Bool
|
|
39
|
+
@State private var floatingLabelHeight: CGFloat = 16
|
|
38
40
|
|
|
39
41
|
public init(
|
|
40
42
|
text: Binding<String>,
|
|
@@ -99,8 +101,13 @@ public struct Input: View {
|
|
|
99
101
|
let textBinding = Binding<String>(
|
|
100
102
|
get: { self.text },
|
|
101
103
|
set: { newValue in
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
// Trim atomically before publishing, mirroring Kotlin's `onValueChange`
|
|
105
|
+
// (`newText.take(maxLength)` before the state ever updates) — avoids a
|
|
106
|
+
// render with the untrimmed value that a separate `.onChange` correction
|
|
107
|
+
// would otherwise briefly show.
|
|
108
|
+
let limited = limitText(newValue)
|
|
109
|
+
self.text = limited
|
|
110
|
+
self.onChangeText?(limited)
|
|
104
111
|
}
|
|
105
112
|
)
|
|
106
113
|
|
|
@@ -118,8 +125,19 @@ public struct Input: View {
|
|
|
118
125
|
}
|
|
119
126
|
}
|
|
120
127
|
.padding(.horizontal, Spacing.S)
|
|
128
|
+
.background(
|
|
129
|
+
GeometryReader { geo in
|
|
130
|
+
Color.clear.onAppear { floatingLabelHeight = geo.size.height }
|
|
131
|
+
}
|
|
132
|
+
)
|
|
121
133
|
.background(theme.colors.background.surface)
|
|
122
|
-
|
|
134
|
+
// Kotlin centers the label within the (box-height-tall) decorationBox
|
|
135
|
+
// by default, then applies `offsetY = -size.values.height / 2` — net
|
|
136
|
+
// effect: the label ends up centered exactly on the box's top border
|
|
137
|
+
// (spanning -labelHeight/2 ... +labelHeight/2 around y=0). Our ZStack
|
|
138
|
+
// anchors top-leading instead, so reproducing that requires offsetting
|
|
139
|
+
// by half the label's OWN measured height, not half the box height.
|
|
140
|
+
.offset(x: Spacing.S, y: -floatingLabelHeight / 2)
|
|
123
141
|
.zIndex(10)
|
|
124
142
|
}
|
|
125
143
|
|
|
@@ -151,6 +169,7 @@ public struct Input: View {
|
|
|
151
169
|
cursorColor: UIColor(theme.colors.primary),
|
|
152
170
|
isDisabled: disabled || readOnly,
|
|
153
171
|
maxLength: maxLength,
|
|
172
|
+
autofocus: autofocus,
|
|
154
173
|
onFocusChange: { focused in
|
|
155
174
|
handleFocusChange(focused)
|
|
156
175
|
},
|
|
@@ -166,11 +185,9 @@ public struct Input: View {
|
|
|
166
185
|
.foregroundColor(getTextColor())
|
|
167
186
|
.disabled(disabled || readOnly)
|
|
168
187
|
.applyCursorColor(theme.colors.primary)
|
|
169
|
-
.
|
|
170
|
-
|
|
171
|
-
if
|
|
172
|
-
text = limited
|
|
173
|
-
}
|
|
188
|
+
.focused($isFieldFocused)
|
|
189
|
+
.onAppear {
|
|
190
|
+
if autofocus { isFieldFocused = true }
|
|
174
191
|
}
|
|
175
192
|
}
|
|
176
193
|
}
|
|
@@ -312,6 +329,7 @@ private struct SecureInputField: UIViewRepresentable {
|
|
|
312
329
|
var cursorColor: UIColor
|
|
313
330
|
var isDisabled: Bool
|
|
314
331
|
var maxLength: Int?
|
|
332
|
+
var autofocus: Bool = false
|
|
315
333
|
var onFocusChange: (Bool) -> Void
|
|
316
334
|
var onChangeText: ((String) -> Void)?
|
|
317
335
|
|
|
@@ -336,6 +354,11 @@ private struct SecureInputField: UIViewRepresentable {
|
|
|
336
354
|
action: #selector(Coordinator.textFieldDidChange(_:)),
|
|
337
355
|
for: .editingChanged
|
|
338
356
|
)
|
|
357
|
+
if autofocus {
|
|
358
|
+
DispatchQueue.main.async {
|
|
359
|
+
textField.becomeFirstResponder()
|
|
360
|
+
}
|
|
361
|
+
}
|
|
339
362
|
return textField
|
|
340
363
|
}
|
|
341
364
|
|