mirror of
https://github.com/yattee/yattee.git
synced 2025-12-03 14:48:16 +00:00
Improve search field layout and responsiveness
SearchTextField improvements: - Add flexible width constraints (minWidth: 250, maxWidth: .infinity) for macOS - Restructure iOS layout to use HStack instead of ZStack for better alignment - Add invisible spacer to maintain consistent width when clear button is hidden - Adjust padding for more balanced appearance - Remove fixed width from fieldBorder to support flexible sizing SearchView improvements: - Wrap in GeometryReader to calculate available width dynamically - Add searchFieldWidth() helper to compute optimal search field width - Account for navigation buttons and internal padding - Apply dynamic width to both FocusableSearchTextField and SearchTextField 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -40,48 +40,50 @@ struct SearchTextField: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.frame(minWidth: 250, idealWidth: 300, maxWidth: .infinity)
|
||||||
.transaction { t in t.animation = nil }
|
.transaction { t in t.animation = nil }
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
HStack(spacing: 0) {
|
||||||
HStack {
|
Image(systemName: "magnifyingglass")
|
||||||
HStack(spacing: 0) {
|
.foregroundColor(.gray)
|
||||||
Image(systemName: "magnifyingglass")
|
.padding(.leading, 8)
|
||||||
.foregroundColor(.gray)
|
.padding(.trailing, 5)
|
||||||
.padding(.leading, 5)
|
.imageScale(.medium)
|
||||||
.padding(.trailing, 5)
|
|
||||||
.imageScale(.medium)
|
|
||||||
|
|
||||||
TextField("Search...", text: $state.queryText) {
|
TextField("Search...", text: $state.queryText) {
|
||||||
state.changeQuery { query in
|
state.changeQuery { query in
|
||||||
query.query = state.queryText
|
query.query = state.queryText
|
||||||
navigation.hideKeyboard()
|
navigation.hideKeyboard()
|
||||||
}
|
|
||||||
RecentsModel.shared.addQuery(state.queryText)
|
|
||||||
}
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.textFieldStyle(.plain)
|
|
||||||
.padding(.vertical, 7)
|
|
||||||
|
|
||||||
if !state.queryText.isEmpty {
|
|
||||||
clearButton
|
|
||||||
.padding(.leading, 5)
|
|
||||||
.padding(.trailing, 5)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.background(
|
RecentsModel.shared.addQuery(state.queryText)
|
||||||
RoundedRectangle(cornerRadius: 8)
|
}
|
||||||
.fill(Color("SearchTextFieldBackground"))
|
.disableAutocorrection(true)
|
||||||
)
|
.textFieldStyle(.plain)
|
||||||
.overlay(
|
.padding(.vertical, 7)
|
||||||
RoundedRectangle(cornerRadius: 8)
|
|
||||||
.stroke(Color(UIColor.secondarySystemBackground), lineWidth: 1)
|
if !state.queryText.isEmpty {
|
||||||
)
|
clearButton
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.padding(.leading, 5)
|
||||||
|
.padding(.trailing, 8)
|
||||||
|
} else {
|
||||||
|
// Invisible spacer to maintain consistent width
|
||||||
|
clearButton
|
||||||
|
.opacity(0)
|
||||||
|
.padding(.leading, 5)
|
||||||
|
.padding(.trailing, 8)
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 0)
|
|
||||||
}
|
}
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.fill(Color("SearchTextFieldBackground"))
|
||||||
|
)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.stroke(Color(UIColor.secondarySystemBackground), lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.horizontal, 16)
|
||||||
.transaction { t in t.animation = nil }
|
.transaction { t in t.animation = nil }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -89,11 +91,11 @@ struct SearchTextField: View {
|
|||||||
private var fieldBorder: some View {
|
private var fieldBorder: some View {
|
||||||
RoundedRectangle(cornerRadius: 5, style: .continuous)
|
RoundedRectangle(cornerRadius: 5, style: .continuous)
|
||||||
.fill(Color.background)
|
.fill(Color.background)
|
||||||
.frame(width: 250, height: 27)
|
.frame(height: 27)
|
||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: 5, style: .continuous)
|
RoundedRectangle(cornerRadius: 5, style: .continuous)
|
||||||
.stroke(Color.gray.opacity(0.4), lineWidth: 1)
|
.stroke(Color.gray.opacity(0.4), lineWidth: 1)
|
||||||
.frame(width: 250, height: 27)
|
.frame(height: 27)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,29 +41,33 @@ struct SearchView: View {
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
GeometryReader { geometry in
|
||||||
VStack {
|
VStack {
|
||||||
if accounts.app.supportsSearchSuggestions, state.query.query != state.queryText {
|
VStack {
|
||||||
SearchSuggestions()
|
if accounts.app.supportsSearchSuggestions, state.query.query != state.queryText {
|
||||||
.opacity(state.queryText.isEmpty ? 0 : 1)
|
SearchSuggestions()
|
||||||
} else {
|
.opacity(state.queryText.isEmpty ? 0 : 1)
|
||||||
results
|
} else {
|
||||||
|
results
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.backport
|
||||||
|
.scrollDismissesKeyboardInteractively()
|
||||||
}
|
}
|
||||||
.backport
|
.environment(\.listingStyle, searchListingStyle)
|
||||||
.scrollDismissesKeyboardInteractively()
|
.toolbar {
|
||||||
}
|
ToolbarItem(placement: .principal) {
|
||||||
.environment(\.listingStyle, searchListingStyle)
|
if #available(iOS 15, *) {
|
||||||
.toolbar {
|
FocusableSearchTextField()
|
||||||
ToolbarItem(placement: .principal) {
|
.frame(width: searchFieldWidth(geometry.size.width))
|
||||||
if #available(iOS 15, *) {
|
} else {
|
||||||
FocusableSearchTextField()
|
SearchTextField()
|
||||||
} else {
|
.frame(width: searchFieldWidth(geometry.size.width))
|
||||||
SearchTextField()
|
}
|
||||||
|
}
|
||||||
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
|
searchMenu
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
|
||||||
searchMenu
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
@@ -645,6 +649,26 @@ struct SearchView: View {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
private func searchFieldWidth(_ viewWidth: CGFloat) -> CGFloat {
|
||||||
|
// Base padding for internal SearchTextField padding (16pt each side = 32 total)
|
||||||
|
var totalDeduction: CGFloat = 32
|
||||||
|
|
||||||
|
// Add space for trailing menu button
|
||||||
|
totalDeduction += 44
|
||||||
|
|
||||||
|
// Add space for sidebar toggle button if in sidebar navigation style
|
||||||
|
if navigationStyle == .sidebar {
|
||||||
|
totalDeduction += 44
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimum width to ensure usability
|
||||||
|
let minWidth: CGFloat = 200
|
||||||
|
|
||||||
|
return max(minWidth, viewWidth - totalDeduction)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
var shouldDisplayHeader: Bool {
|
var shouldDisplayHeader: Bool {
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
!state.query.isEmpty
|
!state.query.isEmpty
|
||||||
|
|||||||
Reference in New Issue
Block a user