Use native text Close button for sheet toolbars on macOS

Sheet dismiss buttons rendered as an iOS-style circular glass "X" on
macOS 26, which looks foreign on the desktop. Add a shared
sheetCloseToolbarItem helper that renders a native text "Close" button
on macOS while keeping the compact icon-only xmark on iOS/tvOS, and
replace the ~14 duplicated inline close buttons across sheets with it.
This commit is contained in:
Arkadiusz Fal
2026-05-24 12:34:53 +02:00
parent c245a0e2a0
commit 9568149c21
15 changed files with 64 additions and 121 deletions

View File

@@ -0,0 +1,50 @@
//
// ToolbarContent+Close.swift
// Yattee
//
// Shared sheet-dismiss toolbar item.
//
import SwiftUI
/// Standard sheet-dismiss toolbar item.
///
/// On macOS this renders a native text button (e.g. "Close"), which fits the desktop
/// convention. On iOS/tvOS it renders the compact icon-only xmark used by the rest of
/// the app's sheets.
///
/// Placement defaults to `.confirmationAction`; only the *label* changes per platform,
/// so it can be dropped into existing `.toolbar { }` blocks without changing layout.
@ToolbarContentBuilder
func sheetCloseToolbarItem(
placement: ToolbarItemPlacement = .confirmationAction,
titleKey: LocalizedStringResource = "common.close",
identifier: String? = nil,
action: @escaping () -> Void
) -> some ToolbarContent {
ToolbarItem(placement: placement) {
sheetCloseButton(titleKey: titleKey, identifier: identifier, action: action)
}
}
@ViewBuilder
private func sheetCloseButton(
titleKey: LocalizedStringResource,
identifier: String?,
action: @escaping () -> Void
) -> some View {
let button = Button(role: .cancel, action: action) {
#if os(macOS)
Text(String(localized: titleKey))
#else
Label(String(localized: titleKey), systemImage: "xmark")
.labelStyle(.iconOnly)
#endif
}
if let identifier {
button.accessibilityIdentifier(identifier)
} else {
button
}
}

View File

@@ -88,14 +88,7 @@ struct MediaBrowserViewOptionsSheet: View {
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
#endif #endif
.toolbar { .toolbar {
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem { dismiss() }
Button(role: .cancel) {
dismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
} }
} }
} }

View File

@@ -31,14 +31,7 @@ struct ChaptersView: View {
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
#endif #endif
.toolbar { .toolbar {
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem { dismiss() }
Button(role: .cancel) {
dismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
} }
.task { .task {
await preloadChapterSheets() await preloadChapterSheets()

View File

@@ -114,14 +114,7 @@ struct ErrorDetailsSheet: View {
#endif #endif
.toolbar { .toolbar {
#if !os(tvOS) #if !os(tvOS)
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem { dismiss() }
Button(role: .cancel) {
dismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
ToolbarItemGroup(placement: .primaryAction) { ToolbarItemGroup(placement: .primaryAction) {
// Copy button // Copy button

View File

@@ -276,14 +276,7 @@ struct QualitySelectorView: View {
#endif #endif
#if !os(tvOS) #if !os(tvOS)
.toolbar { .toolbar {
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem { performDismiss() }
Button(role: .cancel) {
performDismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
} }
#endif #endif
.navigationDestination(for: QualitySelectorDestination.self) { destination in .navigationDestination(for: QualitySelectorDestination.self) { destination in

View File

@@ -125,14 +125,7 @@ struct QueueManagementSheet: View {
ToolbarItem(placement: .cancellationAction) { ToolbarItem(placement: .cancellationAction) {
queueModeMenu queueModeMenu
} }
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem { performDismiss() }
Button(role: .cancel) {
performDismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
} }
} }
.presentationDragIndicator(.visible) .presentationDragIndicator(.visible)

View File

@@ -24,14 +24,7 @@ struct RemoteDevicesSheet: View {
.frame(minWidth: 400, minHeight: 300) .frame(minWidth: 400, minHeight: 300)
#endif #endif
.toolbar { .toolbar {
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem { dismiss() }
Button(role: .cancel) {
dismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
} }
.navigationDestination(item: $selectedDevice) { device in .navigationDestination(item: $selectedDevice) { device in
RemoteControlView(device: device) RemoteControlView(device: device)

View File

@@ -64,14 +64,7 @@ struct AddSourceView: View {
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
#endif #endif
.toolbar { .toolbar {
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem { dismiss() }
Button(role: .cancel) {
dismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
} }
.navigationDestination(isPresented: $navigateToWebDAV) { .navigationDestination(isPresented: $navigateToWebDAV) {
AddWebDAVView( AddWebDAVView(

View File

@@ -54,14 +54,7 @@ struct InstancePickerSheet: View {
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
#endif #endif
.toolbar { .toolbar {
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem { dismiss() }
Button(role: .cancel) {
dismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
} }
} }
.presentationDetents([.medium, .large]) .presentationDetents([.medium, .large])

View File

@@ -278,14 +278,7 @@ private struct LogEntryDetailView: View {
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
#endif #endif
.toolbar { .toolbar {
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem { dismiss() }
Button(role: .cancel) {
dismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
} }
} }
.presentationDetents([.medium, .large]) .presentationDetents([.medium, .large])

View File

@@ -215,14 +215,7 @@ struct PeerTubeInstancesExploreView: View {
@ToolbarContentBuilder @ToolbarContentBuilder
private var toolbarContent: some ToolbarContent { private var toolbarContent: some ToolbarContent {
#if !os(tvOS) #if !os(tvOS)
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem { dismiss() }
Button(role: .cancel) {
dismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
#endif #endif
ToolbarItem(placement: .primaryAction) { ToolbarItem(placement: .primaryAction) {

View File

@@ -187,14 +187,7 @@ struct TapGesturesSettingsView: View {
TapZoneActionPicker(position: position, action: binding) TapZoneActionPicker(position: position, action: binding)
.toolbar { .toolbar {
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem { selectedZonePosition = nil }
Button(role: .cancel) {
selectedZonePosition = nil
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
} }
} }

View File

@@ -328,15 +328,7 @@ struct SettingsView: View {
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbar { .toolbar {
if showCloseButton { if showCloseButton {
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem(identifier: "settings.doneButton") { dismiss() }
Button(role: .cancel) {
dismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
.accessibilityIdentifier("settings.doneButton")
}
} }
} }
.accessibilityIdentifier("settings.view") .accessibilityIdentifier("settings.view")

View File

@@ -89,22 +89,7 @@ struct PlaylistSelectorSheet: View {
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
#endif #endif
.toolbar { .toolbar {
#if os(macOS) sheetCloseToolbarItem { dismiss() }
ToolbarItem(placement: .cancellationAction) {
Button(String(localized: "common.close"), role: .cancel) {
dismiss()
}
}
#else
ToolbarItem(placement: .confirmationAction) {
Button(role: .cancel) {
dismiss()
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
#endif
} }
#endif #endif
.sheet(isPresented: $showingNewPlaylist) { .sheet(isPresented: $showingNewPlaylist) {

View File

@@ -1973,14 +1973,7 @@ struct VideoInfoView: View {
#endif #endif
#if !os(tvOS) #if !os(tvOS)
.toolbar { .toolbar {
ToolbarItem(placement: .confirmationAction) { sheetCloseToolbarItem { showingCommentsSheet = false }
Button(role: .cancel) {
showingCommentsSheet = false
} label: {
Label(String(localized: "common.close"), systemImage: "xmark")
.labelStyle(.iconOnly)
}
}
} }
#endif #endif
} }