@momo-kits/native-kits 0.152.4-beta.1 → 0.152.4-beta.10

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 (157) hide show
  1. package/CODE_OF_CONDUCT.md +133 -0
  2. package/CONTRIBUTING.md +114 -0
  3. package/LICENSE +20 -0
  4. package/README.md +7 -0
  5. package/build.gradle.kts +32 -0
  6. package/compose/MoMoComposeKits.podspec +54 -0
  7. package/compose/build.gradle.kts +149 -0
  8. package/compose/src/androidMain/AndroidManifest.xml +2 -0
  9. package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +105 -0
  10. package/compose/src/commonMain/composeResources/files/lottie_circle_loader.json +1 -0
  11. package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
  12. package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
  13. package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
  14. package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
  15. package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
  16. package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
  17. package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
  18. package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
  19. package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
  20. package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
  21. package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
  22. package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
  23. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
  24. package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
  25. package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +57 -0
  26. package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +201 -0
  27. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +222 -0
  28. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +48 -0
  29. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
  30. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
  31. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
  32. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +306 -0
  33. package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
  34. package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +715 -0
  35. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +214 -0
  36. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +236 -0
  37. package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
  38. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +77 -0
  39. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +27 -0
  40. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +334 -0
  41. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +345 -0
  42. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +90 -0
  43. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +131 -0
  44. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +543 -0
  45. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
  46. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +58 -0
  47. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +143 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +179 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +111 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +384 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +160 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +234 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +223 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +232 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +236 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +228 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +364 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +50 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +34 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +85 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +33 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +338 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +95 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +64 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +89 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +86 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +84 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +208 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +172 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +199 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +237 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +191 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +306 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +13 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +191 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +258 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +35 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -0
  83. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +59 -0
  84. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +68 -0
  85. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
  86. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +49 -0
  87. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
  88. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +232 -0
  89. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +111 -0
  90. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +94 -0
  91. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +159 -0
  92. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +232 -0
  93. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScaleSizeScope.kt +17 -0
  94. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +459 -0
  95. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +169 -0
  96. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +216 -0
  97. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
  98. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +180 -0
  99. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +251 -0
  100. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +80 -0
  101. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +306 -0
  102. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +31 -0
  103. package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +385 -0
  104. package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +38 -0
  105. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
  106. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +62 -0
  107. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +88 -0
  108. package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +144 -0
  109. package/gradle.properties +19 -0
  110. package/gradlew +240 -0
  111. package/gradlew.bat +91 -0
  112. package/ios/Application/ApplicationEnvironment.swift +50 -0
  113. package/ios/Application/Components.swift +263 -0
  114. package/ios/Application/ComposeApi.swift +22 -0
  115. package/ios/Application/FloatingButton.swift +172 -0
  116. package/ios/Application/HeaderRight.swift +271 -0
  117. package/ios/Application/Screen.swift +249 -0
  118. package/ios/Badge/Badge.swift +85 -0
  119. package/ios/Badge/BadgeDot.swift +31 -0
  120. package/ios/Badge/BadgeRibbon.swift +174 -0
  121. package/ios/Button/Button.swift +211 -0
  122. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +126 -0
  123. package/ios/Checkbox/Checkbox.swift +81 -0
  124. package/ios/Chip/Chip.swift +96 -0
  125. package/ios/Colors+Radius+Spacing/Colors.swift +172 -0
  126. package/ios/Colors+Radius+Spacing/Radius.swift +22 -0
  127. package/ios/Colors+Radius+Spacing/Spacing.swift +12 -0
  128. package/ios/Extensions/Color++.swift +25 -0
  129. package/ios/Icon/Icon.swift +51 -0
  130. package/ios/Image/Image.swift +70 -0
  131. package/ios/Input/Input.swift +207 -0
  132. package/ios/Input/InputPhoneNumber.swift +176 -0
  133. package/ios/Input/InputSearch.swift +238 -0
  134. package/ios/Input/InputTextArea.swift +242 -0
  135. package/ios/Lottie/LottieView.swift +86 -0
  136. package/ios/OTPKeyboard/KeyboardButton.swift +41 -0
  137. package/ios/OTPKeyboard/OTPKeyboard.swift +145 -0
  138. package/ios/Popup/PopupDisplay.swift +284 -0
  139. package/ios/Popup/PopupInput.swift +96 -0
  140. package/ios/Popup/PopupPromotion.swift +73 -0
  141. package/ios/PopupView/FullscreenPopup.swift +251 -0
  142. package/ios/PopupView/Modifiers.swift +158 -0
  143. package/ios/PopupView/PopupView.swift +289 -0
  144. package/ios/PopupView/Utils++.swift +281 -0
  145. package/ios/ScrollIndicator/ScrollIndicator.swift +110 -0
  146. package/ios/Swipeable/SwipeCell.swift +278 -0
  147. package/ios/Swipeable/SwipeCellModel.swift +86 -0
  148. package/ios/Switch/Switch.swift +44 -0
  149. package/ios/Template/Logo/Logo.swift +75 -0
  150. package/ios/Template/TrustBanner/TrustBanner.swift +120 -0
  151. package/ios/Theme.md +18 -0
  152. package/ios/Typography/Text.swift +140 -0
  153. package/ios/Typography/Typography.swift +95 -0
  154. package/ios/native-kits.podspec +18 -0
  155. package/package.json +6 -7
  156. package/settings.gradle.kts +25 -0
  157. package/shared/build.gradle.kts +0 -74
@@ -0,0 +1,73 @@
1
+ import SwiftUI
2
+ import SDWebImageSwiftUI
3
+
4
+ public struct PopupPromotion: View {
5
+ var imageUrl: String
6
+ var onIconClose: (() -> Void)?
7
+ var onAction: (() -> Void)?
8
+
9
+
10
+ public init(
11
+ imageUrl: String,
12
+ onIconClose: (() -> Void)? = nil,
13
+ onAction: (() -> Void)? = nil
14
+ ) {
15
+ self.imageUrl = imageUrl
16
+ self.onIconClose = onIconClose
17
+ self.onAction = onAction
18
+ }
19
+
20
+ public var body: some View {
21
+ VStack(spacing: 8) {
22
+ if(imageUrl.isEmpty) {
23
+ Icon(source: "media_fail", size: UIScreen.main.bounds.width - Spacing.XL * 2)
24
+ }
25
+ else {
26
+ WebImage(url: URL(string: imageUrl)!)
27
+ .resizable()
28
+ .placeholder {
29
+ Color.gray
30
+ .aspectRatio(0.72, contentMode: .fit)
31
+ .frame(maxWidth: UIScreen.main.bounds.width - Spacing.XL * 2)
32
+ .clipped()
33
+ }
34
+ .scaledToFill()
35
+ .aspectRatio(0.72, contentMode: .fit)
36
+ .frame(maxWidth: UIScreen.main.bounds.width - Spacing.XL * 2, alignment: .center)
37
+ .clipped()
38
+ .onTapGesture {
39
+ onAction?()
40
+ }
41
+ }
42
+ buildCloseIcon()
43
+ .accessibility(identifier: "ic_popup_close")
44
+ }
45
+ .accessibilityElement(children: .ignore)
46
+ .accessibility(identifier: "popup_promotion")
47
+ }
48
+
49
+ @ViewBuilder
50
+ private func buildCloseIcon() -> some View {
51
+ HStack(alignment: .center, spacing: 0) {
52
+ SwiftUI.Button(action: {
53
+ onIconClose?()
54
+ }) {
55
+ ZStack {
56
+ Circle()
57
+ .strokeBorder(Colors.black01, lineWidth: 2)
58
+ .background(Circle().fill(Colors.black17))
59
+ .frame(width: 22, height: 22)
60
+
61
+ Icon(source: "navigation_close", size: 16, color: Colors.black01)
62
+ }
63
+ .frame(width: 40, height: 40)
64
+ .padding(8)
65
+ .zIndex(1)
66
+ }
67
+ }
68
+ }
69
+
70
+ }
71
+
72
+
73
+
@@ -0,0 +1,251 @@
1
+
2
+ import Foundation
3
+ import SwiftUI
4
+
5
+ public struct FullscreenPopup<Item: Equatable, PopupContent: View>: ViewModifier {
6
+
7
+ // MARK: - Presentaion
8
+ @Binding var isPresented: Bool
9
+ @Binding var item: Item?
10
+
11
+ var isBoolMode: Bool
12
+
13
+ var sheetPresented: Bool {
14
+ item != nil || isPresented
15
+ }
16
+
17
+ // MARK: - Parameters
18
+ var type: Popup<Item, PopupContent>.PopupType
19
+ var position: Popup<Item, PopupContent>.Position
20
+
21
+ var animation: Animation
22
+
23
+ /// If nil - never hides on its own
24
+ var autohideIn: Double?
25
+
26
+ /// Should allow dismiss by dragging
27
+ var dragToDismiss: Bool
28
+
29
+ /// Should close on tap outside - default is `true`
30
+ var closeOnTapOutside: Bool
31
+
32
+ /// Background color for outside area - default is `Color.clear`
33
+ var backgroundColor: Color
34
+
35
+ /// is called on any close action
36
+ var dismissCallback: (DismissSource) -> ()
37
+
38
+ var view: () -> PopupContent
39
+
40
+ // MARK: - Presentation animation
41
+ /// Trigger popup showing/hiding animations and...
42
+ @State private var shouldShowContent = false
43
+
44
+ /// ... once hiding animation is finished remove popup from the memory using this flag
45
+ @State private var showContent = false
46
+
47
+ /// show transparentNonAnimatingFullScreenCover
48
+ @State private var showSheet = false
49
+
50
+ /// opacity of background color
51
+ @State private var opacity = 0.0
52
+
53
+ // MARK: - Autohide
54
+ /// Class reference for capturing a weak reference later in dispatch work holder.
55
+ private var isPresentedRef: ClassReference<Binding<Bool>>?
56
+ private var itemRef: ClassReference<Binding<Item?>>?
57
+
58
+ /// holder for autohiding dispatch work (to be able to cancel it when needed)
59
+ private var dispatchWorkHolder = DispatchWorkHolder()
60
+
61
+ // MARK: - Internal
62
+ /// Set dismiss souce to pass to dismiss callback
63
+ @State private var dismissSource: DismissSource?
64
+
65
+ init(isPresented: Binding<Bool>,
66
+ type: Popup<Item, PopupContent>.PopupType = .`default`,
67
+ position: Popup<Item, PopupContent>.Position = .bottom,
68
+ animation: Animation,
69
+ autohideIn: Double?,
70
+ dragToDismiss: Bool,
71
+ closeOnTapOutside: Bool,
72
+ backgroundColor: Color,
73
+ dismissCallback: @escaping (DismissSource) -> (),
74
+ view: @escaping () -> PopupContent) {
75
+ self._isPresented = isPresented
76
+ self._item = .constant(nil)
77
+ self.isBoolMode = true
78
+
79
+ self.type = type
80
+ self.position = position
81
+ self.animation = animation
82
+ self.autohideIn = autohideIn
83
+ self.dragToDismiss = dragToDismiss
84
+ self.closeOnTapOutside = closeOnTapOutside
85
+ self.backgroundColor = backgroundColor
86
+ self.dismissCallback = dismissCallback
87
+ self.view = view
88
+
89
+ self.isPresentedRef = ClassReference(self.$isPresented)
90
+ self.itemRef = ClassReference(self.$item)
91
+ }
92
+
93
+ init(item: Binding<Item?>,
94
+ type: Popup<Item, PopupContent>.PopupType = .`default`,
95
+ position: Popup<Item, PopupContent>.Position = .bottom,
96
+ animation: Animation,
97
+ autohideIn: Double?,
98
+ dragToDismiss: Bool,
99
+ closeOnTapOutside: Bool,
100
+ backgroundColor: Color,
101
+ dismissCallback: @escaping (DismissSource) -> (),
102
+ view: @escaping () -> PopupContent) {
103
+ self._isPresented = .constant(false)
104
+ self._item = item
105
+ self.isBoolMode = false
106
+
107
+ self.type = type
108
+ self.position = position
109
+ self.animation = animation
110
+ self.autohideIn = autohideIn
111
+ self.dragToDismiss = dragToDismiss
112
+ self.closeOnTapOutside = closeOnTapOutside
113
+ self.backgroundColor = backgroundColor
114
+ self.dismissCallback = dismissCallback
115
+ self.view = view
116
+
117
+ self.isPresentedRef = ClassReference(self.$isPresented)
118
+ self.itemRef = ClassReference(self.$item)
119
+ }
120
+
121
+ public func body(content: Content) -> some View {
122
+ if isBoolMode {
123
+ boolBody(content: content)
124
+ } else {
125
+ itemBody(content: content)
126
+ }
127
+ }
128
+
129
+ func backgroundColorView() -> some View {
130
+ backgroundColor.opacity(opacity)
131
+ .applyIf(closeOnTapOutside) { view in
132
+ view.contentShape(Rectangle())
133
+ }
134
+ .addTapIf(if: closeOnTapOutside) {
135
+ dismissSource = .tapOutside
136
+ isPresented = false
137
+ item = nil
138
+ }
139
+ .edgesIgnoringSafeArea(.all)
140
+ .animation(.linear(duration: 0.2), value: opacity)
141
+ }
142
+
143
+ public func boolBody(content: Content) -> some View {
144
+ content
145
+ .transparentNonAnimatingFullScreenCover(isPresented: $showSheet) {
146
+ backgroundColorView()
147
+ .modifier(
148
+ Popup(
149
+ isPresented: $isPresented,
150
+ type: type,
151
+ position: position,
152
+ animation: animation,
153
+ autohideIn: autohideIn,
154
+ dragToDismiss: dragToDismiss,
155
+ closeOnTapOutside: closeOnTapOutside,
156
+ shouldShowContent: shouldShowContent,
157
+ showContent: showContent,
158
+ dismissCallback: { _ in },
159
+ dismissSource: $dismissSource,
160
+ animationCompletedCallback: onAnimationCompleted,
161
+ view: view)
162
+ )
163
+ }
164
+ .valueChanged(value: isPresented, onChange: { newValue in
165
+ appearAction(sheetPresented: newValue)
166
+ })
167
+ }
168
+
169
+ public func itemBody(content: Content) -> some View {
170
+ content
171
+ .transparentNonAnimatingFullScreenCover(isPresented: $showSheet) {
172
+ backgroundColorView()
173
+ .modifier(
174
+ Popup(
175
+ item: $item,
176
+ type: type,
177
+ position: position,
178
+ animation: animation,
179
+ autohideIn: autohideIn,
180
+ dragToDismiss: dragToDismiss,
181
+ closeOnTapOutside: closeOnTapOutside,
182
+ shouldShowContent: shouldShowContent,
183
+ showContent: showContent,
184
+ dismissCallback: { _ in },
185
+ dismissSource: $dismissSource,
186
+ animationCompletedCallback: onAnimationCompleted,
187
+ view: view)
188
+ )
189
+ }
190
+ .valueChanged(value: item, onChange: { newValue in
191
+ appearAction(sheetPresented: newValue != nil)
192
+ })
193
+ }
194
+
195
+ func appearAction(sheetPresented: Bool) {
196
+ if sheetPresented {
197
+ dismissSource = nil
198
+ showSheet = true // show transparent fullscreen sheet
199
+ showContent = true // immediately load popup body
200
+ performWithDelay(0.01) {
201
+ shouldShowContent = true // this will cause currentOffset change thus triggering the sliding showing animation
202
+ opacity = 1 // this will cause cross disolving animation for background color
203
+ setupAutohide()
204
+ }
205
+ } else {
206
+ dispatchWorkHolder.work?.cancel()
207
+ shouldShowContent = false // this will cause currentOffset change thus triggering the sliding hiding animation
208
+ opacity = 0
209
+ // do the rest once the animation is finished (see onAnimationCompleted())
210
+ performWithDelay(0.3) { // TEMP: imitate onAnimationCompleted for now
211
+ onAnimationCompleted()
212
+ }
213
+ }
214
+ }
215
+
216
+ func onAnimationCompleted() -> () {
217
+ if shouldShowContent { // return if this was called on showing animation, only proceed if called on hiding
218
+ return
219
+ }
220
+ showContent = false // unload popup body after hiding animation is done
221
+ performWithDelay(0.01) {
222
+ showSheet = false
223
+ }
224
+ dismissCallback(dismissSource ?? .binding)
225
+ }
226
+
227
+ func setupAutohide() {
228
+ // if needed, dispatch autohide and cancel previous one
229
+ if let autohideIn = autohideIn {
230
+ dispatchWorkHolder.work?.cancel()
231
+
232
+ // Weak reference to avoid the work item capturing the struct,
233
+ // which would create a retain cycle with the work holder itself.
234
+ dispatchWorkHolder.work = DispatchWorkItem(block: { [weak isPresentedRef, weak itemRef] in
235
+ isPresentedRef?.value.wrappedValue = false
236
+ itemRef?.value.wrappedValue = nil
237
+ dismissSource = .autohide
238
+ })
239
+ if sheetPresented, let work = dispatchWorkHolder.work {
240
+ DispatchQueue.main.asyncAfter(deadline: .now() + autohideIn, execute: work)
241
+ }
242
+ }
243
+ }
244
+
245
+ func performWithDelay(_ delay: Double, block: @escaping ()->()) {
246
+ DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
247
+ block()
248
+ }
249
+ }
250
+
251
+ }
@@ -0,0 +1,158 @@
1
+ import SwiftUI
2
+
3
+ extension View {
4
+
5
+ public func popup<Item: Equatable, PopupContent: View>(
6
+ item: Binding<Item?>,
7
+ type: Popup<Item, PopupContent>.PopupType = .`default`,
8
+ position: Popup<Item, PopupContent>.Position = .bottom,
9
+ animation: Animation = Animation.easeOut(duration: 0.3),
10
+ autohideIn: Double? = nil,
11
+ dragToDismiss: Bool = true,
12
+ closeOnTapOutside: Bool = false,
13
+ backgroundColor: Color = Color.clear,
14
+ @ViewBuilder view: @escaping () -> PopupContent) -> some View {
15
+ self.modifier(
16
+ FullscreenPopup(
17
+ item: item,
18
+ type: type,
19
+ position: position,
20
+ animation: animation,
21
+ autohideIn: autohideIn,
22
+ dragToDismiss: dragToDismiss,
23
+ closeOnTapOutside: closeOnTapOutside,
24
+ backgroundColor: backgroundColor,
25
+ dismissCallback: { _ in },
26
+ view: view)
27
+ )
28
+ }
29
+
30
+ public func popup<PopupContent: View>(
31
+ isPresented: Binding<Bool>,
32
+ type: Popup<Int, PopupContent>.PopupType = .`default`,
33
+ position: Popup<Int, PopupContent>.Position = .bottom,
34
+ animation: Animation = Animation.easeOut(duration: 0.3),
35
+ autohideIn: Double? = nil,
36
+ dragToDismiss: Bool = true,
37
+ closeOnTapOutside: Bool = false,
38
+ backgroundColor: Color = Color.clear,
39
+ @ViewBuilder view: @escaping () -> PopupContent) -> some View {
40
+ self.modifier(
41
+ FullscreenPopup<Int, PopupContent>(
42
+ isPresented: isPresented,
43
+ type: type,
44
+ position: position,
45
+ animation: animation,
46
+ autohideIn: autohideIn,
47
+ dragToDismiss: dragToDismiss,
48
+ closeOnTapOutside: closeOnTapOutside,
49
+ backgroundColor: backgroundColor,
50
+ dismissCallback: { _ in },
51
+ view: view)
52
+ )
53
+ }
54
+
55
+ public func popup<Item: Equatable, PopupContent: View>(
56
+ item: Binding<Item?>,
57
+ type: Popup<Item, PopupContent>.PopupType = .`default`,
58
+ position: Popup<Item, PopupContent>.Position = .bottom,
59
+ animation: Animation = Animation.easeOut(duration: 0.3),
60
+ autohideIn: Double? = nil,
61
+ dragToDismiss: Bool = true,
62
+ closeOnTapOutside: Bool = false,
63
+ backgroundColor: Color = Color.clear,
64
+ dismissCallback: @escaping () -> (),
65
+ @ViewBuilder view: @escaping () -> PopupContent) -> some View {
66
+ self.modifier(
67
+ FullscreenPopup(
68
+ item: item,
69
+ type: type,
70
+ position: position,
71
+ animation: animation,
72
+ autohideIn: autohideIn,
73
+ dragToDismiss: dragToDismiss,
74
+ closeOnTapOutside: closeOnTapOutside,
75
+ backgroundColor: backgroundColor,
76
+ dismissCallback: { _ in dismissCallback() },
77
+ view: view)
78
+ )
79
+ }
80
+
81
+ public func popup<PopupContent: View>(
82
+ isPresented: Binding<Bool>,
83
+ type: Popup<Int, PopupContent>.PopupType = .`default`,
84
+ position: Popup<Int, PopupContent>.Position = .bottom,
85
+ animation: Animation = Animation.easeOut(duration: 0.3),
86
+ autohideIn: Double? = nil,
87
+ dragToDismiss: Bool = true,
88
+ closeOnTapOutside: Bool = false,
89
+ backgroundColor: Color = Color.clear,
90
+ dismissCallback: @escaping () -> (),
91
+ @ViewBuilder view: @escaping () -> PopupContent) -> some View {
92
+ self.modifier(
93
+ FullscreenPopup<Int, PopupContent>(
94
+ isPresented: isPresented,
95
+ type: type,
96
+ position: position,
97
+ animation: animation,
98
+ autohideIn: autohideIn,
99
+ dragToDismiss: dragToDismiss,
100
+ closeOnTapOutside: closeOnTapOutside,
101
+ backgroundColor: backgroundColor,
102
+ dismissCallback: { _ in dismissCallback() },
103
+ view: view)
104
+ )
105
+ }
106
+
107
+ public func popup<Item: Equatable, PopupContent: View>(
108
+ item: Binding<Item?>,
109
+ type: Popup<Item, PopupContent>.PopupType = .`default`,
110
+ position: Popup<Item, PopupContent>.Position = .bottom,
111
+ animation: Animation = Animation.easeOut(duration: 0.3),
112
+ autohideIn: Double? = nil,
113
+ dragToDismiss: Bool = true,
114
+ closeOnTapOutside: Bool = false,
115
+ backgroundColor: Color = Color.clear,
116
+ dismissSourceCallback: @escaping (DismissSource) -> (),
117
+ @ViewBuilder view: @escaping () -> PopupContent) -> some View {
118
+ self.modifier(
119
+ FullscreenPopup(
120
+ item: item,
121
+ type: type,
122
+ position: position,
123
+ animation: animation,
124
+ autohideIn: autohideIn,
125
+ dragToDismiss: dragToDismiss,
126
+ closeOnTapOutside: closeOnTapOutside,
127
+ backgroundColor: backgroundColor,
128
+ dismissCallback: dismissSourceCallback,
129
+ view: view)
130
+ )
131
+ }
132
+
133
+ public func popup<PopupContent: View>(
134
+ isPresented: Binding<Bool>,
135
+ type: Popup<Int, PopupContent>.PopupType = .`default`,
136
+ position: Popup<Int, PopupContent>.Position = .bottom,
137
+ animation: Animation = Animation.easeOut(duration: 0.3),
138
+ autohideIn: Double? = nil,
139
+ dragToDismiss: Bool = true,
140
+ closeOnTapOutside: Bool = false,
141
+ backgroundColor: Color = Color.clear,
142
+ dismissSourceCallback: @escaping (DismissSource) -> (),
143
+ @ViewBuilder view: @escaping () -> PopupContent) -> some View {
144
+ self.modifier(
145
+ FullscreenPopup<Int, PopupContent>(
146
+ isPresented: isPresented,
147
+ type: type,
148
+ position: position,
149
+ animation: animation,
150
+ autohideIn: autohideIn,
151
+ dragToDismiss: dragToDismiss,
152
+ closeOnTapOutside: closeOnTapOutside,
153
+ backgroundColor: backgroundColor,
154
+ dismissCallback: dismissSourceCallback,
155
+ view: view)
156
+ )
157
+ }
158
+ }