@@ -32,7 +32,7 @@ struct DataBrowserView: View {
3232 @State private var goToPageInput = " "
3333 @State private var filters : [ TableFilter ] = [ ]
3434 @State private var filterLogicMode : FilterLogicMode = . and
35- @State private var showFilterBar = false
35+ @State private var showFilterSheet = false
3636
3737 private var isView : Bool {
3838 table. type == . view || table. type == . materializedView
@@ -65,6 +65,15 @@ struct DataBrowserView: View {
6565 . toolbar { paginationToolbar }
6666 . task { await loadData ( isInitial: true ) }
6767 . sheet ( isPresented: $showInsertSheet) { insertSheet }
68+ . sheet ( isPresented: $showFilterSheet) {
69+ FilterSheetView (
70+ filters: $filters,
71+ logicMode: $filterLogicMode,
72+ columns: columns,
73+ onApply: { applyFilters ( ) } ,
74+ onClear: { clearFilters ( ) }
75+ )
76+ }
6877 . confirmationDialog ( " Delete Row " , isPresented: $showDeleteConfirmation, titleVisibility: . visible) {
6978 Button ( " Delete " , role: . destructive) {
7079 if let pkValues = deleteTarget {
@@ -119,50 +128,12 @@ struct DataBrowserView: View {
119128 }
120129 }
121130
131+ private var activeFilterCount : Int {
132+ filters. filter { $0. isEnabled && $0. isValid } . count
133+ }
134+
122135 private var rowList : some View {
123136 List {
124- if showFilterBar {
125- Section {
126- if filters. count > 1 {
127- Picker ( " Logic " , selection: $filterLogicMode) {
128- Text ( " AND " ) . tag ( FilterLogicMode . and)
129- Text ( " OR " ) . tag ( FilterLogicMode . or)
130- }
131- . pickerStyle ( . segmented)
132- }
133-
134- ForEach ( $filters) { $filter in
135- FilterRowView (
136- filter: $filter,
137- columns: columns,
138- onDelete: { filters. removeAll { $0. id == filter. id } }
139- )
140- }
141-
142- HStack {
143- Button {
144- filters. append ( TableFilter ( columnName: columns. first? . name ?? " " ) )
145- } label: {
146- Label ( " Add Filter " , systemImage: " plus.circle " )
147- }
148- Spacer ( )
149- Button ( " Apply " ) {
150- applyFilters ( )
151- }
152- . buttonStyle ( . borderedProminent)
153- . disabled ( !hasActiveFilters)
154- }
155-
156- if hasActiveFilters {
157- Button ( " Clear Filters " , role: . destructive) {
158- clearFilters ( )
159- }
160- }
161- } header: {
162- Text ( " Filters " )
163- }
164- }
165-
166137 ForEach ( Array ( rows. enumerated ( ) ) , id: \. offset) { index, row in
167138 NavigationLink {
168139 RowDetailView (
@@ -207,11 +178,12 @@ struct DataBrowserView: View {
207178 @ToolbarContentBuilder
208179 private var topToolbar : some ToolbarContent {
209180 ToolbarItem ( placement: . topBarTrailing) {
210- Button { withAnimation { showFilterBar . toggle ( ) } } label: {
181+ Button { showFilterSheet = true } label: {
211182 Image ( systemName: hasActiveFilters
212183 ? " line.3.horizontal.decrease.circle.fill "
213184 : " line.3.horizontal.decrease.circle " )
214185 }
186+ . badge ( activeFilterCount)
215187 }
216188 ToolbarItem ( placement: . topBarTrailing) {
217189 NavigationLink {
@@ -438,47 +410,90 @@ struct DataBrowserView: View {
438410 }
439411}
440412
441- // MARK: - Filter Row
413+ // MARK: - Filter Sheet
442414
443- private struct FilterRowView : View {
444- @Binding var filter : TableFilter
415+ private struct FilterSheetView : View {
416+ @Environment ( \. dismiss) private var dismiss
417+ @Binding var filters : [ TableFilter ]
418+ @Binding var logicMode : FilterLogicMode
445419 let columns : [ ColumnInfo ]
446- let onDelete : ( ) -> Void
420+ let onApply : ( ) -> Void
421+ let onClear : ( ) -> Void
447422
448423 var body : some View {
449- VStack ( spacing: 8 ) {
450- HStack {
451- Picker ( " Column " , selection: $filter. columnName) {
452- ForEach ( columns, id: \. name) { col in
453- Text ( col. name) . tag ( col. name)
424+ NavigationStack {
425+ Form {
426+ if filters. count > 1 {
427+ Section {
428+ Picker ( " Logic " , selection: $logicMode) {
429+ Text ( " AND " ) . tag ( FilterLogicMode . and)
430+ Text ( " OR " ) . tag ( FilterLogicMode . or)
431+ }
432+ . pickerStyle ( . segmented)
454433 }
455434 }
456- . pickerStyle ( . menu)
457435
458- Button ( role: . destructive) { onDelete ( ) } label: {
459- Image ( systemName: " minus.circle.fill " )
460- . foregroundStyle ( . red)
436+ ForEach ( $filters) { $filter in
437+ Section {
438+ Picker ( " Column " , selection: $filter. columnName) {
439+ ForEach ( columns, id: \. name) { col in
440+ Text ( col. name) . tag ( col. name)
441+ }
442+ }
443+
444+ Picker ( " Operator " , selection: $filter. filterOperator) {
445+ ForEach ( FilterOperator . allCases, id: \. self) { op in
446+ Text ( op. displayName) . tag ( op)
447+ }
448+ }
449+
450+ if filter. filterOperator. needsValue {
451+ TextField ( " Value " , text: $filter. value)
452+ . textInputAutocapitalization ( . never)
453+ . autocorrectionDisabled ( )
454+ }
455+
456+ if filter. filterOperator == . between {
457+ TextField ( " Second value " , text: $filter. secondValue)
458+ . textInputAutocapitalization ( . never)
459+ . autocorrectionDisabled ( )
460+ }
461+ }
462+ }
463+ . onDelete { indexSet in
464+ filters. remove ( atOffsets: indexSet)
461465 }
462- . buttonStyle ( . plain)
463- }
464466
465- Picker ( " Operator " , selection: $filter. filterOperator) {
466- ForEach ( FilterOperator . allCases, id: \. self) { op in
467- Text ( op. displayName) . tag ( op)
467+ Section {
468+ Button {
469+ filters. append ( TableFilter ( columnName: columns. first? . name ?? " " ) )
470+ } label: {
471+ Label ( " Add Filter " , systemImage: " plus.circle " )
472+ }
468473 }
469- }
470- . pickerStyle ( . menu)
471474
472- if filter. filterOperator. needsValue {
473- TextField ( " Value " , text: $filter. value)
474- . textInputAutocapitalization ( . never)
475- . autocorrectionDisabled ( )
475+ if !filters. isEmpty {
476+ Section {
477+ Button ( " Clear All Filters " , role: . destructive) {
478+ onClear ( )
479+ dismiss ( )
480+ }
481+ }
482+ }
476483 }
477-
478- if filter. filterOperator == . between {
479- TextField ( " Second value " , text: $filter. secondValue)
480- . textInputAutocapitalization ( . never)
481- . autocorrectionDisabled ( )
484+ . navigationTitle ( " Filters " )
485+ . navigationBarTitleDisplayMode ( . inline)
486+ . toolbar {
487+ ToolbarItem ( placement: . cancellationAction) {
488+ Button ( " Cancel " ) { dismiss ( ) }
489+ }
490+ ToolbarItem ( placement: . confirmationAction) {
491+ Button ( " Apply " ) {
492+ onApply ( )
493+ dismiss ( )
494+ }
495+ . disabled ( !filters. contains { $0. isEnabled && $0. isValid } )
496+ }
482497 }
483498 }
484499 }
0 commit comments