diff --git a/Sources/Playbook/Components/Typeahead/PBTypeahead.swift b/Sources/Playbook/Components/Typeahead/PBTypeahead.swift index 4978a654..285f1098 100644 --- a/Sources/Playbook/Components/Typeahead/PBTypeahead.swift +++ b/Sources/Playbook/Components/Typeahead/PBTypeahead.swift @@ -13,8 +13,8 @@ public struct PBTypeahead: View { private let id: Int private let title: String private let placeholder: String - private let options: [Typeahead.Option] - private let selection: Typeahead.Selection + private let options: [PBTypeahead.Option] + private let selection: PBTypeahead.Selection private let noOptionsText: String private let debounce: (time: TimeInterval, numberOfCharacters: Int) private let dropdownMaxHeight: CGFloat? @@ -24,12 +24,12 @@ public struct PBTypeahead: View { @State private var showList: Bool = false @State private var hoveringIndex: Int? - @State private var hoveringOption: Typeahead.Option? + @State private var hoveringOption: PBTypeahead.Option? @State private var isHovering: Bool = false @State private var contentSize: CGSize = .zero @State private var selectedIndex: Int? @State private var focused: Bool = false - @Binding var selectedOptions: [Typeahead.Option] + @Binding var selectedOptions: [PBTypeahead.Option] @Binding var searchText: String @FocusState.Binding private var isFocused: Bool @@ -38,13 +38,13 @@ public struct PBTypeahead: View { title: String, placeholder: String = "Select", searchText: Binding, - options: [Typeahead.Option], - selection: Typeahead.Selection, + options: [PBTypeahead.Option], + selection: PBTypeahead.Selection, debounce: (time: TimeInterval, numberOfCharacters: Int) = (0, 0), dropdownMaxHeight: CGFloat? = nil, listOffset: (x: CGFloat, y: CGFloat) = (0, 0), isFocused: FocusState.Binding, - selectedOptions: Binding<[Typeahead.Option]>, + selectedOptions: Binding<[PBTypeahead.Option]>, clearAction: (() -> Void)? = nil, noOptionsText: String = "No options" ) { @@ -144,7 +144,7 @@ private extension PBTypeahead { .frame(maxWidth: .infinity, alignment: .top) } - func listItemView(index: Int, option: Typeahead.Option) -> some View { + func listItemView(index: Int, option: PBTypeahead.Option) -> some View { HStack { if option.text == noOptionsText { emptyView @@ -182,7 +182,7 @@ private extension PBTypeahead { } } - var searchResults: [Typeahead.Option] { + var searchResults: [PBTypeahead.Option] { let filteredOptions = searchText.isEmpty && debounce.numberOfCharacters == 0 ? options : options.filter { if let text = $0.text { return text.localizedCaseInsensitiveContains(searchText) @@ -193,8 +193,8 @@ private extension PBTypeahead { let selectedIds = Set(selectedOptions.map { $0.id }) let filteredSelectedOptions = filteredOptions.filter { !selectedIds.contains($0.id) } switch selection{ - case .multiple: return filteredSelectedOptions.isEmpty ? [Typeahead.Option(id: "", text: noOptionsText, customView: nil)] : filteredSelectedOptions - case .single: return filteredOptions.isEmpty ? [Typeahead.Option(id: "", text: noOptionsText, customView: nil)] : filteredOptions + case .multiple: return filteredSelectedOptions.isEmpty ? [PBTypeahead.Option(id: "", text: noOptionsText, customView: nil)] : filteredSelectedOptions + case .single: return filteredOptions.isEmpty ? [PBTypeahead.Option(id: "", text: noOptionsText, customView: nil)] : filteredOptions } } @@ -232,7 +232,7 @@ private extension PBTypeahead { } } - func onListSelection(index: Int, option: Typeahead.Option) { + func onListSelection(index: Int, option: PBTypeahead.Option) { if showList { switch selection { case .single: @@ -245,7 +245,7 @@ private extension PBTypeahead { searchText = "" } - func onSingleSelection(index: Int, _ option: Typeahead.Option) { + func onSingleSelection(index: Int, _ option: PBTypeahead.Option) { selectedOptions.removeAll() selectedOptions = [option] selectedIndex = index @@ -253,7 +253,7 @@ private extension PBTypeahead { selectedOptions.append(option) } - func onMultipleSelection(_ option: Typeahead.Option) { + func onMultipleSelection(_ option: PBTypeahead.Option) { selectedOptions.append(option) hoveringIndex = nil selectedIndex = nil diff --git a/Sources/Playbook/Components/Typeahead/PBTypeaheadTemplate.swift b/Sources/Playbook/Components/Typeahead/PBTypeaheadTemplate.swift index 2f41234d..5deeaf26 100644 --- a/Sources/Playbook/Components/Typeahead/PBTypeaheadTemplate.swift +++ b/Sources/Playbook/Components/Typeahead/PBTypeaheadTemplate.swift @@ -13,8 +13,8 @@ public struct PBTypeaheadTemplate: View { private let id: Int private let title: String private let placeholder: String - private var options: [Typeahead.OptionType] - private let selection: Typeahead.Selection + private var options: [PBTypeahead.OptionType] + private let selection: PBTypeahead.Selection private let debounce: (time: TimeInterval, numberOfCharacters: Int) private let dropdownMaxHeight: CGFloat? private let listOffset: (x: CGFloat, y: CGFloat) @@ -23,13 +23,13 @@ public struct PBTypeaheadTemplate: View { @State private var showList: Bool = false @State private var hoveringIndex: (Int?, String?) - @State private var hoveringOption: Typeahead.Option? + @State private var hoveringOption: PBTypeahead.Option? @State private var isHovering: Bool = false @State private var contentSize: CGSize = .zero @State private var selectedIndex: Int? @State private var focused: Bool = false @State var numberOfItemsShown: [String?: Int] = [:] - @Binding var selectedOptions: [Typeahead.Option] + @Binding var selectedOptions: [PBTypeahead.Option] @Binding var searchText: String @FocusState.Binding private var isFocused: Bool @@ -38,13 +38,13 @@ public struct PBTypeaheadTemplate: View { title: String, placeholder: String = "Select", searchText: Binding, - options: [Typeahead.OptionType], - selection: Typeahead.Selection, + options: [PBTypeahead.OptionType], + selection: PBTypeahead.Selection, debounce: (time: TimeInterval, numberOfCharacters: Int) = (0, 0), dropdownMaxHeight: CGFloat? = nil, listOffset: (x: CGFloat, y: CGFloat) = (0, 0), isFocused: FocusState.Binding, - selectedOptions: Binding<[Typeahead.Option]>, + selectedOptions: Binding<[PBTypeahead.Option]>, clearAction: (() -> Void)? = nil ) { self.id = id @@ -152,7 +152,7 @@ private extension PBTypeaheadTemplate { .padding(.leading) } - func listItemView(item: Typeahead.Option, index: Int, for section: String?) -> some View { + func listItemView(item: PBTypeahead.Option, index: Int, for section: String?) -> some View { HStack { if let customView = item.customView?() { customView @@ -175,10 +175,10 @@ private extension PBTypeaheadTemplate { } } - var mapResults: [(String?, [Typeahead.Option], PBButton)] { - var array: [(String?, [Typeahead.Option], PBButton)] = [] + var mapResults: [(String?, [PBTypeahead.Option], PBButton)] { + var array: [(String?, [PBTypeahead.Option], PBButton)] = [] var currentSection: String? = nil - var currentOptions: [Typeahead.Option] = [] + var currentOptions: [PBTypeahead.Option] = [] for result in searchResults { switch result { case .section(let section): @@ -208,8 +208,8 @@ private extension PBTypeaheadTemplate { private func appendSectionToArray( section: String?, - options: [Typeahead.Option], - to array: inout [(String?, [Typeahead.Option], PBButton)] + options: [PBTypeahead.Option], + to array: inout [(String?, [PBTypeahead.Option], PBButton)] ) { let numberOfItems = numberOfItemsShown[section] ?? 2 array.append(( @@ -226,7 +226,7 @@ private extension PBTypeaheadTemplate { )) } - var searchResults: [Typeahead.OptionType] { + var searchResults: [PBTypeahead.OptionType] { let filteredOptions = searchText.isEmpty && debounce.numberOfCharacters == 0 ? options : options.filter { switch $0 { case .item(let item): @@ -334,7 +334,7 @@ private extension PBTypeaheadTemplate { } } - func onListSelection(index: Int, section: String?, option: Typeahead.Option) { + func onListSelection(index: Int, section: String?, option: PBTypeahead.Option) { if showList { switch selection { case .single: @@ -347,7 +347,7 @@ private extension PBTypeaheadTemplate { searchText = "" } - func onSingleSelection(index: Int, section: String?, _ option: Typeahead.Option) { + func onSingleSelection(index: Int, section: String?, _ option: PBTypeahead.Option) { selectedOptions.removeAll() if hoveringIndex.0 == index && hoveringIndex.1 == section { selectedOptions.append(option) @@ -356,7 +356,7 @@ private extension PBTypeaheadTemplate { hoveringIndex = (index, section) } - func onMultipleSelection(index: Int, section: String?, _ option: Typeahead.Option) { + func onMultipleSelection(index: Int, section: String?, _ option: PBTypeahead.Option) { if hoveringIndex.0 == index && hoveringIndex.1 == section { selectedOptions.append(option) } diff --git a/Sources/Playbook/Components/Typeahead/Typeahead.swift b/Sources/Playbook/Components/Typeahead/Typeahead.swift index d43f27ce..b0b022e8 100644 --- a/Sources/Playbook/Components/Typeahead/Typeahead.swift +++ b/Sources/Playbook/Components/Typeahead/Typeahead.swift @@ -9,15 +9,25 @@ import SwiftUI -public enum Typeahead { - public struct Option: Identifiable, Equatable { +public extension PBTypeahead { + struct Option: Identifiable, Equatable { public let id: String public let text: String? public let customView: (() -> AnyView?)? public static func == (lhs: Option, rhs: Option) -> Bool { lhs.id == rhs.id } + public init(id: String, text: String?, customView: ( () -> AnyView?)? = nil) { + self.id = id + self.text = text + self.customView = customView + } + public init(_ id: String, _ text: String? = nil, _ customView: ( () -> AnyView?)? = nil) { + self.id = id + self.text = text + self.customView = customView + } } - public enum OptionType: Identifiable { + enum OptionType: Identifiable { public var id: String { switch self { case .section(let str): @@ -30,7 +40,7 @@ public enum Typeahead { case item(Option) } - public enum Selection { + enum Selection { case single, multiple(variant: GridInputField.Selection.Variant) func selectedOptions(options: [String], placeholder: String) -> GridInputField.Selection { diff --git a/Sources/Playbook/Components/Typeahead/TypeaheadCatalog.swift b/Sources/Playbook/Components/Typeahead/TypeaheadCatalog.swift index 227230df..e900097c 100644 --- a/Sources/Playbook/Components/Typeahead/TypeaheadCatalog.swift +++ b/Sources/Playbook/Components/Typeahead/TypeaheadCatalog.swift @@ -12,21 +12,21 @@ import SwiftUI public struct TypeaheadCatalog: View { private var assetsColors = Mocks.assetsColors @State private var searchTextColors: String = "" - @State private var selectedColors: [Typeahead.Option] = [Mocks.assetsColors[2]] + @State private var selectedColors: [PBTypeahead.Option] = [Mocks.assetsColors[2]] @FocusState private var isFocusedColors private var assetsUsers = Mocks.assetesMultipleUsers @State private var searchTextUsers: String = "" - @State private var selectedUsers: [Typeahead.Option] = [Mocks.assetesMultipleUsers[0], Mocks.assetesMultipleUsers[1]] + @State private var selectedUsers: [PBTypeahead.Option] = [Mocks.assetesMultipleUsers[0], Mocks.assetesMultipleUsers[1]] @FocusState private var isFocusedUsers @State private var searchTextHeight: String = "" - @State private var selectedHeight: [Typeahead.Option] = [Mocks.assetesMultipleUsers[3], Mocks.assetesMultipleUsers[2]] + @State private var selectedHeight: [PBTypeahead.Option] = [Mocks.assetesMultipleUsers[3], Mocks.assetesMultipleUsers[2]] @FocusState private var isFocusedHeight - private var assetsSection: [Typeahead.OptionType] = Mocks.assetsSectionUsers + private var assetsSection: [PBTypeahead.OptionType] = Mocks.assetsSectionUsers @State private var searchTextSections: String = "" - @State private var selectedSections: [Typeahead.Option] = [] + @State private var selectedSections: [PBTypeahead.Option] = [] @FocusState private var isFocusedSection @State private var presentDialog: Bool = false @@ -131,7 +131,7 @@ extension TypeaheadCatalog { @State private var isLoading: Bool = false @State private var searchTextUsers: String = "" @State private var assetsUsers = Mocks.assetesMultipleUsers - @State private var selectedUsers: [Typeahead.Option] = [ + @State private var selectedUsers: [PBTypeahead.Option] = [ Mocks.assetesMultipleUsers[0], Mocks.assetesMultipleUsers[1] ] diff --git a/Sources/Playbook/Resources/Helper Files/Mocks.swift b/Sources/Playbook/Resources/Helper Files/Mocks.swift index caf71474..44b1a5d2 100644 --- a/Sources/Playbook/Resources/Helper Files/Mocks.swift +++ b/Sources/Playbook/Resources/Helper Files/Mocks.swift @@ -28,21 +28,21 @@ enum Mocks { static let picAnna = PBAvatar(image: Image("Anna", bundle: .module), size: .xSmall, status: .online) static let picPatric = PBAvatar(image: Image("Pat", bundle: .module), size: .xSmall) static let picLuccile = PBAvatar(image: Image("Lu", bundle: .module), size: .xSmall) - static let assetsColors: [Typeahead.Option] = [ + static let assetsColors: [PBTypeahead.Option] = [ .init(id: "1", text: "Orange", customView: nil), .init(id: "2", text: "Red", customView: nil), .init(id: "3", text: "Blue", customView: nil), .init(id: "4", text: "Pink", customView: nil), .init(id: "5", text: "Magenta", customView: nil) ] - static let assetesMultipleUsers: [Typeahead.Option] = [ + static let assetesMultipleUsers: [PBTypeahead.Option] = [ .init(id: "1", text: andrew.name, customView: { AnyView(andrew) }), .init(id: "2", text: ana.name, customView: { AnyView(ana) }), .init(id: "3", text: patric.name, customView: { AnyView(patric) }), .init(id: "4", text: luccile.name, customView: { AnyView(luccile) }) ] - static let assetsSectionUsers: [Typeahead.OptionType] = [ + static let assetsSectionUsers: [PBTypeahead.OptionType] = [ .section("section 1"), .item(.init(id: "1", text: andrew.name, customView: { AnyView(andrew) })), .item(.init(id: "2", text: ana.name, customView: { AnyView(ana) })),