@momo-kits/native-kits 0.0.1-beta.4 → 0.2.0-kt22

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 (131) hide show
  1. package/README.md +176 -5
  2. package/ios/Application/ApplicationEnvironment.swift +50 -0
  3. package/ios/Application/Components.swift +263 -0
  4. package/ios/Application/ComposeApi.swift +22 -0
  5. package/ios/Application/FloatingButton.swift +172 -0
  6. package/ios/Application/HeaderRight.swift +271 -0
  7. package/ios/Application/Screen.swift +249 -0
  8. package/ios/Badge/Badge.swift +85 -0
  9. package/ios/Badge/BadgeDot.swift +31 -0
  10. package/ios/Badge/BadgeRibbon.swift +242 -0
  11. package/ios/Button/Button.swift +169 -89
  12. package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +126 -0
  13. package/ios/Checkbox/Checkbox.swift +81 -0
  14. package/ios/Chip/Chip.swift +96 -0
  15. package/ios/{Colors+Spacing → Colors+Radius+Spacing}/Colors.swift +21 -19
  16. package/ios/Colors+Radius+Spacing/Radius.swift +22 -0
  17. package/ios/Icon/Icon.swift +51 -0
  18. package/ios/Image/Image.swift +70 -0
  19. package/ios/Input/ErrorView.swift +55 -0
  20. package/ios/Input/Input.swift +159 -50
  21. package/ios/Input/InputPhoneNumber.swift +158 -0
  22. package/ios/Input/InputSearch.swift +238 -0
  23. package/ios/Input/InputTextArea.swift +242 -0
  24. package/ios/Lottie/LottieView.swift +86 -0
  25. package/ios/OTPKeyboard/KeyboardButton.swift +12 -4
  26. package/ios/OTPKeyboard/OTPKeyboard.swift +1 -1
  27. package/ios/Popup/PopupDisplay.swift +287 -0
  28. package/ios/Popup/PopupInput.swift +96 -0
  29. package/ios/Popup/PopupPromotion.swift +73 -0
  30. package/ios/PopupView/FullscreenPopup.swift +251 -0
  31. package/ios/{Popup → PopupView}/Modifiers.swift +6 -20
  32. package/ios/{Popup → PopupView}/PopupView.swift +38 -146
  33. package/ios/PopupView/Utils++.swift +281 -0
  34. package/ios/ScrollIndicator/ScrollIndicator.swift +110 -0
  35. package/ios/Swipeable/SwipeCell.swift +39 -31
  36. package/ios/Switch/Switch.swift +44 -0
  37. package/ios/Template/Logo/Logo.swift +75 -0
  38. package/ios/Template/TrustBanner/TrustBanner.swift +120 -0
  39. package/ios/Typography/Text.swift +140 -0
  40. package/ios/Typography/Typography.swift +70 -5
  41. package/ios/native-kits.podspec +20 -15
  42. package/package.json +6 -6
  43. package/CODE_OF_CONDUCT.md +0 -133
  44. package/CONTRIBUTING.md +0 -114
  45. package/LICENSE +0 -20
  46. package/android/build.gradle +0 -82
  47. package/android/gradle.properties +0 -5
  48. package/android/src/main/AndroidManifest.xml +0 -4
  49. package/android/src/main/java/com/momoplatform/nativekits/NativeKitsPackage.kt +0 -17
  50. package/android/src/main/java/com/momoplatform/nativekits/NativeKitsViewManager.kt +0 -20
  51. package/example/ios/Example/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -63
  52. package/example/ios/Example/Assets.xcassets/Background.colorset/Contents.json +0 -38
  53. package/example/ios/Example/Assets.xcassets/Border.colorset/Contents.json +0 -38
  54. package/example/ios/Example/Assets.xcassets/Card.colorset/Contents.json +0 -38
  55. package/example/ios/Example/Assets.xcassets/Contents.json +0 -6
  56. package/example/ios/Example/Assets.xcassets/Error.colorset/Contents.json +0 -38
  57. package/example/ios/Example/Assets.xcassets/Primary.colorset/Contents.json +0 -38
  58. package/example/ios/Example/Assets.xcassets/PrimaryDark.colorset/Contents.json +0 -38
  59. package/example/ios/Example/Assets.xcassets/PrimaryLight.colorset/Contents.json +0 -38
  60. package/example/ios/Example/Assets.xcassets/Secondary.colorset/Contents.json +0 -38
  61. package/example/ios/Example/Assets.xcassets/Success.colorset/Contents.json +0 -38
  62. package/example/ios/Example/Assets.xcassets/Text.colorset/Contents.json +0 -38
  63. package/example/ios/Example/Assets.xcassets/TextSecondary.colorset/Contents.json +0 -38
  64. package/example/ios/Example/Assets.xcassets/Warning.colorset/Contents.json +0 -38
  65. package/example/ios/Example/ComponentDetail.swift +0 -35
  66. package/example/ios/Example/ComponentItem.swift +0 -24
  67. package/example/ios/Example/ComponentModel.swift +0 -33
  68. package/example/ios/Example/ContentView.swift +0 -53
  69. package/example/ios/Example/Example.entitlements +0 -10
  70. package/example/ios/Example/Foundation/ButtonDemo.swift +0 -80
  71. package/example/ios/Example/Foundation/Popup/ActionSheets.swift +0 -136
  72. package/example/ios/Example/Foundation/Popup/Floats.swift +0 -127
  73. package/example/ios/Example/Foundation/Popup/Popups.swift +0 -101
  74. package/example/ios/Example/Foundation/PopupDemo.swift +0 -366
  75. package/example/ios/Example/Foundation/TextInputDemo.swift +0 -31
  76. package/example/ios/Example/Foundation/TypographyDemo.swift +0 -53
  77. package/example/ios/Example/Foundation.swift +0 -26
  78. package/example/ios/Example/Info.plist +0 -18
  79. package/example/ios/Example/Library/SwipeableDemo.swift +0 -105
  80. package/example/ios/Example/Library.swift +0 -26
  81. package/example/ios/Example/Main.swift +0 -11
  82. package/example/ios/Example/Overview.swift +0 -20
  83. package/example/ios/Example/Preview Content/Preview Assets.xcassets/Contents.json +0 -6
  84. package/example/ios/Example/Resource/SFProText-Black.ttf +0 -0
  85. package/example/ios/Example/Resource/SFProText-Bold.ttf +0 -0
  86. package/example/ios/Example/Resource/SFProText-Heavy.ttf +0 -0
  87. package/example/ios/Example/Resource/SFProText-Light.ttf +0 -0
  88. package/example/ios/Example/Resource/SFProText-Medium.ttf +0 -0
  89. package/example/ios/Example/Resource/SFProText-Regular.ttf +0 -0
  90. package/example/ios/Example/Resource/SFProText-Semibold.ttf +0 -0
  91. package/example/ios/Example/Resource/SFProText-Thin.ttf +0 -0
  92. package/example/ios/Example/Resource/SFProText-Ultralight.ttf +0 -0
  93. package/example/ios/Example/Resource/foundation.json +0 -33
  94. package/example/ios/Example/Resource/library.json +0 -8
  95. package/example/ios/Example/Utils.swift +0 -30
  96. package/example/ios/Example/ViewModifier/DarkViewModifier.swift +0 -22
  97. package/example/ios/Example.xcodeproj/project.pbxproj +0 -570
  98. package/example/ios/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  99. package/example/ios/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  100. package/example/ios/Example.xcodeproj/project.xcworkspace/xcuserdata/wem.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  101. package/example/ios/Example.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  102. package/example/ios/Example.xcworkspace/contents.xcworkspacedata +0 -10
  103. package/example/ios/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  104. package/example/ios/Example.xcworkspace/xcuserdata/wem.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  105. package/example/ios/Example.xcworkspace/xcuserdata/wem.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
  106. package/example/ios/Podfile +0 -6
  107. package/example/ios/Podfile.lock +0 -16
  108. package/example/ios/Pods/Local Podspecs/MoMoUIKits.podspec.json +0 -29
  109. package/example/ios/Pods/Manifest.lock +0 -16
  110. package/example/ios/Pods/Pods.xcodeproj/project.pbxproj +0 -661
  111. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/MoMoUIKits.xcscheme +0 -58
  112. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/Pods-Example.xcscheme +0 -58
  113. package/example/ios/Pods/Pods.xcodeproj/xcuserdata/wem.xcuserdatad/xcschemes/xcschememanagement.plist +0 -25
  114. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-dummy.m +0 -5
  115. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-prefix.pch +0 -12
  116. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits-umbrella.h +0 -16
  117. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.debug.xcconfig +0 -12
  118. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.modulemap +0 -6
  119. package/example/ios/Pods/Target Support Files/MoMoUIKits/MoMoUIKits.release.xcconfig +0 -12
  120. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.markdown +0 -3
  121. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-acknowledgements.plist +0 -29
  122. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-dummy.m +0 -5
  123. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example-umbrella.h +0 -16
  124. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig +0 -14
  125. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.modulemap +0 -6
  126. package/example/ios/Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig +0 -14
  127. package/ios/Colors+Spacing/Radius.swift +0 -11
  128. package/ios/Popup/Utils++.swift +0 -163
  129. package/ios/Theme.md +0 -18
  130. package/publish.sh +0 -28
  131. /package/ios/{Colors+Spacing → Colors+Radius+Spacing}/Spacing.swift +0 -0
@@ -0,0 +1,281 @@
1
+ import SwiftUI
2
+ import Combine
3
+
4
+ final class DispatchWorkHolder {
5
+ var work: DispatchWorkItem?
6
+ }
7
+
8
+ final class ClassReference<T> {
9
+ var value: T
10
+
11
+ init(_ value: T) {
12
+ self.value = value
13
+ }
14
+ }
15
+
16
+ public extension View {
17
+
18
+ @ViewBuilder
19
+ func valueChanged<T: Equatable>(value: T, onChange: @escaping (T) -> Void) -> some View {
20
+ if #available(iOS 14.0, tvOS 14.0, macOS 11.0, watchOS 7.0, *) {
21
+ self.onChange(of: value, perform: onChange)
22
+ } else {
23
+ self.onReceive(Just(value)) { value in
24
+ onChange(value)
25
+ }
26
+ }
27
+ }
28
+ }
29
+
30
+ public extension View {
31
+ @ViewBuilder
32
+ func applyIf<T: View>(_ condition: Bool, apply: (Self) -> T) -> some View {
33
+ if condition {
34
+ apply(self)
35
+ } else {
36
+ self
37
+ }
38
+ }
39
+
40
+ @ViewBuilder
41
+ func addTapIf(if condition: Bool, onTap: @escaping ()->()) -> some View {
42
+ if condition {
43
+ self.simultaneousGesture(
44
+ TapGesture().onEnded {
45
+ onTap()
46
+ }
47
+ )
48
+ } else {
49
+ self
50
+ }
51
+ }
52
+ }
53
+
54
+ struct FrameGetter: ViewModifier {
55
+
56
+ @Binding var frame: CGRect
57
+
58
+ func body(content: Content) -> some View {
59
+ content
60
+ .background(
61
+ GeometryReader { proxy -> AnyView in
62
+ DispatchQueue.main.async {
63
+ let rect = proxy.frame(in: .global)
64
+ // This avoids an infinite layout loop
65
+ if rect.integral != self.frame.integral {
66
+ self.frame = rect
67
+ }
68
+ }
69
+ return AnyView(EmptyView())
70
+ }
71
+ )
72
+ }
73
+ }
74
+
75
+ extension View {
76
+ public func frameGetter(_ frame: Binding<CGRect>) -> some View {
77
+ modifier(FrameGetter(frame: frame))
78
+ }
79
+ }
80
+
81
+ struct AnimationCompletionObserverModifier<Value>: AnimatableModifier where Value: VectorArithmetic, Value: Comparable {
82
+
83
+ /// While animating, SwiftUI changes the old input value to the new target value using this property. This value is set to the old value until the animation completes.
84
+ var animatableData: Value {
85
+ didSet {
86
+ notifyCompletionIfFinished()
87
+ }
88
+ }
89
+
90
+ /// The target value for which we're observing. This value is directly set once the animation starts. During animation, `animatableData` will hold the oldValue and is only updated to the target value once the animation completes.
91
+ private var targetValue: Value
92
+
93
+ /// The completion callback which is called once the animation completes.
94
+ private var completion: () -> Void
95
+
96
+ init(observedValue: Value, completion: @escaping () -> Void) {
97
+ self.completion = completion
98
+ self.animatableData = observedValue
99
+ targetValue = observedValue
100
+ }
101
+
102
+ /// Verifies whether the current animation is finished and calls the completion callback if true.
103
+ private func notifyCompletionIfFinished() {
104
+ guard animatableData == targetValue else { return }
105
+
106
+ /// Dispatching is needed to take the next runloop for the completion callback.
107
+ /// This prevents errors like "Modifying state during view update, this will cause undefined behavior."
108
+ DispatchQueue.main.async {
109
+ self.completion()
110
+ }
111
+ }
112
+
113
+ func body(content: Content) -> some View {
114
+ /// We're not really modifying the view so we can directly return the original input value.
115
+ return content
116
+ }
117
+ }
118
+
119
+ struct AnimatableModifierDouble: AnimatableModifier {
120
+
121
+ var targetValue: Double
122
+ static var done = false
123
+
124
+ // SwiftUI gradually varies it from old value to the new value
125
+ var animatableData: Double {
126
+ didSet {
127
+ checkIfFinished()
128
+ }
129
+ }
130
+ var completion: () -> ()
131
+
132
+ // Re-created every time the control argument changes
133
+ init(bindedValue: Double, completion: @escaping () -> ()) {
134
+ self.completion = completion
135
+
136
+ // Set animatableData to the new value. But SwiftUI again directly
137
+ // and gradually varies the value while the body
138
+ // is being called to animate. Following line serves the purpose of
139
+ // associating the extenal argument with the animatableData.
140
+ self.animatableData = bindedValue
141
+ targetValue = bindedValue
142
+ AnimatableModifierDouble.done = false
143
+ }
144
+
145
+ func checkIfFinished() -> () {
146
+ if AnimatableModifierDouble.done { return }
147
+ let delta = 0.1
148
+ if animatableData > targetValue - delta &&
149
+ animatableData < targetValue + delta {
150
+ //print("check", animatableData, targetValue)
151
+ AnimatableModifierDouble.done = true
152
+ DispatchQueue.main.async {
153
+ self.completion()
154
+ }
155
+ }
156
+ }
157
+
158
+ func body(content: Content) -> some View {
159
+ content
160
+ }
161
+ }
162
+
163
+ extension View {
164
+
165
+ func onAnimationCompleted(for value: Double, completion: @escaping () -> Void) -> some View {
166
+ modifier(AnimatableModifierDouble(bindedValue: value, completion: completion))
167
+ }
168
+ }
169
+
170
+ extension UIApplication {
171
+ var keyWindow: UIWindow? {
172
+ connectedScenes
173
+ .compactMap {
174
+ $0 as? UIWindowScene
175
+ }
176
+ .flatMap {
177
+ $0.windows
178
+ }
179
+ .first {
180
+ $0.isKeyWindow
181
+ }
182
+ }
183
+ }
184
+
185
+ private struct SafeAreaInsetsKey: EnvironmentKey {
186
+ static var defaultValue: EdgeInsets {
187
+ UIApplication.shared.keyWindow?.safeAreaInsets.swiftUiInsets ?? EdgeInsets()
188
+ }
189
+ }
190
+
191
+ extension EnvironmentValues {
192
+ var safeAreaInsets: EdgeInsets {
193
+ self[SafeAreaInsetsKey.self]
194
+ }
195
+ }
196
+
197
+ private extension UIEdgeInsets {
198
+ var swiftUiInsets: EdgeInsets {
199
+ EdgeInsets(top: top, leading: left, bottom: bottom, trailing: right)
200
+ }
201
+ }
202
+
203
+ extension View {
204
+
205
+ func transparentNonAnimatingFullScreenCover<Content: View>(isPresented: Binding<Bool>, content: @escaping () -> Content) -> some View {
206
+ modifier(TransparentNonAnimatableFullScreenModifier(isPresented: isPresented, fullScreenContent: content))
207
+ }
208
+ }
209
+
210
+ private struct TransparentNonAnimatableFullScreenModifier<FullScreenContent: View>: ViewModifier {
211
+
212
+ @Binding var isPresented: Bool
213
+ let fullScreenContent: () -> (FullScreenContent)
214
+
215
+ func body(content: Content) -> some View {
216
+ if #available(iOS 14.0, *) {
217
+ content
218
+ .valueChanged(value: isPresented, onChange: { isPresented in
219
+ UIView.setAnimationsEnabled(false)
220
+ })
221
+ .fullScreenCover(isPresented: $isPresented, content: {
222
+ ZStack {
223
+ fullScreenContent()
224
+ }
225
+ .background(FullScreenCoverBackgroundRemovalView())
226
+ .onAppear {
227
+ if !UIView.areAnimationsEnabled {
228
+ UIView.setAnimationsEnabled(true)
229
+ }
230
+ }
231
+ .onDisappear {
232
+ if !UIView.areAnimationsEnabled {
233
+ UIView.setAnimationsEnabled(true)
234
+ }
235
+ }
236
+ })
237
+ } else {
238
+ content
239
+ .valueChanged(value: isPresented, onChange: { isPresented in
240
+ UIView.setAnimationsEnabled(false)
241
+ })
242
+ .sheet(isPresented: $isPresented, content: {
243
+ ZStack {
244
+ fullScreenContent()
245
+ }
246
+ .background(FullScreenCoverBackgroundRemovalView())
247
+ .onAppear {
248
+ if !UIView.areAnimationsEnabled {
249
+ UIView.setAnimationsEnabled(true)
250
+ }
251
+ }
252
+ .onDisappear {
253
+ if !UIView.areAnimationsEnabled {
254
+ UIView.setAnimationsEnabled(true)
255
+ }
256
+ }
257
+ })
258
+ }
259
+ }
260
+
261
+ }
262
+
263
+ private struct FullScreenCoverBackgroundRemovalView: UIViewRepresentable {
264
+
265
+ private class BackgroundRemovalView: UIView {
266
+
267
+ override func didMoveToWindow() {
268
+ super.didMoveToWindow()
269
+
270
+ superview?.superview?.backgroundColor = .clear
271
+ }
272
+
273
+ }
274
+
275
+ func makeUIView(context: Context) -> UIView {
276
+ return BackgroundRemovalView()
277
+ }
278
+
279
+ func updateUIView(_ uiView: UIView, context: Context) {}
280
+
281
+ }
@@ -0,0 +1,110 @@
1
+ import Foundation
2
+ import SwiftUI
3
+
4
+ public struct ScrollViewWithScrollBar<Content: View>: View {
5
+ let INDICATOR_HEIGHT: CGFloat = 4
6
+ @State var position = CGPoint(x: 0, y: 2) //2 is INDICATOR_HEIGHT/2
7
+ @State var contentSize: CGFloat = 0
8
+ @State var scrollViewWidth: CGFloat = 0
9
+ var indicatorWidth: CGFloat
10
+ var indicatorContainerWidth: CGFloat
11
+ var indicatorColor: Color
12
+ var indicatorContainerColor: Color
13
+ var spacing: CGFloat = 4
14
+ @ViewBuilder var content: () -> Content
15
+
16
+ public init(
17
+ indicatorWidth: CGFloat = 12, indicatorContainerWidth: CGFloat = 36,
18
+ indicatorColor: Color = Colors.primary, indicatorContainerColor: Color = Colors.black06,
19
+ spacing: CGFloat = 4,
20
+ @ViewBuilder content: @escaping () -> Content
21
+ ) {
22
+ self.indicatorWidth = indicatorWidth
23
+ self.indicatorContainerWidth = indicatorContainerWidth
24
+ self.indicatorColor = indicatorColor
25
+ self.indicatorContainerColor = indicatorContainerColor
26
+ self.content = content
27
+ self.spacing = spacing
28
+ }
29
+
30
+ // MARK: Public
31
+
32
+ public var body: some View {
33
+ return VStack(spacing: spacing) {
34
+ ScrollView(.horizontal, showsIndicators: false) {
35
+ HStack {
36
+ content()
37
+ }
38
+ .fixedSize(horizontal: true, vertical: false)
39
+ .overlay(
40
+ GeometryReader { proxy in
41
+ Color.clear
42
+ .preference(
43
+ key: ViewOffsetKey.self,
44
+ value: -proxy.frame(in: .named("scroll")).origin.x
45
+ )
46
+ .preference(
47
+ key: ContentSizeKey.self,
48
+ value: proxy.size.width)
49
+ },
50
+ alignment: .topLeading
51
+ )
52
+ .onPreferenceChange(ContentSizeKey.self) { newContentSize in
53
+ if newContentSize > 0 {
54
+ self.contentSize = newContentSize
55
+ if self.position == CGPoint(x: 0, y: 2) {
56
+ self.position = CGPoint(x: indicatorWidth / 2, y: INDICATOR_HEIGHT / 2)
57
+ }
58
+ }
59
+ }
60
+ .onPreferenceChange(ViewOffsetKey.self) { offset in
61
+ let newPosition =
62
+ (offset * ((indicatorContainerWidth - indicatorWidth) / (contentSize - scrollViewWidth)))
63
+ + indicatorWidth / 2
64
+
65
+ if !newPosition.isNaN {
66
+ self.position = CGPoint(x: newPosition, y: INDICATOR_HEIGHT / 2)
67
+ }
68
+ }
69
+ }.background(
70
+ GeometryReader { proxy in
71
+ Color.clear.onAppear {
72
+ self.scrollViewWidth = proxy.size.width
73
+ }
74
+ }
75
+ ).coordinateSpace(name: "scroll")
76
+
77
+ HStack(spacing: 0) {
78
+ HStack(spacing: 0) {}.frame(width: indicatorWidth, height: INDICATOR_HEIGHT)
79
+ .background(indicatorColor)
80
+ .cornerRadius(8)
81
+ .position(self.position)
82
+ }.frame(width: indicatorContainerWidth, height: INDICATOR_HEIGHT, alignment: .leading)
83
+ .background(indicatorContainerColor).cornerRadius(8)
84
+ }
85
+ }
86
+ }
87
+
88
+ // MARK: - ViewOffsetKey
89
+
90
+ struct ViewOffsetKey: PreferenceKey {
91
+ typealias Value = CGFloat
92
+
93
+ static var defaultValue = CGFloat.zero
94
+
95
+ static func reduce(value: inout Value, nextValue: () -> Value) {
96
+ value += nextValue()
97
+ }
98
+ }
99
+
100
+ // MARK: - ContentSizeKey
101
+
102
+ struct ContentSizeKey: PreferenceKey {
103
+ typealias Value = CGFloat
104
+
105
+ static var defaultValue = CGFloat.zero
106
+
107
+ static func reduce(value: inout Value, nextValue: () -> Value) {
108
+ value = nextValue()
109
+ }
110
+ }
@@ -10,9 +10,9 @@ public struct SwipeCellModifier: ViewModifier {
10
10
  var trailingSideGroup: [SwipeCellActionItem] = []
11
11
  @Binding var currentUserInteractionCellID: String?
12
12
  var settings: SwipeCellSettings = .init()
13
-
13
+
14
14
  @State private var offsetX: CGFloat = 0
15
-
15
+
16
16
  let generator = UINotificationFeedbackGenerator()
17
17
  @State private var hapticFeedbackOccurred: Bool = false
18
18
  @State private var openSideLock: SwipeGroupSide?
@@ -22,15 +22,23 @@ public struct SwipeCellModifier: ViewModifier {
22
22
  if self.leadingSideGroup.isEmpty == false && self.offsetX != 0 {
23
23
  self.swipeToRevealArea(swipeItemGroup: self.leadingSideGroup, side: .leading)
24
24
  }
25
-
25
+
26
26
  if self.trailingSideGroup.isEmpty == false && self.offsetX != 0 {
27
27
  self.swipeToRevealArea(swipeItemGroup: self.trailingSideGroup, side: .trailing)
28
28
  }
29
-
29
+
30
30
  content
31
31
  .offset(x: self.offsetX)
32
- .gesture(DragGesture(minimumDistance: 30, coordinateSpace: .local).onChanged(self.dragOnChanged(value:)).onEnded(dragOnEnded(value:)))
33
-
32
+ .simultaneousGesture(
33
+ DragGesture(minimumDistance: 30, coordinateSpace: .local)
34
+ .onChanged { value in
35
+ self.dragOnChanged(value: value)
36
+ }
37
+ .onEnded { value in
38
+ self.dragOnEnded(value: value)
39
+ }
40
+ )
41
+
34
42
  }.frame(width: cellWidth)
35
43
  .edgesIgnoringSafeArea(.horizontal)
36
44
  .clipped()
@@ -43,7 +51,7 @@ public struct SwipeCellModifier: ViewModifier {
43
51
  }
44
52
  }
45
53
  }
46
-
54
+
47
55
  internal func swipeToRevealArea(swipeItemGroup: [SwipeCellActionItem], side: SwipeGroupSide)->some View {
48
56
  HStack {
49
57
  if side == .trailing {
@@ -63,17 +71,17 @@ public struct SwipeCellModifier: ViewModifier {
63
71
  }
64
72
  }
65
73
  }.opacity(self.swipeRevealAreaOpacity(side: side))
66
-
74
+
67
75
  if side == .leading {
68
76
  Spacer()
69
77
  }
70
78
  }
71
79
  }
72
-
80
+
73
81
  internal func buttonContentView(item: SwipeCellActionItem, group: [SwipeCellActionItem], side: SwipeGroupSide)->some View {
74
82
  ZStack {
75
83
  item.backgroundColor
76
-
84
+
77
85
  HStack {
78
86
  if self.warnSwipeOutCondition(side: side, hasSwipeOut: item.swipeOutAction) && item.swipeOutButtonView != nil {
79
87
  item.swipeOutButtonView!()
@@ -81,28 +89,28 @@ public struct SwipeCellModifier: ViewModifier {
81
89
  item.buttonView()
82
90
  }
83
91
  }
84
-
92
+
85
93
  }.frame(width: self.itemButtonWidth(item: item, itemGroup: group, side: side))
86
94
  }
87
-
95
+
88
96
  internal func menuWidth(side: SwipeGroupSide)->CGFloat {
89
97
  switch side {
90
98
  case .leading:
91
99
  return self.leadingSideGroup.map { $0.buttonWidth }.reduce(0, +)
92
-
100
+
93
101
  case .trailing:
94
102
  return self.trailingSideGroup.map { $0.buttonWidth }.reduce(0, +)
95
103
  }
96
104
  }
97
-
105
+
98
106
  // MARK: drag gesture
99
-
107
+
100
108
  internal func dragOnChanged(value: DragGesture.Value) {
101
109
  let horizontalTranslation = value.translation.width
102
110
  if self.nonDraggableCondition(horizontalTranslation: horizontalTranslation) {
103
111
  return
104
112
  }
105
-
113
+
106
114
  if self.openSideLock != nil {
107
115
  // if one side is open, we need to add the menu width!
108
116
  let menuWidth = self.openSideLock == .leading ? self.menuWidth(side: .leading) : self.menuWidth(side: .trailing)
@@ -110,9 +118,9 @@ public struct SwipeCellModifier: ViewModifier {
110
118
  self.triggerHapticFeedbackIfNeeded(horizontalTranslation: horizontalTranslation)
111
119
  return
112
120
  }
113
-
121
+
114
122
  self.triggerHapticFeedbackIfNeeded(horizontalTranslation: horizontalTranslation)
115
-
123
+
116
124
  if horizontalTranslation > 8 || horizontalTranslation < -8 { // makes sure the swipe cell doesn't open too easily
117
125
  self.currentUserInteractionCellID = self.id
118
126
  self.offsetX = horizontalTranslation
@@ -120,11 +128,11 @@ public struct SwipeCellModifier: ViewModifier {
120
128
  self.offsetX = 0
121
129
  }
122
130
  }
123
-
131
+
124
132
  internal func nonDraggableCondition(horizontalTranslation: CGFloat)->Bool {
125
133
  return self.offsetX == 0 && (self.leadingSideGroup.isEmpty && horizontalTranslation > 0 || self.trailingSideGroup.isEmpty && horizontalTranslation < 0)
126
134
  }
127
-
135
+
128
136
  internal func dragOnEnded(value: DragGesture.Value) {
129
137
  let swipeOutTriggerValue = self.cellWidth * self.settings.swipeOutTriggerRatio
130
138
 
@@ -139,7 +147,7 @@ public struct SwipeCellModifier: ViewModifier {
139
147
  } else {
140
148
  self.lockSideMenu(side: .leading)
141
149
  }
142
-
150
+
143
151
  } else {
144
152
  // leading group emtpy
145
153
  self.setOffsetX(value: 0)
@@ -160,7 +168,7 @@ public struct SwipeCellModifier: ViewModifier {
160
168
  }
161
169
  }
162
170
  }
163
-
171
+
164
172
  internal func triggerHapticFeedbackIfNeeded(horizontalTranslation: CGFloat) {
165
173
  let side: SwipeGroupSide = horizontalTranslation > 0 ? .leading : .trailing
166
174
  let group = side == .leading ? self.leadingSideGroup : self.trailingSideGroup
@@ -170,7 +178,7 @@ public struct SwipeCellModifier: ViewModifier {
170
178
  self.hapticFeedbackOccurred = true
171
179
  }
172
180
  }
173
-
181
+
174
182
  internal func swipeOutItemWithHapticFeedback(group: [SwipeCellActionItem])->SwipeCellActionItem? {
175
183
  if let item = group.filter({ $0.swipeOutAction == true }).first {
176
184
  if item.swipeOutHapticFeedbackType != nil {
@@ -179,7 +187,7 @@ public struct SwipeCellModifier: ViewModifier {
179
187
  }
180
188
  return nil
181
189
  }
182
-
190
+
183
191
  internal func swipeOutAction(item: SwipeCellActionItem, sideFactor: CGFloat) {
184
192
  if item.swipeOutIsDestructive {
185
193
  let swipeOutWidth = cellWidth + 10
@@ -188,12 +196,12 @@ public struct SwipeCellModifier: ViewModifier {
188
196
  } else {
189
197
  self.setOffsetX(value: 0) // open side lock set in function!
190
198
  }
191
-
199
+
192
200
  DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
193
201
  item.actionCallback()
194
202
  }
195
203
  }
196
-
204
+
197
205
  internal func lockSideMenu(side: SwipeGroupSide) {
198
206
  self.setOffsetX(value: side.sideFactor * self.menuWidth(side: side))
199
207
  self.openSideLock = side
@@ -214,7 +222,7 @@ public struct SwipeCellModifier: ViewModifier {
214
222
  let dynamicButtonWidth = self.dynamicButtonWidth(item: item, itemCount: itemGroup.count, side: side)
215
223
  let triggerValue = self.cellWidth * settings.swipeOutTriggerRatio
216
224
  let swipeOutActionCondition = side == .leading ? self.offsetX > triggerValue : self.offsetX < -triggerValue
217
-
225
+
218
226
  if item.swipeOutAction && swipeOutActionCondition {
219
227
  return self.offsetX.magnitude + settings.addWidthMargin
220
228
  } else if swipeOutActionCondition && item.swipeOutAction == false && itemGroup.contains(where: { $0.swipeOutAction == true }) {
@@ -223,12 +231,12 @@ public struct SwipeCellModifier: ViewModifier {
223
231
  return dynamicButtonWidth
224
232
  }
225
233
  }
226
-
234
+
227
235
  internal func dynamicButtonWidth(item: SwipeCellActionItem, itemCount: Int, side: SwipeGroupSide)->CGFloat {
228
236
  let menuWidth = self.menuWidth(side: side)
229
237
  return (self.offsetX.magnitude + settings.addWidthMargin) * (item.buttonWidth / menuWidth)
230
238
  }
231
-
239
+
232
240
  internal func warnSwipeOutCondition(side: SwipeGroupSide, hasSwipeOut: Bool)->Bool {
233
241
  if hasSwipeOut == false {
234
242
  return false
@@ -236,11 +244,11 @@ public struct SwipeCellModifier: ViewModifier {
236
244
  let triggerValue = self.cellWidth * settings.swipeOutTriggerRatio
237
245
  return (side == .trailing && self.offsetX < -triggerValue) || (side == .leading && self.offsetX > triggerValue)
238
246
  }
239
-
247
+
240
248
  internal func swipeRevealAreaOpacity(side: SwipeGroupSide)->Double {
241
249
  switch side {
242
250
  case .leading:
243
-
251
+
244
252
  return self.offsetX > 5 ? 1 : 0
245
253
  case .trailing:
246
254
  return self.offsetX < -5 ? 1 : 0
@@ -0,0 +1,44 @@
1
+ import Foundation
2
+ import SwiftUI
3
+
4
+ public struct Switch: View{
5
+ @Binding var value: Bool
6
+ var disabled: Bool
7
+ var onChange: ((Bool)->Void)?
8
+
9
+ public init(_ value: Binding<Bool>,disabled: Bool = false, onChange: ((Bool)->Void)? = nil){
10
+ self._value = value
11
+ self.disabled = disabled
12
+ self.onChange = onChange
13
+ }
14
+
15
+
16
+ public var body: some View {
17
+ SwiftUI.Button(action: {
18
+ withAnimation{
19
+ value.toggle()
20
+ onChange?(value)
21
+ }
22
+ }){
23
+ HStack{
24
+ HStack {
25
+ }
26
+ .frame(width: 6, height: 6)
27
+ .background(value ? Colors.pink03 : Colors.black07)
28
+ .clipShape(Circle())
29
+ .opacity(disabled ? 0.4 : 1)
30
+ }
31
+ .frame(width: 14, height: 14)
32
+ .background(Colors.black01)
33
+ .clipShape(Circle())
34
+ .padding(.leading,5)
35
+ .padding(.trailing,5)
36
+
37
+ }
38
+ .disabled(disabled)
39
+ .frame(width: 40, height: 24, alignment: value == true ? .trailing : .leading)
40
+ .background(value ? Colors.pink03 : Colors.black07)
41
+ .clipShape(Capsule())
42
+ .opacity(disabled ? 0.4 : 1)
43
+ }
44
+ }