mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-07 19:06:11 +00:00
129 lines
4.9 KiB
Swift
129 lines
4.9 KiB
Swift
import HapiClient
|
|
import SwiftUI
|
|
|
|
/// Reusable SwiftUI presentation for ``RemoteDirectoryBrowserModel``.
|
|
struct RemoteDirectoryBrowserView: View {
|
|
@Bindable var model: RemoteDirectoryBrowserModel
|
|
let onSelect: (String) -> Void
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
List {
|
|
if model.roots.count > 1 {
|
|
Section("Workspace Roots") {
|
|
ForEach(model.roots, id: \.self) { root in
|
|
Button {
|
|
model.navigate(to: root)
|
|
} label: {
|
|
Label(root, systemImage: "externaldrive")
|
|
.lineLimit(1)
|
|
.truncationMode(.middle)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Section("Current Directory") {
|
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
HStack(spacing: 4) {
|
|
ForEach(model.breadcrumbs) { breadcrumb in
|
|
Button(breadcrumb.label) {
|
|
model.navigate(to: breadcrumb.path)
|
|
}
|
|
.buttonStyle(.bordered)
|
|
.controlSize(.small)
|
|
}
|
|
}
|
|
}
|
|
Text(model.path)
|
|
.font(.system(.footnote, design: .monospaced))
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(2)
|
|
.truncationMode(.middle)
|
|
Toggle(
|
|
"Show Hidden",
|
|
isOn: Binding(
|
|
get: { model.includeHidden },
|
|
set: { model.setIncludeHidden($0) }
|
|
)
|
|
)
|
|
}
|
|
|
|
Section("Subdirectories") {
|
|
if model.isLoading {
|
|
HStack {
|
|
Spacer()
|
|
ProgressView()
|
|
Spacer()
|
|
}
|
|
} else if let error = model.error {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text(error).foregroundStyle(.red)
|
|
Button("Retry") {
|
|
model.refresh()
|
|
}
|
|
}
|
|
} else if model.entries.isEmpty {
|
|
Text("No subdirectories")
|
|
.foregroundStyle(.secondary)
|
|
} else {
|
|
ForEach(model.entries, id: \.name) { entry in
|
|
Button {
|
|
model.navigateEntry(entry.name)
|
|
} label: {
|
|
HStack {
|
|
Image(systemName: "folder")
|
|
Text(entry.name)
|
|
.lineLimit(1)
|
|
.truncationMode(.middle)
|
|
Spacer()
|
|
Image(systemName: "chevron.right")
|
|
.font(.caption)
|
|
.foregroundStyle(.tertiary)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Choose Directory")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button("Cancel") {
|
|
model.close()
|
|
dismiss()
|
|
}
|
|
}
|
|
ToolbarItem(placement: .confirmationAction) {
|
|
Button("Select") {
|
|
let path = model.path
|
|
onSelect(path)
|
|
model.close()
|
|
dismiss()
|
|
}
|
|
.disabled(model.isLoading || model.error != nil)
|
|
}
|
|
ToolbarItemGroup(placement: .bottomBar) {
|
|
Button {
|
|
model.navigateUp()
|
|
} label: {
|
|
Label("Up", systemImage: "arrow.up")
|
|
}
|
|
.disabled(!model.canGoUp || model.isLoading)
|
|
|
|
Spacer()
|
|
|
|
Button {
|
|
model.refresh()
|
|
} label: {
|
|
Label("Refresh", systemImage: "arrow.clockwise")
|
|
}
|
|
.disabled(model.isLoading)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|