Files
rapira-market-widget/crypto/WidgetComponents.swift
T
2026-08-12 07:06:01 +07:00

139 lines
4.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import SwiftUI
// MARK: - Заголовок пары
/// Иконка монеты + «BTC/USDT» + знак Rapira справа.
struct PairHeader: View {
let thumb: SymbolThumb
let icon: Data?
let iconSize: CGFloat
let titleSize: CGFloat
var body: some View {
HStack(spacing: iconSize * 0.3) {
CoinIconView(currency: thumb.quoteCurrency, data: icon, size: iconSize)
HStack(spacing: 1) {
Text(thumb.quoteCurrency)
.foregroundStyle(
LinearGradient(
colors: [.white, .white.opacity(0.85)],
startPoint: .leading, endPoint: .trailing
)
)
Text("/\(thumb.baseCurrency)")
.foregroundStyle(.white.opacity(0.5))
}
.font(.system(size: titleSize, weight: .bold))
.lineLimit(1)
.minimumScaleFactor(0.6)
Spacer(minLength: 4)
RapiraMark(size: iconSize * 0.52)
}
}
}
// MARK: - Цена
/// Крупная цена с приглушённым символом валюты: «70 324,00 USDT».
struct PriceLabel: View {
let thumb: SymbolThumb
let size: CGFloat
var body: some View {
HStack(alignment: .firstTextBaseline, spacing: size * 0.16) {
Text(thumb.priceFormatted)
.font(.system(size: size, weight: .bold))
.monospacedDigit()
.foregroundStyle(
LinearGradient(
colors: [.white, .white.opacity(0.7)],
startPoint: .leading, endPoint: .trailing
)
)
.lineLimit(1)
.minimumScaleFactor(0.5)
Text(thumb.baseSymbol)
.font(.system(size: size * 0.42, weight: .semibold))
.foregroundStyle(.white.opacity(0.5))
}
}
}
// MARK: - Изменение за 24ч
/// «+1 676,00 USDT · 2.34%» одной зелёной/красной строкой, без подложки.
struct StatsLine: View {
let thumb: SymbolThumb
let fontSize: CGFloat
var showAbsolute = true
var body: some View {
HStack(spacing: fontSize * 0.4) {
if showAbsolute {
Text(thumb.changeFormatted)
Text("·")
.foregroundStyle(RapiraPalette.stat(thumb.isPositive).opacity(0.45))
Text(thumb.changePercentPlain)
} else {
Text(thumb.changePercent)
}
}
.font(.system(size: fontSize, weight: .semibold))
.monospacedDigit()
.foregroundStyle(RapiraPalette.stat(thumb.isPositive))
.lineLimit(1)
.minimumScaleFactor(0.7)
}
}
// MARK: - Время обновления
/// Подпись «27 октября 17:10» под парой как в макете.
struct UpdatedAt: View {
let date: Date
let fontSize: CGFloat
var withDate = true
var body: some View {
Text(MarketFormatting.updated(date, withDate: withDate))
.font(.system(size: fontSize, weight: .medium))
.foregroundStyle(.white.opacity(0.5))
.lineLimit(1)
}
}
// MARK: - Пустое состояние
struct EmptyState: View {
enum Reason {
/// Ни одна выбранная пара не найдена в данных.
case noSelection
/// Ни сети, ни кэша.
case noData
}
let reason: Reason
var body: some View {
VStack(spacing: 8) {
RapiraMark(size: 26)
Text(message)
.font(.system(size: 11, weight: .medium))
.multilineTextAlignment(.center)
.foregroundStyle(.white.opacity(0.45))
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
private var message: String {
switch reason {
case .noSelection: "Выберите пары\nв приложении"
case .noData: "Нет данных.\nПроверьте соединение"
}
}
}