@momo-kits/native-kits 0.162.2-beta.26 → 0.162.2-beta.27

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 (180) hide show
  1. package/CLAUDE.md +1 -1
  2. package/ios/Application/ApplicationEnvironment.swift +0 -11
  3. package/ios/Application/Components.swift +44 -34
  4. package/ios/Application/FloatingButton.swift +9 -10
  5. package/ios/Application/HeaderRight.swift +45 -79
  6. package/ios/Application/Screen.swift +1 -2
  7. package/ios/Badge/Badge.swift +14 -11
  8. package/ios/Badge/BadgeRibbon.swift +21 -12
  9. package/ios/Button/Button.swift +22 -61
  10. package/ios/Checkbox/Checkbox.swift +13 -14
  11. package/ios/Chip/Chip.swift +9 -23
  12. package/ios/Colors+Radius+Spacing/Colors.swift +12 -12
  13. package/ios/Extensions/Color++.swift +0 -32
  14. package/ios/Icon/Icon.swift +1 -6
  15. package/ios/Input/Input.swift +25 -41
  16. package/ios/Input/InputPhoneNumber.swift +19 -20
  17. package/ios/Input/InputSearch.swift +47 -100
  18. package/ios/Input/InputTextArea.swift +40 -66
  19. package/ios/Popup/PopupDisplay.swift +16 -8
  20. package/ios/Popup/PopupInput.swift +8 -2
  21. package/ios/Swipeable/SwipeCell.swift +0 -185
  22. package/ios/Swipeable/SwipeCellModel.swift +0 -21
  23. package/ios/Typography/Text.swift +5 -79
  24. package/ios/Typography/Typography.swift +0 -3
  25. package/ios/native-kits.podspec +8 -61
  26. package/package.json +1 -1
  27. package/publish.sh +0 -50
  28. package/.claude/settings.local.json +0 -39
  29. package/ios/Application/Navigation/BottomTab/BottomTab.swift +0 -103
  30. package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +0 -215
  31. package/ios/Application/Navigation/BottomTab/CurvedContainer.swift +0 -65
  32. package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
  33. package/ios/Application/Navigation/HeaderTitle.swift +0 -57
  34. package/ios/Application/Navigation/HeaderUser.swift +0 -209
  35. package/ios/Application/Navigation/NavigationContainer.swift +0 -176
  36. package/ios/Application/Navigation/NavigationOptions.swift +0 -109
  37. package/ios/Application/Navigation/Navigator.swift +0 -364
  38. package/ios/Application/Navigation/Overplay/BottomSheet.swift +0 -188
  39. package/ios/Application/Navigation/Overplay/ModalScreen.swift +0 -68
  40. package/ios/Application/Navigation/Overplay/SnackBar.swift +0 -144
  41. package/ios/Application/Navigation/component/Header.swift +0 -178
  42. package/ios/Application/Navigation/component/HeaderBackground.swift +0 -83
  43. package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
  44. package/ios/Application/StackScreen.swift +0 -453
  45. package/ios/Avatar/Avatar.swift +0 -193
  46. package/ios/BaselineView/BaselineView.swift +0 -163
  47. package/ios/Carousel/Carousel.swift +0 -181
  48. package/ios/Collapse/Collapse.swift +0 -182
  49. package/ios/Colors+Radius+Spacing/Theme.swift +0 -295
  50. package/ios/DateTimePicker/DateTimePicker.swift +0 -210
  51. package/ios/DateTimePicker/DateTimePickerTypes.swift +0 -55
  52. package/ios/DateTimePicker/DateTimePickerUtils.swift +0 -167
  53. package/ios/DateTimePicker/WheelPicker.swift +0 -161
  54. package/ios/Divider/Divider.swift +0 -16
  55. package/ios/IconButton/IconButton.swift +0 -112
  56. package/ios/InputDropDown/InputDropDown.swift +0 -198
  57. package/ios/InputMoney/InputMoney.swift +0 -335
  58. package/ios/InputOTP/InputOTP.swift +0 -210
  59. package/ios/Loader/Loader.swift +0 -113
  60. package/ios/MoMoUIKits.podspec +0 -5
  61. package/ios/Pagination/PaginationDot.swift +0 -61
  62. package/ios/Pagination/PaginationNumber.swift +0 -35
  63. package/ios/Pagination/PaginationScroll.swift +0 -101
  64. package/ios/Pagination/PaginationWhiteDot.swift +0 -37
  65. package/ios/Popup/PopupNotify.swift +0 -219
  66. package/ios/ProgressInfo/ProgressInfo.swift +0 -294
  67. package/ios/Radio/Radio.swift +0 -70
  68. package/ios/Rating/Rating.swift +0 -82
  69. package/ios/Resources/MoMoUIKitsColors.xcassets/AccentColor.colorset/Contents.json +0 -11
  70. package/ios/Resources/MoMoUIKitsColors.xcassets/Background.colorset/Contents.json +0 -38
  71. package/ios/Resources/MoMoUIKitsColors.xcassets/Border.colorset/Contents.json +0 -38
  72. package/ios/Resources/MoMoUIKitsColors.xcassets/Card.colorset/Contents.json +0 -38
  73. package/ios/Resources/MoMoUIKitsColors.xcassets/Contents.json +0 -6
  74. package/ios/Resources/MoMoUIKitsColors.xcassets/Error.colorset/Contents.json +0 -38
  75. package/ios/Resources/MoMoUIKitsColors.xcassets/Primary.colorset/Contents.json +0 -38
  76. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryDark.colorset/Contents.json +0 -38
  77. package/ios/Resources/MoMoUIKitsColors.xcassets/PrimaryLight.colorset/Contents.json +0 -38
  78. package/ios/Resources/MoMoUIKitsColors.xcassets/Secondary.colorset/Contents.json +0 -38
  79. package/ios/Resources/MoMoUIKitsColors.xcassets/Success.colorset/Contents.json +0 -38
  80. package/ios/Resources/MoMoUIKitsColors.xcassets/Text.colorset/Contents.json +0 -38
  81. package/ios/Resources/MoMoUIKitsColors.xcassets/TextSecondary.colorset/Contents.json +0 -38
  82. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/Contents.json +0 -9
  83. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-default.colorset/Contents.json +0 -38
  84. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-disable.colorset/Contents.json +0 -38
  85. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-pressed.colorset/Contents.json +0 -38
  86. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-selected.colorset/Contents.json +0 -38
  87. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-surface.colorset/Contents.json +0 -38
  88. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/background-tonal.colorset/Contents.json +0 -38
  89. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-default.colorset/Contents.json +0 -38
  90. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/border-disable.colorset/Contents.json +0 -38
  91. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/gradient.colorset/Contents.json +0 -38
  92. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/primary.colorset/Contents.json +0 -38
  93. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/secondary.colorset/Contents.json +0 -38
  94. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-container.colorset/Contents.json +0 -38
  95. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-primary.colorset/Contents.json +0 -38
  96. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/success-secondary.colorset/Contents.json +0 -38
  97. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-default.colorset/Contents.json +0 -38
  98. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-disable.colorset/Contents.json +0 -38
  99. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-hint.colorset/Contents.json +0 -38
  100. package/ios/Resources/MoMoUIKitsColors.xcassets/ThemeColors/text-secondary.colorset/Contents.json +0 -38
  101. package/ios/Resources/MoMoUIKitsColors.xcassets/Warning.colorset/Contents.json +0 -38
  102. package/ios/Skeleton/Skeleton.swift +0 -99
  103. package/ios/Slider/Slider.swift +0 -237
  104. package/ios/Stepper/Stepper.swift +0 -220
  105. package/ios/Steps/Steps.swift +0 -451
  106. package/ios/SuggestAction/SuggestAction.swift +0 -97
  107. package/ios/TabView/TabView.swift +0 -530
  108. package/ios/Tag/Tag.swift +0 -98
  109. package/ios/Title/Title.swift +0 -269
  110. package/ios/Tooltip/Tooltip.swift +0 -482
  111. package/ios/Typography/FontScaleStore.swift +0 -19
  112. package/ios/Uploader/Uploader.swift +0 -183
  113. package/ios-demo/MoMoUIKitsDemo.podspec +0 -20
  114. package/ios-demo/Resources/dot_loading.json +0 -558
  115. package/ios-demo/Resources/icon.json +0 -4052
  116. package/ios-demo/Resources/lottie_circle_loader.json +0 -1
  117. package/ios-demo/Sources/MoMoUIKitsDemo/DemoScaffold.swift +0 -48
  118. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsBadgeDemoView.swift +0 -25
  119. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsButtonDemoView.swift +0 -26
  120. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsCheckboxDemoView.swift +0 -19
  121. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsChipDemoView.swift +0 -33
  122. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsInputDemoView.swift +0 -48
  123. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsPopupDemoView.swift +0 -21
  124. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsRadioDemoView.swift +0 -17
  125. package/ios-demo/Sources/MoMoUIKitsDemo/Demos/UIKitsTypographyDemoView.swift +0 -28
  126. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoContext.swift +0 -31
  127. package/ios-demo/Sources/MoMoUIKitsDemo/MoMoUIKitsDemoFactory.swift +0 -24
  128. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AnimatedHeaderDemo.swift +0 -110
  129. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +0 -93
  130. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +0 -269
  131. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BaselineView.swift +0 -117
  132. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BottomTabDemo.swift +0 -89
  133. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +0 -100
  134. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +0 -126
  135. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +0 -63
  136. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +0 -20
  137. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +0 -93
  138. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +0 -107
  139. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +0 -124
  140. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +0 -14
  141. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/HeaderUserDemo.swift +0 -49
  142. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +0 -278
  143. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +0 -58
  144. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +0 -78
  145. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +0 -138
  146. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +0 -111
  147. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +0 -136
  148. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +0 -107
  149. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +0 -73
  150. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +0 -100
  151. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +0 -32
  152. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +0 -57
  153. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +0 -70
  154. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +0 -227
  155. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +0 -72
  156. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupDisplayDemo.swift +0 -175
  157. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +0 -121
  158. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +0 -76
  159. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +0 -89
  160. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +0 -32
  161. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +0 -56
  162. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +0 -32
  163. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +0 -150
  164. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +0 -138
  165. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +0 -103
  166. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +0 -94
  167. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +0 -79
  168. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +0 -152
  169. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +0 -33
  170. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +0 -190
  171. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +0 -34
  172. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +0 -182
  173. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +0 -109
  174. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +0 -187
  175. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +0 -87
  176. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +0 -131
  177. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +0 -51
  178. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoModels.swift +0 -15
  179. package/ios-demo/Sources/MoMoUIKitsDemo/UIKitsDemoRegistry.swift +0 -73
  180. 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