Files
MLBApp/mlbTVOS/Views/Components/LinescoreView.swift
2026-03-26 15:37:31 -05:00

109 lines
4.2 KiB
Swift

import SwiftUI
struct LinescoreView: View {
let linescore: StatsLinescore
let awayCode: String
let homeCode: String
private var totalInnings: Int {
linescore.scheduledInnings ?? 9
}
var body: some View {
VStack(spacing: 0) {
// Header
HStack(spacing: 0) {
Text("")
.frame(width: 70, alignment: .leading)
ForEach(1...totalInnings, id: \.self) { inning in
let isCurrent = inning == linescore.currentInning
HStack(spacing: 0) {
if inning > 1 && inning % 3 == 1 {
Divider()
.frame(width: 1, height: 18)
.background(.secondary.opacity(0.3))
.padding(.trailing, 4)
}
Text("\(inning)")
.font(.callout.weight(.semibold).monospacedDigit())
.foregroundStyle(isCurrent ? .primary : .secondary)
.frame(width: 44)
}
}
Divider().frame(width: 1, height: 20).padding(.horizontal, 6)
ForEach(["R", "H", "E"], id: \.self) { label in
Text(label)
.font(.callout.weight(.bold).monospacedDigit())
.foregroundStyle(.secondary)
.frame(width: 48)
}
}
.padding(.vertical, 10)
.background(.ultraThinMaterial)
Divider()
teamRow(code: awayCode, innings: linescore.innings ?? [], side: .away, totals: linescore.teams?.away)
Divider()
teamRow(code: homeCode, innings: linescore.innings ?? [], side: .home, totals: linescore.teams?.home)
}
.clipShape(RoundedRectangle(cornerRadius: 10))
.background(.regularMaterial)
}
private enum Side { case away, home }
@ViewBuilder
private func teamRow(code: String, innings: [StatsInningScore], side: Side, totals: StatsLinescoreTotals?) -> some View {
HStack(spacing: 0) {
Text(code)
.font(.callout.weight(.bold))
.foregroundStyle(TeamAssets.color(for: code))
.frame(width: 70, alignment: .leading)
ForEach(1...totalInnings, id: \.self) { inning in
let runs = inningRuns(innings: innings, inning: inning, side: side)
let isCurrent = inning == linescore.currentInning
HStack(spacing: 0) {
if inning > 1 && inning % 3 == 1 {
Divider()
.frame(width: 1, height: 22)
.background(.secondary.opacity(0.2))
.padding(.trailing, 4)
}
Text(runs.map { "\($0)" } ?? "-")
.font(.callout.weight(runs != nil ? .semibold : .regular).monospacedDigit())
.foregroundStyle(runs == nil ? .tertiary : isCurrent ? .primary : .secondary)
.frame(width: 44)
}
}
Divider().frame(width: 1, height: 24).padding(.horizontal, 6)
Text(totals?.runs.map { "\($0)" } ?? "-")
.font(.callout.weight(.bold).monospacedDigit())
.frame(width: 48)
Text(totals?.hits.map { "\($0)" } ?? "-")
.font(.callout.weight(.medium).monospacedDigit())
.foregroundStyle(.secondary)
.frame(width: 48)
Text(totals?.errors.map { "\($0)" } ?? "-")
.font(.callout.weight(.medium).monospacedDigit())
.foregroundStyle(.secondary)
.frame(width: 48)
}
.padding(.vertical, 12)
}
private func inningRuns(innings: [StatsInningScore], inning: Int, side: Side) -> Int? {
guard let data = innings.first(where: { $0.num == inning }) else { return nil }
switch side {
case .away: return data.away?.runs
case .home: return data.home?.runs
}
}
}