131 lines
4.0 KiB
Swift
131 lines
4.0 KiB
Swift
import SwiftUI
|
|
|
|
struct MultiFileSelectionView: View {
|
|
|
|
@Environment(\.dismiss)
|
|
private var dismiss
|
|
|
|
@EnvironmentObject
|
|
private var content: Content
|
|
|
|
@Binding
|
|
private var selectedFiles: [FileResource]
|
|
|
|
let allowedType: FileFilterType?
|
|
|
|
let insertSorted: Bool
|
|
|
|
@State
|
|
private var selectedFileType: FileFilterType
|
|
|
|
@State
|
|
private var searchString = ""
|
|
|
|
@State
|
|
private var newSelection: [FileResource]
|
|
|
|
init(selectedFiles: Binding<[FileResource]>, allowedType: FileFilterType? = nil, insertSorted: Bool = false) {
|
|
self._selectedFiles = selectedFiles
|
|
self.newSelection = selectedFiles.wrappedValue
|
|
self.allowedType = allowedType
|
|
self.selectedFileType = allowedType ?? .images
|
|
self.insertSorted = insertSorted
|
|
}
|
|
|
|
private var filesBySelectedType: [FileResource] {
|
|
content.files.filter { selectedFileType.matches($0.type) }
|
|
}
|
|
|
|
private var filteredFiles: [FileResource] {
|
|
guard !searchString.isEmpty else {
|
|
return filesBySelectedType
|
|
}
|
|
return filesBySelectedType.filter { $0.id.contains(searchString) }
|
|
}
|
|
|
|
var body: some View {
|
|
HStack {
|
|
VStack {
|
|
Text("Selected files")
|
|
.font(.title)
|
|
List {
|
|
ForEach(newSelection) { file in
|
|
HStack {
|
|
Image(systemSymbol: .minusCircleFill)
|
|
.foregroundStyle(.red)
|
|
.contentShape(Rectangle())
|
|
.onTapGesture { deselect(file: file) }
|
|
Text(file.id)
|
|
Spacer()
|
|
}
|
|
}
|
|
.onMove(perform: moveSelectedFile)
|
|
}
|
|
HStack {
|
|
Button("Cancel") {
|
|
DispatchQueue.main.async {
|
|
dismiss()
|
|
}
|
|
}
|
|
Button("Save") {
|
|
selectedFiles = newSelection
|
|
dismiss()
|
|
}
|
|
}
|
|
}
|
|
VStack {
|
|
Picker("", selection: $selectedFileType) {
|
|
ForEach(FileFilterType.allCases) { type in
|
|
Text(type.text).tag(type)
|
|
}
|
|
}
|
|
.pickerStyle(.segmented)
|
|
.padding(.trailing, 7)
|
|
.disabled(allowedType != nil)
|
|
TextField("", text: $searchString, prompt: Text("Search"))
|
|
.textFieldStyle(.roundedBorder)
|
|
.padding(.horizontal, 8)
|
|
List(filteredFiles) { file in
|
|
HStack {
|
|
if newSelection.contains(file) {
|
|
Image(systemSymbol: .checkmarkCircleFill)
|
|
.foregroundStyle(.gray)
|
|
} else {
|
|
Image(systemSymbol: .plusCircleFill)
|
|
.foregroundStyle(.green)
|
|
}
|
|
Text(file.id)
|
|
Spacer()
|
|
}
|
|
.contentShape(Rectangle())
|
|
.onTapGesture { select(file: file) }
|
|
}
|
|
}
|
|
}
|
|
.frame(minHeight: 500, idealHeight: 600)
|
|
.padding()
|
|
}
|
|
|
|
private func deselect(file: FileResource) {
|
|
guard let index = newSelection.firstIndex(of: file) else {
|
|
return
|
|
}
|
|
newSelection.remove(at: index)
|
|
}
|
|
|
|
private func select(file: FileResource) {
|
|
guard !newSelection.contains(file) else {
|
|
return
|
|
}
|
|
guard insertSorted else {
|
|
newSelection.append(file)
|
|
return
|
|
}
|
|
newSelection.insertSorted(file)
|
|
}
|
|
|
|
private func moveSelectedFile(from source: IndexSet, to destination: Int) {
|
|
newSelection.move(fromOffsets: source, toOffset: destination)
|
|
}
|
|
}
|