@momo-kits/native-kits 0.163.1-beta.3-debug → 0.163.1-beta.5-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 (215) hide show
  1. package/CLAUDE.md +1 -1
  2. package/compose/build.gradle.kts +2 -1
  3. package/compose/build.gradle.kts.backup +1 -0
  4. package/compose/compose.podspec +1 -1
  5. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +7 -0
  6. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +8 -10
  7. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +3 -0
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +56 -0
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +11 -9
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +40 -55
  11. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +69 -26
  12. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +3 -1
  13. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +21 -0
  14. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +3 -6
  15. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +22 -0
  16. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +28 -21
  17. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +4 -0
  18. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +112 -115
  19. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +11 -6
  20. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +3 -4
  21. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +3 -1
  22. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +2 -1
  23. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +9 -0
  24. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +66 -0
  25. package/gradle/libs.versions.toml +2 -0
  26. package/gradle.properties +1 -1
  27. package/ios/Application/ApplicationEnvironment.swift +11 -1
  28. package/ios/Application/Components.swift +34 -44
  29. package/ios/Application/FloatingButton.swift +10 -9
  30. package/ios/Application/HeaderRight.swift +79 -45
  31. package/ios/Application/Navigation/BottomTab/BottomTab.swift +103 -0
  32. package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +215 -0
  33. package/ios/Application/Navigation/BottomTab/CurvedContainer.swift +65 -0
  34. package/ios/Application/Navigation/HeaderBackProps.swift +66 -0
  35. package/ios/Application/Navigation/HeaderTitle.swift +57 -0
  36. package/ios/Application/Navigation/HeaderUser.swift +209 -0
  37. package/ios/Application/Navigation/NavigationContainer.swift +176 -0
  38. package/ios/Application/Navigation/NavigationOptions.swift +109 -0
  39. package/ios/Application/Navigation/Navigator.swift +364 -0
  40. package/ios/Application/Navigation/Overplay/BottomSheet.swift +188 -0
  41. package/ios/Application/Navigation/Overplay/ModalScreen.swift +68 -0
  42. package/ios/Application/Navigation/Overplay/SnackBar.swift +144 -0
  43. package/ios/Application/Navigation/component/Header.swift +178 -0
  44. package/ios/Application/Navigation/component/HeaderBackground.swift +83 -0
  45. package/ios/Application/Navigation/component/HeaderColor.swift +65 -0
  46. package/ios/Application/Screen.swift +2 -1
  47. package/ios/Application/StackScreen.swift +453 -0
  48. package/ios/Avatar/Avatar.swift +193 -0
  49. package/ios/Badge/Badge.swift +11 -14
  50. package/ios/Badge/BadgeRibbon.swift +12 -21
  51. package/ios/BaselineView/BaselineView.swift +163 -0
  52. package/ios/Button/Button.swift +61 -22
  53. package/ios/Carousel/Carousel.swift +181 -0
  54. package/ios/Checkbox/Checkbox.swift +14 -13
  55. package/ios/Chip/Chip.swift +23 -9
  56. package/ios/Collapse/Collapse.swift +182 -0
  57. package/ios/Colors+Radius+Spacing/Colors.swift +12 -12
  58. package/ios/Colors+Radius+Spacing/Theme.swift +295 -0
  59. package/ios/DateTimePicker/DateTimePicker.swift +210 -0
  60. package/ios/DateTimePicker/DateTimePickerTypes.swift +55 -0
  61. package/ios/DateTimePicker/DateTimePickerUtils.swift +167 -0
  62. package/ios/DateTimePicker/WheelPicker.swift +161 -0
  63. package/ios/Divider/Divider.swift +16 -0
  64. package/ios/Extensions/Color++.swift +32 -0
  65. package/ios/Icon/Icon.swift +6 -1
  66. package/ios/IconButton/IconButton.swift +112 -0
  67. package/ios/Input/Input.swift +41 -25
  68. package/ios/Input/InputPhoneNumber.swift +20 -19
  69. package/ios/Input/InputSearch.swift +100 -47
  70. package/ios/Input/InputTextArea.swift +66 -40
  71. package/ios/InputDropDown/InputDropDown.swift +198 -0
  72. package/ios/InputMoney/InputMoney.swift +335 -0
  73. package/ios/InputOTP/InputOTP.swift +210 -0
  74. package/ios/Loader/Loader.swift +113 -0
  75. package/ios/MoMoUIKits.podspec +5 -0
  76. package/ios/Pagination/PaginationDot.swift +61 -0
  77. package/ios/Pagination/PaginationNumber.swift +35 -0
  78. package/ios/Pagination/PaginationScroll.swift +101 -0
  79. package/ios/Pagination/PaginationWhiteDot.swift +37 -0
  80. package/ios/Popup/PopupDisplay.swift +8 -16
  81. package/ios/Popup/PopupInput.swift +2 -8
  82. package/ios/Popup/PopupNotify.swift +219 -0
  83. package/ios/ProgressInfo/ProgressInfo.swift +294 -0
  84. package/ios/Radio/Radio.swift +70 -0
  85. package/ios/Rating/Rating.swift +82 -0
  86. package/ios/Resources/MoMoUIKitsColors.xcassets/AccentColor.colorset/Contents.json +11 -0
  87. package/ios/Resources/MoMoUIKitsColors.xcassets/Background.colorset/Contents.json +38 -0
  88. package/ios/Resources/MoMoUIKitsColors.xcassets/Border.colorset/Contents.json +38 -0
  89. package/ios/Resources/MoMoUIKitsColors.xcassets/Card.colorset/Contents.json +38 -0
  90. package/ios/Resources/MoMoUIKitsColors.xcassets/Contents.json +6 -0
  91. package/ios/Resources/MoMoUIKitsColors.xcassets/Error.colorset/Contents.json +38 -0
  92. package/ios/Resources/MoMoUIKitsColors.xcassets/Primary.colorset/Contents.json +38 -0
  93. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryDark.colorset/Contents.json +38 -0
  94. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryLight.colorset/Contents.json +38 -0
  95. package/ios/Resources/MoMoUIKitsColors.xcassets/Secondary.colorset/Contents.json +38 -0
  96. package/ios/Resources/MoMoUIKitsColors.xcassets/Success.colorset/Contents.json +38 -0
  97. package/ios/Resources/MoMoUIKitsColors.xcassets/Text.colorset/Contents.json +38 -0
  98. package/ios/Resources/MoMoUIKitsColors.xcassets/TextSecondary.colorset/Contents.json +38 -0
  99. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/Contents.json +9 -0
  100. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-default.colorset/Contents.json +38 -0
  101. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-disable.colorset/Contents.json +38 -0
  102. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-pressed.colorset/Contents.json +38 -0
  103. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-selected.colorset/Contents.json +38 -0
  104. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-surface.colorset/Contents.json +38 -0
  105. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-tonal.colorset/Contents.json +38 -0
  106. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-default.colorset/Contents.json +38 -0
  107. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-disable.colorset/Contents.json +38 -0
  108. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/gradient.colorset/Contents.json +38 -0
  109. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/primary.colorset/Contents.json +38 -0
  110. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/secondary.colorset/Contents.json +38 -0
  111. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-container.colorset/Contents.json +38 -0
  112. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-primary.colorset/Contents.json +38 -0
  113. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-secondary.colorset/Contents.json +38 -0
  114. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-default.colorset/Contents.json +38 -0
  115. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-disable.colorset/Contents.json +38 -0
  116. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-hint.colorset/Contents.json +38 -0
  117. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-secondary.colorset/Contents.json +38 -0
  118. package/ios/Resources/MoMoUIKitsColors.xcassets/Warning.colorset/Contents.json +38 -0
  119. package/ios/Skeleton/Skeleton.swift +99 -0
  120. package/ios/Slider/Slider.swift +237 -0
  121. package/ios/Stepper/Stepper.swift +220 -0
  122. package/ios/Steps/Steps.swift +451 -0
  123. package/ios/SuggestAction/SuggestAction.swift +97 -0
  124. package/ios/Swipeable/SwipeCell.swift +185 -0
  125. package/ios/Swipeable/SwipeCellModel.swift +21 -0
  126. package/ios/TabView/TabView.swift +530 -0
  127. package/ios/Tag/Tag.swift +98 -0
  128. package/ios/Title/Title.swift +269 -0
  129. package/ios/Tooltip/Tooltip.swift +482 -0
  130. package/ios/Typography/FontScaleStore.swift +19 -0
  131. package/ios/Typography/Text.swift +86 -17
  132. package/ios/Typography/Typography.swift +12 -14
  133. package/ios/Uploader/Uploader.swift +183 -0
  134. package/ios/native-kits.podspec +61 -8
  135. package/ios-demo/MoMoUIKitsDemo.podspec +20 -0
  136. package/ios-demo/README.md +120 -0
  137. package/ios-demo/Resources/dot_loading.json +558 -0
  138. package/ios-demo/Resources/icon.json +4052 -0
  139. package/ios-demo/Resources/lottie_circle_loader.json +1 -0
  140. package/ios-demo/Sources/MoMoUIKitsDemo/DemoScaffold.swift +48 -0
  141. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsBadgeDemoView.swift +25 -0
  142. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsButtonDemoView.swift +26 -0
  143. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsCheckboxDemoView.swift +19 -0
  144. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsChipDemoView.swift +33 -0
  145. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsInputDemoView.swift +48 -0
  146. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsPopupDemoView.swift +21 -0
  147. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsRadioDemoView.swift +17 -0
  148. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsTypographyDemoView.swift +28 -0
  149. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoContext.swift +31 -0
  150. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoFactory.swift +24 -0
  151. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AnimatedHeaderDemo.swift +110 -0
  152. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +93 -0
  153. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +269 -0
  154. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BaselineView.swift +117 -0
  155. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BottomTabDemo.swift +89 -0
  156. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +100 -0
  157. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +126 -0
  158. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +63 -0
  159. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +20 -0
  160. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +93 -0
  161. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +107 -0
  162. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +124 -0
  163. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +14 -0
  164. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +49 -0
  165. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +278 -0
  166. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +58 -0
  167. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +78 -0
  168. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +138 -0
  169. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +111 -0
  170. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +136 -0
  171. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +107 -0
  172. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +73 -0
  173. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +100 -0
  174. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +32 -0
  175. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +57 -0
  176. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +70 -0
  177. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +227 -0
  178. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +72 -0
  179. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +175 -0
  180. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +121 -0
  181. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +76 -0
  182. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +89 -0
  183. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +32 -0
  184. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +56 -0
  185. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +32 -0
  186. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +150 -0
  187. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +138 -0
  188. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +103 -0
  189. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +94 -0
  190. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +79 -0
  191. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +152 -0
  192. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +33 -0
  193. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +190 -0
  194. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +34 -0
  195. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +182 -0
  196. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +109 -0
  197. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +187 -0
  198. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +87 -0
  199. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +131 -0
  200. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +51 -0
  201. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +15 -0
  202. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +73 -0
  203. package/package.json +1 -1
  204. package/publish.sh +50 -0
  205. package/scripts/gen-ios-color.mjs +136 -0
  206. package/example/ios/Example.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  207. package/example/ios/Example.xcworkspace/xcuserdata/huynhdung.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  208. package/example/ios/Example.xcworkspace/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +0 -5
  209. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  210. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  211. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SDWebImage.xcscheme +0 -58
  212. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SDWebImageSwiftUI.xcscheme +0 -58
  213. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/SkeletonUI.xcscheme +0 -58
  214. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/huynhdung.xcuserdatad/xcschemes/xcschememanagement.plist +0 -46
  215. package/ios/Application/FontScaleStore.swift +0 -59
@@ -0,0 +1,193 @@
1
+ import SwiftUI
2
+
3
+ let LOGO_MOMO_URL =
4
+ "https://static.momocdn.net/app/img/kits/logo_momo_circle.png"
5
+ let ICON_SUPPORT_DEFAULT_URL =
6
+ "https://static.momocdn.net/app/img/kits/_indicator.png"
7
+
8
+ public enum AvatarSize {
9
+ case size24
10
+ case size32
11
+ case size40
12
+ case size48
13
+ case size56
14
+ case size72
15
+
16
+ var sizeDp: CGFloat {
17
+ switch self {
18
+ case .size24: return 24
19
+ case .size32: return 32
20
+ case .size40: return 40
21
+ case .size48: return 48
22
+ case .size56: return 56
23
+ case .size72: return 72
24
+ }
25
+ }
26
+
27
+ var iconSizeDp: CGFloat {
28
+ switch self {
29
+ case .size24: return 16
30
+ case .size32: return 16
31
+ case .size40: return 24
32
+ case .size48: return 32
33
+ case .size56: return 32
34
+ case .size72: return 40
35
+ }
36
+ }
37
+ }
38
+
39
+ public struct Avatar: View {
40
+ @Environment(\.appTheme) private var theme
41
+
42
+ // MARK: Lifecycle
43
+
44
+ public init(
45
+ size: AvatarSize = .size32,
46
+ rounded: Bool = true,
47
+ name: String? = nil,
48
+ image: String? = nil,
49
+ showIconMomo: Bool = false,
50
+ showIconSupport: Bool = false,
51
+ iconSupport: String = "https://static.momocdn.net/app/img/kits/_indicator.png",
52
+ accessibilityLabel: String? = nil
53
+ ) {
54
+ self.size = size
55
+ self.rounded = rounded
56
+ self.name = name
57
+ self.image = image
58
+ self.showIconMomo = showIconMomo
59
+ self.showIconSupport = showIconSupport
60
+ self.iconSupport = iconSupport
61
+ self.accessibilityLabel = accessibilityLabel
62
+ }
63
+
64
+ // MARK: Public
65
+
66
+ public var body: some View {
67
+ ZStack {
68
+ // Inner content clipped by shape with border
69
+ ZStack {
70
+ content
71
+ }
72
+ .frame(width: size.sizeDp, height: size.sizeDp)
73
+ .background(Colors.pink09)
74
+ .clipShape(RoundedRectangle(cornerRadius: cornerRadius))
75
+ .overlay(
76
+ RoundedRectangle(cornerRadius: cornerRadius)
77
+ .stroke(theme.colors.border.default, lineWidth: 1)
78
+ )
79
+
80
+ // Badge icons — outside clip so they are not cut by border
81
+ if shouldShowIconTop {
82
+ ImageView(LOGO_MOMO_URL)
83
+ .frame(width: badgeSizeDp, height: badgeSizeDp)
84
+ .clipShape(RoundedRectangle(cornerRadius: badgeCornerRadius))
85
+ .offset(x: 1, y: -1)
86
+ .frame(
87
+ width: size.sizeDp,
88
+ height: size.sizeDp,
89
+ alignment: .topTrailing
90
+ )
91
+ .zIndex(1)
92
+ }
93
+
94
+ if shouldShowIconBottom {
95
+ ImageView(iconSupport)
96
+ .frame(width: badgeSizeDp, height: badgeSizeDp)
97
+ .clipShape(RoundedRectangle(cornerRadius: badgeCornerRadius))
98
+ .offset(x: 1, y: 1)
99
+ .frame(
100
+ width: size.sizeDp,
101
+ height: size.sizeDp,
102
+ alignment: .bottomTrailing
103
+ )
104
+ .zIndex(1)
105
+ }
106
+ }
107
+ .frame(width: size.sizeDp, height: size.sizeDp)
108
+ .accessibilityElement(children: .ignore)
109
+ .accessibilityLabel(automationId)
110
+ }
111
+
112
+ // MARK: Internal
113
+
114
+ let size: AvatarSize
115
+ let rounded: Bool
116
+ let name: String?
117
+ let image: String?
118
+ let showIconMomo: Bool
119
+ let showIconSupport: Bool
120
+ let iconSupport: String
121
+ let accessibilityLabel: String?
122
+
123
+ @ViewBuilder
124
+ private var content: some View {
125
+ if let image {
126
+ ImageView(image, contentMode: .fill)
127
+ .frame(width: size.sizeDp, height: size.sizeDp)
128
+ .clipShape(RoundedRectangle(cornerRadius: cornerRadius))
129
+ } else if let name {
130
+ MomoText(
131
+ Self.avatarInitials(name),
132
+ typography: .descriptionXsRegular,
133
+ color: Colors.pink03
134
+ )
135
+ } else {
136
+ Icon(
137
+ source: "basic_person",
138
+ size: size.iconSizeDp,
139
+ color: Colors.pink03
140
+ )
141
+ }
142
+ }
143
+
144
+ private var cornerRadius: CGFloat {
145
+ rounded ? size.sizeDp / 2 : Spacing.XS
146
+ }
147
+
148
+ private var badgeSizeDp: CGFloat {
149
+ size.iconSizeDp / 2
150
+ }
151
+
152
+ private var badgeCornerRadius: CGFloat {
153
+ size.iconSizeDp / 4
154
+ }
155
+
156
+ private var shouldShowIconTop: Bool {
157
+ showIconMomo && rounded
158
+ }
159
+
160
+ private var shouldShowIconBottom: Bool {
161
+ showIconSupport && rounded
162
+ }
163
+
164
+ private var automationId: String {
165
+ accessibilityLabel ?? "Avatar\(name != nil ? "/\(name!)" : "")"
166
+ }
167
+
168
+ private static func avatarInitials(_ name: String) -> String {
169
+ let words = name.trimmingCharacters(in: .whitespaces)
170
+ .split(separator: " ")
171
+ .filter { !$0.isEmpty }
172
+ return words.suffix(2)
173
+ .compactMap { $0.first?.uppercased() }
174
+ .joined()
175
+ }
176
+ }
177
+
178
+ // MARK: - Preview
179
+ #if DEBUG
180
+ struct Avatar_Previews: PreviewProvider {
181
+ static var previews: some View {
182
+ HStack(spacing: 12) {
183
+ Avatar(size: .size40)
184
+ Avatar(size: .size40, name: "Nguyen Van A")
185
+ Avatar(size: .size40, rounded: false, name: "John Doe")
186
+ Avatar(size: .size56, image: "https://i.pravatar.cc/150?img=1", showIconMomo: true)
187
+ Avatar(size: .size56, showIconSupport: true)
188
+ }
189
+ .padding()
190
+ .previewLayout(.sizeThatFits)
191
+ }
192
+ }
193
+ #endif
@@ -1,6 +1,7 @@
1
1
  import SwiftUI
2
2
 
3
3
  public struct Badge: View {
4
+ @Environment(\.appTheme) private var theme
4
5
  let label: String
5
6
  let backgroundColor: Color?
6
7
 
@@ -38,24 +39,21 @@ public struct Badge: View {
38
39
  }
39
40
 
40
41
  private var badgeColor: Color {
41
- // If a custom background color is provided, use it
42
- if let bgColor = backgroundColor {
42
+ // Default colors based on label type
43
+ let defaultColor: Color = isNumber(label)
44
+ ? theme.colors.error.primary
45
+ : theme.colors.warning.primary
46
+
47
+ // Mirror Compose: only apply backgroundColor when it is one of the
48
+ // allowed primaryColors; otherwise fall back to the label-based default.
49
+ if let bgColor = backgroundColor, primaryColors.contains(bgColor) {
43
50
  return bgColor
44
51
  }
45
-
46
- // Otherwise, use default colors based on label type
47
- if isNumber(label) {
48
- return Colors.red03 // error.primary
49
- } else {
50
- return Colors.orange03 // warning.primary
51
- }
52
+ return defaultColor
52
53
  }
53
54
 
54
55
  public var body: some View {
55
- Text(formatTitle(label))
56
- .font(.system(size: scaleSize(10), weight: .bold))
57
- .ignoreBoldTextSetting()
58
- .foregroundColor(Colors.black01)
56
+ MomoText(formatTitle(label), typography: .actionXxsBold, color: Colors.black01)
59
57
  .padding(.horizontal, Spacing.XS)
60
58
  .frame(minWidth: scaleSize(16), minHeight: scaleSize(16))
61
59
  .background(badgeColor)
@@ -83,4 +81,3 @@ struct Badge_Previews: PreviewProvider {
83
81
  }
84
82
  }
85
83
  #endif
86
-
@@ -77,10 +77,11 @@ public enum RibbonPosition {
77
77
  }
78
78
 
79
79
  public struct BadgeRibbon: View {
80
+ @Environment(\.appTheme) private var theme
80
81
  let position: RibbonPosition
81
82
  let label: String
82
83
  let isRound: Bool
83
- let backgroundColor: Color
84
+ let backgroundColor: Color?
84
85
 
85
86
  public init(
86
87
  position: RibbonPosition = .topRight,
@@ -91,7 +92,7 @@ public struct BadgeRibbon: View {
91
92
  self.position = position
92
93
  self.label = label
93
94
  self.isRound = isRound
94
- self.backgroundColor = backgroundColor ?? Colors.orange03
95
+ self.backgroundColor = backgroundColor
95
96
  }
96
97
 
97
98
  private var rotation: Angle {
@@ -117,6 +118,8 @@ public struct BadgeRibbon: View {
117
118
  }
118
119
 
119
120
  public var body: some View {
121
+ let bgColor = backgroundColor ?? theme.colors.warning.primary
122
+
120
123
  HStack(alignment: verticalAlignment, spacing: 0) {
121
124
  if useUpTail {
122
125
  upTail
@@ -125,22 +128,18 @@ public struct BadgeRibbon: View {
125
128
  }
126
129
 
127
130
  if isRound {
128
- roundContent
131
+ roundContent(backgroundColor: bgColor)
129
132
  } else {
130
- skewContent
133
+ skewContent(backgroundColor: bgColor)
131
134
  }
132
135
  }
133
136
  .frame(height: RibbonConstants.ribbonHeight)
134
137
  .rotationEffect(rotation)
135
138
  }
136
139
 
137
- private var roundContent: some View {
140
+ private func roundContent(backgroundColor: Color) -> some View {
138
141
  HStack(spacing: 0) {
139
- Text(label)
140
- .font(.system(size: scaleSize(12), weight: .medium))
141
- .ignoreBoldTextSetting()
142
- .foregroundColor(Colors.black01)
143
- .lineLimit(1)
142
+ MomoText(label, typography: .labelXsMedium, color: Colors.black01, maxLines: 1)
144
143
  .rotationEffect(rotation)
145
144
  }
146
145
  .frame(height: RibbonConstants.roundHeight)
@@ -156,13 +155,9 @@ public struct BadgeRibbon: View {
156
155
  )
157
156
  }
158
157
 
159
- private var skewContent: some View {
158
+ private func skewContent(backgroundColor: Color) -> some View {
160
159
  HStack(spacing: 0) {
161
- Text(label)
162
- .font(.system(size: scaleSize(12), weight: .medium))
163
- .ignoreBoldTextSetting()
164
- .foregroundColor(Colors.black01)
165
- .lineLimit(1)
160
+ MomoText(label, typography: .labelXsMedium, color: Colors.black01, maxLines: 1)
166
161
  .rotationEffect(rotation)
167
162
  .frame(height: RibbonConstants.skewBodyHeight)
168
163
  .padding(.horizontal, 8)
@@ -179,7 +174,6 @@ public struct BadgeRibbon: View {
179
174
  )
180
175
  .frame(width: RibbonConstants.headTailWidth, height: RibbonConstants.headTailHeight)
181
176
  .rotationEffect(.degrees(180))
182
- .offset(x: 1)
183
177
  .clipped()
184
178
  }
185
179
 
@@ -189,7 +183,6 @@ public struct BadgeRibbon: View {
189
183
  placeholder: AnyView(Color.clear)
190
184
  )
191
185
  .frame(width: RibbonConstants.headTailWidth, height: RibbonConstants.headTailHeight)
192
- .offset(x: 1)
193
186
  .clipped()
194
187
  }
195
188
 
@@ -199,7 +192,6 @@ public struct BadgeRibbon: View {
199
192
  placeholder: AnyView(Color.clear)
200
193
  )
201
194
  .frame(width: RibbonConstants.skewTailWidth, height: RibbonConstants.skewTailHeight)
202
- .offset(x: -2)
203
195
  .clipped()
204
196
  }
205
197
  }
@@ -209,7 +201,7 @@ private struct RibbonConstants {
209
201
  static let ribbonHeight: CGFloat = 20
210
202
  static let roundHeight: CGFloat = 16
211
203
  static let skewBodyHeight: CGFloat = 16
212
- static let roundRightRadius: CGFloat = 8
204
+ static let roundRightRadius: CGFloat = 12
213
205
  static let roundPaddingEnd: CGFloat = 6
214
206
  static let skewTailWidth: CGFloat = 8
215
207
  static let skewTailHeight: CGFloat = 16
@@ -241,4 +233,3 @@ struct BadgeRibbon_Previews: PreviewProvider {
241
233
  }
242
234
  }
243
235
  #endif
244
-
@@ -0,0 +1,163 @@
1
+ import SwiftUI
2
+
3
+ public struct BaselineView: View {
4
+ private let enabled: Bool
5
+
6
+ public init(enabled: Bool = true) {
7
+ self.enabled = enabled
8
+ }
9
+
10
+ public var body: some View {
11
+ if enabled {
12
+ GeometryReader { geometry in
13
+ let topInset = geometry.safeAreaInsets.top
14
+ let bottomInset = min(geometry.safeAreaInsets.bottom, 21)
15
+
16
+ ZStack(alignment: .topLeading) {
17
+ dangerZones(topInset: topInset, bottomInset: bottomInset)
18
+ guideLines(
19
+ topInset: topInset,
20
+ bottomInset: bottomInset,
21
+ width: geometry.size.width
22
+ )
23
+ }
24
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
25
+ .ignoresSafeArea()
26
+ .allowsHitTesting(false)
27
+ .accessibilityHidden(true)
28
+ }
29
+ }
30
+ }
31
+
32
+ private func dangerZones(topInset: CGFloat, bottomInset: CGFloat) -> some View {
33
+ let safeBackground = Color.red.opacity(0.15)
34
+
35
+ return ZStack(alignment: .topLeading) {
36
+ safeBackground
37
+ .frame(maxWidth: .infinity)
38
+ .frame(height: topInset)
39
+
40
+ HStack(spacing: 0) {
41
+ safeBackground
42
+ .frame(width: 12)
43
+ Spacer(minLength: 0)
44
+ safeBackground
45
+ .frame(width: 12)
46
+ }
47
+ .padding(.top, topInset)
48
+ .padding(.bottom, bottomInset)
49
+
50
+ VStack(spacing: 0) {
51
+ Spacer(minLength: 0)
52
+ safeBackground
53
+ .frame(maxWidth: .infinity)
54
+ .frame(height: bottomInset)
55
+ }
56
+ }
57
+ }
58
+
59
+ private func guideLines(topInset: CGFloat, bottomInset: CGFloat, width: CGFloat) -> some View {
60
+ ZStack(alignment: .topLeading) {
61
+ baselineLine(isDotted: false, color: Color(hex: 0xE400FF))
62
+ .offset(y: topInset)
63
+
64
+ baselineLine(color: Color(hex: 0xE400FF))
65
+ .offset(y: topInset + 52)
66
+
67
+ baselineLine(color: Color(hex: 0xE400FF))
68
+ .offset(y: topInset + 104)
69
+
70
+ bottomLine(bottomInset: bottomInset + 64, color: Color(hex: 0xE400FF))
71
+
72
+ bottomLine(bottomInset: bottomInset, isDotted: false, color: Color(hex: 0xE400FF))
73
+
74
+ baselineLine(orientation: .vertical, isDotted: false, color: Color(hex: 0xE400FF))
75
+ .offset(x: 12)
76
+
77
+ baselineLine(orientation: .vertical, isDotted: false, color: Color(hex: 0xE400FF))
78
+ .offset(x: width - 12)
79
+
80
+ baselineLine(color: Color(hex: 0xFF7A00))
81
+ .offset(y: topInset + 26)
82
+
83
+ bottomLine(bottomInset: bottomInset + 56, color: Color(hex: 0xFFCC00))
84
+
85
+ bottomLine(bottomInset: bottomInset + 8, color: Color(hex: 0xFFCC00))
86
+
87
+ baselineLine(orientation: .vertical, color: Color(hex: 0x00C520))
88
+ .frame(height: 52)
89
+ .offset(x: 40, y: topInset)
90
+
91
+ baselineLine(orientation: .vertical, color: Color(hex: 0x00C520))
92
+ .frame(height: 52)
93
+ .offset(x: 48, y: topInset)
94
+ }
95
+ }
96
+
97
+ private func bottomLine(
98
+ bottomInset: CGFloat,
99
+ isDotted: Bool = true,
100
+ color: Color
101
+ ) -> some View {
102
+ VStack(spacing: 0) {
103
+ Spacer(minLength: 0)
104
+ baselineLine(isDotted: isDotted, color: color)
105
+ .padding(.bottom, bottomInset)
106
+ }
107
+ }
108
+
109
+ private func baselineLine(
110
+ orientation: BaselineOrientation = .horizontal,
111
+ isDotted: Bool = true,
112
+ color: Color
113
+ ) -> some View {
114
+ BaselineLineShape(orientation: orientation)
115
+ .stroke(
116
+ color,
117
+ style: StrokeStyle(
118
+ lineWidth: 1,
119
+ lineCap: .round,
120
+ dash: isDotted ? [8, 8] : []
121
+ )
122
+ )
123
+ .frame(
124
+ maxWidth: orientation == .horizontal ? .infinity : 1,
125
+ maxHeight: orientation == .vertical ? .infinity : 1
126
+ )
127
+ }
128
+ }
129
+
130
+ public enum BaselineOrientation: Sendable {
131
+ case horizontal
132
+ case vertical
133
+ }
134
+
135
+ private struct BaselineLineShape: Shape {
136
+ let orientation: BaselineOrientation
137
+
138
+ func path(in rect: CGRect) -> Path {
139
+ var path = Path()
140
+
141
+ switch orientation {
142
+ case .horizontal:
143
+ path.move(to: CGPoint(x: rect.minX, y: rect.midY))
144
+ path.addLine(to: CGPoint(x: rect.maxX, y: rect.midY))
145
+ case .vertical:
146
+ path.move(to: CGPoint(x: rect.midX, y: rect.minY))
147
+ path.addLine(to: CGPoint(x: rect.midX, y: rect.maxY))
148
+ }
149
+
150
+ return path
151
+ }
152
+ }
153
+
154
+ #if DEBUG
155
+ struct BaselineView_Previews: PreviewProvider {
156
+ static var previews: some View {
157
+ ZStack {
158
+ Colors.black01
159
+ BaselineView()
160
+ }
161
+ }
162
+ }
163
+ #endif
@@ -31,33 +31,45 @@ public struct ButtonStyleData {
31
31
 
32
32
  public extension ButtonType {
33
33
  func backgroundColor(loading: Bool) -> Color {
34
+ backgroundColor(loading: loading, theme: defaultTheme)
35
+ }
36
+
37
+ func backgroundColor(loading: Bool, theme: Theme) -> Color {
34
38
  switch self {
35
- case .disabled: return Colors.black05.opacity(loading ? 0.75 : 1)
36
- case .primary: return Colors.primary.opacity(loading ? 0.75 : 1)
37
- case .secondary: return Colors.card.opacity(loading ? 0.75 : 1)
38
- case .outline: return Colors.card.opacity(loading ? 0.75 : 1)
39
- case .tonal: return Colors.primaryLight.opacity(loading ? 0.75 : 1)
40
- case .danger: return Colors.error.opacity(loading ? 0.75 : 1)
39
+ case .disabled: return theme.colors.background.disable.opacity(loading ? 0.75 : 1)
40
+ case .primary: return theme.colors.primary.opacity(loading ? 0.75 : 1)
41
+ case .secondary: return theme.colors.background.surface.opacity(loading ? 0.75 : 1)
42
+ case .outline: return theme.colors.background.surface.opacity(loading ? 0.75 : 1)
43
+ case .tonal: return theme.colors.background.tonal.opacity(loading ? 0.75 : 1)
44
+ case .danger: return theme.colors.error.primary.opacity(loading ? 0.75 : 1)
41
45
  case .text: return .clear
42
46
  }
43
47
  }
44
48
 
45
49
  func contentColor(loading: Bool) -> Color {
50
+ contentColor(loading: loading, theme: defaultTheme)
51
+ }
52
+
53
+ func contentColor(loading: Bool, theme: Theme) -> Color {
46
54
  switch self {
47
- case .disabled: return Colors.black06.opacity(loading ? 0.75 : 1)
55
+ case .disabled: return theme.colors.text.disable.opacity(loading ? 0.75 : 1)
48
56
  case .primary: return Colors.black01
49
- case .secondary: return Colors.black17
50
- case .outline: return Colors.primary
51
- case .tonal: return Colors.primary
57
+ case .secondary: return theme.colors.text.default
58
+ case .outline: return theme.colors.primary
59
+ case .tonal: return theme.colors.primary
52
60
  case .danger: return Colors.black01
53
- case .text: return Colors.primary
61
+ case .text: return theme.colors.primary
54
62
  }
55
63
  }
56
64
 
57
65
  var borderColor: Color? {
66
+ borderColor(theme: defaultTheme)
67
+ }
68
+
69
+ func borderColor(theme: Theme) -> Color? {
58
70
  switch self {
59
- case .outline: return Colors.primary
60
- case .secondary: return Colors.black04
71
+ case .outline: return theme.colors.primary
72
+ case .secondary: return theme.colors.border.default
61
73
  default: return nil
62
74
  }
63
75
  }
@@ -117,6 +129,7 @@ extension ButtonSize {
117
129
  }
118
130
 
119
131
  public struct Button: View {
132
+ @Environment(\.appTheme) private var theme
120
133
  var title: String
121
134
  var action: () -> Void
122
135
  var type: ButtonType
@@ -125,6 +138,7 @@ public struct Button: View {
125
138
  var iconRight: AnyView?
126
139
  var isFull: Bool
127
140
  var loading: Bool
141
+ var useTintColor: Bool
128
142
 
129
143
  public init(
130
144
  title: String = "",
@@ -134,7 +148,8 @@ public struct Button: View {
134
148
  iconLeft: AnyView? = nil,
135
149
  iconRight: AnyView? = nil,
136
150
  isFull: Bool = true,
137
- loading: Bool = false
151
+ loading: Bool = false,
152
+ useTintColor: Bool = true
138
153
  ) {
139
154
  self.title = title
140
155
  self.action = action
@@ -144,6 +159,7 @@ public struct Button: View {
144
159
  self.iconRight = iconRight
145
160
  self.isFull = isFull
146
161
  self.loading = loading
162
+ self.useTintColor = useTintColor
147
163
  }
148
164
 
149
165
  private func shouldLoadingOnLeft(_ left: AnyView?, _ right: AnyView?) -> Bool {
@@ -160,14 +176,15 @@ public struct Button: View {
160
176
 
161
177
  public var body: some View {
162
178
  let loadingOnLeft = shouldLoadingOnLeft(iconLeft, iconRight)
179
+ let isEnabled = type != .disabled && !loading
163
180
 
164
- let bg = type.backgroundColor(loading: loading)
165
- let fg = type.contentColor(loading: loading)
166
- let border = type.borderColor
181
+ let bg = type.backgroundColor(loading: loading, theme: theme)
182
+ let fg = type.contentColor(loading: loading, theme: theme)
183
+ let border = type.borderColor(theme: theme)
167
184
  let borderWidth = type.borderWidth
168
185
 
169
186
  SwiftUI.Button(action: {
170
- if !loading, type != .disabled {
187
+ if isEnabled {
171
188
  action()
172
189
  }
173
190
  }) {
@@ -178,7 +195,7 @@ public struct Button: View {
178
195
  .colorMultiply(fg)
179
196
 
180
197
  } else if let iconLeft = iconLeft {
181
- iconLeft.frame(width: size.iconSize, height: size.iconSize)
198
+ tintedIcon(iconLeft, color: fg)
182
199
  }
183
200
 
184
201
  MomoText(title, typography: size.typographyStyle, color: fg)
@@ -191,7 +208,7 @@ public struct Button: View {
191
208
  .colorMultiply(fg)
192
209
 
193
210
  } else if let iconRight = iconRight {
194
- iconRight.frame(width: size.iconSize, height: size.iconSize)
211
+ tintedIcon(iconRight, color: fg)
195
212
  }
196
213
  }
197
214
  .frame(maxWidth: isFull ? .infinity : nil)
@@ -203,8 +220,30 @@ public struct Button: View {
203
220
  .strokeBorder(border ?? .clear, lineWidth: border != nil ? borderWidth : 0)
204
221
  )
205
222
  .clipShape(RoundedRectangle(cornerRadius: size.radius))
206
- .opacity(loading ? 0.75 : 1)
207
223
  }
208
- .disabled(type == .disabled || loading)
224
+ .buttonStyle(ButtonPressFeedbackStyle(isEnabled: isEnabled))
225
+ .disabled(!isEnabled)
226
+ }
227
+
228
+ @ViewBuilder
229
+ private func tintedIcon(_ icon: AnyView, color: Color) -> some View {
230
+ if useTintColor {
231
+ icon
232
+ .frame(width: size.iconSize, height: size.iconSize)
233
+ .colorMultiply(color)
234
+ } else {
235
+ icon
236
+ .frame(width: size.iconSize, height: size.iconSize)
237
+ }
238
+ }
239
+ }
240
+
241
+ private struct ButtonPressFeedbackStyle: ButtonStyle {
242
+ let isEnabled: Bool
243
+
244
+ func makeBody(configuration: Configuration) -> some View {
245
+ configuration.label
246
+ .opacity(configuration.isPressed && isEnabled ? 0.5 : 1)
247
+ .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
209
248
  }
210
249
  }