@momo-kits/native-kits 0.0.1-beta.4 → 0.89.1-2

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 (195) hide show
  1. package/README.md +1 -0
  2. package/android/.gradle/8.0/checksums/checksums.lock +0 -0
  3. package/android/.gradle/8.0/dependencies-accessors/dependencies-accessors.lock +0 -0
  4. package/android/.gradle/8.0/dependencies-accessors/gc.properties +0 -0
  5. package/android/.gradle/8.0/fileChanges/last-build.bin +0 -0
  6. package/android/.gradle/8.0/fileHashes/fileHashes.lock +0 -0
  7. package/android/.gradle/8.0/gc.properties +0 -0
  8. package/android/.gradle/config.properties +2 -0
  9. package/android/.gradle/vcs-1/gc.properties +0 -0
  10. package/android/.idea/gradle.xml +12 -0
  11. package/android/.idea/migrations.xml +10 -0
  12. package/android/.idea/misc.xml +10 -0
  13. package/android/.idea/vcs.xml +6 -0
  14. package/android/.idea/workspace.xml +50 -0
  15. package/android/build.gradle +35 -69
  16. package/android/local.properties +8 -0
  17. package/android/proguard-rules.pro +21 -0
  18. package/android/src/main/AndroidManifest.xml +2 -2
  19. package/android/src/main/java/com/momoplatform/nativekits/foundations/Button.kt +16 -0
  20. package/android/src/main/java/com/momoplatform/nativekits/libs/Logo.kt +63 -0
  21. package/android/src/main/java/com/momoplatform/nativekits/libs/TrustBanner.kt +246 -0
  22. package/android/src/main/res/drawable/ic_launcher_background.xml +170 -0
  23. package/android/src/main/res/drawable-v24/ic_launcher_foreground.xml +30 -0
  24. package/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
  25. package/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
  26. package/android/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  27. package/android/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  28. package/android/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  29. package/android/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  30. package/android/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  31. package/android/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  32. package/android/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  33. package/android/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  34. package/android/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  35. package/android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  36. package/android/src/main/res/values/colors.xml +10 -0
  37. package/android/src/main/res/values/strings.xml +3 -0
  38. package/android/src/main/res/values/themes.xml +3 -0
  39. package/android/src/main/res/values-night/themes.xml +3 -0
  40. package/android/src/main/res/xml/backup_rules.xml +13 -0
  41. package/android/src/main/res/xml/data_extraction_rules.xml +19 -0
  42. package/build.gradle.kts +18 -0
  43. package/compose/build.gradle.kts +82 -0
  44. package/compose/src/androidMain/AndroidManifest.xml +2 -0
  45. package/compose/src/androidMain/kotlin/vn/momo/kits/utils/Platform.kt +3 -0
  46. package/compose/src/androidMain/kotlin/vn/momo/kits/utils/SetMaskFilter.kt +8 -0
  47. package/compose/src/androidMain/kotlin/vn/momo/kits/utils/Utils.android.kt +14 -0
  48. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Components.kt +194 -0
  49. package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +197 -0
  50. package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +133 -0
  51. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +62 -0
  52. package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +27 -0
  53. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +288 -0
  54. package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +80 -0
  55. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
  56. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +42 -0
  57. package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +126 -0
  58. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +51 -0
  59. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +299 -0
  60. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +246 -0
  61. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +223 -0
  62. package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +244 -0
  63. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +281 -0
  64. package/compose/src/commonMain/kotlin/vn/momo/kits/components/PromotionPopup.kt +72 -0
  65. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +65 -0
  66. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +64 -0
  67. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +63 -0
  68. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +73 -0
  69. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +42 -0
  70. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +150 -0
  71. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
  72. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +13 -0
  73. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +173 -0
  74. package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +242 -0
  75. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
  76. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +2 -0
  77. package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -0
  78. package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +50 -0
  79. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1321 -0
  80. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Platform.kt +3 -0
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/SetMaskFilter.kt +5 -0
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +97 -0
  83. package/compose/src/iosMain/kotlin/vn/momo/kits/utils/Platform.kt +3 -0
  84. package/compose/src/iosMain/kotlin/vn/momo/kits/utils/SetMaskFilter.kt +9 -0
  85. package/compose/src/iosMain/kotlin/vn/momo/kits/utils/Utils.ios.kt +18 -0
  86. package/gradle.properties +19 -0
  87. package/gradlew +240 -0
  88. package/gradlew.bat +91 -0
  89. package/ios/Button/Button.swift +9 -2
  90. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +126 -0
  91. package/ios/Checkbox/Checkbox.swift +38 -0
  92. package/ios/{Colors+Spacing → Colors+Radius+Spacing}/Colors.swift +21 -19
  93. package/ios/Colors+Radius+Spacing/Radius.swift +22 -0
  94. package/ios/Image/Image.swift +60 -0
  95. package/ios/Input/Input.swift +88 -37
  96. package/ios/OTPKeyboard/KeyboardButton.swift +12 -4
  97. package/ios/OTPKeyboard/OTPKeyboard.swift +1 -1
  98. package/ios/Popup/PopupDisplay.swift +169 -0
  99. package/ios/Popup/PopupInput.swift +96 -0
  100. package/ios/Popup/PopupPromotion.swift +52 -0
  101. package/ios/PopupView/FullscreenPopup.swift +251 -0
  102. package/ios/{Popup → PopupView}/Modifiers.swift +6 -20
  103. package/ios/{Popup → PopupView}/PopupView.swift +38 -146
  104. package/ios/PopupView/Utils++.swift +281 -0
  105. package/ios/ScrollIndicator/ScrollIndicator.swift +79 -0
  106. package/ios/Switch/Switch.swift +44 -0
  107. package/ios/Template/Logo/Logo.swift +75 -0
  108. package/ios/Template/TrustBanner/TrustBanner.swift +189 -0
  109. package/ios/native-kits.podspec +2 -0
  110. package/local.properties +8 -0
  111. package/package.json +6 -6
  112. package/publish.sh +1 -10
  113. package/settings.gradle.kts +41 -0
  114. package/android/gradle.properties +0 -5
  115. package/android/src/main/java/com/momoplatform/nativekits/NativeKitsPackage.kt +0 -17
  116. package/android/src/main/java/com/momoplatform/nativekits/NativeKitsViewManager.kt +0 -20
  117. package/example/ios/Example/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -63
  118. package/example/ios/Example/Assets.xcassets/Background.colorset/Contents.json +0 -38
  119. package/example/ios/Example/Assets.xcassets/Border.colorset/Contents.json +0 -38
  120. package/example/ios/Example/Assets.xcassets/Card.colorset/Contents.json +0 -38
  121. package/example/ios/Example/Assets.xcassets/Contents.json +0 -6
  122. package/example/ios/Example/Assets.xcassets/Error.colorset/Contents.json +0 -38
  123. package/example/ios/Example/Assets.xcassets/Primary.colorset/Contents.json +0 -38
  124. package/example/ios/Example/Assets.xcassets/PrimaryDark.colorset/Contents.json +0 -38
  125. package/example/ios/Example/Assets.xcassets/PrimaryLight.colorset/Contents.json +0 -38
  126. package/example/ios/Example/Assets.xcassets/Secondary.colorset/Contents.json +0 -38
  127. package/example/ios/Example/Assets.xcassets/Success.colorset/Contents.json +0 -38
  128. package/example/ios/Example/Assets.xcassets/Text.colorset/Contents.json +0 -38
  129. package/example/ios/Example/Assets.xcassets/TextSecondary.colorset/Contents.json +0 -38
  130. package/example/ios/Example/Assets.xcassets/Warning.colorset/Contents.json +0 -38
  131. package/example/ios/Example/ComponentDetail.swift +0 -35
  132. package/example/ios/Example/ComponentItem.swift +0 -24
  133. package/example/ios/Example/ComponentModel.swift +0 -33
  134. package/example/ios/Example/ContentView.swift +0 -53
  135. package/example/ios/Example/Example.entitlements +0 -10
  136. package/example/ios/Example/Foundation/ButtonDemo.swift +0 -80
  137. package/example/ios/Example/Foundation/Popup/ActionSheets.swift +0 -136
  138. package/example/ios/Example/Foundation/Popup/Floats.swift +0 -127
  139. package/example/ios/Example/Foundation/Popup/Popups.swift +0 -101
  140. package/example/ios/Example/Foundation/PopupDemo.swift +0 -366
  141. package/example/ios/Example/Foundation/TextInputDemo.swift +0 -31
  142. package/example/ios/Example/Foundation/TypographyDemo.swift +0 -53
  143. package/example/ios/Example/Foundation.swift +0 -26
  144. package/example/ios/Example/Info.plist +0 -18
  145. package/example/ios/Example/Library/SwipeableDemo.swift +0 -105
  146. package/example/ios/Example/Library.swift +0 -26
  147. package/example/ios/Example/Main.swift +0 -11
  148. package/example/ios/Example/Overview.swift +0 -20
  149. package/example/ios/Example/Preview Content/Preview Assets.xcassets/Contents.json +0 -6
  150. package/example/ios/Example/Resource/SFProText-Black.ttf +0 -0
  151. package/example/ios/Example/Resource/SFProText-Bold.ttf +0 -0
  152. package/example/ios/Example/Resource/SFProText-Heavy.ttf +0 -0
  153. package/example/ios/Example/Resource/SFProText-Light.ttf +0 -0
  154. package/example/ios/Example/Resource/SFProText-Medium.ttf +0 -0
  155. package/example/ios/Example/Resource/SFProText-Regular.ttf +0 -0
  156. package/example/ios/Example/Resource/SFProText-Semibold.ttf +0 -0
  157. package/example/ios/Example/Resource/SFProText-Thin.ttf +0 -0
  158. package/example/ios/Example/Resource/SFProText-Ultralight.ttf +0 -0
  159. package/example/ios/Example/Resource/foundation.json +0 -33
  160. package/example/ios/Example/Resource/library.json +0 -8
  161. package/example/ios/Example/Utils.swift +0 -30
  162. package/example/ios/Example/ViewModifier/DarkViewModifier.swift +0 -22
  163. package/example/ios/Example.xcodeproj/project.pbxproj +0 -570
  164. package/example/ios/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  165. package/example/ios/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  166. package/example/ios/Example.xcodeproj/project.xcworkspace/xcuserdata/wem.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  167. package/example/ios/Example.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  168. package/example/ios/Example.xcworkspace/contents.xcworkspacedata +0 -10
  169. package/example/ios/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  170. package/example/ios/Example.xcworkspace/xcuserdata/wem.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  171. package/example/ios/Example.xcworkspace/xcuserdata/wem.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
  172. package/example/ios/Podfile +0 -6
  173. package/example/ios/Podfile.lock +0 -16
  174. package/example/ios/Pods/Local Podspecs/MoMoUIKits.podspec.json +0 -29
  175. package/example/ios/Pods/Manifest.lock +0 -16
  176. package/example/ios/Pods/Pods.xcodeproj/project.pbxproj +0 -661
  177. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  178. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  179. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/xcschememanagement.plist +0 -25
  180. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-dummy.m +0 -5
  181. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-prefix.pch +0 -12
  182. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-umbrella.h +0 -16
  183. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.debug.xcconfig +0 -12
  184. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.modulemap +0 -6
  185. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.release.xcconfig +0 -12
  186. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.markdown +0 -3
  187. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.plist +0 -29
  188. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-dummy.m +0 -5
  189. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-umbrella.h +0 -16
  190. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig +0 -14
  191. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.modulemap +0 -6
  192. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig +0 -14
  193. package/ios/Colors+Spacing/Radius.swift +0 -11
  194. package/ios/Popup/Utils++.swift +0 -163
  195. /package/ios/{Colors+Spacing → Colors+Radius+Spacing}/Spacing.swift +0 -0
@@ -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
+ }
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  import SwiftUI
4
2
 
5
3
  extension View {
@@ -11,19 +9,17 @@ extension View {
11
9
  animation: Animation = Animation.easeOut(duration: 0.3),
12
10
  autohideIn: Double? = nil,
13
11
  dragToDismiss: Bool = true,
14
- closeOnTap: Bool = true,
15
12
  closeOnTapOutside: Bool = false,
16
13
  backgroundColor: Color = Color.clear,
17
14
  @ViewBuilder view: @escaping () -> PopupContent) -> some View {
18
15
  self.modifier(
19
- Popup(
16
+ FullscreenPopup(
20
17
  item: item,
21
18
  type: type,
22
19
  position: position,
23
20
  animation: animation,
24
21
  autohideIn: autohideIn,
25
22
  dragToDismiss: dragToDismiss,
26
- closeOnTap: closeOnTap,
27
23
  closeOnTapOutside: closeOnTapOutside,
28
24
  backgroundColor: backgroundColor,
29
25
  dismissCallback: { _ in },
@@ -38,19 +34,17 @@ extension View {
38
34
  animation: Animation = Animation.easeOut(duration: 0.3),
39
35
  autohideIn: Double? = nil,
40
36
  dragToDismiss: Bool = true,
41
- closeOnTap: Bool = true,
42
37
  closeOnTapOutside: Bool = false,
43
38
  backgroundColor: Color = Color.clear,
44
39
  @ViewBuilder view: @escaping () -> PopupContent) -> some View {
45
40
  self.modifier(
46
- Popup<Int, PopupContent>(
41
+ FullscreenPopup<Int, PopupContent>(
47
42
  isPresented: isPresented,
48
43
  type: type,
49
44
  position: position,
50
45
  animation: animation,
51
46
  autohideIn: autohideIn,
52
47
  dragToDismiss: dragToDismiss,
53
- closeOnTap: closeOnTap,
54
48
  closeOnTapOutside: closeOnTapOutside,
55
49
  backgroundColor: backgroundColor,
56
50
  dismissCallback: { _ in },
@@ -65,20 +59,18 @@ extension View {
65
59
  animation: Animation = Animation.easeOut(duration: 0.3),
66
60
  autohideIn: Double? = nil,
67
61
  dragToDismiss: Bool = true,
68
- closeOnTap: Bool = true,
69
62
  closeOnTapOutside: Bool = false,
70
63
  backgroundColor: Color = Color.clear,
71
64
  dismissCallback: @escaping () -> (),
72
65
  @ViewBuilder view: @escaping () -> PopupContent) -> some View {
73
66
  self.modifier(
74
- Popup(
67
+ FullscreenPopup(
75
68
  item: item,
76
69
  type: type,
77
70
  position: position,
78
71
  animation: animation,
79
72
  autohideIn: autohideIn,
80
73
  dragToDismiss: dragToDismiss,
81
- closeOnTap: closeOnTap,
82
74
  closeOnTapOutside: closeOnTapOutside,
83
75
  backgroundColor: backgroundColor,
84
76
  dismissCallback: { _ in dismissCallback() },
@@ -93,20 +85,18 @@ extension View {
93
85
  animation: Animation = Animation.easeOut(duration: 0.3),
94
86
  autohideIn: Double? = nil,
95
87
  dragToDismiss: Bool = true,
96
- closeOnTap: Bool = true,
97
88
  closeOnTapOutside: Bool = false,
98
89
  backgroundColor: Color = Color.clear,
99
90
  dismissCallback: @escaping () -> (),
100
91
  @ViewBuilder view: @escaping () -> PopupContent) -> some View {
101
92
  self.modifier(
102
- Popup<Int, PopupContent>(
93
+ FullscreenPopup<Int, PopupContent>(
103
94
  isPresented: isPresented,
104
95
  type: type,
105
96
  position: position,
106
97
  animation: animation,
107
98
  autohideIn: autohideIn,
108
99
  dragToDismiss: dragToDismiss,
109
- closeOnTap: closeOnTap,
110
100
  closeOnTapOutside: closeOnTapOutside,
111
101
  backgroundColor: backgroundColor,
112
102
  dismissCallback: { _ in dismissCallback() },
@@ -121,20 +111,18 @@ extension View {
121
111
  animation: Animation = Animation.easeOut(duration: 0.3),
122
112
  autohideIn: Double? = nil,
123
113
  dragToDismiss: Bool = true,
124
- closeOnTap: Bool = true,
125
114
  closeOnTapOutside: Bool = false,
126
115
  backgroundColor: Color = Color.clear,
127
116
  dismissSourceCallback: @escaping (DismissSource) -> (),
128
117
  @ViewBuilder view: @escaping () -> PopupContent) -> some View {
129
118
  self.modifier(
130
- Popup(
119
+ FullscreenPopup(
131
120
  item: item,
132
121
  type: type,
133
122
  position: position,
134
123
  animation: animation,
135
124
  autohideIn: autohideIn,
136
125
  dragToDismiss: dragToDismiss,
137
- closeOnTap: closeOnTap,
138
126
  closeOnTapOutside: closeOnTapOutside,
139
127
  backgroundColor: backgroundColor,
140
128
  dismissCallback: dismissSourceCallback,
@@ -149,20 +137,18 @@ extension View {
149
137
  animation: Animation = Animation.easeOut(duration: 0.3),
150
138
  autohideIn: Double? = nil,
151
139
  dragToDismiss: Bool = true,
152
- closeOnTap: Bool = true,
153
140
  closeOnTapOutside: Bool = false,
154
141
  backgroundColor: Color = Color.clear,
155
142
  dismissSourceCallback: @escaping (DismissSource) -> (),
156
143
  @ViewBuilder view: @escaping () -> PopupContent) -> some View {
157
144
  self.modifier(
158
- Popup<Int, PopupContent>(
145
+ FullscreenPopup<Int, PopupContent>(
159
146
  isPresented: isPresented,
160
147
  type: type,
161
148
  position: position,
162
149
  animation: animation,
163
150
  autohideIn: autohideIn,
164
151
  dragToDismiss: dragToDismiss,
165
- closeOnTap: closeOnTap,
166
152
  closeOnTapOutside: closeOnTapOutside,
167
153
  backgroundColor: backgroundColor,
168
154
  dismissCallback: dismissSourceCallback,
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  import SwiftUI
4
2
 
5
3
  public enum DismissSource {
@@ -18,10 +16,12 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
18
16
  animation: Animation,
19
17
  autohideIn: Double?,
20
18
  dragToDismiss: Bool,
21
- closeOnTap: Bool,
22
19
  closeOnTapOutside: Bool,
23
- backgroundColor: Color,
20
+ shouldShowContent: Bool = true,
21
+ showContent: Bool = true,
24
22
  dismissCallback: @escaping (DismissSource) -> (),
23
+ dismissSource: Binding<DismissSource?>,
24
+ animationCompletedCallback: @escaping () -> (),
25
25
  view: @escaping () -> PopupContent) {
26
26
  self._isPresented = isPresented
27
27
  self._item = .constant(nil)
@@ -30,13 +30,13 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
30
30
  self.animation = animation
31
31
  self.autohideIn = autohideIn
32
32
  self.dragToDismiss = dragToDismiss
33
- self.closeOnTap = closeOnTap
34
33
  self.closeOnTapOutside = closeOnTapOutside
35
- self.backgroundColor = backgroundColor
34
+ self.shouldShowContent = shouldShowContent
35
+ self.showContent = showContent
36
36
  self.dismissCallback = dismissCallback
37
+ self._dismissSource = dismissSource
38
+ self.animationCompletedCallback = animationCompletedCallback
37
39
  self.view = view
38
- self.isPresentedRef = ClassReference(self.$isPresented)
39
- self.itemRef = ClassReference(self.$item)
40
40
  }
41
41
 
42
42
  init(item: Binding<Item?>,
@@ -45,10 +45,12 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
45
45
  animation: Animation,
46
46
  autohideIn: Double?,
47
47
  dragToDismiss: Bool,
48
- closeOnTap: Bool,
49
48
  closeOnTapOutside: Bool,
50
- backgroundColor: Color,
49
+ shouldShowContent: Bool = true,
50
+ showContent: Bool = true,
51
51
  dismissCallback: @escaping (DismissSource) -> (),
52
+ dismissSource: Binding<DismissSource?>,
53
+ animationCompletedCallback: @escaping () -> (),
52
54
  view: @escaping () -> PopupContent) {
53
55
  self._isPresented = .constant(false)
54
56
  self._item = item
@@ -57,13 +59,13 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
57
59
  self.animation = animation
58
60
  self.autohideIn = autohideIn
59
61
  self.dragToDismiss = dragToDismiss
60
- self.closeOnTap = closeOnTap
61
62
  self.closeOnTapOutside = closeOnTapOutside
62
- self.backgroundColor = backgroundColor
63
+ self.shouldShowContent = shouldShowContent
64
+ self.showContent = showContent
63
65
  self.dismissCallback = dismissCallback
66
+ self._dismissSource = dismissSource
67
+ self.animationCompletedCallback = animationCompletedCallback
64
68
  self.view = view
65
- self.isPresentedRef = ClassReference(self.$isPresented)
66
- self.itemRef = ClassReference(self.$item)
67
69
  }
68
70
 
69
71
  public enum PopupType {
@@ -85,7 +87,6 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
85
87
  public enum Position {
86
88
  case top
87
89
  case bottom
88
- case center
89
90
  }
90
91
 
91
92
  private enum DragState {
@@ -112,15 +113,10 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
112
113
  }
113
114
 
114
115
  // MARK: - Public Properties
115
-
116
116
  /// Tells if the sheet should be presented or not
117
117
  @Binding var isPresented: Bool
118
118
  @Binding var item: Item?
119
119
 
120
- var sheetPresented: Bool {
121
- item != nil || isPresented
122
- }
123
-
124
120
  var type: PopupType
125
121
  var position: Position
126
122
 
@@ -129,33 +125,31 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
129
125
  /// If nil - never hides on its own
130
126
  var autohideIn: Double?
131
127
 
132
- /// Should close on tap - default is `true`
133
- var closeOnTap: Bool
134
-
135
128
  /// Should allow dismiss by dragging
136
129
  var dragToDismiss: Bool
137
130
 
138
131
  /// Should close on tap outside - default is `true`
139
132
  var closeOnTapOutside: Bool
140
-
141
- /// Background color for outside area - default is `Color.clear`
142
- var backgroundColor: Color
133
+
134
+ /// Trigger popup showing/hiding animations and...
135
+ var shouldShowContent: Bool
136
+
137
+ /// ... once hiding animation is finished remove popup from the memory using this flag
138
+ var showContent: Bool
143
139
 
144
140
  /// is called on any close action
145
141
  var dismissCallback: (DismissSource) -> ()
146
142
 
147
- var view: () -> PopupContent
143
+ /// Set dismiss souce to pass to dismiss callback
144
+ @Binding private var dismissSource: DismissSource?
148
145
 
149
- /// holder for autohiding dispatch work (to be able to cancel it when needed)
150
- var dispatchWorkHolder = DispatchWorkHolder()
146
+ /// called on showing/hiding sliding animation completed
147
+ var animationCompletedCallback: () -> ()
151
148
 
152
- // MARK: - Private Properties
149
+ var view: () -> PopupContent
153
150
 
151
+ // MARK: - Private Properties
154
152
  @Environment(\.safeAreaInsets) private var safeAreaInsets
155
-
156
- /// Class reference for capturing a weak reference later in dispatch work holder.
157
- private var isPresentedRef: ClassReference<Binding<Bool>>?
158
- private var itemRef: ClassReference<Binding<Item?>>?
159
153
 
160
154
  /// The rect and safe area of the hosting controller
161
155
  @State private var presenterContentRect: CGRect = .zero
@@ -168,15 +162,6 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
168
162
 
169
163
  /// Last position for drag gesture
170
164
  @State private var lastDragPosition: CGFloat = 0
171
-
172
- /// Trigger popup showing/hiding animations and...
173
- @State private var shouldShowContent: Bool = false
174
-
175
- /// ... once hiding animation is finished remove popup from the memory using this flag
176
- @State private var showContent: Bool = false
177
-
178
- /// Set dismiss souce to pass to dismiss callback
179
- @State private var dismissSource: DismissSource?
180
165
 
181
166
  /// The offset when the popup is displayed
182
167
  private var displayedOffset: CGFloat {
@@ -200,9 +185,7 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
200
185
 
201
186
  /// The offset when the popup is hidden
202
187
  private var hiddenOffset: CGFloat {
203
- if position == .center {
204
- return displayedOffset
205
- } else if position == .top {
188
+ if position == .top {
206
189
  if presenterContentRect.isEmpty {
207
190
  return -1000
208
191
  }
@@ -219,11 +202,6 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
219
202
  private var currentOffset: CGFloat {
220
203
  return shouldShowContent ? displayedOffset : hiddenOffset
221
204
  }
222
-
223
- /// The current background opacity, based on the **presented** property
224
- private var currentBackgroundOpacity: Double {
225
- return shouldShowContent ? 1.0 : 0.0
226
- }
227
205
 
228
206
  private var screenSize: CGSize {
229
207
  #if os(iOS) || os(tvOS)
@@ -240,94 +218,26 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
240
218
  }
241
219
 
242
220
  // MARK: - Content Builders
243
-
244
221
  public func body(content: Content) -> some View {
245
- main(content: content)
246
- .onAppear {
247
- appearAction(sheetPresented: sheetPresented)
248
- }
249
- .valueChanged(value: isPresented) { isPresented in
250
- appearAction(sheetPresented: isPresented)
251
- if isPresented {
252
- dismissSource = .binding
253
- }
254
- if !isPresented {
255
- dismissCallback(dismissSource ?? .autohide)
222
+ content
223
+ .frameGetter($presenterContentRect)
224
+ .overlay(
225
+ Group {
226
+ if showContent {
227
+ sheet()
228
+ }
256
229
  }
257
- }
258
- .valueChanged(value: item) { item in
259
- appearAction(sheetPresented: item != nil)
260
- if item != nil {
261
- dismissSource = .binding
262
- }
263
- if item == nil {
264
- dismissCallback(dismissSource ?? .autohide)
265
- }
266
- }
267
- }
268
-
269
- private func main(content: Content) -> some View {
270
- ZStack {
271
- content
272
- .frameGetter($presenterContentRect)
273
-
274
- if showContent {
275
- popupBackground()
276
- }
277
- }
278
- .overlay(
279
- Group {
280
- if showContent {
281
- sheet()
282
- }
283
- }
284
- )
285
- }
286
-
287
- private func popupBackground() -> some View {
288
- backgroundColor
289
- .applyIf(closeOnTapOutside) { view in
290
- view.contentShape(Rectangle())
291
- }
292
- .addTapIf(if: closeOnTapOutside) {
293
- dismissSource = .tapOutside
294
- dismiss()
295
- }
296
- .edgesIgnoringSafeArea(.all)
297
- .opacity(currentBackgroundOpacity)
298
- .animation(.linear)
230
+ )
299
231
  }
300
232
 
301
233
  /// This is the builder for the sheet content
302
234
  func sheet() -> some View {
303
-
304
- // if needed, dispatch autohide and cancel previous one
305
- if let autohideIn = autohideIn {
306
- dispatchWorkHolder.work?.cancel()
307
-
308
- // Weak reference to avoid the work item capturing the struct,
309
- // which would create a retain cycle with the work holder itself.
310
-
311
- dispatchWorkHolder.work = DispatchWorkItem(block: { [weak isPresentedRef, weak itemRef] in
312
- isPresentedRef?.value.wrappedValue = false
313
- itemRef?.value.wrappedValue = nil
314
- dismissSource = .autohide
315
- })
316
- if sheetPresented, let work = dispatchWorkHolder.work {
317
- DispatchQueue.main.asyncAfter(deadline: .now() + autohideIn, execute: work)
318
- }
319
- }
320
-
321
235
  let sheet = ZStack {
322
236
  self.view()
323
- .addTapIf(if: closeOnTap) {
324
- dismissSource = .tapInside
325
- dismiss()
326
- }
327
237
  .frameGetter($sheetContentRect)
328
238
  .offset(y: currentOffset)
329
239
  .onAnimationCompleted(for: currentOffset) {
330
- showContent = shouldShowContent
240
+ animationCompletedCallback()
331
241
  }
332
242
  .animation(animation)
333
243
  }
@@ -372,26 +282,8 @@ public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
372
282
  }
373
283
  #endif
374
284
 
375
- private func appearAction(sheetPresented: Bool) {
376
- if sheetPresented {
377
- showContent = true // immediately load popup body
378
- DispatchQueue.main.async {
379
- shouldShowContent = true // this will cause currentOffset change thus triggering the showing animation
380
- }
381
- } else {
382
- shouldShowContent = false // this will cause currentOffset change thus triggering the hiding animation
383
- // unload popup body after hiding animation is done (see sheet())
384
- }
385
- }
386
-
387
285
  private func dismiss() {
388
- dispatchWorkHolder.work?.cancel()
389
286
  isPresented = false
390
287
  item = nil
391
- if position == .center {
392
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
393
- showContent = false
394
- }
395
- }
396
288
  }
397
289
  }