1
1
//
2
- // EditDetail .swift
2
+ // SwiftUIView .swift
3
3
//
4
4
// Copyright 2022 FlowAllocator LLC
5
5
//
18
18
19
19
import SwiftUI
20
20
21
- struct EditDetail < Element, Detail> : View
22
- where Element: Identifiable ,
23
- Detail: View
21
+ struct EditDetailBase < Element, Detail> : View
22
+ where Element: Identifiable ,
23
+ Detail: View
24
24
{
25
25
@Environment ( \. presentationMode) var presentationMode : Binding < PresentationMode >
26
-
27
- typealias DetailContent = ( DetailerContext < Element > , Binding < Element > ) -> Detail
26
+
27
+ typealias DetailContent = ( DetailerContext < Element > ) -> Detail
28
28
typealias Validate < T> = ( KeyPath < Element , T > ) -> Void
29
-
29
+
30
30
// MARK: Parameters
31
-
32
- // NOTE `element` not a binding, because we don't want to change data live
33
- // NOTE `isAdd` will be set to `false` on dismissal of sheet
34
-
31
+
35
32
var config : DetailerConfig < Element >
36
- @ State var element : Element
33
+ var element : Element
37
34
@Binding var isAdd : Bool
38
35
var detailContent : DetailContent
39
-
36
+
40
37
// MARK: Locals
41
-
38
+
42
39
@State private var invalidFields : Set < AnyKeyPath > = Set ( )
43
40
@State private var showValidationAlert = false
44
41
@State private var alertMessage : String ? = nil
45
-
42
+
46
43
private var context : DetailerContext < Element > {
47
44
DetailerContext < Element > ( config: config,
48
45
onValidate: validateAction,
49
46
isAdd: isAdd)
50
47
}
51
-
48
+
52
49
private var isDeleteAvailable : Bool {
53
50
config. onDelete != nil
54
51
}
@@ -60,29 +57,29 @@ struct EditDetail<Element, Detail>: View
60
57
private var isSaveAvailable : Bool {
61
58
config. onSave != nil
62
59
}
63
-
60
+
64
61
private var canSave : Bool {
65
62
isSaveAvailable && invalidFields. isEmpty
66
63
}
67
-
64
+
68
65
// MARK: Views
69
-
66
+
70
67
var body : some View {
71
68
VStack ( alignment: . leading) { // .leading needed to keep title from centering
72
- #if os(macOS)
69
+ #if os(macOS)
73
70
Text ( config. titler ( element) ) . font ( . largeTitle)
74
- #endif
71
+ #endif
75
72
// this is where the user will typically declare a Form or VStack
76
- detailContent ( context, $element )
73
+ detailContent ( context)
77
74
// .animation(.default)
78
75
}
79
76
. alert ( isPresented: $showValidationAlert) {
80
77
Alert ( title: Text ( " Validation Failure " ) ,
81
78
message: Text ( alertMessage ?? " Requires valid entry before save. " ) )
82
79
}
83
- #if os(macOS)
80
+ #if os(macOS)
84
81
. padding( )
85
- #endif
82
+ #endif
86
83
. toolbar {
87
84
ToolbarItem ( placement: . destructiveAction) {
88
85
Button ( action: deleteAction) {
@@ -92,7 +89,7 @@ struct EditDetail<Element, Detail>: View
92
89
. opacity ( isDeleteAvailable ? 1 : 0 )
93
90
. disabled ( !canDelete)
94
91
}
95
-
92
+
96
93
ToolbarItem ( placement: . cancellationAction) {
97
94
Button ( action: cancelAction) {
98
95
Text ( " Cancel " )
@@ -107,18 +104,18 @@ struct EditDetail<Element, Detail>: View
107
104
. disabled ( !canSave)
108
105
}
109
106
}
110
- #if os(macOS)
107
+ #if os(macOS)
111
108
// NOTE on macOS, this seems to be needed to avoid excessive height
112
109
. frame( minWidth: config. minWidth, maxWidth: . infinity, maxHeight: . infinity, alignment: . leading)
113
- #endif
114
-
115
- #if os(iOS) || targetEnvironment(macCatalyst)
110
+ #endif
111
+
112
+ #if os(iOS) || targetEnvironment(macCatalyst)
116
113
. navigationTitle( config. titler ( element) )
117
- #endif
114
+ #endif
118
115
}
119
-
116
+
120
117
// MARK: Action Handlers
121
-
118
+
122
119
// NOTE: should be invoked via async to avoid updating the state during view render
123
120
private func validateAction( _ anyKeyPath: AnyKeyPath , _ result: Bool ) {
124
121
if result {
@@ -133,40 +130,40 @@ struct EditDetail<Element, Detail>: View
133
130
}
134
131
}
135
132
}
136
-
133
+
137
134
private func saveAction( ) {
138
135
guard let _onSave = config. onSave else { return }
139
-
136
+
140
137
// display any validation changes
141
138
let messages = config. onValidate ( context, element)
142
139
if messages. count > 0 {
143
140
alertMessage = config. onValidate ( context, element) . joined ( separator: " \n \n " )
144
141
showValidationAlert = true
145
142
return
146
143
}
147
-
144
+
148
145
let invalidCount = invalidFields. count
149
146
if invalidCount > 0 {
150
147
alertMessage = " \( invalidCount) field(s) require valid values before you can save. "
151
148
showValidationAlert = true
152
149
return
153
150
}
154
-
151
+
155
152
_onSave ( context, element)
156
153
dismissAction ( )
157
154
}
158
-
155
+
159
156
private func deleteAction( ) {
160
157
guard let _onDelete = config. onDelete else { return }
161
158
_onDelete ( element. id)
162
159
dismissAction ( )
163
160
}
164
-
161
+
165
162
private func cancelAction( ) {
166
163
config. onCancel ( context, element)
167
164
dismissAction ( )
168
165
}
169
-
166
+
170
167
private func dismissAction( ) {
171
168
isAdd = false
172
169
presentationMode. wrappedValue. dismiss ( )
0 commit comments