Skip to content

Commit 75a3d20

Browse files
Selection Filter issues are resolved.
Add new option and Highlight selection filtering issues are resolved.
2 parents fee42ea + 5a23924 commit 75a3d20

File tree

1 file changed

+70
-41
lines changed

1 file changed

+70
-41
lines changed

Sources/DropdownTextField/DropdownTextField.swift

Lines changed: 70 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -141,51 +141,66 @@ public struct SearchableMenu: View {
141141
public var body: some View {
142142
VStack(spacing: 2){
143143
HStack(spacing: 0){
144-
ZStack(alignment: .leading){
145-
if searchText.isEmpty {
146-
Text(placeholder)
147-
.foregroundColor(placeholderColor.opacity(0.7))
148-
.font(font)
149-
.padding(.horizontal, 10)
150-
.padding(.vertical, 5)
151-
}
152-
TextField("", text: $searchText, onEditingChanged: { isEditing in
153-
isDropdownVisible = isEditing
154-
if isEditing {
155-
isOptionSelected = false
156-
onTap()
157-
isSearchFieldFocused = true
144+
HStack {
145+
ZStack(alignment: .leading){
146+
if searchText.isEmpty {
147+
Text(placeholder)
148+
.foregroundColor(placeholderColor.opacity(0.7))
149+
.font(font)
150+
.padding(.horizontal, 10)
151+
.padding(.vertical, 5)
158152
}
159-
})
160-
.font(font)
161-
.tint(accentColor)
162-
.foregroundColor(textColor)
163-
.padding(.horizontal, 10)
164-
.padding(.vertical, 5)
165-
.textInputAutocapitalization(.never)
166-
.disableAutocorrection(true)
167-
.focused($isSearchFieldFocused)
168-
}
169-
170-
Button(action: {
171-
isDropdownVisible.toggle()
172-
isSearchFieldFocused = isDropdownVisible
173-
}){
174-
dropdownIcon
175-
.resizable()
176-
.aspectRatio(contentMode: .fit)
177-
.frame(width: 18, height: 18)
178-
.foregroundColor(accentColor)
179-
.rotationEffect(.degrees(isDropdownVisible ? 180 : 0))
153+
TextField("", text: $searchText, onEditingChanged: { isEditing in isDropdownVisible = isEditing
154+
if isEditing {
155+
isOptionSelected = false
156+
onTap()
157+
isSearchFieldFocused = true
158+
}
159+
})
160+
.font(font)
161+
.tint(accentColor)
162+
.foregroundColor(textColor)
163+
.padding(.horizontal, 10)
164+
.padding(.vertical, 5)
165+
.textInputAutocapitalization(.never)
166+
.disableAutocorrection(true)
167+
.focused($isSearchFieldFocused)
168+
.onTapGesture {
169+
if !searchText.isEmpty {
170+
searchText = ""
171+
isOptionSelected = false
172+
isDropdownVisible = true
173+
onTap()
174+
isSearchFieldFocused = true
175+
}
176+
}
177+
.onSubmit {
178+
if let topOption = filteredOptions.first {
179+
selectOption(topOption)
180+
} else if addNew && !searchText.isEmpty {
181+
selectOption(searchText)
182+
}
183+
}
184+
}
185+
Button(action: {
186+
isDropdownVisible.toggle()
187+
isSearchFieldFocused = isDropdownVisible
188+
}){
189+
dropdownIcon
190+
.resizable()
191+
.aspectRatio(contentMode: .fit)
192+
.frame(width: 18, height: 18)
193+
.foregroundColor(borderColor)
194+
.rotationEffect(.degrees(isDropdownVisible ? 180 : 0))
195+
}
196+
.frame(width: 35)
197+
.padding(.trailing, 8)
180198
}
181-
.frame(width: 35)
182-
.padding(.trailing, 8)
183199
}
184200
.frame(maxWidth: .infinity, alignment: .leading)
185201
.frame(height: height)
186-
.background(
187-
RoundedRectangle(cornerRadius: cornerRadius)
188-
.strokeBorder(computedBorderColor, lineWidth: 1)
202+
.background( RoundedRectangle(cornerRadius: cornerRadius)
203+
.strokeBorder(computedBorderColor, lineWidth: 1)
189204
)
190205

191206
if isDropdownVisible {
@@ -204,9 +219,22 @@ public struct SearchableMenu: View {
204219
}
205220
.padding(.vertical, 4)
206221
.background(index == 0 && !searchText.isEmpty ? accentColor : Color.clear)
222+
.cornerRadius(8)
207223
}
208224
}
209-
if addNew && searchText != "" && !options.contains(where: { $0.lowercased() == searchText.lowercased()}){
225+
let trimmedSearch = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
226+
let allowedCharacters = CharacterSet.alphanumerics
227+
let normalizedSearch = trimmedSearch.lowercased()
228+
.components(separatedBy: allowedCharacters.inverted)
229+
.joined()
230+
let normalizedOptions = options.map {
231+
$0.lowercased()
232+
.components(separatedBy: allowedCharacters.inverted)
233+
.joined()
234+
}
235+
let hasExactMatch = normalizedOptions.contains { $0 == normalizedSearch }
236+
let hasPrefixMatch = normalizedOptions.contains { normalizedSearch != "" && $0.hasPrefix(normalizedSearch)}
237+
if addNew && !trimmedSearch.isEmpty && !hasExactMatch && !hasPrefixMatch {
210238
Button(action: {
211239
isOptionSelected = true
212240
isDropdownVisible = false
@@ -220,6 +248,7 @@ public struct SearchableMenu: View {
220248
}
221249
.padding(.vertical, 4)
222250
.background(accentColor)
251+
.cornerRadius(8)
223252
}
224253
} else if !addNew && searchText != "" && filteredOptions.isEmpty {
225254
Text(noMatchText)

0 commit comments

Comments
 (0)