mirror of
https://github.com/logseq/logseq.git
synced 2026-06-01 19:01:22 +00:00
enhance(ux): iOS search toolbar
This commit is contained in:
@@ -35,7 +35,6 @@
|
||||
D3989CC32ECB0E5700D06615 /* LiquidTabsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3989CBF2ECB0E5700D06615 /* LiquidTabsStore.swift */; };
|
||||
D3989CC42ECB0E5700D06615 /* LiquidTabsPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3989CBD2ECB0E5700D06615 /* LiquidTabsPlugin.swift */; };
|
||||
D3989CC52ECB0E5700D06615 /* NativeNavHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3989CC02ECB0E5700D06615 /* NativeNavHost.swift */; };
|
||||
D3989CC62ECB0E5700D06615 /* SearchTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3989CC12ECB0E5700D06615 /* SearchTabView.swift */; };
|
||||
D3989CC82ECB174A00D06615 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3989CC72ECB174A00D06615 /* SceneDelegate.swift */; };
|
||||
D39D1FE02E7DAFB000C903D1 /* LogseqIntents.swift in Sources */ = {isa = PBXBuildFile; fileRef = D39D1FDF2E7DAFB000C903D1 /* LogseqIntents.swift */; };
|
||||
D39D1FE12E7DAFB000C903D1 /* LogseqIntents.swift in Sources */ = {isa = PBXBuildFile; fileRef = D39D1FDF2E7DAFB000C903D1 /* LogseqIntents.swift */; };
|
||||
@@ -116,7 +115,6 @@
|
||||
D3989CBE2ECB0E5700D06615 /* LiquidTabsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiquidTabsRootView.swift; sourceTree = "<group>"; };
|
||||
D3989CBF2ECB0E5700D06615 /* LiquidTabsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiquidTabsStore.swift; sourceTree = "<group>"; };
|
||||
D3989CC02ECB0E5700D06615 /* NativeNavHost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeNavHost.swift; sourceTree = "<group>"; };
|
||||
D3989CC12ECB0E5700D06615 /* SearchTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTabView.swift; sourceTree = "<group>"; };
|
||||
D3989CC72ECB174A00D06615 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
D39D1FDF2E7DAFB000C903D1 /* LogseqIntents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogseqIntents.swift; sourceTree = "<group>"; };
|
||||
D3D62A09275C92880003FBDC /* FileContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileContainer.swift; sourceTree = "<group>"; };
|
||||
@@ -201,7 +199,6 @@
|
||||
D3989CBE2ECB0E5700D06615 /* LiquidTabsRootView.swift */,
|
||||
D3989CBF2ECB0E5700D06615 /* LiquidTabsStore.swift */,
|
||||
D3989CC02ECB0E5700D06615 /* NativeNavHost.swift */,
|
||||
D3989CC12ECB0E5700D06615 /* SearchTabView.swift */,
|
||||
D39D1FDF2E7DAFB000C903D1 /* LogseqIntents.swift */,
|
||||
A1B2C3D41E2F3A4B5C6D7E8F /* NativePageViewController.swift */,
|
||||
A1B2C3D41E2F3A4B5C6D7E91 /* SharedWebViewController.swift */,
|
||||
@@ -453,7 +450,6 @@
|
||||
D3989CC32ECB0E5700D06615 /* LiquidTabsStore.swift in Sources */,
|
||||
D3989CC42ECB0E5700D06615 /* LiquidTabsPlugin.swift in Sources */,
|
||||
D3989CC52ECB0E5700D06615 /* NativeNavHost.swift in Sources */,
|
||||
D3989CC62ECB0E5700D06615 /* SearchTabView.swift in Sources */,
|
||||
D3989CC82ECB174A00D06615 /* SceneDelegate.swift in Sources */,
|
||||
5FF8632A283B5ADB0047731B /* Utils.swift in Sources */,
|
||||
CBF2D2E22DE95970006338BE /* AppViewController.swift in Sources */,
|
||||
|
||||
@@ -4,46 +4,129 @@ struct LiquidTabsRootView: View {
|
||||
@StateObject private var store = LiquidTabsStore.shared
|
||||
let navController: UINavigationController
|
||||
|
||||
@State private var searchText: String = ""
|
||||
|
||||
// Convenience helpers: first two tabs from CLJS, rest ignored
|
||||
private var firstTab: LiquidTab? {
|
||||
store.tabs.first
|
||||
}
|
||||
|
||||
private var secondTab: LiquidTab? {
|
||||
store.tabs.count > 1 ? store.tabs[1] : nil
|
||||
}
|
||||
|
||||
private var thirdTab: LiquidTab? {
|
||||
store.tabs.count > 2 ? store.tabs[2] : nil
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if #available(iOS 26.0, *) {
|
||||
// iOS 26+: static TabView with a dedicated search tab (role: .search)
|
||||
if store.tabs.isEmpty {
|
||||
// Fallback: just show your existing nav + webview
|
||||
NativeNavHost(navController: navController)
|
||||
.ignoresSafeArea()
|
||||
.ignoresSafeArea()
|
||||
} else {
|
||||
TabView(selection: Binding(
|
||||
get: { store.effectiveSelectedId() },
|
||||
set: { newValue in
|
||||
guard let id = newValue else { return }
|
||||
store.selectedId = id
|
||||
LiquidTabsPlugin.shared?.notifyTabSelected(id: id)
|
||||
TabView {
|
||||
// ---- Tab 1 (normal) ----
|
||||
if let tab = firstTab {
|
||||
Tab {
|
||||
NativeNavHost(navController: navController)
|
||||
.ignoresSafeArea()
|
||||
.onAppear {
|
||||
store.selectedId = tab.id
|
||||
LiquidTabsPlugin.shared?.notifyTabSelected(id: tab.id)
|
||||
}
|
||||
} label: {
|
||||
Label(tab.title, systemImage: tab.systemImage)
|
||||
}
|
||||
}
|
||||
)) {
|
||||
ForEach(store.tabs) { tab in
|
||||
tabView(for: tab)
|
||||
|
||||
// ---- Tab 2 (optional normal) ----
|
||||
if let tab = secondTab {
|
||||
Tab {
|
||||
NativeNavHost(navController: navController)
|
||||
.ignoresSafeArea()
|
||||
.onAppear {
|
||||
store.selectedId = tab.id
|
||||
LiquidTabsPlugin.shared?.notifyTabSelected(id: tab.id)
|
||||
}
|
||||
} label: {
|
||||
Label(tab.title, systemImage: tab.systemImage)
|
||||
}
|
||||
}
|
||||
|
||||
if let tab = thirdTab {
|
||||
Tab {
|
||||
NativeNavHost(navController: navController)
|
||||
.ignoresSafeArea()
|
||||
.onAppear {
|
||||
store.selectedId = tab.id
|
||||
LiquidTabsPlugin.shared?.notifyTabSelected(id: tab.id)
|
||||
}
|
||||
} label: {
|
||||
Label(tab.title, systemImage: tab.systemImage)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Search tab (static, special pill) ----
|
||||
Tab(role: .search) {
|
||||
SearchView(searchText: $searchText)
|
||||
.onAppear {
|
||||
// Use a fixed id for search, or map it from CLJS if you prefer
|
||||
LiquidTabsPlugin.shared?.notifyTabSelected(id: "search")
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
} else {
|
||||
// iOS < 26: fall back to the old dynamic tabItem-based tabs
|
||||
TabView(selection: Binding(
|
||||
get: { store.selectedId ?? firstTab?.id },
|
||||
set: { newValue in
|
||||
guard let id = newValue else { return }
|
||||
store.selectedId = id
|
||||
LiquidTabsPlugin.shared?.notifyTabSelected(id: id)
|
||||
}
|
||||
)) {
|
||||
ForEach(store.tabs) { tab in
|
||||
NativeNavHost(navController: navController)
|
||||
.ignoresSafeArea()
|
||||
.tabItem {
|
||||
Label(tab.title, systemImage: tab.systemImage)
|
||||
}
|
||||
.tag(tab.id as String?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func tabView(for tab: LiquidTab) -> some View {
|
||||
switch tab.role {
|
||||
case .normal:
|
||||
NativeNavHost(navController: navController)
|
||||
.ignoresSafeArea()
|
||||
.tabItem {
|
||||
Label(tab.title, systemImage: tab.systemImage)
|
||||
}
|
||||
.tag(tab.id as String?)
|
||||
|
||||
case .search:
|
||||
SearchTabView()
|
||||
.tabItem {
|
||||
Label(tab.title, systemImage: tab.systemImage)
|
||||
struct SearchView: View {
|
||||
@Binding var searchText: String
|
||||
|
||||
var body: some View {
|
||||
if #available(iOS 26.0, *) {
|
||||
NavigationStack {
|
||||
if #available(iOS 17.0, *) {
|
||||
ContentUnavailableView("Search", systemImage: "magnifyingglass")
|
||||
.navigationTitle("Search")
|
||||
} else {
|
||||
Text("Search")
|
||||
}
|
||||
.tag(tab.id as String?)
|
||||
}
|
||||
.searchable(
|
||||
text: $searchText,
|
||||
placement: .automatic,
|
||||
prompt: "Search"
|
||||
)
|
||||
.searchToolbarBehavior(.minimize) // Liquid behavior on iOS 26
|
||||
.onChange(of: searchText) { newValue in
|
||||
LiquidTabsPlugin.shared?.notifySearchChanged(query: newValue)
|
||||
}
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SearchTabView: View {
|
||||
@State private var searchText: String = ""
|
||||
|
||||
var body: some View {
|
||||
if #available(iOS 26.0, *) {
|
||||
NavigationStack {
|
||||
// Placeholder content – your web app will react to search anyway
|
||||
if #available(iOS 17.0, *) {
|
||||
ContentUnavailableView("Search", systemImage: "magnifyingglass")
|
||||
.navigationTitle("Search")
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
.searchable(
|
||||
text: $searchText,
|
||||
placement: .automatic,
|
||||
prompt: "Search"
|
||||
)
|
||||
.searchToolbarBehavior(.minimize)
|
||||
.onChange(of: searchText) { newValue in
|
||||
LiquidTabsPlugin.shared?.notifySearchChanged(query: newValue)
|
||||
}
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user