@momo-kits/native-kits 0.162.49 → 0.162.50-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 +4 -2
- package/build.gradle.kts +11 -0
- package/compose/build.gradle.kts +198 -0
- package/compose/build.gradle.kts.backup +198 -0
- package/compose/compose.podspec +47 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +20 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +6 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +12 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +153 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +6 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +110 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/DesignSystemKits.kt +62 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +1071 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +269 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +56 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +157 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +81 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +32 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +338 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +175 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +357 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +115 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +89 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +128 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +209 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +542 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +49 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +125 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +231 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +189 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +478 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +114 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +265 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +235 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +223 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +265 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +185 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +343 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +108 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +56 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +41 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +84 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +40 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +370 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +108 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +308 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +59 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +87 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +90 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +330 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +220 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +473 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +122 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +207 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +92 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +560 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +125 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +207 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +501 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +176 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +182 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +193 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +236 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +180 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +325 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +17 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +16 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +194 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +287 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +54 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +50 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +321 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +41 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +52 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +66 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +104 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +48 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +502 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +58 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +148 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +116 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +205 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +422 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +8 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +732 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +148 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +35 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +228 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +217 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +97 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +377 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +169 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +455 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +105 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +106 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +74 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +399 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +39 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +363 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +192 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +133 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +127 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +12 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +91 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +74 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +20 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +55 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +15 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +118 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +42 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +45 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +32 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +38 -0
- 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 +218 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +138 -0
- package/gradle/libs.versions.toml +69 -0
- package/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle/wrapper/gradle-wrapper.properties +8 -0
- package/gradle.properties +26 -0
- package/gradlew +252 -0
- package/gradlew.bat +94 -0
- package/ios/Application/DesignSystemKits.swift +64 -1
- package/ios/Layout/ScreenLayout.swift +23 -3
- package/ios/Navigation/BottomSheet.swift +150 -22
- package/ios/Navigation/Component/SnackBar.swift +4 -0
- package/ios/Navigation/ModalScreen.swift +7 -3
- package/ios/Popup/PopupDisplay.swift +2 -2
- package/ios/Popup/PopupInput.swift +2 -2
- package/ios/Popup/PopupLayout.swift +15 -8
- package/ios/Popup/PopupNotify.swift +2 -2
- package/ios/Typography/FontScale.swift +15 -4
- package/ios/native-kits.podspec +1 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/README.md +120 -0
- package/package.json +1 -1
- package/settings.gradle.kts +64 -0
|
@@ -32,7 +32,8 @@ public struct ScreenLayout: Equatable {
|
|
|
32
32
|
public let width: CGFloat
|
|
33
33
|
public let height: CGFloat
|
|
34
34
|
public let isIPhoneDuo: Bool
|
|
35
|
-
///
|
|
35
|
+
/// iPhone Duo folded or not, whatever the window size (its hinge, or the outer display's one-sided strip);
|
|
36
|
+
/// drives popups, modal and bottom sheet.
|
|
36
37
|
public let isDuoDevice: Bool
|
|
37
38
|
public let insets: EdgeInsets
|
|
38
39
|
public let insetLeft: CGFloat
|
|
@@ -75,6 +76,11 @@ enum DuoMetrics {
|
|
|
75
76
|
static let floatingTabBarHeight: CGFloat = 62
|
|
76
77
|
static let floatingTabBarMargin: CGFloat = 20
|
|
77
78
|
static let floatingTabBarInsetOverlap: CGFloat = 13
|
|
79
|
+
// Width of a system alert on the inner display, in both orientations.
|
|
80
|
+
static let modalMaxWidth: CGFloat = 440
|
|
81
|
+
// Fits a PopupNotify with image, scrolled description and actions at the max width (RN
|
|
82
|
+
// Application/device.ts); taller content scrolls instead of filling the inner display.
|
|
83
|
+
static let modalMaxHeight: CGFloat = 640
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
enum HeaderMetrics {
|
|
@@ -178,8 +184,10 @@ extension ScreenLayout {
|
|
|
178
184
|
)
|
|
179
185
|
}
|
|
180
186
|
|
|
187
|
+
// the outer display reports no hinge; its one-sided strip still gives it away
|
|
181
188
|
return ScreenLayout(
|
|
182
|
-
width: width, height: height, isIPhoneDuo: true,
|
|
189
|
+
width: width, height: height, isIPhoneDuo: true,
|
|
190
|
+
isDuoDevice: isDuoDevice || insets.leading != insets.trailing, insets: insets,
|
|
183
191
|
insetLeft: insets.leading, insetRight: insets.trailing,
|
|
184
192
|
contentWidth: width - insets.leading - insets.trailing,
|
|
185
193
|
headerHeight: headerHeight, headerStatusBarHeight: headerStatusBarHeight, sideBar: sideBar,
|
|
@@ -256,12 +264,24 @@ extension ScreenLayout {
|
|
|
256
264
|
public var duoTopInset: CGFloat {
|
|
257
265
|
max(insets.top, height > width ? max(insets.leading, insets.trailing) * 2 : 0)
|
|
258
266
|
}
|
|
267
|
+
|
|
268
|
+
/// Room for a dialog on iPhone Duo: inside the safe area, so clear of the side toolbar strip, and
|
|
269
|
+
/// capped so it stays a dialog on the inner display.
|
|
270
|
+
var duoModalMaxWidth: CGFloat {
|
|
271
|
+
min(width - insets.leading - insets.trailing - Spacing.XL * 2, DuoMetrics.modalMaxWidth)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
var duoModalMaxHeight: CGFloat {
|
|
275
|
+
min(height - insets.top - insets.bottom - Spacing.XL * 2, DuoMetrics.modalMaxHeight)
|
|
276
|
+
}
|
|
259
277
|
}
|
|
260
278
|
|
|
261
279
|
/// Snapshot for call sites without `\.screenLayout`; not reactive. `windowSize` is ignored,
|
|
262
280
|
/// kept for host call sites.
|
|
263
281
|
public func isIPhoneDuoDevice(windowSize: CGSize? = nil) -> Bool {
|
|
264
|
-
isPhoneIdiom
|
|
282
|
+
guard isPhoneIdiom else { return false }
|
|
283
|
+
let insets = keyWindow?.safeAreaInsets
|
|
284
|
+
return FoldableDevice.isFoldable(measuredIn: keyWindow) || insets.map { $0.left != $0.right } == true
|
|
265
285
|
}
|
|
266
286
|
|
|
267
287
|
/// Apple's foldable check (as the host's `SideTabRailView.isFoldableDevice`): only a device with a
|
|
@@ -15,16 +15,25 @@ import SwiftUI
|
|
|
15
15
|
|
|
16
16
|
/// Mirrors Compose's `sealed class BottomHeader` (Title / Custom).
|
|
17
17
|
public struct BottomSheetHeader {
|
|
18
|
+
struct Back {
|
|
19
|
+
let icon: String
|
|
20
|
+
let action: () -> Void
|
|
21
|
+
}
|
|
22
|
+
|
|
18
23
|
enum Style {
|
|
19
|
-
case title(String)
|
|
24
|
+
case title(String, back: Back?)
|
|
20
25
|
case custom(() -> AnyView)
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
let style: Style
|
|
24
29
|
|
|
25
|
-
/// Mirrors `BottomHeader.Title`.
|
|
26
|
-
public static func title(
|
|
27
|
-
|
|
30
|
+
/// Mirrors `BottomHeader.Title`. `onBack` shows a back button; the sheet closes, then it runs.
|
|
31
|
+
public static func title(
|
|
32
|
+
_ text: String = "Bottom Sheet Title",
|
|
33
|
+
backIcon: String = "arrow-back",
|
|
34
|
+
onBack: (() -> Void)? = nil
|
|
35
|
+
) -> BottomSheetHeader {
|
|
36
|
+
BottomSheetHeader(style: .title(text, back: onBack.map { Back(icon: backIcon, action: $0) }))
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
/// Mirrors `BottomHeader.Custom`.
|
|
@@ -68,6 +77,9 @@ struct BottomSheetView<Content: View>: View {
|
|
|
68
77
|
/// Mirrors Compose's `sheetCloseOffset` (100dp drag threshold to dismiss).
|
|
69
78
|
private let closeThreshold: CGFloat = 100
|
|
70
79
|
|
|
80
|
+
private let indicatorHeight: CGFloat = 16
|
|
81
|
+
private let headerRowHeight: CGFloat = 56
|
|
82
|
+
|
|
71
83
|
/// Mirrors Compose's per-term peak (0.3f). Combined with `backgroundAlpha` the
|
|
72
84
|
/// steady-state (fully open) scrim peaks at 0.6, matching ModalScreen's peak.
|
|
73
85
|
private let maxDynamicAlpha: CGFloat = 0.3
|
|
@@ -78,6 +90,21 @@ struct BottomSheetView<Content: View>: View {
|
|
|
78
90
|
private var isDuo: Bool { layout.isDuoDevice }
|
|
79
91
|
// iPhone Duo inner display: center the sheet instead of spanning the width
|
|
80
92
|
private var isDuoCentered: Bool { isDuo && layout.width >= 600 }
|
|
93
|
+
/// Outer display: the sheet floats Spacing.M off the side edges like system sheets, its content
|
|
94
|
+
/// stays in the safe area and its buttons move to the side bar column (HIG).
|
|
95
|
+
private var isDuoFloating: Bool { isDuo && !isDuoCentered }
|
|
96
|
+
|
|
97
|
+
private var titleBack: BottomSheetHeader.Back? {
|
|
98
|
+
if case .title(_, let back) = header.style { return back }
|
|
99
|
+
return nil
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private var sideColumn: SideBarLayout? {
|
|
103
|
+
guard isDuoFloating, case .title = header.style, let sideBar = layout.sideBar,
|
|
104
|
+
sideBar.width - Spacing.M >= DuoMetrics.controlSize + Spacing.M
|
|
105
|
+
else { return nil }
|
|
106
|
+
return sideBar
|
|
107
|
+
}
|
|
81
108
|
|
|
82
109
|
/// Keeps the portrait width in landscape, like system sheets.
|
|
83
110
|
private var duoCenteredWidth: CGFloat {
|
|
@@ -87,18 +114,19 @@ struct BottomSheetView<Content: View>: View {
|
|
|
87
114
|
)
|
|
88
115
|
}
|
|
89
116
|
|
|
90
|
-
///
|
|
91
|
-
/// side status bar only covers the top corner.
|
|
117
|
+
/// The floating sheet spans the side safe area, its content stays clear of it.
|
|
92
118
|
private var sideInsetLeading: CGFloat {
|
|
93
|
-
|
|
119
|
+
isDuoFloating ? max(layout.insets.leading - Spacing.M, 0) : 0
|
|
94
120
|
}
|
|
95
121
|
|
|
96
122
|
private var sideInsetTrailing: CGFloat {
|
|
97
|
-
|
|
123
|
+
isDuoFloating ? max(layout.insets.trailing - Spacing.M, 0) : 0
|
|
98
124
|
}
|
|
99
125
|
|
|
100
126
|
private var maxHeight: CGFloat {
|
|
101
|
-
isDuo
|
|
127
|
+
guard isDuo else { return screenHeight - 90 }
|
|
128
|
+
// with its buttons in the side column the sheet can pass the side status bar
|
|
129
|
+
return layout.height - max(sideColumn != nil ? layout.insets.top : layout.duoTopInset, Spacing.XL)
|
|
102
130
|
}
|
|
103
131
|
private var bottomInset: CGFloat { useBottomInset ? safeAreaBottomInset() : 0 }
|
|
104
132
|
/// While the keyboard is up, it already covers the home-indicator area, so it
|
|
@@ -170,19 +198,21 @@ struct BottomSheetView<Content: View>: View {
|
|
|
170
198
|
.frame(width: 40, height: 4)
|
|
171
199
|
Spacer().frame(height: 4)
|
|
172
200
|
headerView
|
|
173
|
-
.frame(height:
|
|
201
|
+
.frame(height: headerRowHeight)
|
|
174
202
|
.padding(.horizontal, Spacing.M)
|
|
175
|
-
.padding(.leading, sideInsetLeading)
|
|
176
|
-
.padding(.trailing, sideInsetTrailing)
|
|
177
203
|
}
|
|
204
|
+
.padding(.leading, sideInsetLeading)
|
|
205
|
+
.padding(.trailing, sideInsetTrailing)
|
|
178
206
|
.frame(maxWidth: .infinity)
|
|
179
|
-
.frame(height:
|
|
207
|
+
.frame(height: indicatorHeight + headerRowHeight)
|
|
180
208
|
.contentShape(Rectangle())
|
|
181
209
|
.gesture(dragGesture, including: draggable ? .all : .subviews)
|
|
182
210
|
|
|
183
211
|
Rectangle()
|
|
184
212
|
.fill(theme.colors.border.default)
|
|
185
213
|
.frame(height: 1)
|
|
214
|
+
.padding(.leading, sideInsetLeading)
|
|
215
|
+
.padding(.trailing, sideInsetTrailing)
|
|
186
216
|
|
|
187
217
|
content()
|
|
188
218
|
.padding(.leading, sideInsetLeading)
|
|
@@ -191,7 +221,7 @@ struct BottomSheetView<Content: View>: View {
|
|
|
191
221
|
.layoutPriority(-1)
|
|
192
222
|
|
|
193
223
|
if let footer {
|
|
194
|
-
BottomSheetFooter(content: footer)
|
|
224
|
+
BottomSheetFooter(content: footer, leadingInset: sideInsetLeading, trailingInset: sideInsetTrailing)
|
|
195
225
|
}
|
|
196
226
|
}
|
|
197
227
|
// The filler stays outside the cap, which shrinks by it instead: the sheet still ends at
|
|
@@ -209,27 +239,61 @@ struct BottomSheetView<Content: View>: View {
|
|
|
209
239
|
}
|
|
210
240
|
)
|
|
211
241
|
.background(backgroundColor)
|
|
212
|
-
.
|
|
242
|
+
.overlay {
|
|
243
|
+
if let sideColumn {
|
|
244
|
+
BottomSheetSideColumn(
|
|
245
|
+
sideBar: sideColumn,
|
|
246
|
+
windowHeight: layout.height,
|
|
247
|
+
topOffset: indicatorHeight + (headerRowHeight - DuoMetrics.controlSize) / 2,
|
|
248
|
+
back: titleBack,
|
|
249
|
+
onBack: goBack,
|
|
250
|
+
onClose: { close() }
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
.clipShape(RoundedCorner(radius: isDuo ? Radius.XL : Radius.M, corners: [.topLeft, .topRight]))
|
|
255
|
+
.padding(.horizontal, isDuoFloating ? Spacing.M : 0)
|
|
213
256
|
}
|
|
214
257
|
|
|
215
258
|
@ViewBuilder
|
|
216
259
|
private var headerView: some View {
|
|
217
260
|
switch header.style {
|
|
218
|
-
case .title(let text):
|
|
261
|
+
case .title(let text, let back):
|
|
262
|
+
// the side column holds back and close, the row keeps only the title
|
|
263
|
+
let showsButtons = sideColumn == nil
|
|
219
264
|
HStack(spacing: 0) {
|
|
220
|
-
|
|
265
|
+
if showsButtons {
|
|
266
|
+
if let back {
|
|
267
|
+
headerButton(icon: back.icon, accessibilityId: "btn_bottom_sheet_back") { goBack(back) }
|
|
268
|
+
} else {
|
|
269
|
+
Color.clear.frame(width: 24, height: 24)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
221
272
|
MomoText(text, typography: .headerDefaultBold, color: Colors.black17)
|
|
222
273
|
.frame(maxWidth: .infinity, alignment: .center)
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
274
|
+
.padding(.horizontal, showsButtons ? 0 : Spacing.M)
|
|
275
|
+
if showsButtons {
|
|
276
|
+
headerButton(icon: "navigation_close", accessibilityId: "btn_bottom_sheet_close") { close() }
|
|
277
|
+
}
|
|
227
278
|
}
|
|
228
279
|
case .custom(let builder):
|
|
229
280
|
builder()
|
|
230
281
|
}
|
|
231
282
|
}
|
|
232
283
|
|
|
284
|
+
private func headerButton(icon: String, accessibilityId: String, action: @escaping () -> Void) -> some View {
|
|
285
|
+
Icon(source: icon, size: 24, color: Colors.black17)
|
|
286
|
+
.frame(width: 24, height: 24)
|
|
287
|
+
.contentShape(Rectangle())
|
|
288
|
+
.onTapGesture(perform: action)
|
|
289
|
+
.accessibilityIdentifier(accessibilityId)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/// RN `leftOptions.onPressIconLeft`: the sheet closes first, then the back runs.
|
|
293
|
+
private func goBack(_ back: BottomSheetHeader.Back) {
|
|
294
|
+
close(then: back.action)
|
|
295
|
+
}
|
|
296
|
+
|
|
233
297
|
// MARK: - Drag handling (mirrors Compose's detectDragGestures)
|
|
234
298
|
|
|
235
299
|
private var dragGesture: some Gesture {
|
|
@@ -282,7 +346,7 @@ struct BottomSheetView<Content: View>: View {
|
|
|
282
346
|
|
|
283
347
|
/// Mirrors Compose's `closeEvent`: slide out over 200ms, then fade the scrim
|
|
284
348
|
/// out over 100ms only after the slide finishes, then dismiss.
|
|
285
|
-
private func close() {
|
|
349
|
+
private func close(then onClosed: (() -> Void)? = nil) {
|
|
286
350
|
guard !isClosing else { return }
|
|
287
351
|
isClosing = true
|
|
288
352
|
withAnimation(.timingCurve(0.3, 0, 0.8, 0.15, duration: 0.2)) {
|
|
@@ -294,7 +358,67 @@ struct BottomSheetView<Content: View>: View {
|
|
|
294
358
|
}
|
|
295
359
|
}
|
|
296
360
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
|
361
|
+
// a newer overlay replaced this one: it stays, and this sheet's back is dropped
|
|
362
|
+
guard navigator.overplay?.id == overplayId else { return }
|
|
297
363
|
navigator.finishOverplayDismiss(id: overplayId)
|
|
364
|
+
onClosed?()
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/// iPhone Duo outer display: back and close sit on the part of the sheet under the side safe area,
|
|
370
|
+
/// 12pt from the strip's inner edge like the screen side bar. They follow the header row while the
|
|
371
|
+
/// sheet is short and stay below the status bar cluster while it is tall.
|
|
372
|
+
private struct BottomSheetSideColumn: View {
|
|
373
|
+
let sideBar: SideBarLayout
|
|
374
|
+
let windowHeight: CGFloat
|
|
375
|
+
/// Lines the controls up with the header row.
|
|
376
|
+
let topOffset: CGFloat
|
|
377
|
+
let back: BottomSheetHeader.Back?
|
|
378
|
+
let onBack: (BottomSheetHeader.Back) -> Void
|
|
379
|
+
let onClose: () -> Void
|
|
380
|
+
|
|
381
|
+
@Environment(\.appTheme) private var theme
|
|
382
|
+
|
|
383
|
+
private var isRight: Bool { sideBar.edge == .right }
|
|
384
|
+
|
|
385
|
+
private var columnHeight: CGFloat {
|
|
386
|
+
let count: CGFloat = back != nil ? 2 : 1
|
|
387
|
+
return count * DuoMetrics.controlSize + (count - 1) * Spacing.M
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private var headerColor: HeaderColor {
|
|
391
|
+
getHeaderColor(isAnimated: false, isSurface: true, opacity: 1, tintColor: nil, defaultColor: theme.colors.text.default)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
var body: some View {
|
|
395
|
+
GeometryReader { proxy in
|
|
396
|
+
let sheetHeight = proxy.size.height
|
|
397
|
+
// the sheet rests on the window bottom; its slide is an offset outside this layout
|
|
398
|
+
let sheetTop = windowHeight - sheetHeight
|
|
399
|
+
// clear of a camera at the bottom end of the strip when it can
|
|
400
|
+
let lowest = sheetHeight - sideBar.bottom - columnHeight
|
|
401
|
+
let top = max(min(max(sideBar.top - sheetTop, topOffset), lowest), 0)
|
|
402
|
+
VStack(alignment: isRight ? .leading : .trailing, spacing: Spacing.M) {
|
|
403
|
+
if let back {
|
|
404
|
+
SideBarButton(
|
|
405
|
+
icon: back.icon,
|
|
406
|
+
headerColor: headerColor,
|
|
407
|
+
accessibilityId: "btn_bottom_sheet_back",
|
|
408
|
+
action: { onBack(back) }
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
SideBarButton(
|
|
412
|
+
icon: "navigation_close",
|
|
413
|
+
headerColor: headerColor,
|
|
414
|
+
accessibilityId: "btn_bottom_sheet_close",
|
|
415
|
+
action: onClose
|
|
416
|
+
)
|
|
417
|
+
}
|
|
418
|
+
.padding(isRight ? .leading : .trailing, Spacing.M)
|
|
419
|
+
.frame(width: sideBar.width - Spacing.M, alignment: isRight ? .leading : .trailing)
|
|
420
|
+
.padding(.top, top)
|
|
421
|
+
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: isRight ? .topTrailing : .topLeading)
|
|
298
422
|
}
|
|
299
423
|
}
|
|
300
424
|
}
|
|
@@ -371,12 +495,16 @@ private struct CapHeightLayout: Layout {
|
|
|
371
495
|
/// Mirrors Compose's `BottomSheetFooter`: a surface band with a soft shadow cast on the content.
|
|
372
496
|
private struct BottomSheetFooter: View {
|
|
373
497
|
let content: () -> AnyView
|
|
498
|
+
var leadingInset: CGFloat = 0
|
|
499
|
+
var trailingInset: CGFloat = 0
|
|
374
500
|
|
|
375
501
|
@Environment(\.appTheme) private var theme
|
|
376
502
|
|
|
377
503
|
var body: some View {
|
|
378
504
|
content()
|
|
379
505
|
.frame(maxWidth: .infinity)
|
|
506
|
+
.padding(.leading, leadingInset)
|
|
507
|
+
.padding(.trailing, trailingInset)
|
|
380
508
|
.padding(.vertical, Spacing.S)
|
|
381
509
|
.background(theme.colors.background.surface)
|
|
382
510
|
.overlay(alignment: .top) {
|
|
@@ -57,6 +57,7 @@ struct SnackBarView: View {
|
|
|
57
57
|
let bottomInset: CGFloat
|
|
58
58
|
|
|
59
59
|
@EnvironmentObject private var navigator: Navigator
|
|
60
|
+
@Environment(\.screenLayout) private var layout
|
|
60
61
|
|
|
61
62
|
@State private var contentHeight: CGFloat = 0
|
|
62
63
|
@State private var offsetY: CGFloat = 0
|
|
@@ -75,6 +76,9 @@ struct SnackBarView: View {
|
|
|
75
76
|
)
|
|
76
77
|
.offset(y: offsetY)
|
|
77
78
|
.padding(.bottom, bottomInset)
|
|
79
|
+
// iPhone Duo: clear of the side toolbar strip, like the screen content (0 elsewhere)
|
|
80
|
+
.padding(.leading, layout.insetLeft)
|
|
81
|
+
.padding(.trailing, layout.insetRight)
|
|
78
82
|
}
|
|
79
83
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
80
84
|
.ignoresSafeArea()
|
|
@@ -34,11 +34,15 @@ struct ModalScreen<Content: View>: View {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
content()
|
|
37
|
+
// iPhone Duo: capped so it stays a dialog on the inner display; nil leaves the size alone
|
|
38
|
+
.frame(
|
|
39
|
+
maxWidth: layout.isDuoDevice ? layout.duoModalMaxWidth : nil,
|
|
40
|
+
maxHeight: layout.isDuoDevice ? layout.duoModalMaxHeight : nil
|
|
41
|
+
)
|
|
37
42
|
.scaleEffect(contentScale)
|
|
38
43
|
.opacity(contentOpacity)
|
|
39
|
-
// iPhone Duo: center
|
|
40
|
-
.padding(
|
|
41
|
-
.padding(.bottom, layout.isDuoDevice ? layout.insets.bottom : 0)
|
|
44
|
+
// iPhone Duo: center in the safe area, clear of the side toolbar strip and the home indicator
|
|
45
|
+
.padding(layout.isDuoDevice ? layout.insets : EdgeInsets())
|
|
42
46
|
}
|
|
43
47
|
// Full screen like RN's Modal, so asymmetric landscape insets don't shift the content sideways.
|
|
44
48
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
@@ -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,19 +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
|
-
/// Card width cap (Compose `popupMaxWidth`); unbounded
|
|
5
|
+
/// Card width cap on iPhone Duo (the modal bounds, Compose `popupMaxWidth`); unbounded elsewhere,
|
|
6
|
+
/// where the popup's own padding sets the width.
|
|
9
7
|
var popupMaxWidth: CGFloat {
|
|
10
|
-
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
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
///
|
|
16
|
+
/// Tallest a popup gets on iPhone Duo, landscape and the inner display included; nil elsewhere.
|
|
14
17
|
var popupMaxHeight: CGFloat? {
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
17
24
|
}
|
|
18
25
|
}
|
|
19
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
|
}
|
|
@@ -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
|
|
@@ -58,7 +66,10 @@ public struct FontScale: Equatable {
|
|
|
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
|
}
|
package/ios/native-kits.podspec
CHANGED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# MoMoUIKitsDemo
|
|
2
|
+
|
|
3
|
+
SwiftUI demo browser for `MoMoUIKits`.
|
|
4
|
+
|
|
5
|
+
The package can optionally host a Compose UIKits screen by receiving a
|
|
6
|
+
`UIViewController` factory from the host app. It does not import the KMP
|
|
7
|
+
`shared` module directly.
|
|
8
|
+
|
|
9
|
+
SwiftUI mode reuses the full demo screen set from
|
|
10
|
+
`sample/iosApp/iosApp/Demo`, including the sample `HomeView` registry and demo
|
|
11
|
+
screens for all currently available components.
|
|
12
|
+
|
|
13
|
+
## CocoaPods
|
|
14
|
+
|
|
15
|
+
In the host app `Podfile`, include both local pods:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
pod 'MoMoUIKits', :path => '../momo-native-kits/ios/native-kits.podspec'
|
|
19
|
+
pod 'MoMoUIKitsDemo', :path => '../momo-native-kits/ios-demo'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Adjust the relative paths to match the host app checkout.
|
|
23
|
+
|
|
24
|
+
## Host App Usage
|
|
25
|
+
|
|
26
|
+
```swift
|
|
27
|
+
import MoMoUIKitsDemo
|
|
28
|
+
import shared
|
|
29
|
+
|
|
30
|
+
class UIKitsViewController: BaseComposeAppViewController {
|
|
31
|
+
override func getViewController() -> UIViewController? {
|
|
32
|
+
let params = self.application.data as? [String: Any]
|
|
33
|
+
|
|
34
|
+
return MoMoUIKitsDemoViewControllerFactory.make(
|
|
35
|
+
context: MoMoUIKitsDemoContext(
|
|
36
|
+
appId: params?["appId"] as? String ?? "",
|
|
37
|
+
appCode: params?["code"] as? String ?? "",
|
|
38
|
+
appName: params?["name"] as? String,
|
|
39
|
+
appIcon: params?["icon"] as? String ?? ""
|
|
40
|
+
),
|
|
41
|
+
composeViewController: {
|
|
42
|
+
UIKitsScreenComposeViewControllerKt.UIKitsScreenComposeViewController(
|
|
43
|
+
params: params
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
override func viewDidDisappear(_ animated: Bool) {
|
|
50
|
+
super.viewDidDisappear(animated)
|
|
51
|
+
composeViewController = nil
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If the host does not pass `composeViewController`, the demo opens directly in
|
|
57
|
+
SwiftUI mode.
|
|
58
|
+
|
|
59
|
+
## Included SwiftUI Demos
|
|
60
|
+
|
|
61
|
+
The pod includes the sample demo screens for:
|
|
62
|
+
|
|
63
|
+
- Animated Header
|
|
64
|
+
- Avatar
|
|
65
|
+
- Badge
|
|
66
|
+
- Baseline View
|
|
67
|
+
- Bottom Tab
|
|
68
|
+
- Button
|
|
69
|
+
- Carousel
|
|
70
|
+
- Checkbox
|
|
71
|
+
- Chip
|
|
72
|
+
- Collapse
|
|
73
|
+
- DateTimePicker
|
|
74
|
+
- Divider
|
|
75
|
+
- Header User
|
|
76
|
+
- Icon Button
|
|
77
|
+
- Image
|
|
78
|
+
- Information
|
|
79
|
+
- Input
|
|
80
|
+
- Input DropDown
|
|
81
|
+
- Input Money
|
|
82
|
+
- Input OTP
|
|
83
|
+
- Input Phone Number
|
|
84
|
+
- Input Search
|
|
85
|
+
- Input TextArea
|
|
86
|
+
- Loader
|
|
87
|
+
- Navigation
|
|
88
|
+
- Pagination
|
|
89
|
+
- Popup Display
|
|
90
|
+
- Popup Notify
|
|
91
|
+
- Popup Promotion
|
|
92
|
+
- Progress Info
|
|
93
|
+
- Radio
|
|
94
|
+
- Rating
|
|
95
|
+
- Skeleton
|
|
96
|
+
- Slider
|
|
97
|
+
- SnackBar
|
|
98
|
+
- Stepper
|
|
99
|
+
- Steps
|
|
100
|
+
- Suggest Action
|
|
101
|
+
- Swipe
|
|
102
|
+
- Switch
|
|
103
|
+
- TabView
|
|
104
|
+
- Tag
|
|
105
|
+
- Theme
|
|
106
|
+
- Title
|
|
107
|
+
- Tooltip
|
|
108
|
+
- Typography
|
|
109
|
+
- Uploader
|
|
110
|
+
|
|
111
|
+
## Validation Notes
|
|
112
|
+
|
|
113
|
+
Validated locally with:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
pod ipc spec ios/MoMoUIKits.podspec
|
|
117
|
+
pod ipc spec ios-demo/MoMoUIKitsDemo.podspec
|
|
118
|
+
pod lib lint ios/MoMoUIKits.podspec --allow-warnings --skip-import-validation
|
|
119
|
+
pod lib lint ios-demo/MoMoUIKitsDemo.podspec --allow-warnings --skip-import-validation --include-podspecs=ios/MoMoUIKits.podspec
|
|
120
|
+
```
|