@@ -13,7 +13,7 @@ struct KeyValueItem: Identifiable, Equatable {
13
13
let value : String
14
14
}
15
15
16
- private struct NewListTableItemView : View {
16
+ private struct NewListTableItemView < HeaderView : View > : View {
17
17
@Environment ( \. dismiss)
18
18
var dismiss
19
19
@@ -24,17 +24,21 @@ private struct NewListTableItemView: View {
24
24
let valueColumnName : String
25
25
let newItemInstruction : String
26
26
let validKeys : [ String ]
27
- let headerView : AnyView ?
27
+ let headerView : HeaderView ?
28
28
var completion : ( String , String ) -> Void
29
29
30
30
init (
31
+ key: String ? = nil ,
32
+ value: String ? = nil ,
31
33
_ keyColumnName: String ,
32
34
_ valueColumnName: String ,
33
35
_ newItemInstruction: String ,
34
36
validKeys: [ String ] ,
35
- headerView: AnyView ? = nil ,
37
+ headerView: HeaderView ? = nil ,
36
38
completion: @escaping ( String , String ) -> Void
37
39
) {
40
+ self . key = key ?? " "
41
+ self . value = value ?? " "
38
42
self . keyColumnName = keyColumnName
39
43
self . valueColumnName = valueColumnName
40
44
self . newItemInstruction = newItemInstruction
@@ -62,7 +66,11 @@ private struct NewListTableItemView: View {
62
66
TextField ( valueColumnName, text: $value)
63
67
. textFieldStyle ( . plain)
64
68
} header: {
65
- headerView
69
+ if HeaderView . self == EmptyView . self {
70
+ Text ( newItemInstruction)
71
+ } else {
72
+ headerView
73
+ }
66
74
}
67
75
}
68
76
. formStyle ( . grouped)
@@ -94,17 +102,18 @@ private struct NewListTableItemView: View {
94
102
}
95
103
}
96
104
97
- struct KeyValueTable < Header: View > : View {
105
+ struct KeyValueTable < Header: View , ActionBarView : View > : View {
98
106
@Binding var items : [ String : String ]
99
107
100
108
let validKeys : [ String ]
101
109
let keyColumnName : String
102
110
let valueColumnName : String
103
111
let newItemInstruction : String
104
- let header : ( ) -> Header
112
+ let newItemHeader : ( ) -> Header
113
+ let actionBarTrailing : ( ) -> ActionBarView
105
114
106
- @State private var showingModal = false
107
- @State private var selection : UUID ?
115
+ @State private var editingItem : KeyValueItem ?
116
+ @State private var selection : Set < UUID > = [ ]
108
117
@State private var tableItems : [ KeyValueItem ] = [ ]
109
118
110
119
init (
@@ -113,14 +122,16 @@ struct KeyValueTable<Header: View>: View {
113
122
keyColumnName: String ,
114
123
valueColumnName: String ,
115
124
newItemInstruction: String ,
116
- @ViewBuilder header: @escaping ( ) -> Header = { EmptyView ( ) }
125
+ @ViewBuilder newItemHeader: @escaping ( ) -> Header = { EmptyView ( ) } ,
126
+ @ViewBuilder actionBarTrailing: @escaping ( ) -> ActionBarView = { EmptyView ( ) }
117
127
) {
118
128
self . _items = items
119
129
self . validKeys = validKeys
120
130
self . keyColumnName = keyColumnName
121
131
self . valueColumnName = valueColumnName
122
132
self . newItemInstruction = newItemInstruction
123
- self . header = header
133
+ self . newItemHeader = newItemHeader
134
+ self . actionBarTrailing = actionBarTrailing
124
135
}
125
136
126
137
var body : some View {
@@ -132,11 +143,24 @@ struct KeyValueTable<Header: View>: View {
132
143
Text ( item. value)
133
144
}
134
145
}
135
- . frame ( height: 200 )
146
+ . contextMenu (
147
+ forSelectionType: UUID . self,
148
+ menu: { selectedItems in
149
+ Button ( " Edit " ) {
150
+ editItem ( id: selectedItems. first)
151
+ }
152
+ Button ( " Remove " ) {
153
+ removeItem ( selectedItems)
154
+ }
155
+ } ,
156
+ primaryAction: { selectedItems in
157
+ editItem ( id: selectedItems. first)
158
+ }
159
+ )
136
160
. actionBar {
137
161
HStack ( spacing: 2 ) {
138
162
Button {
139
- showingModal = true
163
+ editingItem = KeyValueItem ( key : " " , value : " " )
140
164
} label: {
141
165
Image ( systemName: " plus " )
142
166
}
@@ -149,38 +173,64 @@ struct KeyValueTable<Header: View>: View {
149
173
} label: {
150
174
Image ( systemName: " minus " )
151
175
}
152
- . disabled ( selection == nil )
153
- . opacity ( selection == nil ? 0.5 : 1 )
176
+ . disabled ( selection. isEmpty)
177
+ . opacity ( selection. isEmpty ? 0.5 : 1 )
178
+
179
+ Spacer ( )
180
+
181
+ actionBarTrailing ( )
154
182
}
155
- Spacer ( )
156
183
}
157
- . sheet ( isPresented : $showingModal ) {
184
+ . sheet ( item : $editingItem ) { item in
158
185
NewListTableItemView (
186
+ key: item. key,
187
+ value: item. value,
159
188
keyColumnName,
160
189
valueColumnName,
161
190
newItemInstruction,
162
191
validKeys: validKeys,
163
- headerView: AnyView ( header ( ) )
192
+ headerView: newItemHeader ( )
164
193
) { key, value in
165
194
items [ key] = value
166
- updateTableItems ( )
167
- showingModal = false
195
+ editingItem = nil
168
196
}
169
197
}
170
198
. cornerRadius ( 6 )
171
- . onAppear ( perform: updateTableItems)
199
+ . onAppear {
200
+ updateTableItems ( items)
201
+ if let first = tableItems. first? . id {
202
+ selection = [ first]
203
+ }
204
+ selection = [ ]
205
+ }
206
+ . onChange ( of: items) { newValue in
207
+ updateTableItems ( newValue)
208
+ }
172
209
}
173
210
174
- private func updateTableItems( ) {
175
- tableItems = items. map { KeyValueItem ( key: $0. key, value: $0. value) }
211
+ private func updateTableItems( _ newValue: [ String : String ] ) {
212
+ tableItems = items
213
+ . sorted { $0. key < $1. key }
214
+ . map { KeyValueItem ( key: $0. key, value: $0. value) }
176
215
}
177
216
178
217
private func removeItem( ) {
179
- guard let selectedId = selection else { return }
180
- if let selectedItem = tableItems. first ( where: { $0. id == selectedId } ) {
181
- items. removeValue ( forKey: selectedItem. key)
182
- updateTableItems ( )
218
+ removeItem ( selection)
219
+ self . selection. removeAll ( )
220
+ }
221
+
222
+ private func removeItem( _ selection: Set < UUID > ) {
223
+ for selectedId in selection {
224
+ if let selectedItem = tableItems. first ( where: { $0. id == selectedId } ) {
225
+ items. removeValue ( forKey: selectedItem. key)
226
+ }
227
+ }
228
+ }
229
+
230
+ private func editItem( id: UUID ? ) {
231
+ guard let id, let item = tableItems. first ( where: { $0. id == id } ) else {
232
+ return
183
233
}
184
- selection = nil
234
+ editingItem = item
185
235
}
186
236
}
0 commit comments