Files
yattee/Yattee/Views/Player/Panels/PanelPageBar.swift
Arkadiusz Fal d5285b2b83 Add unified page-based details panel on existing layouts
Phase 1 of the player panels redesign:

- New PanelSettings model (pages, regions, layout mode, default
  visibility) stored as an optional field on PlayerControlsLayout with
  an effectivePanelSettings accessor; lenient Codable so future page
  types and settings payloads never corrupt saved presets.
- New Views/Player/Panels content engine: PlayerPanelContentView
  renders a page bar plus the selected page (info, comments, related,
  chapters, queue, tracks), with runtime hiding of empty pages.
- PortraitDetailsPanel and FloatingDetailsPanel now host the shared
  content instead of their duplicated info bodies; drag machinery,
  pills, and fades are untouched.
- Comments list extracted into CommentsListContent shared by the
  overlay and the comments page; comments pill routes to the comments
  page (expanding the portrait panel) when the page is enabled, and
  falls back to the overlay when it is not.
- Tracks page embeds QualitySelectorView via a new isEmbedded mode.
- Deleted the unused video info bodies in ExpandedPlayerSheet+VideoInfo
  and the dead PlayerInfoTab model.
2026-09-21 23:20:45 +02:00

73 lines
2.1 KiB
Swift

//
// PanelPageBar.swift
// Yattee
//
// Scrolling pill row for switching details panel pages.
//
import SwiftUI
#if os(iOS) || os(macOS)
struct PanelPageBar: View {
let pages: [PanelPageConfiguration]
@Binding var selectedPage: PanelPageType
let accentColor: Color
var showLabels: Bool = true
var body: some View {
ScrollViewReader { proxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
ForEach(pages) { page in
pageButton(page.pageType)
.id(page.pageType)
}
}
.padding(.horizontal, 12)
.padding(.vertical, 6)
}
.onChange(of: selectedPage) { _, newValue in
withAnimation(.easeInOut(duration: 0.2)) {
proxy.scrollTo(newValue, anchor: .center)
}
}
}
}
@ViewBuilder
private func pageButton(_ pageType: PanelPageType) -> some View {
let isSelected = pageType == selectedPage
Button {
withAnimation(.easeInOut(duration: 0.2)) {
selectedPage = pageType
}
} label: {
HStack(spacing: 5) {
Image(systemName: pageType.systemImage)
.font(.system(size: 13, weight: .medium))
if showLabels || isSelected {
Text(pageType.displayName)
.font(.subheadline)
.fontWeight(.medium)
.lineLimit(1)
.fixedSize()
}
}
.foregroundStyle(isSelected ? Color.white : Color.primary)
.padding(.horizontal, 12)
.padding(.vertical, 7)
.background {
Capsule()
.fill(isSelected ? AnyShapeStyle(accentColor) : AnyShapeStyle(.quaternary.opacity(0.6)))
}
.contentShape(Capsule())
}
.buttonStyle(.plain)
}
}
#endif