Refactor Dashboard traffic cards with line charts
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct TrafficLineChart: View {
|
||||
private let data: [CGFloat]
|
||||
private let lineColor: Color
|
||||
private let gridColor: Color
|
||||
private let chartHeight: CGFloat
|
||||
|
||||
public init(
|
||||
data: [CGFloat],
|
||||
lineColor: Color = .primary,
|
||||
gridColor: Color = Color.secondary.opacity(0.3),
|
||||
chartHeight: CGFloat = 60
|
||||
) {
|
||||
self.data = data
|
||||
self.lineColor = lineColor
|
||||
self.gridColor = gridColor
|
||||
self.chartHeight = chartHeight
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Canvas { context, size in
|
||||
let width = size.width
|
||||
let height = size.height
|
||||
let maxValue = max((data.max() ?? 1) * 1.2, 1)
|
||||
let pointCount = data.count
|
||||
|
||||
drawGrid(context: context, width: width, height: height)
|
||||
|
||||
guard pointCount > 1 else { return }
|
||||
|
||||
let spacing = width / CGFloat(pointCount - 1)
|
||||
let points = data.enumerated().map { index, value in
|
||||
let x = CGFloat(index) * spacing
|
||||
let normalizedValue = min(max(value / maxValue, 0), 1)
|
||||
let y = height * (1 - normalizedValue)
|
||||
return CGPoint(x: x, y: y)
|
||||
}
|
||||
|
||||
drawLine(context: context, points: points, height: height)
|
||||
}
|
||||
.frame(height: chartHeight)
|
||||
}
|
||||
|
||||
private func drawGrid(context: GraphicsContext, width: CGFloat, height: CGFloat) {
|
||||
let gridLineCount = 3
|
||||
for i in 0 ... gridLineCount {
|
||||
let y = height * CGFloat(i) / CGFloat(gridLineCount)
|
||||
var path = Path()
|
||||
path.move(to: CGPoint(x: 0, y: y))
|
||||
path.addLine(to: CGPoint(x: width, y: y))
|
||||
context.stroke(
|
||||
path,
|
||||
with: .color(gridColor),
|
||||
style: StrokeStyle(lineWidth: 1, dash: [5, 5])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func drawLine(context: GraphicsContext, points: [CGPoint], height: CGFloat) {
|
||||
guard !points.isEmpty else { return }
|
||||
|
||||
var linePath = Path()
|
||||
linePath.move(to: points[0])
|
||||
for point in points.dropFirst() {
|
||||
linePath.addLine(to: point)
|
||||
}
|
||||
|
||||
context.stroke(
|
||||
linePath,
|
||||
with: .color(lineColor),
|
||||
style: StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round)
|
||||
)
|
||||
|
||||
var fillPath = linePath
|
||||
if let lastPoint = points.last {
|
||||
fillPath.addLine(to: CGPoint(x: lastPoint.x, y: height))
|
||||
fillPath.addLine(to: CGPoint(x: 0, y: height))
|
||||
fillPath.addLine(to: points[0])
|
||||
}
|
||||
|
||||
context.fill(fillPath, with: .color(lineColor.opacity(0.1)))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user