@@ -13,7 +13,7 @@ struct KeyValueItem: Identifiable, Equatable {
1313 let value : String
1414}
1515
16- private struct NewListTableItemView : View {
16+ private struct NewListTableItemView < HeaderView : View > : View {
1717 @Environment ( \. dismiss)
1818 var dismiss
1919
@@ -24,17 +24,21 @@ private struct NewListTableItemView: View {
2424 let valueColumnName : String
2525 let newItemInstruction : String
2626 let validKeys : [ String ]
27- let headerView : AnyView ?
27+ let headerView : HeaderView ?
2828 var completion : ( String , String ) -> Void
2929
3030 init (
31+ key: String ? = nil ,
32+ value: String ? = nil ,
3133 _ keyColumnName: String ,
3234 _ valueColumnName: String ,
3335 _ newItemInstruction: String ,
3436 validKeys: [ String ] ,
35- headerView: AnyView ? = nil ,
37+ headerView: HeaderView ? = nil ,
3638 completion: @escaping ( String , String ) -> Void
3739 ) {
40+ self . key = key ?? " "
41+ self . value = value ?? " "
3842 self . keyColumnName = keyColumnName
3943 self . valueColumnName = valueColumnName
4044 self . newItemInstruction = newItemInstruction
@@ -62,7 +66,11 @@ private struct NewListTableItemView: View {
6266 TextField ( valueColumnName, text: $value)
6367 . textFieldStyle ( . plain)
6468 } header: {
65- headerView
69+ if HeaderView . self == EmptyView . self {
70+ Text ( newItemInstruction)
71+ } else {
72+ headerView
73+ }
6674 }
6775 }
6876 . formStyle ( . grouped)
@@ -94,17 +102,18 @@ private struct NewListTableItemView: View {
94102 }
95103}
96104
97- struct KeyValueTable < Header: View > : View {
105+ struct KeyValueTable < Header: View , ActionBarView : View > : View {
98106 @Binding var items : [ String : String ]
99107
100108 let validKeys : [ String ]
101109 let keyColumnName : String
102110 let valueColumnName : String
103111 let newItemInstruction : String
104- let header : ( ) -> Header
112+ let newItemHeader : ( ) -> Header
113+ let actionBarTrailing : ( ) -> ActionBarView
105114
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 > = [ ]
108117 @State private var tableItems : [ KeyValueItem ] = [ ]
109118
110119 init (
@@ -113,14 +122,16 @@ struct KeyValueTable<Header: View>: View {
113122 keyColumnName: String ,
114123 valueColumnName: String ,
115124 newItemInstruction: String ,
116- @ViewBuilder header: @escaping ( ) -> Header = { EmptyView ( ) }
125+ @ViewBuilder newItemHeader: @escaping ( ) -> Header = { EmptyView ( ) } ,
126+ @ViewBuilder actionBarTrailing: @escaping ( ) -> ActionBarView = { EmptyView ( ) }
117127 ) {
118128 self . _items = items
119129 self . validKeys = validKeys
120130 self . keyColumnName = keyColumnName
121131 self . valueColumnName = valueColumnName
122132 self . newItemInstruction = newItemInstruction
123- self . header = header
133+ self . newItemHeader = newItemHeader
134+ self . actionBarTrailing = actionBarTrailing
124135 }
125136
126137 var body : some View {
@@ -132,11 +143,24 @@ struct KeyValueTable<Header: View>: View {
132143 Text ( item. value)
133144 }
134145 }
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+ )
136160 . actionBar {
137161 HStack ( spacing: 2 ) {
138162 Button {
139- showingModal = true
163+ editingItem = KeyValueItem ( key : " " , value : " " )
140164 } label: {
141165 Image ( systemName: " plus " )
142166 }
@@ -149,38 +173,64 @@ struct KeyValueTable<Header: View>: View {
149173 } label: {
150174 Image ( systemName: " minus " )
151175 }
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 ( )
154182 }
155- Spacer ( )
156183 }
157- . sheet ( isPresented : $showingModal ) {
184+ . sheet ( item : $editingItem ) { item in
158185 NewListTableItemView (
186+ key: item. key,
187+ value: item. value,
159188 keyColumnName,
160189 valueColumnName,
161190 newItemInstruction,
162191 validKeys: validKeys,
163- headerView: AnyView ( header ( ) )
192+ headerView: newItemHeader ( )
164193 ) { key, value in
165194 items [ key] = value
166- updateTableItems ( )
167- showingModal = false
195+ editingItem = nil
168196 }
169197 }
170198 . 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+ }
172209 }
173210
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) }
176215 }
177216
178217 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
183233 }
184- selection = nil
234+ editingItem = item
185235 }
186236}
0 commit comments