This commit is contained in:
2026-08-12 07:06:01 +07:00
parent 1025658518
commit 9bc96dbc2d
39 changed files with 1682 additions and 658 deletions
+138
View File
@@ -0,0 +1,138 @@
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Проверьте соединение"
}
}
}