-
-
Notifications
You must be signed in to change notification settings - Fork 298
feat(datagrid): apply filters individually (enable, disable, solo) (#1561) #1562
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -154,7 +154,6 @@ final class FilterCoordinator { | |
| } | ||
|
|
||
| newFilter.filterOperator = settings.defaultOperator.toFilterOperator() | ||
| newFilter.isSelected = true | ||
|
|
||
| mutateSelectedTabFilterState { state in | ||
| state.filters.append(newFilter) | ||
|
|
@@ -166,7 +165,6 @@ final class FilterCoordinator { | |
| var newFilter = TableFilter() | ||
| newFilter.columnName = columnName | ||
| newFilter.filterOperator = settings.defaultOperator.toFilterOperator() | ||
| newFilter.isSelected = true | ||
|
|
||
| mutateSelectedTabFilterState { state in | ||
| state.filters.append(newFilter) | ||
|
|
@@ -179,7 +177,7 @@ final class FilterCoordinator { | |
| func setFKFilter(_ filter: TableFilter) { | ||
| mutateSelectedTabFilterState { state in | ||
| state.filters = [filter] | ||
| state.appliedFilters = [filter] | ||
| state.commit = .all | ||
| state.isVisible = true | ||
| state.filterLogicMode = .and | ||
| } | ||
|
|
@@ -192,7 +190,6 @@ final class FilterCoordinator { | |
| filterOperator: filter.filterOperator, | ||
| value: filter.value, | ||
| secondValue: filter.secondValue, | ||
| isSelected: true, | ||
| isEnabled: filter.isEnabled, | ||
| rawSQL: filter.rawSQL | ||
| ) | ||
|
|
@@ -208,7 +205,9 @@ final class FilterCoordinator { | |
| func removeFilter(_ filter: TableFilter) { | ||
| mutateSelectedTabFilterState { state in | ||
| state.filters.removeAll { $0.id == filter.id } | ||
| state.appliedFilters.removeAll { $0.id == filter.id } | ||
| if case .solo(let id) = state.commit, id == filter.id { | ||
| state.commit = nil | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -279,28 +278,29 @@ final class FilterCoordinator { | |
| guard filter.isValid else { return } | ||
| mutateSelectedTabFilterState { state in | ||
| state.filters = [filter] | ||
| state.appliedFilters = [filter] | ||
| state.commit = .all | ||
| state.isVisible = true | ||
| } | ||
| } | ||
|
|
||
| func applySelectedFilters() { | ||
| func applyAllFilters() { | ||
| mutateSelectedTabFilterState { state in | ||
| state.appliedFilters = state.filters.filter { $0.isSelected && $0.isValid } | ||
| state.commit = .all | ||
| } | ||
| saveLastFiltersForActiveTable() | ||
| } | ||
|
|
||
| func applyAllFilters() { | ||
| func applySoloFilter(_ filter: TableFilter) { | ||
| guard filter.isValid else { return } | ||
| mutateSelectedTabFilterState { state in | ||
| state.appliedFilters = state.filters.filter { $0.isEnabled && $0.isValid } | ||
| state.commit = .solo(filter.id) | ||
| } | ||
| saveLastFiltersForActiveTable() | ||
| } | ||
|
|
||
| func clearAppliedFilters() { | ||
| mutateSelectedTabFilterState { state in | ||
| state.appliedFilters = [] | ||
| state.commit = nil | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -330,31 +330,13 @@ final class FilterCoordinator { | |
| } | ||
| } | ||
|
|
||
| // MARK: - Selection | ||
|
|
||
| func selectAllFilters(_ selected: Bool) { | ||
| mutateSelectedTabFilterState { state in | ||
| for index in 0..<state.filters.count { | ||
| state.filters[index].isSelected = selected | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func toggleFilterSelection(_ filter: TableFilter) { | ||
| mutateSelectedTabFilterState { state in | ||
| if let index = state.filters.firstIndex(where: { $0.id == filter.id }) { | ||
| state.filters[index].isSelected.toggle() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // MARK: - Persistence | ||
|
|
||
| func saveLastFiltersForActiveTable() { | ||
| guard let tab = parent.tabManager.selectedTab, | ||
| let tableName = tab.tableContext.tableName else { return } | ||
| FilterSettingsStorage.shared.saveLastFilters( | ||
| tab.filterState.appliedFilters, | ||
| tab.filterState.filters.filter(\.isValid), | ||
| for: tableName, | ||
|
Comment on lines
338
to
340
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This path saves all valid rows so disabled filters can be restored, but switching away from the tab still calls Useful? React with 👍 / 👎. |
||
| connectionId: parent.connectionId, | ||
| databaseName: tab.tableContext.databaseName, | ||
|
|
@@ -365,7 +347,7 @@ final class FilterCoordinator { | |
| func saveLastFilters(for tableName: String) { | ||
| guard let tab = parent.tabManager.selectedTab else { return } | ||
| FilterSettingsStorage.shared.saveLastFilters( | ||
| tab.filterState.appliedFilters, | ||
| tab.filterState.filters.filter(\.isValid), | ||
| for: tableName, | ||
| connectionId: parent.connectionId, | ||
| databaseName: tab.tableContext.databaseName, | ||
|
|
@@ -412,15 +394,15 @@ final class FilterCoordinator { | |
| switch panelState { | ||
| case .alwaysHide: | ||
| state.filters = [] | ||
| state.appliedFilters = [] | ||
| state.commit = nil | ||
| state.isVisible = false | ||
| case .alwaysShow: | ||
| state.filters = saved | ||
| state.appliedFilters = saved | ||
| state.commit = .all | ||
| state.isVisible = true | ||
| case .restoreLast: | ||
| state.filters = saved | ||
| state.appliedFilters = saved | ||
| state.commit = .all | ||
| state.isVisible = !saved.isEmpty | ||
| } | ||
| return state | ||
|
|
@@ -429,7 +411,7 @@ final class FilterCoordinator { | |
| func clearFilterState() { | ||
| mutateSelectedTabFilterState { state in | ||
| state.filters = [] | ||
| state.appliedFilters = [] | ||
| state.commit = nil | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -475,16 +457,7 @@ final class FilterCoordinator { | |
| } | ||
|
|
||
| private func filtersForPreview(in state: TabFilterState) -> [TableFilter] { | ||
| var valid: [TableFilter] = [] | ||
| var selectedValid: [TableFilter] = [] | ||
| for filter in state.filters where filter.isEnabled && filter.isValid { | ||
| valid.append(filter) | ||
| if filter.isSelected { selectedValid.append(filter) } | ||
| } | ||
| if selectedValid.count == valid.count || selectedValid.isEmpty { | ||
| return valid | ||
| } | ||
| return selectedValid | ||
| state.filters.filter { $0.isEnabled && $0.isValid } | ||
| } | ||
|
|
||
| // MARK: - Private | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -94,13 +94,12 @@ enum FilterOperator: String, CaseIterable, Identifiable, Codable { | |
| /// Represents a single table filter condition | ||
| struct TableFilter: Identifiable, Equatable, Hashable, Codable { | ||
| let id: UUID | ||
| var columnName: String // Column to filter on, or "__RAW__" for raw SQL | ||
| var columnName: String | ||
| var filterOperator: FilterOperator | ||
| var value: String | ||
| var secondValue: String? // For BETWEEN operator | ||
| var isSelected: Bool // For multi-select apply | ||
| var isEnabled: Bool // Whether filter is active | ||
| var rawSQL: String? // For raw SQL mode | ||
| var secondValue: String? | ||
| var isEnabled: Bool | ||
| var rawSQL: String? | ||
|
|
||
| /// Special column name for raw SQL mode | ||
| static let rawSQLColumn = "__RAW__" | ||
|
|
@@ -111,7 +110,6 @@ struct TableFilter: Identifiable, Equatable, Hashable, Codable { | |
| filterOperator: FilterOperator = .equal, | ||
| value: String = "", | ||
| secondValue: String? = nil, | ||
| isSelected: Bool = false, | ||
| isEnabled: Bool = true, | ||
| rawSQL: String? = nil | ||
| ) { | ||
|
|
@@ -120,7 +118,6 @@ struct TableFilter: Identifiable, Equatable, Hashable, Codable { | |
| self.filterOperator = filterOperator | ||
| self.value = value | ||
| self.secondValue = secondValue | ||
| self.isSelected = isSelected | ||
| self.isEnabled = isEnabled | ||
| self.rawSQL = rawSQL | ||
| } | ||
|
|
@@ -187,19 +184,31 @@ extension TableFilter { | |
| /// Stores per-tab filter state (preserves filters when switching tabs) | ||
| struct TabFilterState: Equatable, Hashable, Codable { | ||
| var filters: [TableFilter] | ||
| var appliedFilters: [TableFilter] | ||
| var commit: FilterCommit? | ||
| var isVisible: Bool | ||
| var filterLogicMode: FilterLogicMode | ||
|
|
||
| init(isVisible: Bool = false) { | ||
| self.filters = [] | ||
| self.appliedFilters = [] | ||
| self.commit = nil | ||
| self.isVisible = isVisible | ||
| self.filterLogicMode = .and | ||
| } | ||
|
|
||
| var hasChanges: Bool { | ||
| !filters.isEmpty || !appliedFilters.isEmpty | ||
| var appliedFilters: [TableFilter] { | ||
| guard let commit else { return [] } | ||
| return Self.resolve(commit, in: filters) | ||
|
Comment on lines
+198
to
+200
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Because Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| static func resolve(_ commit: FilterCommit, in filters: [TableFilter]) -> [TableFilter] { | ||
| switch commit { | ||
| case .all: | ||
| return filters.filter { $0.isEnabled && $0.isValid } | ||
| case .solo(let id): | ||
| guard var match = filters.first(where: { $0.id == id }), match.isValid else { return [] } | ||
| match.isEnabled = true | ||
| return [match] | ||
| } | ||
| } | ||
|
|
||
| var hasAppliedFilters: Bool { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the last action is a per-row Apply, this saves through
saveLastFiltersForActiveTable(), which persiststab.filterState.filters.filter(\.isValid)without the.solo(filter.id)commit. On a later table restore,restoreLastFiltersrebuilds the state withcommit = .all, so a table that was last queried by only one soloed row reopens/apply-rebuilds with every enabled valid row instead. Persist the commit separately or save a last-applied snapshot for solo restores while keeping the full draft panel rows elsewhere.Useful? React with 👍 / 👎.