109 lines
4.2 KiB
Swift
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
|
|
}
|
|
}
|
|
}
|