Files
workoutsplus/WorkoutsPlus/Components/AutocompleteTextField.swift

99 lines
2.3 KiB
Swift

//
// AutocompleteTextField.swift
// WorkoutsPlus
//
// Created by Felix Förtsch on 19.09.24.
//
import SwiftUI
struct AutocompleteTextField<Item: Nameable>: View {
var placeholder: String
@Binding var item: Item?
var items: [Item]
@State private var searchText = ""
var filteredItems: [Item] {
if searchText.isEmpty {
return []
} else {
return items.filter { $0.name.localizedCaseInsensitiveContains(searchText) }
}
}
@State private var isAutoCompleteShown: Bool = true
var body: some View {
Group {
HStack {
TextField(placeholder, text: $searchText)
// TODO: Fix "List line" not extending to the full width
if item == nil && !searchText.isEmpty {
Text("NEW")
.font(.caption)
.foregroundStyle(.green)
}
if item != nil {
Text("EDIT")
.font(.caption)
.foregroundStyle(.orange)
}
}
.onAppear() {
if item != nil {
self.searchText = item!.name
}
}
.onChange(of: searchText) {
if filteredItems.count == 1 && filteredItems.first?.name == searchText {
self.item = filteredItems.first
isAutoCompleteShown = false
} else {
self.item = nil
isAutoCompleteShown = true
}
}
if isAutoCompleteShown {
ForEach(filteredItems, id: \.self) { item in
HStack {
Text(item.name)
.foregroundStyle(.gray)
Spacer()
Image(systemName: "arrow.up.left")
.foregroundStyle(.blue)
}
// This .contentShape makes the whole row tappable
.contentShape(Rectangle())
.onTapGesture {
self.item = item
searchText = item.name
isAutoCompleteShown = false
}
}
}
}
}
}
private struct Item: Nameable {
var id = UUID()
var name: String
}
#Preview {
@Previewable @State var item: Item? = nil
VStack {
Text("Preview only: ")
Text(item?.name ?? "No item selected")
}
.background(.red)
List {
AutocompleteTextField<Item>(placeholder: "New Item", item: $item, items: [
Item(name: "Item 1"),
Item(name: "Item 2"),
Item(name: "Item 3")
])
}
}