feat: update Sparkline and Widget components for improved layout and visuals
This commit is contained in:
+22
-6
@@ -46,6 +46,9 @@ struct Sparkline: View {
|
||||
var areaOpacity: Double = 0.25
|
||||
/// Отступ справа в диаметрах точки: линия обрывается, не доходя до края.
|
||||
var trailingRoom: CGFloat = 1.4
|
||||
/// В карточке-герое график уходит под края виджета и левая кромка заливки
|
||||
/// не видна. Отдельно стоящий график гасит её, иначе получается срез.
|
||||
var isFullBleed: Bool = true
|
||||
|
||||
/// Больше точек глазом уже не различить, а форма тяжелеет.
|
||||
private static let maxPoints = 96
|
||||
@@ -66,12 +69,7 @@ struct Sparkline: View {
|
||||
startPoint: .top, endPoint: .bottom
|
||||
)
|
||||
)
|
||||
.mask(
|
||||
LinearGradient(
|
||||
colors: [.white, .white.opacity(0.25)],
|
||||
startPoint: .leading, endPoint: .trailing
|
||||
)
|
||||
)
|
||||
.mask(areaMask)
|
||||
|
||||
// Гало точки лежит под линией, ядро — над ней.
|
||||
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: - Геометрия
|
||||
|
||||
private func points(in size: CGSize) -> [CGPoint] {
|
||||
|
||||
@@ -47,7 +47,7 @@ struct HeroCard: View {
|
||||
values: quote.series,
|
||||
positive: thumb.isPositive,
|
||||
lineWidth: 2,
|
||||
pulseSize: iconSize * 0.52
|
||||
pulseSize: chartHeight * 0.26
|
||||
)
|
||||
} else {
|
||||
// Нет истории — держим ритм макета ровной линией.
|
||||
@@ -76,7 +76,14 @@ struct CryptoRow: View {
|
||||
let compact: Bool
|
||||
|
||||
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 {
|
||||
HStack(spacing: 8) {
|
||||
@@ -91,6 +98,8 @@ struct CryptoRow: View {
|
||||
}
|
||||
.font(.system(size: compact ? 12 : 13, weight: .bold))
|
||||
.lineLimit(1)
|
||||
// Длинные тикеры вроде MATIC/USDT сжимаются, а не обрезаются.
|
||||
.minimumScaleFactor(0.8)
|
||||
|
||||
Text(thumb.quoteCurrencyName)
|
||||
.font(.system(size: compact ? 9 : 10, weight: .medium))
|
||||
@@ -107,9 +116,10 @@ struct CryptoRow: View {
|
||||
lineWidth: 1.5,
|
||||
pulseSize: 7,
|
||||
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) {
|
||||
|
||||
@@ -9,8 +9,12 @@ struct PairHeader: View {
|
||||
let iconSize: CGFloat
|
||||
let titleSize: CGFloat
|
||||
|
||||
/// Знак и отступы привязаны к кеглю заголовка, а не к иконке: размер
|
||||
/// иконки подбирается отдельно и не должен тянуть за собой логотип.
|
||||
private var markSize: CGFloat { titleSize * 1.05 }
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: iconSize * 0.3) {
|
||||
HStack(spacing: titleSize * 0.55) {
|
||||
CoinIconView(currency: thumb.quoteCurrency, data: icon, size: iconSize)
|
||||
|
||||
HStack(spacing: 1) {
|
||||
@@ -30,7 +34,7 @@ struct PairHeader: View {
|
||||
|
||||
Spacer(minLength: 4)
|
||||
|
||||
RapiraMark(size: iconSize * 0.52)
|
||||
RapiraMark(size: markSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ struct SmallWidgetView: View {
|
||||
quote: hero,
|
||||
updatedAt: entry.date,
|
||||
horizontalPadding: padding,
|
||||
iconSize: 24,
|
||||
iconSize: 20,
|
||||
titleSize: 13,
|
||||
priceSize: 21,
|
||||
chartHeight: 38,
|
||||
chartHeight: 40,
|
||||
showFullDate: false,
|
||||
showAbsolute: false
|
||||
)
|
||||
@@ -98,10 +98,10 @@ struct LargeWidgetView: View {
|
||||
quote: hero,
|
||||
updatedAt: entry.date,
|
||||
horizontalPadding: padding,
|
||||
iconSize: 36,
|
||||
iconSize: 28,
|
||||
titleSize: 19,
|
||||
priceSize: 32,
|
||||
chartHeight: 72
|
||||
chartHeight: 70
|
||||
)
|
||||
|
||||
if !rest.isEmpty {
|
||||
|
||||
+5
@@ -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>
|
||||
+16
@@ -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>
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
<key>cryptoExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>rapira-market-widget.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
||||
Reference in New Issue
Block a user