@momo-kits/native-kits 0.152.4-beta.6 → 0.152.4-maxapi
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.
- package/CODE_OF_CONDUCT.md +133 -0
- package/CONTRIBUTING.md +114 -0
- package/LICENSE +20 -0
- package/README.md +7 -0
- package/build.gradle.kts +32 -0
- package/compose/MoMoComposeKits.podspec +54 -0
- package/compose/build.gradle.kts +149 -0
- package/compose/src/androidMain/AndroidManifest.xml +2 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +105 -0
- package/compose/src/commonMain/composeResources/files/lottie_circle_loader.json +1 -0
- package/compose/src/commonMain/composeResources/font/momosignature.otf +0 -0
- package/compose/src/commonMain/composeResources/font/momotrustdisplay.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_black.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_black.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_bold.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_heavy.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_light.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_medium.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_regular.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_semibold.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_thin.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_thin.ttf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.otf +0 -0
- package/compose/src/commonMain/composeResources/font/sfprotext_ultralight.ttf +0 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +57 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +201 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +222 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +48 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +76 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +306 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +33 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +715 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +214 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +236 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +69 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +77 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +27 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +334 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +345 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +90 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +131 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +543 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +23 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +58 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +143 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +179 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +111 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +384 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +160 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +234 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +223 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +236 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +228 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +364 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +50 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +34 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +85 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +33 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +338 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +95 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +64 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +89 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +91 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +84 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +208 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +172 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +199 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +29 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +237 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +191 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +306 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +12 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +13 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +191 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +258 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +2 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +35 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +2 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +59 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +68 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +11 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +49 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +51 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +111 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +94 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +159 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScaleSizeScope.kt +17 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +459 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +169 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +216 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +86 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +180 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +251 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +80 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +306 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +31 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +385 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +38 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +1329 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +62 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +88 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +144 -0
- package/gradle.properties +19 -0
- package/gradlew +240 -0
- package/gradlew.bat +91 -0
- package/ios/Application/ApplicationEnvironment.swift +50 -0
- package/ios/Application/Components.swift +263 -0
- package/ios/Application/ComposeApi.swift +22 -0
- package/ios/Application/FloatingButton.swift +172 -0
- package/ios/Application/HeaderRight.swift +271 -0
- package/ios/Application/Screen.swift +249 -0
- package/ios/Badge/BadgeDot.swift +31 -0
- package/ios/Button/Button.swift +211 -0
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +126 -0
- package/ios/Checkbox/Checkbox.swift +81 -0
- package/ios/Chip/Chip.swift +96 -0
- package/ios/Colors+Radius+Spacing/Colors.swift +172 -0
- package/ios/Colors+Radius+Spacing/Radius.swift +22 -0
- package/ios/Colors+Radius+Spacing/Spacing.swift +12 -0
- package/ios/Extensions/Color++.swift +25 -0
- package/ios/Icon/Icon.swift +51 -0
- package/ios/Image/Image.swift +70 -0
- package/ios/Input/Input.swift +207 -0
- package/ios/Input/InputPhoneNumber.swift +176 -0
- package/ios/Input/InputSearch.swift +238 -0
- package/ios/Input/InputTextArea.swift +242 -0
- package/ios/Lottie/LottieView.swift +86 -0
- package/ios/OTPKeyboard/KeyboardButton.swift +41 -0
- package/ios/OTPKeyboard/OTPKeyboard.swift +145 -0
- package/ios/Popup/PopupDisplay.swift +284 -0
- package/ios/Popup/PopupInput.swift +96 -0
- package/ios/Popup/PopupPromotion.swift +73 -0
- package/ios/PopupView/FullscreenPopup.swift +251 -0
- package/ios/PopupView/Modifiers.swift +158 -0
- package/ios/PopupView/PopupView.swift +289 -0
- package/ios/PopupView/Utils++.swift +281 -0
- package/ios/ScrollIndicator/ScrollIndicator.swift +110 -0
- package/ios/Swipeable/SwipeCell.swift +278 -0
- package/ios/Swipeable/SwipeCellModel.swift +86 -0
- package/ios/Switch/Switch.swift +44 -0
- package/ios/Template/Logo/Logo.swift +75 -0
- package/ios/Template/TrustBanner/TrustBanner.swift +120 -0
- package/ios/Theme.md +18 -0
- package/ios/Typography/Text.swift +140 -0
- package/ios/Typography/Typography.swift +95 -0
- package/ios/native-kits.podspec +18 -0
- package/package.json +6 -7
- package/settings.gradle.kts +25 -0
- package/shared/build.gradle.kts +0 -74
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
public enum DismissSource {
|
|
4
|
+
case binding // set isPresented to false ot item to nil
|
|
5
|
+
case tapInside
|
|
6
|
+
case tapOutside
|
|
7
|
+
case drag
|
|
8
|
+
case autohide
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public struct Popup<Item: Equatable, PopupContent: View>: ViewModifier {
|
|
12
|
+
|
|
13
|
+
init(isPresented: Binding<Bool>,
|
|
14
|
+
type: PopupType,
|
|
15
|
+
position: Position,
|
|
16
|
+
animation: Animation,
|
|
17
|
+
autohideIn: Double?,
|
|
18
|
+
dragToDismiss: Bool,
|
|
19
|
+
closeOnTapOutside: Bool,
|
|
20
|
+
shouldShowContent: Bool = true,
|
|
21
|
+
showContent: Bool = true,
|
|
22
|
+
dismissCallback: @escaping (DismissSource) -> (),
|
|
23
|
+
dismissSource: Binding<DismissSource?>,
|
|
24
|
+
animationCompletedCallback: @escaping () -> (),
|
|
25
|
+
view: @escaping () -> PopupContent) {
|
|
26
|
+
self._isPresented = isPresented
|
|
27
|
+
self._item = .constant(nil)
|
|
28
|
+
self.type = type
|
|
29
|
+
self.position = position
|
|
30
|
+
self.animation = animation
|
|
31
|
+
self.autohideIn = autohideIn
|
|
32
|
+
self.dragToDismiss = dragToDismiss
|
|
33
|
+
self.closeOnTapOutside = closeOnTapOutside
|
|
34
|
+
self.shouldShowContent = shouldShowContent
|
|
35
|
+
self.showContent = showContent
|
|
36
|
+
self.dismissCallback = dismissCallback
|
|
37
|
+
self._dismissSource = dismissSource
|
|
38
|
+
self.animationCompletedCallback = animationCompletedCallback
|
|
39
|
+
self.view = view
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
init(item: Binding<Item?>,
|
|
43
|
+
type: PopupType,
|
|
44
|
+
position: Position,
|
|
45
|
+
animation: Animation,
|
|
46
|
+
autohideIn: Double?,
|
|
47
|
+
dragToDismiss: Bool,
|
|
48
|
+
closeOnTapOutside: Bool,
|
|
49
|
+
shouldShowContent: Bool = true,
|
|
50
|
+
showContent: Bool = true,
|
|
51
|
+
dismissCallback: @escaping (DismissSource) -> (),
|
|
52
|
+
dismissSource: Binding<DismissSource?>,
|
|
53
|
+
animationCompletedCallback: @escaping () -> (),
|
|
54
|
+
view: @escaping () -> PopupContent) {
|
|
55
|
+
self._isPresented = .constant(false)
|
|
56
|
+
self._item = item
|
|
57
|
+
self.type = type
|
|
58
|
+
self.position = position
|
|
59
|
+
self.animation = animation
|
|
60
|
+
self.autohideIn = autohideIn
|
|
61
|
+
self.dragToDismiss = dragToDismiss
|
|
62
|
+
self.closeOnTapOutside = closeOnTapOutside
|
|
63
|
+
self.shouldShowContent = shouldShowContent
|
|
64
|
+
self.showContent = showContent
|
|
65
|
+
self.dismissCallback = dismissCallback
|
|
66
|
+
self._dismissSource = dismissSource
|
|
67
|
+
self.animationCompletedCallback = animationCompletedCallback
|
|
68
|
+
self.view = view
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public enum PopupType {
|
|
72
|
+
|
|
73
|
+
case `default`
|
|
74
|
+
case toast
|
|
75
|
+
case floater(verticalPadding: CGFloat = 10, useSafeAreaInset: Bool = true)
|
|
76
|
+
|
|
77
|
+
func shouldBeCentered() -> Bool {
|
|
78
|
+
switch self {
|
|
79
|
+
case .`default`:
|
|
80
|
+
return true
|
|
81
|
+
default:
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public enum Position {
|
|
88
|
+
case top
|
|
89
|
+
case bottom
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private enum DragState {
|
|
93
|
+
case inactive
|
|
94
|
+
case dragging(translation: CGSize)
|
|
95
|
+
|
|
96
|
+
var translation: CGSize {
|
|
97
|
+
switch self {
|
|
98
|
+
case .inactive:
|
|
99
|
+
return .zero
|
|
100
|
+
case .dragging(let translation):
|
|
101
|
+
return translation
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
var isDragging: Bool {
|
|
106
|
+
switch self {
|
|
107
|
+
case .inactive:
|
|
108
|
+
return false
|
|
109
|
+
case .dragging:
|
|
110
|
+
return true
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// MARK: - Public Properties
|
|
116
|
+
/// Tells if the sheet should be presented or not
|
|
117
|
+
@Binding var isPresented: Bool
|
|
118
|
+
@Binding var item: Item?
|
|
119
|
+
|
|
120
|
+
var type: PopupType
|
|
121
|
+
var position: Position
|
|
122
|
+
|
|
123
|
+
var animation: Animation
|
|
124
|
+
|
|
125
|
+
/// If nil - never hides on its own
|
|
126
|
+
var autohideIn: Double?
|
|
127
|
+
|
|
128
|
+
/// Should allow dismiss by dragging
|
|
129
|
+
var dragToDismiss: Bool
|
|
130
|
+
|
|
131
|
+
/// Should close on tap outside - default is `true`
|
|
132
|
+
var closeOnTapOutside: Bool
|
|
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
|
|
139
|
+
|
|
140
|
+
/// is called on any close action
|
|
141
|
+
var dismissCallback: (DismissSource) -> ()
|
|
142
|
+
|
|
143
|
+
/// Set dismiss souce to pass to dismiss callback
|
|
144
|
+
@Binding private var dismissSource: DismissSource?
|
|
145
|
+
|
|
146
|
+
/// called on showing/hiding sliding animation completed
|
|
147
|
+
var animationCompletedCallback: () -> ()
|
|
148
|
+
|
|
149
|
+
var view: () -> PopupContent
|
|
150
|
+
|
|
151
|
+
// MARK: - Private Properties
|
|
152
|
+
@Environment(\.safeAreaInsets) private var safeAreaInsets
|
|
153
|
+
|
|
154
|
+
/// The rect and safe area of the hosting controller
|
|
155
|
+
@State private var presenterContentRect: CGRect = .zero
|
|
156
|
+
|
|
157
|
+
/// The rect and safe area of popup content
|
|
158
|
+
@State private var sheetContentRect: CGRect = .zero
|
|
159
|
+
|
|
160
|
+
/// Drag to dismiss gesture state
|
|
161
|
+
@GestureState private var dragState = DragState.inactive
|
|
162
|
+
|
|
163
|
+
/// Last position for drag gesture
|
|
164
|
+
@State private var lastDragPosition: CGFloat = 0
|
|
165
|
+
|
|
166
|
+
/// The offset when the popup is displayed
|
|
167
|
+
private var displayedOffset: CGFloat {
|
|
168
|
+
switch type {
|
|
169
|
+
case .`default`:
|
|
170
|
+
return -presenterContentRect.midY + screenHeight/2
|
|
171
|
+
case .toast:
|
|
172
|
+
if position == .bottom {
|
|
173
|
+
return presenterContentRect.minY + safeAreaInsets.bottom + presenterContentRect.height - presenterContentRect.midY - sheetContentRect.height/2
|
|
174
|
+
} else {
|
|
175
|
+
return presenterContentRect.minY - safeAreaInsets.top - presenterContentRect.midY + sheetContentRect.height/2
|
|
176
|
+
}
|
|
177
|
+
case .floater(let verticalPadding, let useSafeAreaInset):
|
|
178
|
+
if position == .bottom {
|
|
179
|
+
return presenterContentRect.minY + safeAreaInsets.bottom + presenterContentRect.height - presenterContentRect.midY - sheetContentRect.height/2 - verticalPadding + (useSafeAreaInset ? -safeAreaInsets.bottom : 0)
|
|
180
|
+
} else {
|
|
181
|
+
return presenterContentRect.minY - safeAreaInsets.top - presenterContentRect.midY + sheetContentRect.height/2 + verticalPadding + (useSafeAreaInset ? safeAreaInsets.top : 0)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/// The offset when the popup is hidden
|
|
187
|
+
private var hiddenOffset: CGFloat {
|
|
188
|
+
if position == .top {
|
|
189
|
+
if presenterContentRect.isEmpty {
|
|
190
|
+
return -1000
|
|
191
|
+
}
|
|
192
|
+
return -presenterContentRect.midY - sheetContentRect.height/2 - 5
|
|
193
|
+
} else {
|
|
194
|
+
if presenterContentRect.isEmpty {
|
|
195
|
+
return 1000
|
|
196
|
+
}
|
|
197
|
+
return screenHeight - presenterContentRect.midY + sheetContentRect.height/2 + 5
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// The current offset, based on the **presented** property
|
|
202
|
+
private var currentOffset: CGFloat {
|
|
203
|
+
return shouldShowContent ? displayedOffset : hiddenOffset
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private var screenSize: CGSize {
|
|
207
|
+
#if os(iOS) || os(tvOS)
|
|
208
|
+
return UIScreen.main.bounds.size
|
|
209
|
+
#elseif os(watchOS)
|
|
210
|
+
return WKInterfaceDevice.current().screenBounds.size
|
|
211
|
+
#else
|
|
212
|
+
return NSScreen.main?.frame.size ?? .zero
|
|
213
|
+
#endif
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private var screenHeight: CGFloat {
|
|
217
|
+
screenSize.height
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// MARK: - Content Builders
|
|
221
|
+
public func body(content: Content) -> some View {
|
|
222
|
+
content
|
|
223
|
+
.frameGetter($presenterContentRect)
|
|
224
|
+
.overlay(
|
|
225
|
+
Group {
|
|
226
|
+
if showContent {
|
|
227
|
+
sheet()
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/// This is the builder for the sheet content
|
|
234
|
+
func sheet() -> some View {
|
|
235
|
+
let sheet = ZStack {
|
|
236
|
+
self.view()
|
|
237
|
+
.frameGetter($sheetContentRect)
|
|
238
|
+
.offset(y: currentOffset)
|
|
239
|
+
.onAnimationCompleted(for: currentOffset) {
|
|
240
|
+
animationCompletedCallback()
|
|
241
|
+
}
|
|
242
|
+
.animation(animation)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
#if !os(tvOS)
|
|
246
|
+
let drag = DragGesture()
|
|
247
|
+
.updating($dragState) { drag, state, _ in
|
|
248
|
+
state = .dragging(translation: drag.translation)
|
|
249
|
+
}
|
|
250
|
+
.onEnded(onDragEnded)
|
|
251
|
+
|
|
252
|
+
return sheet
|
|
253
|
+
.applyIf(dragToDismiss) {
|
|
254
|
+
$0.offset(y: dragOffset())
|
|
255
|
+
.simultaneousGesture(drag)
|
|
256
|
+
}
|
|
257
|
+
#else
|
|
258
|
+
return sheet
|
|
259
|
+
#endif
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
#if !os(tvOS)
|
|
263
|
+
func dragOffset() -> CGFloat {
|
|
264
|
+
if (position == .bottom && dragState.translation.height > 0) ||
|
|
265
|
+
(position == .top && dragState.translation.height < 0) {
|
|
266
|
+
return dragState.translation.height
|
|
267
|
+
}
|
|
268
|
+
return lastDragPosition
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private func onDragEnded(drag: DragGesture.Value) {
|
|
272
|
+
let reference = sheetContentRect.height / 3
|
|
273
|
+
if (position == .bottom && drag.translation.height > reference) ||
|
|
274
|
+
(position == .top && drag.translation.height < -reference) {
|
|
275
|
+
lastDragPosition = drag.translation.height
|
|
276
|
+
withAnimation {
|
|
277
|
+
lastDragPosition = 0
|
|
278
|
+
}
|
|
279
|
+
dismissSource = .drag
|
|
280
|
+
dismiss()
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
#endif
|
|
284
|
+
|
|
285
|
+
private func dismiss() {
|
|
286
|
+
isPresented = false
|
|
287
|
+
item = nil
|
|
288
|
+
}
|
|
289
|
+
}
|