fix: sync download table header layout

This commit is contained in:
nimbold
2026-06-01 05:45:50 +03:30
parent caa20c8c2c
commit bde506bbf5
+109 -84
View File
@@ -232,38 +232,36 @@ private struct DownloadTable: View {
} }
.padding(.horizontal, 12) .padding(.horizontal, 12)
.padding(.vertical, 8) .padding(.vertical, 8)
tableHeader GeometryReader { proxy in
Divider() let tableWidth = max(totalWidth, proxy.size.width)
ScrollView([.horizontal, .vertical]) { let trailingWidth = max(0, tableWidth - totalWidth)
LazyVStack(spacing: 0) {
ForEach(sortedItems) { item in ScrollView(.horizontal) {
DownloadRow( VStack(spacing: 0) {
item: item, tableHeader(trailingWidth: trailingWidth)
visibleColumns: orderedVisibleColumns,
columnWidth: { width(for: $0) }
)
.id(item.id)
.background(selection == item.id ? Color.accentColor.opacity(0.12) : Color.clear)
.contentShape(Rectangle())
.onTapGesture {
selection = item.id
}
.contextMenu {
rowContextMenu(for: item)
}
Divider() Divider()
ScrollView(.vertical) {
LazyVStack(spacing: 0) {
ForEach(sortedItems) { item in
tableRow(for: item, tableWidth: tableWidth, trailingWidth: trailingWidth)
Divider()
}
}
.frame(width: tableWidth, alignment: .topLeading)
.frame(maxHeight: .infinity, alignment: .topLeading)
}
.defaultScrollAnchor(.topLeading)
} }
.frame(width: tableWidth, height: proxy.size.height, alignment: .topLeading)
} }
.frame(minWidth: totalWidth, maxHeight: .infinity, alignment: .topLeading) .overlay {
} if items.isEmpty {
.defaultScrollAnchor(.topLeading) ContentUnavailableView(
.overlay { "No Downloads",
if items.isEmpty { systemImage: "arrow.down.circle",
ContentUnavailableView( description: Text("Use Add to paste one or more links.")
"No Downloads", )
systemImage: "arrow.down.circle", }
description: Text("Use Add to paste one or more links.")
)
} }
} }
} }
@@ -301,69 +299,90 @@ private struct DownloadTable: View {
} }
} }
private var tableHeader: some View { private func tableRow(for item: DownloadItem, tableWidth: CGFloat, trailingWidth: CGFloat) -> some View {
DownloadRow(
item: item,
visibleColumns: orderedVisibleColumns,
columnWidth: { width(for: $0) },
trailingWidth: trailingWidth
)
.id(item.id)
.frame(width: tableWidth, alignment: .leading)
.background(selection == item.id ? Color.accentColor.opacity(0.12) : Color.clear)
.contentShape(Rectangle())
.onTapGesture {
selection = item.id
}
.contextMenu {
rowContextMenu(for: item)
}
}
private func tableHeader(trailingWidth: CGFloat) -> some View {
HStack(spacing: 0) { HStack(spacing: 0) {
ForEach(orderedVisibleColumns) { column in ForEach(orderedVisibleColumns) { column in
HStack(spacing: 0) { ZStack(alignment: .trailing) {
ZStack(alignment: .trailing) { Button {
Button { if sortColumn == column {
if sortColumn == column { sortDirection.toggle()
sortDirection.toggle() } else {
} else { sortColumn = column
sortColumn = column sortDirection = .ascending
sortDirection = .ascending
}
} label: {
HStack(spacing: 4) {
Spacer(minLength: 0)
Text(column.rawValue)
.font(.caption.weight(.semibold))
.lineLimit(1)
.truncationMode(.tail)
.multilineTextAlignment(.center)
.layoutPriority(1)
if sortColumn == column {
Image(systemName: sortDirection == .ascending ? "chevron.up" : "chevron.down")
.font(.caption2)
}
Spacer(minLength: 0)
}
.padding(.horizontal, 8)
.frame(width: width(for: column), height: 34, alignment: .center)
.clipped()
} }
.buttonStyle(.plain) } label: {
HStack(spacing: 4) {
Rectangle() Spacer(minLength: 0)
.fill(.secondary.opacity(0.18)) Text(column.rawValue)
.frame(width: 1, height: 20) .font(.caption.weight(.semibold))
.frame(width: 8, height: 34) .lineLimit(1)
.contentShape(Rectangle()) .truncationMode(.tail)
.onHover { isHovering in .multilineTextAlignment(.center)
if isHovering { .layoutPriority(1)
NSCursor.resizeLeftRight.push() if sortColumn == column {
} else { Image(systemName: sortDirection == .ascending ? "chevron.up" : "chevron.down")
NSCursor.pop() .font(.caption2)
}
} }
.gesture( Spacer(minLength: 0)
DragGesture(minimumDistance: 1) }
.onChanged { value in .padding(.horizontal, 8)
let baseWidth = resizeBaseWidths[column] ?? width(for: column) .frame(width: width(for: column), height: 34, alignment: .center)
resizeBaseWidths[column] = baseWidth .clipped()
columnWidths[column] = max(70, baseWidth + value.translation.width)
}
.onEnded { _ in
resizeBaseWidths[column] = nil
}
)
} }
.frame(width: width(for: column), height: 34) .buttonStyle(.plain)
.clipped()
Rectangle()
.fill(.secondary.opacity(0.18))
.frame(width: 1, height: 20)
.frame(width: 8, height: 34)
.contentShape(Rectangle())
.onHover { isHovering in
if isHovering {
NSCursor.resizeLeftRight.push()
} else {
NSCursor.pop()
}
}
.gesture(
DragGesture(minimumDistance: 1)
.onChanged { value in
let baseWidth = resizeBaseWidths[column] ?? width(for: column)
resizeBaseWidths[column] = baseWidth
columnWidths[column] = max(70, baseWidth + value.translation.width)
}
.onEnded { _ in
resizeBaseWidths[column] = nil
}
)
} }
.frame(width: width(for: column), height: 34)
.clipped()
}
if trailingWidth > 0 {
Color.clear
.frame(width: trailingWidth, height: 34)
} }
} }
.frame(minWidth: totalWidth, alignment: .leading)
.background(.bar) .background(.bar)
.contextMenu { .contextMenu {
Section("Columns") { Section("Columns") {
@@ -509,6 +528,7 @@ private struct DownloadRow: View {
let item: DownloadItem let item: DownloadItem
let visibleColumns: [DownloadColumn] let visibleColumns: [DownloadColumn]
let columnWidth: (DownloadColumn) -> CGFloat let columnWidth: (DownloadColumn) -> CGFloat
let trailingWidth: CGFloat
var body: some View { var body: some View {
HStack(alignment: .top, spacing: 0) { HStack(alignment: .top, spacing: 0) {
@@ -519,6 +539,11 @@ private struct DownloadRow: View {
.frame(width: columnWidth(column), alignment: alignment(for: column)) .frame(width: columnWidth(column), alignment: alignment(for: column))
.clipped() .clipped()
} }
if trailingWidth > 0 {
Color.clear
.frame(width: trailingWidth)
}
} }
} }