From 3f47cea2045b318b45a252077f74aa75f37acb81 Mon Sep 17 00:00:00 2001 From: "isis.silva" Date: Wed, 30 Oct 2024 10:11:29 -0400 Subject: [PATCH 1/6] wip --- .../Components/Typeahead/PBTypeahead.swift | 8 +- .../Typeahead/PBTypeaheadTemplate.swift | 10 +- .../Typeahead/TypeaheadCatalog.swift | 103 ++++++++---------- .../Resources/Helper Files/Mocks.swift | 30 +++-- 4 files changed, 75 insertions(+), 76 deletions(-) diff --git a/Sources/Playbook/Components/Typeahead/PBTypeahead.swift b/Sources/Playbook/Components/Typeahead/PBTypeahead.swift index f24ebfb9..8948ff8d 100644 --- a/Sources/Playbook/Components/Typeahead/PBTypeahead.swift +++ b/Sources/Playbook/Components/Typeahead/PBTypeahead.swift @@ -14,6 +14,7 @@ public struct PBTypeahead: View { private let id: Int private let title: String private let placeholder: String + private let options: [Option] private let selection: Selection private let noOptionsText: String private let debounce: (time: TimeInterval, numberOfCharacters: Int) @@ -23,14 +24,12 @@ public struct PBTypeahead: View { private let popoverManager = PopoverManager() @State private var showList: Bool = false - @State private var isCollapsed = false @State private var hoveringIndex: Int? @State private var hoveringOption: Option? @State private var isHovering: Bool = false @State private var contentSize: CGSize = .zero @State private var selectedIndex: Int? @State private var focused: Bool = false - let options: [Option] @Binding var selectedOptions: [Option] @Binding var searchText: String @FocusState.Binding private var isFocused: Bool @@ -136,7 +135,7 @@ private extension PBTypeahead { ScrollView { VStack(spacing: 0) { ForEach(Array(zip(searchResults.indices, searchResults)), id: \.0) { index, result in - listCell(index: index, option: result) + listItemView(index: index, option: result) } } } @@ -146,10 +145,9 @@ private extension PBTypeahead { .frame(maxWidth: .infinity, alignment: .top) } .frame(maxWidth: .infinity, alignment: .top) - .transition(.opacity) } - func listCell(index: Int, option: Option) -> some View { + func listItemView(index: Int, option: Option) -> some View { HStack { if option.0 == noOptionsText { emptyView diff --git a/Sources/Playbook/Components/Typeahead/PBTypeaheadTemplate.swift b/Sources/Playbook/Components/Typeahead/PBTypeaheadTemplate.swift index b1a51a6a..2a1ebf1b 100644 --- a/Sources/Playbook/Components/Typeahead/PBTypeaheadTemplate.swift +++ b/Sources/Playbook/Components/Typeahead/PBTypeaheadTemplate.swift @@ -14,14 +14,15 @@ public struct PBTypeaheadTemplate: View { private let id: Int private let title: String private let placeholder: String + private var options: [OptionType] private let selection: Selection private let debounce: (time: TimeInterval, numberOfCharacters: Int) private let dropdownMaxHeight: CGFloat? private let listOffset: (x: CGFloat, y: CGFloat) - private let popoverManager = PopoverManager() private let clearAction: (() -> Void)? + private let popoverManager = PopoverManager() + @State private var showList: Bool = false - @State private var isCollapsed = false @State private var hoveringIndex: (Int?, String?) @State private var hoveringOption: Option? @State private var isHovering: Bool = false @@ -29,7 +30,6 @@ public struct PBTypeaheadTemplate: View { @State private var selectedIndex: Int? @State private var focused: Bool = false @State var numberOfItemsShown: [String?: Int] = [:] - @Binding var options: [OptionType] @Binding var selectedOptions: [Option] @Binding var searchText: String @FocusState.Binding private var isFocused: Bool @@ -39,7 +39,7 @@ public struct PBTypeaheadTemplate: View { title: String, placeholder: String = "Select", searchText: Binding, - options: Binding<[OptionType]>, + options: [OptionType], selection: Selection, debounce: (time: TimeInterval, numberOfCharacters: Int) = (0, 0), dropdownMaxHeight: CGFloat? = nil, @@ -53,7 +53,7 @@ public struct PBTypeaheadTemplate: View { self.placeholder = placeholder self._searchText = searchText self.selection = selection - self._options = options + self.options = options self.debounce = debounce self.dropdownMaxHeight = dropdownMaxHeight self.listOffset = listOffset diff --git a/Sources/Playbook/Components/Typeahead/TypeaheadCatalog.swift b/Sources/Playbook/Components/Typeahead/TypeaheadCatalog.swift index 81f2eb6d..b8097e6d 100644 --- a/Sources/Playbook/Components/Typeahead/TypeaheadCatalog.swift +++ b/Sources/Playbook/Components/Typeahead/TypeaheadCatalog.swift @@ -10,46 +10,33 @@ import SwiftUI public struct TypeaheadCatalog: View { - @State private var assetsColors = Mocks.assetsColors - @State private var selectedColors: [(String, (String, (() -> AnyView?)?)?)] = [] - @State private var selectedAssetsColors: [(String, (String, (() -> AnyView?)?)?)] = [] - @State private var assetsUsers = Mocks.multipleUsersDictionary - @State private var selectedUsers: [(String, (String, (() -> PBUser?)?)?)] = [ - ("1", (Mocks.andrew.name, { Mocks.andrew })), - ("2", (Mocks.ana.name, { Mocks.ana })) - ] - @State private var selectedUsers1: [(String, (String, (() -> PBUser?)?)?)] = [ - ("1", (Mocks.andrew.name, { Mocks.andrew })), - ("2", (Mocks.ana.name, { Mocks.ana })) - ] - @State private var selectedSections: [(String, (String, (() -> PBUser?)?)?)] = [] - @State private var searchTextUsers: String = "" - @State private var searchTextUsers1: String = "" + private var assetsColors = Mocks.assetsColors @State private var searchTextColors: String = "" + @State private var selectedColors: [(String, (String, (() -> AnyView?)?)?)] = [Mocks.assetsColors.first!] + @FocusState private var isFocusedColors + + private var assetsUsers = Mocks.assetesMultipleUsers + @State private var searchTextUsers: String = "" + @State private var selectedUsers: [(String, (String, (() -> PBUser?)?)?)] = [Mocks.assetesMultipleUsers[0], Mocks.assetesMultipleUsers[1]] + @FocusState private var isFocusedUsers + + @State private var searchTextHeight: String = "" + @State private var selectedHeight: [(String, (String, (() -> PBUser?)?)?)] = [Mocks.assetesMultipleUsers[3], Mocks.assetesMultipleUsers[2]] + @FocusState private var isFocusedHeight + + private var assetsSection: [PBTypeaheadTemplate.OptionType] = Mocks.assetsSectionUsers @State private var searchTextSections: String = "" - @State private var searchText: String = "" - @State private var searchTextDebounce: String = "" + @State private var selectedSections: [(String, (String, (() -> PBUser?)?)?)] = [] + @FocusState private var isFocusedSection + + @State private var didTapOutside: Bool? = false @State private var isPresented1: Bool = false @State private var presentDialog: Bool = false @State private var isLoading: Bool = false - @State private var assetsUser = Mocks.multipleUsersDictionary - @FocusState var isFocused1 - @FocusState var isFocused2 - @FocusState var isFocused3 - @FocusState var isFocused4 - @State private var sectionUsers: [PBTypeaheadTemplate.OptionType] = [ - .section("section 1"), - .item(("1", (Mocks.andrew.name, { Mocks.andrew }))), - .item(("2", (Mocks.ana.name, { Mocks.ana }))), - .item(("3", (Mocks.patric.name, { Mocks.patric }))), - .item(("4", (Mocks.luccile.name, { Mocks.luccile }))), - .section("section 2"), - .item(("1", (Mocks.andrew.name, { Mocks.andrew }))), - .item(("2", (Mocks.ana.name, { Mocks.ana }))), - .item(("3", (Mocks.patric.name, { Mocks.patric }))), - .item(("4", (Mocks.luccile.name, { Mocks.luccile }))) - ] + + + var popoverManager = PopoverManager() public var body: some View { @@ -65,10 +52,10 @@ public struct TypeaheadCatalog: View { } .onTapGesture { - isFocused1 = false - isFocused2 = false - isFocused3 = false - isFocused4 = false + isFocusedColors = false + isFocusedUsers = false + isFocusedHeight = false + isFocusedSection = false } .popoverHandler(id: 1) .popoverHandler(id: 2) @@ -85,7 +72,7 @@ extension TypeaheadCatalog { searchText: $searchTextColors, options: assetsColors, selection: .single, - isFocused: $isFocused1, + isFocused: $isFocusedColors, selectedOptions: $selectedColors ) } @@ -98,7 +85,7 @@ extension TypeaheadCatalog { searchText: $searchTextUsers, options: assetsUsers, selection: .multiple(variant: .pill), - isFocused: $isFocused2, + isFocused: $isFocusedUsers, selectedOptions: $selectedUsers ) } @@ -108,12 +95,24 @@ extension TypeaheadCatalog { id: 3, title: "Users", placeholder: "type the name of a user", - searchText: $searchTextUsers1, + searchText: $searchTextHeight, options: assetsUsers, selection: .multiple(variant: .pill), dropdownMaxHeight: 150, - isFocused: $isFocused3, - selectedOptions: $selectedUsers1 + isFocused: $isFocusedHeight, + selectedOptions: $selectedHeight + ) + } + + var sections: some View { + PBTypeaheadTemplate( + id: 4, + title: "Sections", + searchText: $searchTextSections, + options: assetsSection, + selection: .multiple(variant: .pill), + isFocused: $isFocusedSection, + selectedOptions: $selectedSections ) } @@ -124,25 +123,13 @@ extension TypeaheadCatalog { } .presentationMode(isPresented: $presentDialog) { DialogView(isPresented: $presentDialog) - .popoverHandler(id: 4) + .popoverHandler(id: 5) #if os(macOS) .frame(minWidth: 500, minHeight: 390) #endif } } - var sections: some View { - PBTypeaheadTemplate( - id: 4, - title: "Sections", - searchText: $searchTextSections, - options: $sectionUsers, - selection: .multiple(variant: .pill), - isFocused: $isFocused4, - selectedOptions: $selectedSections - ) - } - func closeToast() { presentDialog = false } @@ -151,7 +138,7 @@ extension TypeaheadCatalog { @Binding var isPresented: Bool @State private var isLoading: Bool = false @State private var searchTextUsers: String = "" - @State private var assetsUsers = Mocks.multipleUsersDictionary + @State private var assetsUsers = Mocks.assetesMultipleUsers @State private var selectedUsers: [(String, (String, (() -> PBUser?)?)?)] = [ ("1", (Mocks.andrew.name, { Mocks.andrew })), ("2", (Mocks.ana.name, { Mocks.ana })) @@ -165,7 +152,7 @@ extension TypeaheadCatalog { shouldCloseOnOverlay: false) { VStack { PBTypeahead( - id: 4, + id: 5, title: "Users", placeholder: "type the name of a user", searchText: $searchTextUsers, diff --git a/Sources/Playbook/Resources/Helper Files/Mocks.swift b/Sources/Playbook/Resources/Helper Files/Mocks.swift index ec66e975..6e1bce01 100644 --- a/Sources/Playbook/Resources/Helper Files/Mocks.swift +++ b/Sources/Playbook/Resources/Helper Files/Mocks.swift @@ -19,16 +19,11 @@ enum Mocks { static let twoUsers = [andrew, ana] static let threeUsers = [andrew, ana, patric] static let multipleUsers = [andrew, ana, patric, luccile] - static let multipleUsersDictionary: [(String, (String, (() -> PBUser?)?)?)] = [ - ("1", (andrew.name, { andrew })), - ("2", (ana.name, { ana })), - ("3", (patric.name, { patric })), - ("4", (luccile.name, { luccile })) - ] + static let avatarXSmall = PBAvatar(image: Image("andrew", bundle: .module), size: .xSmall) static let avatarXSmallStatus = PBAvatar(image: Image("andrew", bundle: .module), size: .xSmall, status: .online) - static let userName = "Andrew Black" - static let message: AttributedString = "How can we assist you today?" +// static let userName = "Andrew Black" +// static let message: AttributedString = "How can we assist you today?" static let timestamp = PBTimestamp(Date(), showDate: false) static let picAnna = PBAvatar(image: Image("Anna", bundle: .module), size: .xSmall, status: .online) static let picPatric = PBAvatar(image: Image("Pat", bundle: .module), size: .xSmall) @@ -44,6 +39,25 @@ enum Mocks { // ("Indigo", nil), ("Magenta", nil) ] + static let assetesMultipleUsers: [(String, (String, (() -> PBUser?)?)?)] = [ + ("1", (andrew.name, { andrew })), + ("2", (ana.name, { ana })), + ("3", (patric.name, { patric })), + ("4", (luccile.name, { luccile })) + ] + + static let assetsSectionUsers: [PBTypeaheadTemplate.OptionType] = [ + .section("section 1"), + .item(("1", (Mocks.andrew.name, { Mocks.andrew }))), + .item(("2", (Mocks.ana.name, { Mocks.ana }))), + .item(("3", (Mocks.patric.name, { Mocks.patric }))), + .item(("4", (Mocks.luccile.name, { Mocks.luccile }))), + .section("section 2"), + .item(("1", (Mocks.andrew.name, { Mocks.andrew }))), + .item(("2", (Mocks.ana.name, { Mocks.ana }))), + .item(("3", (Mocks.patric.name, { Mocks.patric }))), + .item(("4", (Mocks.luccile.name, { Mocks.luccile }))) + ] static let cities: [String] = [ "Philadelphia", From b55912d35b8662c2a60a1bd7925450e77d662dc0 Mon Sep 17 00:00:00 2001 From: "isis.silva" Date: Wed, 30 Oct 2024 19:12:53 -0400 Subject: [PATCH 2/6] fix single selection highlight --- .../Components/Typeahead/PBTypeahead.swift | 160 ++++++++---------- .../Typeahead/PBTypeaheadTemplate.swift | 87 +++------- .../Components/Typeahead/Typeahead.swift | 44 +++++ .../Typeahead/TypeaheadCatalog.swift | 28 ++- .../Resources/Helper Files/Mocks.swift | 48 +++--- 5 files changed, 172 insertions(+), 195 deletions(-) create mode 100644 Sources/Playbook/Components/Typeahead/Typeahead.swift diff --git a/Sources/Playbook/Components/Typeahead/PBTypeahead.swift b/Sources/Playbook/Components/Typeahead/PBTypeahead.swift index 8948ff8d..34931eeb 100644 --- a/Sources/Playbook/Components/Typeahead/PBTypeahead.swift +++ b/Sources/Playbook/Components/Typeahead/PBTypeahead.swift @@ -9,13 +9,12 @@ import SwiftUI -public struct PBTypeahead: View { - public typealias Option = (String, (String, (() -> Content?)?)?) +public struct PBTypeahead: View { private let id: Int private let title: String private let placeholder: String - private let options: [Option] - private let selection: Selection + private let options: [Typeahead.Option] + private let selection: Typeahead.Selection private let noOptionsText: String private let debounce: (time: TimeInterval, numberOfCharacters: Int) private let dropdownMaxHeight: CGFloat? @@ -25,12 +24,12 @@ public struct PBTypeahead: View { @State private var showList: Bool = false @State private var hoveringIndex: Int? - @State private var hoveringOption: Option? + @State private var hoveringOption: Typeahead.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: [Option] + @Binding var selectedOptions: [Typeahead.Option] @Binding var searchText: String @FocusState.Binding private var isFocused: Bool @@ -39,13 +38,13 @@ public struct PBTypeahead: View { title: String, placeholder: String = "Select", searchText: Binding, - options: [Option], - selection: Selection, + options: [Typeahead.Option], + selection: Typeahead.Selection, debounce: (time: TimeInterval, numberOfCharacters: Int) = (0, 0), dropdownMaxHeight: CGFloat? = nil, listOffset: (x: CGFloat, y: CGFloat) = (0, 0), isFocused: FocusState.Binding, - selectedOptions: Binding<[Option]>, + selectedOptions: Binding<[Typeahead.Option]>, clearAction: (() -> Void)? = nil, noOptionsText: String = "No options" ) { @@ -97,9 +96,10 @@ public struct PBTypeahead: View { showList = isFocused } setKeyboardControls + selectedIndex = options.firstIndex(of: selectedOptions[0]) } .onChange(of: isFocused) { newValue in - Timer.scheduledTimer(withTimeInterval: 0.03, repeats: false) { _ in + Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { _ in showList = newValue } } @@ -147,15 +147,15 @@ private extension PBTypeahead { .frame(maxWidth: .infinity, alignment: .top) } - func listItemView(index: Int, option: Option) -> some View { + func listItemView(index: Int, option: Typeahead.Option) -> some View { HStack { - if option.0 == noOptionsText { + if option.text == noOptionsText { emptyView } else { - if let customView = option.1?.1?() { + if let customView = option.customView?() { customView } else { - Text(option.1?.0 ?? option.0) + Text(option.text ?? option.id) .pbFont(.body, color: listTextolor(index)) } } @@ -170,7 +170,7 @@ private extension PBTypeahead { hoveringOption = option } .onTapGesture { - if option.0 != "No Options" { + if option.text != noOptionsText { onListSelection(index: index, option: option) } } @@ -185,28 +185,28 @@ private extension PBTypeahead { } } - var searchResults: [Option] { + var searchResults: [Typeahead.Option] { let filteredOptions = searchText.isEmpty && debounce.numberOfCharacters == 0 ? options : options.filter { - if let text = $0.1?.0 { + if let text = $0.text { return text.localizedCaseInsensitiveContains(searchText) } else { - return $0.0.localizedCaseInsensitiveContains(searchText) + return $0.id.localizedCaseInsensitiveContains(searchText) } } - let selectedIds = Set(selectedOptions.map { $0.0 }) - let filteredSelectedOptions = filteredOptions.filter { !selectedIds.contains($0.0) } + let selectedIds = Set(selectedOptions.map { $0.id }) + let filteredSelectedOptions = filteredOptions.filter { !selectedIds.contains($0.id) } switch selection{ - case .multiple: return filteredSelectedOptions.isEmpty ? [(noOptionsText, nil)] : filteredSelectedOptions - case .single: return filteredOptions.isEmpty ? [(noOptionsText, nil)] : filteredOptions + 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 } } var optionsSelected: GridInputField.Selection { let optionsSelected = selectedOptions.map { value in - if let content = value.1 { - return content.0 + if let content = value.text { + return content } else { - return value.0 + return value.id } } return selection.selectedOptions(options: optionsSelected, placeholder: placeholder) @@ -214,14 +214,8 @@ private extension PBTypeahead { var clear: Void { if let action = clearAction { - clearText action() - } else { - clearText } - } - - var clearText: Void { searchText = "" selectedOptions.removeAll() selectedOptions = [] @@ -230,52 +224,6 @@ private extension PBTypeahead { showList = false } - var setKeyboardControls: Void { - #if os(macOS) - NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in - if event.keyCode == 48 { // tab - focused = true - } - if event.keyCode == 36 { // return bar - if let index = hoveringIndex, index <= searchResults.count-1, showList { - onListSelection(index: index, option: searchResults[index]) - } - } - if event.keyCode == 49 { // space - if isFocused { - if let index = hoveringIndex, index <= searchResults.count-1, showList, searchText.isEmpty { - onListSelection(index: index, option: searchResults[index]) - } else { - showList = true - } - } - } - if event.keyCode == 51 { // delete - if let lastElementIndex = selectedOptions.indices.last, isFocused, searchText.isEmpty, !selectedOptions.isEmpty { - removeSelected(lastElementIndex) - } - } - if event.keyCode == 125 { // arrow down - if isFocused { - if let index = hoveringIndex { - hoveringIndex = index < searchResults.count ? (index + 1) : 0 - } else { - hoveringIndex = 0 - } - } - } - else { - if event.keyCode == 126 { // arrow up - if isFocused, let index = hoveringIndex { - hoveringIndex = index > 1 ? (index - 1) : 0 - } - } - } - return event - } - #endif - } - var onViewTap: Void { showList.toggle() isFocused = true @@ -287,7 +235,7 @@ private extension PBTypeahead { } } - func onListSelection(index: Int, option: Option) { + func onListSelection(index: Int, option: Typeahead.Option) { if showList { switch selection { case .single: @@ -300,7 +248,7 @@ private extension PBTypeahead { searchText = "" } - func onSingleSelection(index: Int, _ option: Option) { + func onSingleSelection(index: Int, _ option: Typeahead.Option) { selectedOptions.removeAll() selectedOptions = [option] selectedIndex = index @@ -308,7 +256,7 @@ private extension PBTypeahead { selectedOptions.append(option) } - func onMultipleSelection(_ option: Option) { + func onMultipleSelection(_ option: Typeahead.Option) { selectedOptions.append(option) hoveringIndex = nil selectedIndex = nil @@ -343,19 +291,51 @@ private extension PBTypeahead { return .text(.default) } } -} - -public extension PBTypeahead { - enum Selection { - case single, multiple(variant: GridInputField.Selection.Variant) - - func selectedOptions(options: [String], placeholder: String) -> GridInputField.Selection { - switch self { - case .single: return .single(options.first) - case .multiple(let variant): return .multiple(variant, options) + var setKeyboardControls: Void { + #if os(macOS) + NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in + if event.keyCode == 48 { // tab + focused = true + } + if event.keyCode == 36 { // return bar + if let index = hoveringIndex, index <= searchResults.count-1, showList { + onListSelection(index: index, option: searchResults[index]) + } + } + if event.keyCode == 49 { // space + if isFocused { + if let index = hoveringIndex, index <= searchResults.count-1, showList, searchText.isEmpty { + onListSelection(index: index, option: searchResults[index]) + } else { + showList = true + } + } } + if event.keyCode == 51 { // delete + if let lastElementIndex = selectedOptions.indices.last, isFocused, searchText.isEmpty, !selectedOptions.isEmpty { + removeSelected(lastElementIndex) + } + } + if event.keyCode == 125 { // arrow down + if isFocused { + if let index = hoveringIndex { + hoveringIndex = index < searchResults.count ? (index + 1) : 0 + } else { + hoveringIndex = 0 + } + } + } + else { + if event.keyCode == 126 { // arrow up + if isFocused, let index = hoveringIndex { + hoveringIndex = index > 1 ? (index - 1) : 0 + } + } + } + return event } + #endif } } diff --git a/Sources/Playbook/Components/Typeahead/PBTypeaheadTemplate.swift b/Sources/Playbook/Components/Typeahead/PBTypeaheadTemplate.swift index 2a1ebf1b..2f41234d 100644 --- a/Sources/Playbook/Components/Typeahead/PBTypeaheadTemplate.swift +++ b/Sources/Playbook/Components/Typeahead/PBTypeaheadTemplate.swift @@ -9,13 +9,12 @@ import SwiftUI -public struct PBTypeaheadTemplate: View { - public typealias Option = (String, (String, (() -> Content?)?)?) +public struct PBTypeaheadTemplate: View { private let id: Int private let title: String private let placeholder: String - private var options: [OptionType] - private let selection: Selection + private var options: [Typeahead.OptionType] + private let selection: Typeahead.Selection private let debounce: (time: TimeInterval, numberOfCharacters: Int) private let dropdownMaxHeight: CGFloat? private let listOffset: (x: CGFloat, y: CGFloat) @@ -24,13 +23,13 @@ public struct PBTypeaheadTemplate: View { @State private var showList: Bool = false @State private var hoveringIndex: (Int?, String?) - @State private var hoveringOption: Option? + @State private var hoveringOption: Typeahead.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: [Option] + @Binding var selectedOptions: [Typeahead.Option] @Binding var searchText: String @FocusState.Binding private var isFocused: Bool @@ -39,13 +38,13 @@ public struct PBTypeaheadTemplate: View { title: String, placeholder: String = "Select", searchText: Binding, - options: [OptionType], - selection: Selection, + options: [Typeahead.OptionType], + selection: Typeahead.Selection, debounce: (time: TimeInterval, numberOfCharacters: Int) = (0, 0), dropdownMaxHeight: CGFloat? = nil, listOffset: (x: CGFloat, y: CGFloat) = (0, 0), isFocused: FocusState.Binding, - selectedOptions: Binding<[Option]>, + selectedOptions: Binding<[Typeahead.Option]>, clearAction: (() -> Void)? = nil ) { self.id = id @@ -153,12 +152,12 @@ private extension PBTypeaheadTemplate { .padding(.leading) } - func listItemView(item: Option, index: Int, for section: String?) -> some View { + func listItemView(item: Typeahead.Option, index: Int, for section: String?) -> some View { HStack { - if let customView = item.1?.1?() { + if let customView = item.customView?() { customView } else { - Text(item.1?.0 ?? item.0) + Text(item.text ?? item.id) .pbFont(.body, color: listTextolor(index)) } } @@ -176,10 +175,10 @@ private extension PBTypeaheadTemplate { } } - var mapResults: [(String?, [Option], PBButton)] { - var array: [(String?, [Option], PBButton)] = [] + var mapResults: [(String?, [Typeahead.Option], PBButton)] { + var array: [(String?, [Typeahead.Option], PBButton)] = [] var currentSection: String? = nil - var currentOptions: [Option] = [] + var currentOptions: [Typeahead.Option] = [] for result in searchResults { switch result { case .section(let section): @@ -209,8 +208,8 @@ private extension PBTypeaheadTemplate { private func appendSectionToArray( section: String?, - options: [Option], - to array: inout [(String?, [Option], PBButton)] + options: [Typeahead.Option], + to array: inout [(String?, [Typeahead.Option], PBButton)] ) { let numberOfItems = numberOfItemsShown[section] ?? 2 array.append(( @@ -227,20 +226,20 @@ private extension PBTypeaheadTemplate { )) } - var searchResults: [OptionType] { + var searchResults: [Typeahead.OptionType] { let filteredOptions = searchText.isEmpty && debounce.numberOfCharacters == 0 ? options : options.filter { switch $0 { case .item(let item): - if let text = item.1?.0 { + if let text = item.text { return text.localizedCaseInsensitiveContains(searchText) } else { - return item.0.localizedCaseInsensitiveContains(searchText) + return item.id.localizedCaseInsensitiveContains(searchText) } case .section(_): return true } } - let selectedIds = Set(selectedOptions.map { $0.0 }) + let selectedIds = Set(selectedOptions.map { $0.id }) let filteredSelectedOptions = filteredOptions.filter { !selectedIds.contains($0.id) } switch selection{ case .multiple: return filteredSelectedOptions @@ -250,10 +249,10 @@ private extension PBTypeaheadTemplate { var optionsSelected: GridInputField.Selection { let optionsSelected = selectedOptions.map { value in - if let content = value.1 { - return content.0 + if let content = value.text { + return content } else { - return value.0 + return value.id } } return selection.selectedOptions(options: optionsSelected, placeholder: placeholder) @@ -261,14 +260,8 @@ private extension PBTypeaheadTemplate { var clear: Void { if let action = clearAction { - clearText action() - } else { - clearText - } } - - var clearText: Void { searchText = "" selectedOptions.removeAll() selectedIndex = nil @@ -341,7 +334,7 @@ private extension PBTypeaheadTemplate { } } - func onListSelection(index: Int, section: String?, option: Option) { + func onListSelection(index: Int, section: String?, option: Typeahead.Option) { if showList { switch selection { case .single: @@ -354,7 +347,7 @@ private extension PBTypeaheadTemplate { searchText = "" } - func onSingleSelection(index: Int, section: String?, _ option: Option) { + func onSingleSelection(index: Int, section: String?, _ option: Typeahead.Option) { selectedOptions.removeAll() if hoveringIndex.0 == index && hoveringIndex.1 == section { selectedOptions.append(option) @@ -363,7 +356,7 @@ private extension PBTypeaheadTemplate { hoveringIndex = (index, section) } - func onMultipleSelection(index: Int, section: String?, _ option: Option) { + func onMultipleSelection(index: Int, section: String?, _ option: Typeahead.Option) { if hoveringIndex.0 == index && hoveringIndex.1 == section { selectedOptions.append(option) } @@ -402,34 +395,6 @@ private extension PBTypeaheadTemplate { } } -public extension PBTypeaheadTemplate { - enum Selection { - case single, multiple(variant: GridInputField.Selection.Variant) - - func selectedOptions(options: [String], placeholder: String) -> GridInputField.Selection { - switch self { - case .single: return .single(options.first) - case .multiple(let variant): return .multiple(variant, options) - } - } - } -} - -public extension PBTypeaheadTemplate { - enum OptionType: Identifiable { - public var id: String { - switch self { - case .section(let str): - return str - case .item(let item): - return item.0 - } - } - case section(String) - case item(PBTypeaheadTemplate.Option) - } -} - #Preview { registerFonts() return TypeaheadCatalog() diff --git a/Sources/Playbook/Components/Typeahead/Typeahead.swift b/Sources/Playbook/Components/Typeahead/Typeahead.swift new file mode 100644 index 00000000..d43f27ce --- /dev/null +++ b/Sources/Playbook/Components/Typeahead/Typeahead.swift @@ -0,0 +1,44 @@ +// +// Playbook Swift Design System +// +// Copyright © 2024 Power Home Remodeling Group +// This software is distributed under the ISC License +// +// Typeahead.swift +// + +import SwiftUI + +public enum Typeahead { + public 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 enum OptionType: Identifiable { + public var id: String { + switch self { + case .section(let str): + return str + case .item(let item): + return item.id + } + } + case section(String) + case item(Option) + } + + public enum Selection { + case single, multiple(variant: GridInputField.Selection.Variant) + + func selectedOptions(options: [String], placeholder: String) -> GridInputField.Selection { + switch self { + case .single: return .single(options.first) + case .multiple(let variant): return .multiple(variant, options) + } + } + } +} + diff --git a/Sources/Playbook/Components/Typeahead/TypeaheadCatalog.swift b/Sources/Playbook/Components/Typeahead/TypeaheadCatalog.swift index b8097e6d..227230df 100644 --- a/Sources/Playbook/Components/Typeahead/TypeaheadCatalog.swift +++ b/Sources/Playbook/Components/Typeahead/TypeaheadCatalog.swift @@ -12,44 +12,36 @@ import SwiftUI public struct TypeaheadCatalog: View { private var assetsColors = Mocks.assetsColors @State private var searchTextColors: String = "" - @State private var selectedColors: [(String, (String, (() -> AnyView?)?)?)] = [Mocks.assetsColors.first!] + @State private var selectedColors: [Typeahead.Option] = [Mocks.assetsColors[2]] @FocusState private var isFocusedColors private var assetsUsers = Mocks.assetesMultipleUsers @State private var searchTextUsers: String = "" - @State private var selectedUsers: [(String, (String, (() -> PBUser?)?)?)] = [Mocks.assetesMultipleUsers[0], Mocks.assetesMultipleUsers[1]] + @State private var selectedUsers: [Typeahead.Option] = [Mocks.assetesMultipleUsers[0], Mocks.assetesMultipleUsers[1]] @FocusState private var isFocusedUsers @State private var searchTextHeight: String = "" - @State private var selectedHeight: [(String, (String, (() -> PBUser?)?)?)] = [Mocks.assetesMultipleUsers[3], Mocks.assetesMultipleUsers[2]] + @State private var selectedHeight: [Typeahead.Option] = [Mocks.assetesMultipleUsers[3], Mocks.assetesMultipleUsers[2]] @FocusState private var isFocusedHeight - private var assetsSection: [PBTypeaheadTemplate.OptionType] = Mocks.assetsSectionUsers + private var assetsSection: [Typeahead.OptionType] = Mocks.assetsSectionUsers @State private var searchTextSections: String = "" - @State private var selectedSections: [(String, (String, (() -> PBUser?)?)?)] = [] + @State private var selectedSections: [Typeahead.Option] = [] @FocusState private var isFocusedSection - - @State private var didTapOutside: Bool? = false - @State private var isPresented1: Bool = false @State private var presentDialog: Bool = false - @State private var isLoading: Bool = false - - - var popoverManager = PopoverManager() public var body: some View { PBDocStack(title: "Typeahead") { PBDoc(title: "Default", spacing: Spacing.small) { colors } PBDoc(title: "With Pills", spacing: Spacing.small) { users } - PBDoc(title: "Height Adjusted Dropdown", spacing: Spacing.small) { heightAdjusted } #if os(macOS) PBDoc(title: "Dialog") { dialog } #endif - PBDoc(title: "Sections", spacing: Spacing.small) { sections } + PBDoc(title: "Height Adjusted Dropdown", spacing: Spacing.small) { heightAdjusted } +// PBDoc(title: "Sections", spacing: Spacing.small) { sections } .padding(.bottom, 500) - } .onTapGesture { isFocusedColors = false @@ -139,9 +131,9 @@ extension TypeaheadCatalog { @State private var isLoading: Bool = false @State private var searchTextUsers: String = "" @State private var assetsUsers = Mocks.assetesMultipleUsers - @State private var selectedUsers: [(String, (String, (() -> PBUser?)?)?)] = [ - ("1", (Mocks.andrew.name, { Mocks.andrew })), - ("2", (Mocks.ana.name, { Mocks.ana })) + @State private var selectedUsers: [Typeahead.Option] = [ + Mocks.assetesMultipleUsers[0], + Mocks.assetesMultipleUsers[1] ] @FocusState var isFocused diff --git a/Sources/Playbook/Resources/Helper Files/Mocks.swift b/Sources/Playbook/Resources/Helper Files/Mocks.swift index 6e1bce01..caf71474 100644 --- a/Sources/Playbook/Resources/Helper Files/Mocks.swift +++ b/Sources/Playbook/Resources/Helper Files/Mocks.swift @@ -22,41 +22,37 @@ enum Mocks { static let avatarXSmall = PBAvatar(image: Image("andrew", bundle: .module), size: .xSmall) static let avatarXSmallStatus = PBAvatar(image: Image("andrew", bundle: .module), size: .xSmall, status: .online) -// static let userName = "Andrew Black" -// static let message: AttributedString = "How can we assist you today?" + static let userName = "Andrew Black" + static let message: AttributedString = "How can we assist you today?" static let timestamp = PBTimestamp(Date(), showDate: false) 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: [(String, (String, (() -> AnyView?)?)?)] = [ - ("Orange", ("Orange", nil)), - ("Red", ("Red", nil)), -// ("Green", nil), - ("Blue", ("Blue", nil)), - ("Pink", ("Pink", nil)), -// ("Yellow", nil), -// ("Violet", nil), -// ("Indigo", nil), - ("Magenta", nil) + static let assetsColors: [Typeahead.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: [(String, (String, (() -> PBUser?)?)?)] = [ - ("1", (andrew.name, { andrew })), - ("2", (ana.name, { ana })), - ("3", (patric.name, { patric })), - ("4", (luccile.name, { luccile })) + static let assetesMultipleUsers: [Typeahead.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: [PBTypeaheadTemplate.OptionType] = [ + static let assetsSectionUsers: [Typeahead.OptionType] = [ .section("section 1"), - .item(("1", (Mocks.andrew.name, { Mocks.andrew }))), - .item(("2", (Mocks.ana.name, { Mocks.ana }))), - .item(("3", (Mocks.patric.name, { Mocks.patric }))), - .item(("4", (Mocks.luccile.name, { Mocks.luccile }))), + .item(.init(id: "1", text: andrew.name, customView: { AnyView(andrew) })), + .item(.init(id: "2", text: ana.name, customView: { AnyView(ana) })), + .item(.init(id: "3", text: patric.name, customView: { AnyView(patric) })), + .item(.init(id: "4", text: luccile.name, customView: { AnyView(luccile) })), .section("section 2"), - .item(("1", (Mocks.andrew.name, { Mocks.andrew }))), - .item(("2", (Mocks.ana.name, { Mocks.ana }))), - .item(("3", (Mocks.patric.name, { Mocks.patric }))), - .item(("4", (Mocks.luccile.name, { Mocks.luccile }))) + .item(.init(id: "5", text: andrew.name, customView: { AnyView(andrew) })), + .item(.init(id: "6", text: ana.name, customView: { AnyView(ana) })), + .item(.init(id: "7", text: patric.name, customView: { AnyView(patric) })), + .item(.init(id: "8", text: luccile.name, customView: { AnyView(luccile) })) ] static let cities: [String] = [ From 930619e6fe59fa3925743a8b2615998473ea80e2 Mon Sep 17 00:00:00 2001 From: "isis.silva" Date: Thu, 31 Oct 2024 14:56:51 -0400 Subject: [PATCH 3/6] fix focus on backspace --- .../Components/Typeahead/GridInputField.swift | 33 +++++++++---------- .../Components/Typeahead/PBTypeahead.swift | 15 ++++----- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/Sources/Playbook/Components/Typeahead/GridInputField.swift b/Sources/Playbook/Components/Typeahead/GridInputField.swift index 3d1f24d4..dcfbdd7f 100644 --- a/Sources/Playbook/Components/Typeahead/GridInputField.swift +++ b/Sources/Playbook/Components/Typeahead/GridInputField.swift @@ -16,11 +16,11 @@ public struct GridInputField: View { private let onItemTap: ((Int) -> Void)? private let onViewTap: (() -> Void)? private let shape = RoundedRectangle(cornerRadius: BorderRadius.medium) + private var isFocused: FocusState.Binding @Binding var searchText: String @State private var isHovering: Bool = false @State private var clearButtonIsHovering: Bool = false @State private var indicatorIsHovering: Bool = false - var isFocused: FocusState.Binding init( placeholder: String = "Select", @@ -52,22 +52,9 @@ public struct GridInputField: View { ForEach(indices, id: \.self) { index in if indices.last != index { gridView(index: index) - } else { - textfieldWithCustomPlaceholder - .fixedSize() - .frame(minWidth: 60, alignment: .leading) - .overlay { - Color.white - .opacity(isFocused.wrappedValue ? 0.001 : 0) - .onTapGesture { - if isFocused.wrappedValue { - onViewTap?() - } - } - } - .clipped() } } + textfieldWithCustomPlaceholder } .padding(.horizontal, Spacing.small) .padding(.vertical, Spacing.xSmall) @@ -115,6 +102,18 @@ private extension GridInputField { .textFieldStyle(.plain) .pbFont(.body, color: textColor) } + .fixedSize() + .frame(minWidth: 60, alignment: .leading) + .overlay { + Color.white + .opacity(isFocused.wrappedValue ? 0.001 : 0) + .onTapGesture { + if isFocused.wrappedValue { + onViewTap?() + } + } + } + .clipped() } func gridView(index: Int?) -> AnyView? { @@ -254,13 +253,13 @@ public struct WrappedInputFieldCatalog: View { GridInputField( searchText: $text, - selection: .multiple(.pill, ["title1", "title2", "title2", "title2", "title2"]), + selection: .multiple(.pill, ["title1", "title2", "title2"]), isFocused: $isFocused ) GridInputField( searchText: $text, - selection: .multiple(.other(AnyView(PBPill("oi", variant: .primary))), ["title1", "title2", "title2", "title2", "title2", "title2", "title2", "title2"]), + selection: .multiple(.other(AnyView(PBPill("oi", variant: .primary))), ["title1", "title2", "title2"]), isFocused: $isFocused ) diff --git a/Sources/Playbook/Components/Typeahead/PBTypeahead.swift b/Sources/Playbook/Components/Typeahead/PBTypeahead.swift index 34931eeb..4978a654 100644 --- a/Sources/Playbook/Components/Typeahead/PBTypeahead.swift +++ b/Sources/Playbook/Components/Typeahead/PBTypeahead.swift @@ -96,24 +96,21 @@ public struct PBTypeahead: View { showList = isFocused } setKeyboardControls - selectedIndex = options.firstIndex(of: selectedOptions[0]) + if !selectedOptions.isEmpty { + selectedIndex = options.firstIndex(of: selectedOptions[0]) + } } .onChange(of: isFocused) { newValue in - Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { _ in - showList = newValue - } + showList = newValue } .onChange(of: searchText, debounce: debounce) { _ in _ = searchResults reloadList } - .onChange(of: options.count) { _ in - reloadList - } - .onChange(of: searchResults.count) { _ in + .onChange(of: contentSize) { _ in reloadList } - .onChange(of: contentSize) { _ in + .onChange(of: selectedOptions.count) { _ in reloadList } .onChange(of: hoveringIndex) { index in From 1e45f98d3d006cbc9f32ab85e696f011ef7db158 Mon Sep 17 00:00:00 2001 From: "isis.silva" Date: Mon, 4 Nov 2024 13:59:04 -0500 Subject: [PATCH 4/6] fix jumping on pill removal --- .../Playbook/Components/Popover/PopoverHandler.swift | 11 +++++++++++ .../Components/Typeahead/GridInputField.swift | 1 + 2 files changed, 12 insertions(+) diff --git a/Sources/Playbook/Components/Popover/PopoverHandler.swift b/Sources/Playbook/Components/Popover/PopoverHandler.swift index 262d3f10..1fc22647 100644 --- a/Sources/Playbook/Components/Popover/PopoverHandler.swift +++ b/Sources/Playbook/Components/Popover/PopoverHandler.swift @@ -12,6 +12,7 @@ import SwiftUI struct PopoverView: View { let id: Int let blockBackgroundInteractions: Bool + @State private var opacity: CGFloat = 0.01 @EnvironmentObject var popoverManager: PopoverManager init( @@ -35,6 +36,16 @@ struct PopoverView: View { .onTapGesture { popoverManager.closeInside(id) } + .onAppear { + if popover?.position != nil { + Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { _ in + opacity = 1 + } + } else { + opacity = 0.01 + } + } + .opacity(opacity) } } } diff --git a/Sources/Playbook/Components/Typeahead/GridInputField.swift b/Sources/Playbook/Components/Typeahead/GridInputField.swift index dcfbdd7f..3de49fc7 100644 --- a/Sources/Playbook/Components/Typeahead/GridInputField.swift +++ b/Sources/Playbook/Components/Typeahead/GridInputField.swift @@ -101,6 +101,7 @@ private extension GridInputField { } .textFieldStyle(.plain) .pbFont(.body, color: textColor) + .frame(height: 24) } .fixedSize() .frame(minWidth: 60, alignment: .leading) From 9a65455f4035067b309d89d56ea4c7f763ea7df4 Mon Sep 17 00:00:00 2001 From: "isis.silva" Date: Mon, 4 Nov 2024 16:50:33 -0500 Subject: [PATCH 5/6] fix list items ui --- .../Components/Popover/PBPopover.swift | 2 +- .../Components/Typeahead/PBTypeahead.swift | 60 +++++++++---------- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/Sources/Playbook/Components/Popover/PBPopover.swift b/Sources/Playbook/Components/Popover/PBPopover.swift index 4d28c9ca..50e74544 100644 --- a/Sources/Playbook/Components/Popover/PBPopover.swift +++ b/Sources/Playbook/Components/Popover/PBPopover.swift @@ -60,8 +60,8 @@ public struct Popover: ViewModifier { } } .onChange(of: isPresented) { newValue in - popoverManager.presentPopover(with: id, value: newValue) updateViewFrame() + popoverManager.presentPopover(with: id, value: newValue) } .onChange(of: popoverPosition) { position in updateViewFrame() diff --git a/Sources/Playbook/Components/Typeahead/PBTypeahead.swift b/Sources/Playbook/Components/Typeahead/PBTypeahead.swift index 4978a654..e9955377 100644 --- a/Sources/Playbook/Components/Typeahead/PBTypeahead.swift +++ b/Sources/Playbook/Components/Typeahead/PBTypeahead.swift @@ -26,7 +26,6 @@ public struct PBTypeahead: View { @State private var hoveringIndex: Int? @State private var hoveringOption: Typeahead.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] @@ -76,7 +75,6 @@ public struct PBTypeahead: View { onItemTap: { removeSelected($0) }, onViewTap: { onViewTap } ) - .sizeReader { contentSize = $0 } .pbPopover( isPresented: $showList, id: id, @@ -107,16 +105,15 @@ public struct PBTypeahead: View { _ = searchResults reloadList } - .onChange(of: contentSize) { _ in - reloadList - } .onChange(of: selectedOptions.count) { _ in reloadList } .onChange(of: hoveringIndex) { index in reloadList } - .onChange(of: searchText, debounce: debounce) { _ in + .onChange(of: searchText, debounce: debounce) { text in + _ = searchResults + reloadList if !searchText.isEmpty { showList = true } @@ -139,9 +136,7 @@ private extension PBTypeahead { .scrollDismissesKeyboard(.immediately) .frame(maxHeight: dropdownMaxHeight) .fixedSize(horizontal: false, vertical: true) - .frame(maxWidth: .infinity, alignment: .top) } - .frame(maxWidth: .infinity, alignment: .top) } func listItemView(index: Int, option: Typeahead.Option) -> some View { @@ -149,26 +144,26 @@ private extension PBTypeahead { if option.text == noOptionsText { emptyView } else { - if let customView = option.customView?() { - customView - } else { - Text(option.text ?? option.id) - .pbFont(.body, color: listTextolor(index)) + Group { + if let customView = option.customView?() { + customView + } else { + Text(option.text ?? option.id) + .pbFont(.body, color: listTextolor(index)) + } + } + .padding(.horizontal, Spacing.xSmall + 4) + .padding(.vertical, Spacing.xSmall + 4) + .frame(maxWidth: .infinity, alignment: .leading) + .background(listBackgroundColor(index)) + .onHover(disabled: false) { hover in + isHovering = hover + hoveringIndex = index + hoveringOption = option + } + .onTapGesture { + onListSelection(index: index, option: option) } - } - } - .padding(.horizontal, Spacing.xSmall + 4) - .padding(.vertical, Spacing.xSmall + 4) - .frame(maxWidth: .infinity, alignment: .leading) - .background(listBackgroundColor(index)) - .onHover(disabled: false) { hover in - isHovering = hover - hoveringIndex = index - hoveringOption = option - } - .onTapGesture { - if option.text != noOptionsText { - onListSelection(index: index, option: option) } } } @@ -180,6 +175,8 @@ private extension PBTypeahead { .pbFont(.body, color: .text(.light)) Spacer() } + .padding(.horizontal, Spacing.xSmall + 4) + .padding(.vertical, Spacing.xSmall + 4) } var searchResults: [Typeahead.Option] { @@ -227,13 +224,11 @@ private extension PBTypeahead { } var reloadList: Void { - if showList { - isHovering.toggle() - } + isHovering.toggle() } func onListSelection(index: Int, option: Typeahead.Option) { - if showList { + if showList, option.text != noOptionsText { switch selection { case .single: onSingleSelection(index: index, option) @@ -243,6 +238,7 @@ private extension PBTypeahead { } showList = false searchText = "" + reloadList } func onSingleSelection(index: Int, _ option: Typeahead.Option) { @@ -263,7 +259,9 @@ private extension PBTypeahead { if let selectedElementIndex = selectedOptions.indices.first(where: { $0 == index }) { let _ = selectedOptions.remove(at: selectedElementIndex) selectedIndex = nil + hoveringIndex = nil } + reloadList } func listBackgroundColor(_ index: Int?) -> Color { From 3136b82dbe5b4a143106478351171e3f50824b10 Mon Sep 17 00:00:00 2001 From: "isis.silva" Date: Tue, 12 Nov 2024 19:21:03 -0500 Subject: [PATCH 6/6] fix update list --- Sources/Playbook/Components/Popover/PBPopover.swift | 2 +- Sources/Playbook/Components/Typeahead/PBTypeahead.swift | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Sources/Playbook/Components/Popover/PBPopover.swift b/Sources/Playbook/Components/Popover/PBPopover.swift index 50e74544..4d28c9ca 100644 --- a/Sources/Playbook/Components/Popover/PBPopover.swift +++ b/Sources/Playbook/Components/Popover/PBPopover.swift @@ -60,8 +60,8 @@ public struct Popover: ViewModifier { } } .onChange(of: isPresented) { newValue in - updateViewFrame() popoverManager.presentPopover(with: id, value: newValue) + updateViewFrame() } .onChange(of: popoverPosition) { position in updateViewFrame() diff --git a/Sources/Playbook/Components/Typeahead/PBTypeahead.swift b/Sources/Playbook/Components/Typeahead/PBTypeahead.swift index e9955377..b4f69b8c 100644 --- a/Sources/Playbook/Components/Typeahead/PBTypeahead.swift +++ b/Sources/Playbook/Components/Typeahead/PBTypeahead.swift @@ -101,17 +101,13 @@ public struct PBTypeahead: View { .onChange(of: isFocused) { newValue in showList = newValue } - .onChange(of: searchText, debounce: debounce) { _ in - _ = searchResults - reloadList - } .onChange(of: selectedOptions.count) { _ in reloadList } .onChange(of: hoveringIndex) { index in reloadList } - .onChange(of: searchText, debounce: debounce) { text in + .onChange(of: searchText, debounce: debounce) { _ in _ = searchResults reloadList if !searchText.isEmpty {