feat: update Sparkline and Widget components for improved layout and visuals

This commit is contained in:
2026-08-14 21:19:32 +07:00
parent 9bc96dbc2d
commit b641198bee
7 changed files with 68 additions and 17 deletions
+22 -6
View File
@@ -46,6 +46,9 @@ struct Sparkline: View {
var areaOpacity: Double = 0.25 var areaOpacity: Double = 0.25
/// Отступ справа в диаметрах точки: линия обрывается, не доходя до края. /// Отступ справа в диаметрах точки: линия обрывается, не доходя до края.
var trailingRoom: CGFloat = 1.4 var trailingRoom: CGFloat = 1.4
/// В карточке-герое график уходит под края виджета и левая кромка заливки
/// не видна. Отдельно стоящий график гасит её, иначе получается срез.
var isFullBleed: Bool = true
/// Больше точек глазом уже не различить, а форма тяжелеет. /// Больше точек глазом уже не различить, а форма тяжелеет.
private static let maxPoints = 96 private static let maxPoints = 96
@@ -66,12 +69,7 @@ struct Sparkline: View {
startPoint: .top, endPoint: .bottom startPoint: .top, endPoint: .bottom
) )
) )
.mask( .mask(areaMask)
LinearGradient(
colors: [.white, .white.opacity(0.25)],
startPoint: .leading, endPoint: .trailing
)
)
// Гало точки лежит под линией, ядро над ней. // Гало точки лежит под линией, ядро над ней.
if showPulse, let last = points.last { if showPulse, let last = points.last {
@@ -104,6 +102,24 @@ struct Sparkline: View {
} }
} }
/// Горизонтальная маска заливки: свечение сосредоточено слева, как в макете.
private var areaMask: LinearGradient {
LinearGradient(
gradient: Gradient(stops: isFullBleed
? [
.init(color: .white, location: 0),
.init(color: .white.opacity(0.25), location: 1),
]
: [
.init(color: .white.opacity(0), location: 0),
.init(color: .white, location: 0.3),
.init(color: .white.opacity(0.3), location: 1),
]),
startPoint: .leading,
endPoint: .trailing
)
}
// MARK: - Геометрия // MARK: - Геометрия
private func points(in size: CGSize) -> [CGPoint] { private func points(in size: CGSize) -> [CGPoint] {
+14 -4
View File
@@ -47,7 +47,7 @@ struct HeroCard: View {
values: quote.series, values: quote.series,
positive: thumb.isPositive, positive: thumb.isPositive,
lineWidth: 2, lineWidth: 2,
pulseSize: iconSize * 0.52 pulseSize: chartHeight * 0.26
) )
} else { } else {
// Нет истории держим ритм макета ровной линией. // Нет истории держим ритм макета ровной линией.
@@ -76,7 +76,14 @@ struct CryptoRow: View {
let compact: Bool let compact: Bool
private var thumb: SymbolThumb { quote.thumb } private var thumb: SymbolThumb { quote.thumb }
private var iconSize: CGFloat { compact ? 24 : 26 } private var iconSize: CGFloat { compact ? 21 : 23 }
/// График занимает почти весь зазор между названием и ценой: на узком
/// поле линия выглядит сплюснутой, поэтому тянем её вширь.
/// В большом виджете строк больше там график ниже.
private var chartSize: CGSize {
compact ? CGSize(width: 92, height: 28) : CGSize(width: 88, height: 26)
}
var body: some View { var body: some View {
HStack(spacing: 8) { HStack(spacing: 8) {
@@ -91,6 +98,8 @@ struct CryptoRow: View {
} }
.font(.system(size: compact ? 12 : 13, weight: .bold)) .font(.system(size: compact ? 12 : 13, weight: .bold))
.lineLimit(1) .lineLimit(1)
// Длинные тикеры вроде MATIC/USDT сжимаются, а не обрезаются.
.minimumScaleFactor(0.8)
Text(thumb.quoteCurrencyName) Text(thumb.quoteCurrencyName)
.font(.system(size: compact ? 9 : 10, weight: .medium)) .font(.system(size: compact ? 9 : 10, weight: .medium))
@@ -107,9 +116,10 @@ struct CryptoRow: View {
lineWidth: 1.5, lineWidth: 1.5,
pulseSize: 7, pulseSize: 7,
areaOpacity: 0.16, areaOpacity: 0.16,
trailingRoom: 0.6 trailingRoom: 0.6,
isFullBleed: false
) )
.frame(width: compact ? 46 : 56, height: 22) .frame(width: chartSize.width, height: chartSize.height)
} }
VStack(alignment: .trailing, spacing: 2) { VStack(alignment: .trailing, spacing: 2) {
+6 -2
View File
@@ -9,8 +9,12 @@ struct PairHeader: View {
let iconSize: CGFloat let iconSize: CGFloat
let titleSize: CGFloat let titleSize: CGFloat
/// Знак и отступы привязаны к кеглю заголовка, а не к иконке: размер
/// иконки подбирается отдельно и не должен тянуть за собой логотип.
private var markSize: CGFloat { titleSize * 1.05 }
var body: some View { var body: some View {
HStack(spacing: iconSize * 0.3) { HStack(spacing: titleSize * 0.55) {
CoinIconView(currency: thumb.quoteCurrency, data: icon, size: iconSize) CoinIconView(currency: thumb.quoteCurrency, data: icon, size: iconSize)
HStack(spacing: 1) { HStack(spacing: 1) {
@@ -30,7 +34,7 @@ struct PairHeader: View {
Spacer(minLength: 4) Spacer(minLength: 4)
RapiraMark(size: iconSize * 0.52) RapiraMark(size: markSize)
} }
} }
} }
+4 -4
View File
@@ -16,10 +16,10 @@ struct SmallWidgetView: View {
quote: hero, quote: hero,
updatedAt: entry.date, updatedAt: entry.date,
horizontalPadding: padding, horizontalPadding: padding,
iconSize: 24, iconSize: 20,
titleSize: 13, titleSize: 13,
priceSize: 21, priceSize: 21,
chartHeight: 38, chartHeight: 40,
showFullDate: false, showFullDate: false,
showAbsolute: false showAbsolute: false
) )
@@ -98,10 +98,10 @@ struct LargeWidgetView: View {
quote: hero, quote: hero,
updatedAt: entry.date, updatedAt: entry.date,
horizontalPadding: padding, horizontalPadding: padding,
iconSize: 36, iconSize: 28,
titleSize: 19, titleSize: 19,
priceSize: 32, priceSize: 32,
chartHeight: 72 chartHeight: 70
) )
if !rest.isEmpty { if !rest.isEmpty {
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildLocationStyle</key>
<string>UseAppPreferences</string>
<key>CompilationCachingSetting</key>
<string>Default</string>
<key>CustomBuildLocationType</key>
<string>RelativeToDerivedData</string>
<key>DerivedDataLocationStyle</key>
<string>Default</string>
<key>ShowSharedSchemesAutomaticallyEnabled</key>
<true/>
</dict>
</plist>
@@ -7,7 +7,7 @@
<key>cryptoExtension.xcscheme_^#shared#^_</key> <key>cryptoExtension.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>1</integer> <integer>0</integer>
</dict> </dict>
<key>rapira-market-widget.xcscheme_^#shared#^_</key> <key>rapira-market-widget.xcscheme_^#shared#^_</key>
<dict> <dict>