Files
yattee/Yattee/Views/Components/SourceListRow.swift
Arkadiusz Fal d8f10e984a Make sources list feel native on macOS
Drop the iOS-grouped rounded card, per-row chevron, and oversized
metrics on macOS. Use tighter padding, smaller icon/title fonts,
uppercase section headers, and top/bottom dividers so the list reads
like a native grouped Mac list. Force .buttonStyle(.plain) on row
buttons/NavigationLinks and add .contentShape(Rectangle()) so the
full row is hit-testable without picking up macOS's default link
styling. iOS and tvOS unchanged.
2026-04-20 21:07:05 +02:00

134 lines
3.8 KiB
Swift

//
// SourceListRow.swift
// Yattee
//
// Row wrapper for source list items with consistent padding and dividers.
//
import SwiftUI
/// A wrapper for source list row content that applies consistent padding and dividers.
///
/// Similar to VideoListRow but with fixed icon width for source rows.
struct SourceListRow<Content: View>: View {
let isLast: Bool
let listStyle: VideoListStyle
@ViewBuilder let content: () -> Content
/// Horizontal padding for row content.
#if os(macOS)
private let horizontalPadding: CGFloat = 12
#else
private let horizontalPadding: CGFloat = 16
#endif
/// Vertical padding for row content.
#if os(macOS)
private let verticalPadding: CGFloat = 8
#else
private let verticalPadding: CGFloat = 12
#endif
/// Width of the icon column.
#if os(macOS)
private let iconWidth: CGFloat = 24
#else
private let iconWidth: CGFloat = 32
#endif
/// Spacing between icon and text.
private let iconTextSpacing: CGFloat = 12
/// Calculated divider leading padding (aligns with text after icon).
private var dividerLeadingPadding: CGFloat {
horizontalPadding + iconWidth + iconTextSpacing
}
var body: some View {
VStack(spacing: 0) {
content()
.padding(.horizontal, horizontalPadding)
.padding(.vertical, verticalPadding)
if !isLast {
divider
}
}
}
// MARK: - Private
@ViewBuilder
private var divider: some View {
#if os(iOS)
Rectangle()
.fill(Color(uiColor: .separator))
.frame(height: 1 / UIScreen.main.scale)
.padding(.leading, dividerLeadingPadding)
#elseif os(macOS)
Rectangle()
.fill(Color(nsColor: .separatorColor))
.frame(height: 1)
.padding(.leading, dividerLeadingPadding)
#else
EmptyView()
#endif
}
}
// MARK: - Preview
#Preview("Inset Style") {
ScrollView {
LazyVStack(spacing: 0) {
ForEach(0..<5) { index in
SourceListRow(isLast: index == 4, listStyle: .inset) {
HStack(spacing: 12) {
Image(systemName: "server.rack")
.font(.title2)
.foregroundStyle(.blue)
.frame(width: 32)
VStack(alignment: .leading) {
Text("Source \(index + 1)")
.font(.headline)
Text("example.com")
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
}
}
}
}
.background(Color.gray.opacity(0.1))
.clipShape(RoundedRectangle(cornerRadius: 10))
.padding()
}
}
#Preview("Plain Style") {
ScrollView {
LazyVStack(spacing: 0) {
ForEach(0..<5) { index in
SourceListRow(isLast: index == 4, listStyle: .plain) {
HStack(spacing: 12) {
Image(systemName: "server.rack")
.font(.title2)
.foregroundStyle(.blue)
.frame(width: 32)
VStack(alignment: .leading) {
Text("Source \(index + 1)")
.font(.headline)
Text("example.com")
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
}
}
}
}
}
}