@momo-kits/native-kits 0.162.2-beta.26-debug → 0.162.2-beta.27-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 (197) hide show
  1. package/CLAUDE.md +1 -1
  2. package/compose/build.gradle.kts +1 -1
  3. package/compose/compose.podspec +1 -1
  4. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -5
  5. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +1 -1
  6. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -3
  7. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +55 -40
  8. package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -4
  9. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +2 -8
  10. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +75 -93
  11. package/gradle.properties +1 -1
  12. package/ios/Application/ApplicationEnvironment.swift +0 -11
  13. package/ios/Application/Components.swift +44 -34
  14. package/ios/Application/FloatingButton.swift +9 -10
  15. package/ios/Application/HeaderRight.swift +45 -79
  16. package/ios/Application/Screen.swift +1 -2
  17. package/ios/Badge/Badge.swift +14 -11
  18. package/ios/Badge/BadgeRibbon.swift +21 -12
  19. package/ios/Button/Button.swift +22 -61
  20. package/ios/Checkbox/Checkbox.swift +13 -14
  21. package/ios/Chip/Chip.swift +9 -23
  22. package/ios/Colors+Radius+Spacing/Colors.swift +12 -12
  23. package/ios/Extensions/Color++.swift +0 -32
  24. package/ios/Icon/Icon.swift +1 -6
  25. package/ios/Input/Input.swift +25 -41
  26. package/ios/Input/InputPhoneNumber.swift +19 -20
  27. package/ios/Input/InputSearch.swift +47 -100
  28. package/ios/Input/InputTextArea.swift +40 -66
  29. package/ios/Popup/PopupDisplay.swift +16 -8
  30. package/ios/Popup/PopupInput.swift +8 -2
  31. package/ios/Swipeable/SwipeCell.swift +0 -185
  32. package/ios/Swipeable/SwipeCellModel.swift +0 -21
  33. package/ios/Typography/Text.swift +5 -79
  34. package/ios/Typography/Typography.swift +0 -3
  35. package/ios/native-kits.podspec +8 -61
  36. package/package.json +1 -1
  37. package/publish.sh +0 -50
  38. package/.claude/settings.local.json +0 -39
  39. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
  43. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
  44. package/ios/Application/Navigation/BottomTab/BottomTab.swift +0 -103
  45. package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +0 -215
  46. package/ios/Application/Navigation/BottomTab/CurvedContainer.swift +0 -65
  47. package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
  48. package/ios/Application/Navigation/HeaderTitle.swift +0 -57
  49. package/ios/Application/Navigation/HeaderUser.swift +0 -209
  50. package/ios/Application/Navigation/NavigationContainer.swift +0 -176
  51. package/ios/Application/Navigation/NavigationOptions.swift +0 -109
  52. package/ios/Application/Navigation/Navigator.swift +0 -364
  53. package/ios/Application/Navigation/Overplay/BottomSheet.swift +0 -188
  54. package/ios/Application/Navigation/Overplay/ModalScreen.swift +0 -68
  55. package/ios/Application/Navigation/Overplay/SnackBar.swift +0 -144
  56. package/ios/Application/Navigation/component/Header.swift +0 -178
  57. package/ios/Application/Navigation/component/HeaderBackground.swift +0 -83
  58. package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
  59. package/ios/Application/StackScreen.swift +0 -453
  60. package/ios/Avatar/Avatar.swift +0 -193
  61. package/ios/BaselineView/BaselineView.swift +0 -163
  62. package/ios/Carousel/Carousel.swift +0 -181
  63. package/ios/Collapse/Collapse.swift +0 -182
  64. package/ios/Colors+Radius+Spacing/Theme.swift +0 -295
  65. package/ios/DateTimePicker/DateTimePicker.swift +0 -210
  66. package/ios/DateTimePicker/DateTimePickerTypes.swift +0 -55
  67. package/ios/DateTimePicker/DateTimePickerUtils.swift +0 -167
  68. package/ios/DateTimePicker/WheelPicker.swift +0 -161
  69. package/ios/Divider/Divider.swift +0 -16
  70. package/ios/IconButton/IconButton.swift +0 -112
  71. package/ios/InputDropDown/InputDropDown.swift +0 -198
  72. package/ios/InputMoney/InputMoney.swift +0 -335
  73. package/ios/InputOTP/InputOTP.swift +0 -210
  74. package/ios/Loader/Loader.swift +0 -113
  75. package/ios/MoMoUIKits.podspec +0 -5
  76. package/ios/Pagination/PaginationDot.swift +0 -61
  77. package/ios/Pagination/PaginationNumber.swift +0 -35
  78. package/ios/Pagination/PaginationScroll.swift +0 -101
  79. package/ios/Pagination/PaginationWhiteDot.swift +0 -37
  80. package/ios/Popup/PopupNotify.swift +0 -219
  81. package/ios/ProgressInfo/ProgressInfo.swift +0 -294
  82. package/ios/Radio/Radio.swift +0 -70
  83. package/ios/Rating/Rating.swift +0 -82
  84. package/ios/Resources/MoMoUIKitsColors.xcassets/AccentColor.colorset/Contents.json +0 -11
  85. package/ios/Resources/MoMoUIKitsColors.xcassets/Background.colorset/Contents.json +0 -38
  86. package/ios/Resources/MoMoUIKitsColors.xcassets/Border.colorset/Contents.json +0 -38
  87. package/ios/Resources/MoMoUIKitsColors.xcassets/Card.colorset/Contents.json +0 -38
  88. package/ios/Resources/MoMoUIKitsColors.xcassets/Contents.json +0 -6
  89. package/ios/Resources/MoMoUIKitsColors.xcassets/Error.colorset/Contents.json +0 -38
  90. package/ios/Resources/MoMoUIKitsColors.xcassets/Primary.colorset/Contents.json +0 -38
  91. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryDark.colorset/Contents.json +0 -38
  92. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryLight.colorset/Contents.json +0 -38
  93. package/ios/Resources/MoMoUIKitsColors.xcassets/Secondary.colorset/Contents.json +0 -38
  94. package/ios/Resources/MoMoUIKitsColors.xcassets/Success.colorset/Contents.json +0 -38
  95. package/ios/Resources/MoMoUIKitsColors.xcassets/Text.colorset/Contents.json +0 -38
  96. package/ios/Resources/MoMoUIKitsColors.xcassets/TextSecondary.colorset/Contents.json +0 -38
  97. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/Contents.json +0 -9
  98. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-default.colorset/Contents.json +0 -38
  99. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-disable.colorset/Contents.json +0 -38
  100. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-pressed.colorset/Contents.json +0 -38
  101. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-selected.colorset/Contents.json +0 -38
  102. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-surface.colorset/Contents.json +0 -38
  103. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-tonal.colorset/Contents.json +0 -38
  104. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-default.colorset/Contents.json +0 -38
  105. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-disable.colorset/Contents.json +0 -38
  106. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/gradient.colorset/Contents.json +0 -38
  107. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/primary.colorset/Contents.json +0 -38
  108. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/secondary.colorset/Contents.json +0 -38
  109. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-container.colorset/Contents.json +0 -38
  110. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-primary.colorset/Contents.json +0 -38
  111. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-secondary.colorset/Contents.json +0 -38
  112. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-default.colorset/Contents.json +0 -38
  113. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-disable.colorset/Contents.json +0 -38
  114. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-hint.colorset/Contents.json +0 -38
  115. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-secondary.colorset/Contents.json +0 -38
  116. package/ios/Resources/MoMoUIKitsColors.xcassets/Warning.colorset/Contents.json +0 -38
  117. package/ios/Skeleton/Skeleton.swift +0 -99
  118. package/ios/Slider/Slider.swift +0 -237
  119. package/ios/Stepper/Stepper.swift +0 -220
  120. package/ios/Steps/Steps.swift +0 -451
  121. package/ios/SuggestAction/SuggestAction.swift +0 -97
  122. package/ios/TabView/TabView.swift +0 -530
  123. package/ios/Tag/Tag.swift +0 -98
  124. package/ios/Title/Title.swift +0 -269
  125. package/ios/Tooltip/Tooltip.swift +0 -482
  126. package/ios/Typography/FontScaleStore.swift +0 -19
  127. package/ios/Uploader/Uploader.swift +0 -183
  128. package/ios-demo/MoMoUIKitsDemo.podspec +0 -20
  129. package/ios-demo/README.md +0 -120
  130. package/ios-demo/Resources/dot_loading.json +0 -558
  131. package/ios-demo/Resources/icon.json +0 -4052
  132. package/ios-demo/Resources/lottie_circle_loader.json +0 -1
  133. package/ios-demo/Sources/MoMoUIKitsDemo/DemoScaffold.swift +0 -48
  134. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsBadgeDemoView.swift +0 -25
  135. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsButtonDemoView.swift +0 -26
  136. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsCheckboxDemoView.swift +0 -19
  137. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsChipDemoView.swift +0 -33
  138. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsInputDemoView.swift +0 -48
  139. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsPopupDemoView.swift +0 -21
  140. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsRadioDemoView.swift +0 -17
  141. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsTypographyDemoView.swift +0 -28
  142. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoContext.swift +0 -31
  143. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoFactory.swift +0 -24
  144. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AnimatedHeaderDemo.swift +0 -110
  145. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +0 -93
  146. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +0 -269
  147. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BaselineView.swift +0 -117
  148. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BottomTabDemo.swift +0 -89
  149. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +0 -100
  150. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +0 -126
  151. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +0 -63
  152. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +0 -20
  153. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +0 -93
  154. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +0 -107
  155. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +0 -124
  156. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +0 -14
  157. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +0 -49
  158. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +0 -278
  159. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +0 -58
  160. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +0 -78
  161. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +0 -138
  162. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +0 -111
  163. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +0 -136
  164. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +0 -107
  165. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +0 -73
  166. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +0 -100
  167. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +0 -32
  168. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +0 -57
  169. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +0 -70
  170. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +0 -227
  171. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +0 -72
  172. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +0 -175
  173. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +0 -121
  174. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +0 -76
  175. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +0 -89
  176. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +0 -32
  177. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +0 -56
  178. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +0 -32
  179. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +0 -150
  180. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +0 -138
  181. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +0 -103
  182. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +0 -94
  183. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +0 -79
  184. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +0 -152
  185. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +0 -33
  186. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +0 -190
  187. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +0 -34
  188. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +0 -182
  189. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +0 -109
  190. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +0 -187
  191. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +0 -87
  192. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +0 -131
  193. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +0 -51
  194. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +0 -15
  195. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +0 -73
  196. package/local.properties +0 -8
  197. package/scripts/gen-ios-color.mjs +0 -136
@@ -1,482 +0,0 @@
1
- import SwiftUI
2
-
3
- // MARK: - Types
4
-
5
- /// Tooltip placement relative to the anchor element.
6
- public enum TooltipPlacement {
7
- case top
8
- case bottom
9
- case left
10
- case right
11
- }
12
-
13
- /// Cross-axis alignment of the tooltip relative to the anchor.
14
- public enum TooltipAlign {
15
- case start
16
- case center
17
- case end
18
- }
19
-
20
- /// Describes a single action button displayed in the tooltip.
21
- public struct TooltipButton {
22
- public let title: String?
23
- public let icon: String?
24
- public let onPress: (() -> Void)?
25
-
26
- public init(
27
- title: String? = nil,
28
- icon: String? = nil,
29
- onPress: (() -> Void)? = nil
30
- ) {
31
- self.title = title
32
- self.icon = icon
33
- self.onPress = onPress
34
- }
35
- }
36
-
37
- /// State holder for controlling tooltip visibility imperatively.
38
- /// Mirrors the Compose `TooltipState` / `rememberTooltipState()` pattern.
39
- public final class TooltipState: ObservableObject {
40
- @Published public private(set) var isVisible: Bool = false
41
-
42
- public init() {}
43
-
44
- /// Shows the tooltip.
45
- public func show() {
46
- isVisible = true
47
- }
48
-
49
- /// Hides the tooltip.
50
- public func hide() {
51
- isVisible = false
52
- }
53
-
54
- /// Toggles the tooltip visibility.
55
- public func toggle() {
56
- isVisible.toggle()
57
- }
58
- }
59
-
60
- // MARK: - Constants
61
-
62
- private enum TooltipConst {
63
- static let offset = Spacing.S // TOOLTIP_OFFSET
64
- static let arrowSize: CGFloat = 6 // ARROW_SIZE
65
- static let maxWidth: CGFloat = 300
66
- static let cornerRadius = Radius.S
67
- static let arrowCornerRadius: CGFloat = 2
68
- static let arrowEdgeMargin = Spacing.M
69
- static let arrowOverlap: CGFloat = 1
70
- }
71
-
72
- // MARK: - Arrow Shape
73
-
74
- /// Triangular arrow pointing away from the anchor in the given placement direction.
75
- struct TooltipArrowShape: Shape {
76
- let placement: TooltipPlacement
77
-
78
- func path(in rect: CGRect) -> Path {
79
- let w = rect.width
80
- let h = rect.height
81
- var path = Path()
82
- switch placement {
83
- case .top:
84
- path.move(to: CGPoint(x: 0, y: 0))
85
- path.addLine(to: CGPoint(x: w, y: 0))
86
- path.addLine(to: CGPoint(x: w / 2, y: h))
87
- case .bottom:
88
- path.move(to: CGPoint(x: 0, y: h))
89
- path.addLine(to: CGPoint(x: w, y: h))
90
- path.addLine(to: CGPoint(x: w / 2, y: 0))
91
- case .left:
92
- path.move(to: CGPoint(x: 0, y: 0))
93
- path.addLine(to: CGPoint(x: 0, y: h))
94
- path.addLine(to: CGPoint(x: w, y: h / 2))
95
- case .right:
96
- path.move(to: CGPoint(x: w, y: 0))
97
- path.addLine(to: CGPoint(x: w, y: h))
98
- path.addLine(to: CGPoint(x: 0, y: h / 2))
99
- }
100
- path.closeSubpath()
101
- return path
102
- }
103
- }
104
-
105
- // MARK: - Anchor bounds preference
106
-
107
- private struct TooltipAnchorBoundsKey: PreferenceKey {
108
- static var defaultValue: Anchor<CGRect>? = nil
109
- static func reduce(value: inout Anchor<CGRect>?, nextValue: () -> Anchor<CGRect>?) {
110
- value = nextValue() ?? value
111
- }
112
- }
113
-
114
- // MARK: - Tooltip
115
-
116
- /// A tooltip component that wraps `content` (the anchor) and shows a positioned
117
- /// tooltip popup with title, description, close button, and action buttons.
118
- ///
119
- /// Port of the Compose / React Native `Tooltip` component.
120
- public struct Tooltip<Content: View>: View {
121
- @ObservedObject private var state: TooltipState
122
- private let title: String?
123
- private let description: String
124
- private let buttons: [TooltipButton]
125
- private let placement: TooltipPlacement
126
- private let align: TooltipAlign
127
- private let onVisibleChange: ((Bool) -> Void)?
128
- private let onPressClose: (() -> Void)?
129
- private let content: Content
130
-
131
- public init(
132
- state: TooltipState,
133
- title: String? = nil,
134
- description: String,
135
- buttons: [TooltipButton] = [],
136
- placement: TooltipPlacement = .top,
137
- align: TooltipAlign = .center,
138
- onVisibleChange: ((Bool) -> Void)? = nil,
139
- onPressClose: (() -> Void)? = nil,
140
- @ViewBuilder content: () -> Content
141
- ) {
142
- self.state = state
143
- self.title = title
144
- self.description = description
145
- self.buttons = buttons
146
- self.placement = placement
147
- self.align = align
148
- self.onVisibleChange = onVisibleChange
149
- self.onPressClose = onPressClose
150
- self.content = content()
151
- }
152
-
153
- public var body: some View {
154
- content
155
- .anchorPreference(key: TooltipAnchorBoundsKey.self, value: .bounds) { $0 }
156
- .overlayPreferenceValue(TooltipAnchorBoundsKey.self) { anchor in
157
- GeometryReader { proxy in
158
- if state.isVisible, let anchor = anchor {
159
- let anchorRect = proxy[anchor]
160
- TooltipPopupContent(
161
- title: title,
162
- description: description,
163
- buttons: buttons,
164
- placement: placement,
165
- align: align,
166
- onPressClose: onPressClose
167
- )
168
- .fixedSize()
169
- .modifier(
170
- TooltipPositionModifier(
171
- anchorRect: anchorRect,
172
- containerSize: proxy.size,
173
- placement: placement,
174
- align: align
175
- )
176
- )
177
- }
178
- }
179
- }
180
- .onChange(of: state.isVisible) { newValue in
181
- onVisibleChange?(newValue)
182
- }
183
- }
184
- }
185
-
186
- // MARK: - Positioning
187
-
188
- /// Positions the tooltip (already sized via `.fixedSize()`) relative to the anchor,
189
- /// mirroring `TooltipPositionProvider.calculatePosition`.
190
- private struct TooltipPositionModifier: ViewModifier {
191
- let anchorRect: CGRect
192
- let containerSize: CGSize
193
- let placement: TooltipPlacement
194
- let align: TooltipAlign
195
-
196
- @State private var popupSize: CGSize = .zero
197
-
198
- private var offsetPx: CGFloat {
199
- TooltipConst.offset + TooltipConst.arrowSize
200
- }
201
-
202
- func body(content: Content) -> some View {
203
- content
204
- .background(
205
- GeometryReader { p in
206
- Color.clear
207
- .onAppear { popupSize = p.size }
208
- .onChange(of: p.size) { popupSize = $0 }
209
- }
210
- )
211
- .offset(x: originX, y: originY)
212
- }
213
-
214
- private var originX: CGFloat {
215
- let popW = popupSize.width
216
- switch placement {
217
- case .top, .bottom:
218
- return alignHorizontal(anchorRect.minX, anchorRect.width, popW)
219
- case .left:
220
- return anchorRect.minX - popW - offsetPx
221
- case .right:
222
- return anchorRect.maxX + offsetPx
223
- }
224
- }
225
-
226
- private var originY: CGFloat {
227
- let popH = popupSize.height
228
- switch placement {
229
- case .top:
230
- return anchorRect.minY - popH - offsetPx
231
- case .bottom:
232
- return anchorRect.maxY + offsetPx
233
- case .left, .right:
234
- return alignVertical(anchorRect.minY, anchorRect.height, popH)
235
- }
236
- }
237
-
238
- private func alignHorizontal(_ anchorX: CGFloat, _ anchorW: CGFloat, _ popW: CGFloat) -> CGFloat {
239
- switch align {
240
- case .start: return anchorX
241
- case .end: return anchorX + anchorW - popW
242
- case .center: return anchorX + (anchorW - popW) / 2
243
- }
244
- }
245
-
246
- private func alignVertical(_ anchorY: CGFloat, _ anchorH: CGFloat, _ popH: CGFloat) -> CGFloat {
247
- switch align {
248
- case .start: return anchorY
249
- case .end: return anchorY + anchorH - popH
250
- case .center: return anchorY + (anchorH - popH) / 2
251
- }
252
- }
253
- }
254
-
255
- // MARK: - Popup content
256
-
257
- private struct TooltipPopupContent: View {
258
- let title: String?
259
- let description: String?
260
- let buttons: [TooltipButton]
261
- let placement: TooltipPlacement
262
- let align: TooltipAlign
263
- let onPressClose: (() -> Void)?
264
-
265
- var body: some View {
266
- ZStack {
267
- VStack(alignment: .leading, spacing: 0) {
268
- HStack(alignment: .top, spacing: 0) {
269
- VStack(alignment: .leading, spacing: 0) {
270
- if let title = title, !title.isEmpty {
271
- MomoText(title, typography: .headerSSemibold, color: Colors.black01)
272
- .lineLimit(1)
273
- .truncationMode(.tail)
274
- .padding(.bottom, Spacing.XS)
275
- }
276
- if let description = description, !description.isEmpty {
277
- MomoText(description, typography: .descriptionDefaultRegular, color: Colors.black01)
278
- .lineLimit(2)
279
- .truncationMode(.tail)
280
- }
281
- }
282
- .frame(maxWidth: .infinity, alignment: .leading)
283
-
284
- if let onPressClose = onPressClose {
285
- Spacer().frame(width: Spacing.M)
286
- SwiftUI.Button(action: onPressClose) {
287
- Icon(source: "navigation_close", size: 20, color: Colors.black01)
288
- .frame(width: 20, height: 20)
289
- }
290
- .buttonStyle(.plain)
291
- }
292
- }
293
-
294
- if !buttons.isEmpty {
295
- Spacer().frame(height: Spacing.M)
296
- TooltipButtons(buttons: buttons)
297
- .frame(maxWidth: .infinity, alignment: .trailing)
298
- }
299
- }
300
- .padding(Spacing.M)
301
- .frame(maxWidth: TooltipConst.maxWidth, alignment: .leading)
302
- .background(Colors.black17)
303
- .clipShape(RoundedRectangle(cornerRadius: TooltipConst.cornerRadius))
304
- }
305
- .overlay(arrow, alignment: arrowAlignment)
306
- }
307
-
308
- // MARK: Arrow
309
-
310
- @ViewBuilder
311
- private var arrow: some View {
312
- let arrowWidth = TooltipConst.arrowSize * 2
313
- let arrowHeight = TooltipConst.arrowSize
314
- let isVertical = placement == .top || placement == .bottom
315
- TooltipArrowShape(placement: placement)
316
- .fill(Colors.black17)
317
- .frame(
318
- width: isVertical ? arrowWidth : arrowHeight,
319
- height: isVertical ? arrowHeight : arrowWidth
320
- )
321
- .offset(x: arrowOffsetX, y: arrowOffsetY)
322
- }
323
-
324
- private var arrowAlignment: Alignment {
325
- switch placement {
326
- case .top:
327
- switch align {
328
- case .start: return .bottomLeading
329
- case .center: return .bottom
330
- case .end: return .bottomTrailing
331
- }
332
- case .bottom:
333
- switch align {
334
- case .start: return .topLeading
335
- case .center: return .top
336
- case .end: return .topTrailing
337
- }
338
- case .left:
339
- switch align {
340
- case .start: return .topTrailing
341
- case .center: return .trailing
342
- case .end: return .bottomTrailing
343
- }
344
- case .right:
345
- switch align {
346
- case .start: return .topLeading
347
- case .center: return .leading
348
- case .end: return .bottomLeading
349
- }
350
- }
351
- }
352
-
353
- private var arrowOffsetX: CGFloat {
354
- let arrowHeight = TooltipConst.arrowSize
355
- switch placement {
356
- case .left: return arrowHeight - TooltipConst.arrowOverlap
357
- case .right: return -arrowHeight + TooltipConst.arrowOverlap
358
- default:
359
- switch align {
360
- case .start: return TooltipConst.arrowEdgeMargin
361
- case .end: return -TooltipConst.arrowEdgeMargin
362
- case .center: return 0
363
- }
364
- }
365
- }
366
-
367
- private var arrowOffsetY: CGFloat {
368
- let arrowHeight = TooltipConst.arrowSize
369
- switch placement {
370
- case .top: return arrowHeight - TooltipConst.arrowOverlap
371
- case .bottom: return -arrowHeight + TooltipConst.arrowOverlap
372
- default:
373
- switch align {
374
- case .start: return TooltipConst.arrowEdgeMargin
375
- case .end: return -TooltipConst.arrowEdgeMargin
376
- case .center: return 0
377
- }
378
- }
379
- }
380
- }
381
-
382
- // MARK: - Buttons row
383
-
384
- private struct TooltipButtons: View {
385
- let buttons: [TooltipButton]
386
-
387
- var body: some View {
388
- HStack(spacing: 0) {
389
- content
390
- }
391
- }
392
-
393
- @ViewBuilder
394
- private var content: some View {
395
- if buttons.count == 1 {
396
- TooltipSingleButton(btn: buttons[0])
397
- } else if buttons.count == 2 {
398
- let primary = buttons[0]
399
- let secondary = buttons[1]
400
- let bothIcons = primary.icon != nil && secondary.icon != nil
401
- if bothIcons {
402
- TooltipIconButton(icon: secondary.icon ?? "", onPress: secondary.onPress ?? {})
403
- Spacer().frame(width: Spacing.S)
404
- TooltipIconButton(icon: primary.icon ?? "", onPress: primary.onPress ?? {})
405
- } else {
406
- TooltipSecondaryButton(title: secondary.title ?? "", onPress: secondary.onPress ?? {})
407
- Spacer().frame(width: Spacing.S)
408
- TooltipPrimaryButton(title: primary.title ?? "", onPress: primary.onPress ?? {})
409
- }
410
- } else {
411
- ForEach(Array(buttons.enumerated()), id: \.offset) { index, btn in
412
- if index > 0 {
413
- Spacer().frame(width: Spacing.XXS)
414
- }
415
- TooltipSingleButton(btn: btn)
416
- }
417
- }
418
- }
419
- }
420
-
421
- private func tooltipLimitWithEllipsis(_ value: String, max: Int = 16) -> String {
422
- value.count > max ? String(value.prefix(max)) + "…" : value
423
- }
424
-
425
- private struct TooltipSingleButton: View {
426
- let btn: TooltipButton
427
-
428
- var body: some View {
429
- if let icon = btn.icon {
430
- TooltipIconButton(icon: icon, onPress: btn.onPress ?? {})
431
- } else {
432
- TooltipPrimaryButton(title: btn.title ?? "", onPress: btn.onPress ?? {})
433
- }
434
- }
435
- }
436
-
437
- private struct TooltipPrimaryButton: View {
438
- let title: String
439
- let onPress: () -> Void
440
-
441
- var body: some View {
442
- Button(
443
- title: tooltipLimitWithEllipsis(title),
444
- action: onPress,
445
- type: .secondary,
446
- size: .medium,
447
- isFull: false
448
- )
449
- }
450
- }
451
-
452
- private struct TooltipSecondaryButton: View {
453
- let title: String
454
- let onPress: () -> Void
455
-
456
- var body: some View {
457
- SwiftUI.Button(action: onPress) {
458
- MomoText(tooltipLimitWithEllipsis(title), typography: .actionSBold, color: Colors.black01)
459
- .lineLimit(1)
460
- .truncationMode(.tail)
461
- .padding(.horizontal, Spacing.M)
462
- .padding(.vertical, Spacing.S)
463
- }
464
- .buttonStyle(.plain)
465
- }
466
- }
467
-
468
- private struct TooltipIconButton: View {
469
- let icon: String
470
- let onPress: () -> Void
471
-
472
- var body: some View {
473
- let iconButtonSize = scaleSize(36)
474
- SwiftUI.Button(action: onPress) {
475
- Icon(source: icon, size: 24)
476
- .frame(width: iconButtonSize, height: iconButtonSize)
477
- .background(Colors.black01)
478
- .clipShape(RoundedRectangle(cornerRadius: Radius.XL))
479
- }
480
- .buttonStyle(.plain)
481
- }
482
- }
@@ -1,19 +0,0 @@
1
- import CoreGraphics
2
-
3
- /// App-wide font-scale preference for the SwiftUI kit. An exported holder the host writes once at
4
- /// app startup (from the cache-backed `FontScaleProvider`); `scaleSize`/`appFont` read the snapshot.
5
- /// No realtime: the SwiftUI kit has no navigation container to scope reactive updates, so a startup
6
- /// snapshot is enough. When `useOSScaleRate` is false the kit applies `userScaleRate` instead of the
7
- /// OS Dynamic Type factor.
8
- public final class FontScaleStore {
9
- public static let shared = FontScaleStore()
10
- private init() {}
11
-
12
- public private(set) var userScaleRate: CGFloat = 1
13
- public private(set) var useOSScaleRate: Bool = true
14
-
15
- public func update(userScaleRate: CGFloat, useOSScaleRate: Bool) {
16
- self.userScaleRate = userScaleRate
17
- self.useOSScaleRate = useOSScaleRate
18
- }
19
- }
@@ -1,183 +0,0 @@
1
- import SwiftUI
2
-
3
- /// Native SwiftUI port of the Compose `Uploader` component.
4
- ///
5
- /// Renders a horizontal row with an optional dashed "add image" picker cell
6
- /// followed by a horizontally scrollable list of uploaded image thumbnails.
7
- /// Each thumbnail can show a loading shimmer overlay and an optional cancel
8
- /// button.
9
- ///
10
- /// Mirrors the Compose surface in
11
- /// `compose/.../components/Uploader.kt`:
12
- /// - `images`, `numberOfImages`, `disabled`, `width`, `height`
13
- /// - callbacks `onAdd`, `onCancel`, `onPressImage`
14
- ///
15
- /// Note: like Compose, this component does NOT perform any native photo-picker
16
- /// or file-system access. It exposes the `onAdd` callback so the host can
17
- /// present a picker and supply the resulting image data back via the `images`
18
- /// list (the demo passes mock image URLs).
19
- public struct UploadImage: Identifiable {
20
- public let id = UUID()
21
- public var uri: String?
22
- public var loading: Bool
23
-
24
- public init(uri: String? = nil, loading: Bool = false) {
25
- self.uri = uri
26
- self.loading = loading
27
- }
28
- }
29
-
30
- public struct Uploader: View {
31
- // MARK: Properties
32
-
33
- private let images: [UploadImage]
34
- private let numberOfImages: Int
35
- private let disabled: Bool
36
- private let width: CGFloat
37
- private let height: CGFloat
38
- private let onAdd: () -> Void
39
- private let onCancel: ((UploadImage, Int) -> Void)?
40
- private let onPressImage: (UploadImage, Int) -> Void
41
-
42
- // MARK: Lifecycle
43
-
44
- public init(
45
- images: [UploadImage],
46
- numberOfImages: Int = 1,
47
- disabled: Bool = false,
48
- width: CGFloat = 64,
49
- height: CGFloat = 64,
50
- onAdd: @escaping () -> Void = {},
51
- onCancel: ((UploadImage, Int) -> Void)? = nil,
52
- onPressImage: @escaping (UploadImage, Int) -> Void = { _, _ in }
53
- ) {
54
- self.images = images
55
- self.numberOfImages = numberOfImages
56
- self.disabled = disabled
57
- self.width = width
58
- self.height = height
59
- self.onAdd = onAdd
60
- self.onCancel = onCancel
61
- self.onPressImage = onPressImage
62
- }
63
-
64
- // MARK: Body
65
-
66
- private var shouldShowPicker: Bool {
67
- images.count < numberOfImages
68
- }
69
-
70
- public var body: some View {
71
- HStack(alignment: .top, spacing: Spacing.S) {
72
- if shouldShowPicker {
73
- UploaderPickerCell(
74
- disabled: disabled,
75
- width: width,
76
- height: height,
77
- onAdd: onAdd
78
- )
79
- }
80
-
81
- ScrollView(.horizontal, showsIndicators: false) {
82
- HStack(alignment: .top, spacing: Spacing.S) {
83
- ForEach(Array(images.enumerated()), id: \.element.id) { index, image in
84
- UploaderImageItem(
85
- image: image,
86
- width: width,
87
- height: height,
88
- index: index,
89
- onCancel: onCancel,
90
- onPressImage: { onPressImage(image, index) }
91
- )
92
- }
93
- }
94
- }
95
- }
96
- }
97
- }
98
-
99
- // MARK: - Picker cell
100
-
101
- private struct UploaderPickerCell: View {
102
- @Environment(\.appTheme) private var theme
103
-
104
- let disabled: Bool
105
- let width: CGFloat
106
- let height: CGFloat
107
- let onAdd: () -> Void
108
-
109
- // text.disable / text.hint and border.disable / border.default tokens.
110
- private var mainColor: Color { disabled ? theme.colors.text.disable : theme.colors.text.hint }
111
- private var borderColor: Color { disabled ? theme.colors.border.disable : theme.colors.border.default }
112
-
113
- var body: some View {
114
- SwiftUI.Button(action: { if !disabled { onAdd() } }) {
115
- VStack(spacing: Spacing.XXS) {
116
- Icon(source: "16_navigation_plus", size: 16, color: mainColor)
117
- MomoText("Thêm hình", typography: .descriptionXsRegular, color: mainColor)
118
- }
119
- .frame(width: width, height: height)
120
- .background(
121
- RoundedRectangle(cornerRadius: Radius.XS)
122
- .strokeBorder(
123
- borderColor,
124
- style: StrokeStyle(lineWidth: 1, dash: [10, 6])
125
- )
126
- )
127
- .clipShape(RoundedRectangle(cornerRadius: Radius.XS))
128
- }
129
- .buttonStyle(ActiveOpacityButtonStyle())
130
- .disabled(disabled)
131
- }
132
- }
133
-
134
- // MARK: - Image item
135
-
136
- private struct UploaderImageItem: View {
137
- let image: UploadImage
138
- let width: CGFloat
139
- let height: CGFloat
140
- let index: Int
141
- let onCancel: ((UploadImage, Int) -> Void)?
142
- let onPressImage: () -> Void
143
-
144
- var body: some View {
145
- SwiftUI.Button(action: onPressImage) {
146
- ZStack(alignment: .topTrailing) {
147
- ImageView(image.uri ?? "", contentMode: .fill)
148
- .frame(width: width, height: height)
149
- .clipShape(RoundedRectangle(cornerRadius: Radius.XS))
150
-
151
- if image.loading {
152
- Skeleton()
153
- .frame(width: width, height: height)
154
- .clipShape(RoundedRectangle(cornerRadius: Radius.XS))
155
- .background(
156
- Color.white.opacity(0.4)
157
- .clipShape(RoundedRectangle(cornerRadius: Radius.XS))
158
- )
159
- }
160
-
161
- if let onCancel, !image.loading {
162
- SwiftUI.Button(action: { onCancel(image, index) }) {
163
- Icon(source: "navigation_close_circle_full", size: 16, color: nil)
164
- }
165
- .buttonStyle(ActiveOpacityButtonStyle())
166
- .padding(.top, Spacing.XXS)
167
- .padding(.trailing, Spacing.XXS)
168
- }
169
- }
170
- .frame(width: width, height: height)
171
- }
172
- .buttonStyle(ActiveOpacityButtonStyle())
173
- }
174
- }
175
-
176
- // MARK: - Active opacity button style
177
-
178
- private struct ActiveOpacityButtonStyle: ButtonStyle {
179
- func makeBody(configuration: Configuration) -> some View {
180
- configuration.label
181
- .opacity(configuration.isPressed ? 0.5 : 1)
182
- }
183
- }
@@ -1,20 +0,0 @@
1
- Pod::Spec.new do |s|
2
- s.name = 'MoMoUIKitsDemo'
3
- s.version = '0.162.2-beta.26'
4
- s.summary = 'Demo browser for MoMoUIKits SwiftUI components'
5
- s.homepage = 'https://momo.vn'
6
- s.license = { :type => 'MIT' }
7
- s.author = { 'MoMo' => 'dev@momo.vn' }
8
- s.source = { :path => '.' }
9
-
10
- s.ios.deployment_target = '15.0'
11
- s.swift_version = '5.0'
12
-
13
- s.source_files = 'Sources/**/*.{swift}'
14
- s.resource_bundles = {
15
- 'MoMoUIKitsDemoResources' => ['Resources/**/*']
16
- }
17
- s.frameworks = 'SwiftUI', 'UIKit'
18
- s.dependency 'MoMoUIKits'
19
- s.dependency 'lottie-ios', '4.6.0'
20
- end