From 4e12df6d32fc8e0e40bc83bdf3ba60fd26127b40 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Mon, 9 Mar 2026 16:43:13 +0700 Subject: [PATCH] refactor: replace custom sidebar search field with native .searchable() --- TablePro.xcodeproj/project.pbxproj | 12 ++-- .../xcshareddata/xcschemes/TablePro.xcscheme | 2 +- TablePro/ContentView.swift | 13 +++++ TablePro/Resources/Localizable.xcstrings | 1 + TablePro/Views/Sidebar/SidebarView.swift | 55 +------------------ 5 files changed, 22 insertions(+), 61 deletions(-) diff --git a/TablePro.xcodeproj/project.pbxproj b/TablePro.xcodeproj/project.pbxproj index 62045dc25..e8182bf4f 100644 --- a/TablePro.xcodeproj/project.pbxproj +++ b/TablePro.xcodeproj/project.pbxproj @@ -24,12 +24,6 @@ 5A867000D00000000 /* RedisDriver.tableplugin in Copy Plug-Ins */ = {isa = PBXBuildFile; fileRef = 5A867000100000000 /* RedisDriver.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 5A868000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; }; 5A868000D00000000 /* PostgreSQLDriver.tableplugin in Copy Plug-Ins */ = {isa = PBXBuildFile; fileRef = 5A868000100000000 /* PostgreSQLDriver.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 5ACE00012F4F000000000004 /* CodeEditSourceEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F000000000002 /* CodeEditSourceEditor */; }; - 5ACE00012F4F000000000005 /* CodeEditLanguages in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F000000000003 /* CodeEditLanguages */; }; - 5ACE00012F4F000000000006 /* CodeEditTextView in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F000000000007 /* CodeEditTextView */; }; - 5ACE00012F4F00000000000A /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F000000000009 /* Sparkle */; }; - 5ACE00012F4F00000000000D /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F00000000000C /* MarkdownUI */; }; - 5AEE5B362F5C9B7B00FA84D7 /* OracleNIO in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F00000000000F /* OracleNIO */; }; 5A86A000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; }; 5A86A000D00000000 /* CSVExport.tableplugin in Copy Plug-Ins */ = {isa = PBXBuildFile; fileRef = 5A86A000100000000 /* CSVExport.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 5A86B000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; }; @@ -40,6 +34,12 @@ 5A86D000D00000000 /* XLSXExport.tableplugin in Copy Plug-Ins */ = {isa = PBXBuildFile; fileRef = 5A86D000100000000 /* XLSXExport.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 5A86E000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; }; 5A86E000D00000000 /* MQLExport.tableplugin in Copy Plug-Ins */ = {isa = PBXBuildFile; fileRef = 5A86E000100000000 /* MQLExport.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 5ACE00012F4F000000000004 /* CodeEditSourceEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F000000000002 /* CodeEditSourceEditor */; }; + 5ACE00012F4F000000000005 /* CodeEditLanguages in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F000000000003 /* CodeEditLanguages */; }; + 5ACE00012F4F000000000006 /* CodeEditTextView in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F000000000007 /* CodeEditTextView */; }; + 5ACE00012F4F00000000000A /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F000000000009 /* Sparkle */; }; + 5ACE00012F4F00000000000D /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F00000000000C /* MarkdownUI */; }; + 5AEE5B362F5C9B7B00FA84D7 /* OracleNIO in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F00000000000F /* OracleNIO */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ diff --git a/TablePro.xcodeproj/xcshareddata/xcschemes/TablePro.xcscheme b/TablePro.xcodeproj/xcshareddata/xcschemes/TablePro.xcscheme index f99c67cbd..a2d8da8aa 100644 --- a/TablePro.xcodeproj/xcshareddata/xcschemes/TablePro.xcscheme +++ b/TablePro.xcodeproj/xcshareddata/xcschemes/TablePro.xcscheme @@ -1,7 +1,7 @@ + version = "1.8"> Binding { + let state = SharedSidebarState.forConnection(connectionId) + return Binding( + get: { state.searchText }, + set: { state.searchText = $0 } + ) + } + private var sessionTableOperationOptionsBinding: Binding<[String: TableOperationOptions]> { createSessionBinding( get: { $0.tableOperationOptions }, diff --git a/TablePro/Resources/Localizable.xcstrings b/TablePro/Resources/Localizable.xcstrings index bfcd5a151..06a3e6a88 100644 --- a/TablePro/Resources/Localizable.xcstrings +++ b/TablePro/Resources/Localizable.xcstrings @@ -1810,6 +1810,7 @@ } }, "Clear table filter" : { + "extractionState" : "stale", "localizations" : { "vi" : { "stringUnit" : { diff --git a/TablePro/Views/Sidebar/SidebarView.swift b/TablePro/Views/Sidebar/SidebarView.swift index bd37a9a8b..ed6347906 100644 --- a/TablePro/Views/Sidebar/SidebarView.swift +++ b/TablePro/Views/Sidebar/SidebarView.swift @@ -12,10 +12,6 @@ import SwiftUI /// Sidebar view displaying list of database tables struct SidebarView: View { @State private var viewModel: SidebarViewModel - /// Local search text for responsive typing; synced to/from shared state - @State private var localSearchText: String = "" - /// Debounce task for writing local search text to the shared state - @State private var searchSyncTask: Task? // Keep @Binding on the view for SwiftUI change tracking. // The ViewModel stores the same bindings for write access. @@ -56,7 +52,6 @@ struct SidebarView: View { ) { _tables = tables self.sidebarState = sidebarState - _localSearchText = State(initialValue: sidebarState.searchText) _pendingTruncates = pendingTruncates _pendingDeletes = pendingDeletes let selectedBinding = Binding( @@ -84,28 +79,11 @@ struct SidebarView: View { var body: some View { VStack(alignment: .leading, spacing: 0) { - if !tables.isEmpty { - searchField - } content } .frame(minWidth: 280) - .onChange(of: localSearchText) { _, newValue in - viewModel.debouncedSearchText = newValue - searchSyncTask?.cancel() - searchSyncTask = Task { @MainActor in - try? await Task.sleep(nanoseconds: 150_000_000) - guard !Task.isCancelled else { return } - if sidebarState.searchText != newValue { - sidebarState.searchText = newValue - } - } - } .onChange(of: sidebarState.searchText) { _, newValue in - if localSearchText != newValue { - localSearchText = newValue - viewModel.debouncedSearchText = newValue - } + viewModel.debouncedSearchText = newValue } .onChange(of: tables) { _, newTables in let hasSession = DatabaseManager.shared.activeSessions[connectionId] != nil @@ -135,37 +113,6 @@ struct SidebarView: View { } } - // MARK: - Search Field - - private var searchField: some View { - HStack(spacing: 6) { - Image(systemName: "magnifyingglass") - .foregroundStyle(.secondary) - .font(.system(size: DesignConstants.FontSize.medium)) - - TextField("Filter", text: $localSearchText) - .textFieldStyle(.plain) - .font(.system(size: DesignConstants.FontSize.body)) - - if !localSearchText.isEmpty { - Button(action: { localSearchText = "" }) { - Image(systemName: "xmark.circle.fill") - .foregroundStyle(.secondary) - .font(.system(size: DesignConstants.FontSize.medium)) - } - .buttonStyle(.plain) - .accessibilityLabel(String(localized: "Clear table filter")) - } - } - .padding(.horizontal, 8) - .padding(.vertical, DesignConstants.Spacing.xxs) - .background(Color(nsColor: .quaternaryLabelColor)) - .cornerRadius(6) - .padding(.horizontal, 10) - .padding(.top, 8) - .padding(.bottom, 4) - } - // MARK: - Content States @ViewBuilder