feat: add iOS support and harden receipt scanning
This commit is contained in:
96
app/ios/ScanReceipts/Design/ZenithButtonStyle.swift
Normal file
96
app/ios/ScanReceipts/Design/ZenithButtonStyle.swift
Normal file
@@ -0,0 +1,96 @@
|
||||
import SwiftUI
|
||||
|
||||
/// Buttons per `DESIGN (1).md` → Components → Buttons: "Solid #000000
|
||||
/// background with #FFFFFF text for primary actions. 1px solid #000000
|
||||
/// border with no fill for secondary. All buttons are rectangular [0px
|
||||
/// radius] with no padding-inline under 24px." And → Elevation: "On hover,
|
||||
/// elements do not lift; they shift color... a button fill turns from Black
|
||||
/// to Slate Blue-Grey" — the pressed state here plays the role "hover" plays
|
||||
/// on the web.
|
||||
/// Every Zenith button style dims to this opacity when `.disabled(true)` —
|
||||
/// centralised here so a disabled `.zenithPrimary` etc. dims automatically,
|
||||
/// the way `.borderedProminent`/`.bordered` do out of the box. Without this,
|
||||
/// each call site would need its own manual `.opacity(isEnabled ? 1 : 0.4)`,
|
||||
/// which is easy to forget (an earlier pass of this app's screens did, in
|
||||
/// fact, forget it in a couple of places before this was added centrally).
|
||||
private let zenithDisabledOpacity: Double = 0.4
|
||||
|
||||
struct ZenithPrimaryButtonStyle: ButtonStyle {
|
||||
var isDestructive = false
|
||||
|
||||
@Environment(\.isEnabled) private var isEnabled
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.font(.zenithBodyMd)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.white)
|
||||
.frame(maxWidth: .infinity, minHeight: 50)
|
||||
.padding(.horizontal, ZenithSpacing.md)
|
||||
.background(fill(pressed: configuration.isPressed))
|
||||
// Sharp corners are the point of this system — a plain
|
||||
// Rectangle fill, never `.cornerRadius`.
|
||||
.opacity(isEnabled ? 1 : zenithDisabledOpacity)
|
||||
}
|
||||
|
||||
private func fill(pressed: Bool) -> Color {
|
||||
if isDestructive { return pressed ? Color.zenithError.opacity(0.8) : .zenithError }
|
||||
return pressed ? .zenithSlate : .zenithBlack
|
||||
}
|
||||
}
|
||||
|
||||
struct ZenithSecondaryButtonStyle: ButtonStyle {
|
||||
var isDestructive = false
|
||||
|
||||
@Environment(\.isEnabled) private var isEnabled
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.font(.zenithBodyMd)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(isDestructive ? Color.zenithError : Color.zenithBlack)
|
||||
.frame(maxWidth: .infinity, minHeight: 50)
|
||||
.padding(.horizontal, ZenithSpacing.md)
|
||||
.background(Color.zenithSurface)
|
||||
.overlay(
|
||||
// Border THICKENS on press instead of the fill lifting —
|
||||
// same "no lift, shift instead" rule as the primary style.
|
||||
Rectangle()
|
||||
.strokeBorder(
|
||||
isDestructive ? Color.zenithError : Color.zenithBlack,
|
||||
lineWidth: configuration.isPressed ? 2 : 1
|
||||
)
|
||||
)
|
||||
.opacity(isEnabled ? 1 : zenithDisabledOpacity)
|
||||
}
|
||||
}
|
||||
|
||||
/// A plain-text button with no fill/border at all — for tertiary actions
|
||||
/// ("Noch kein Konto? Registrieren", "Abbrechen") that shouldn't compete
|
||||
/// visually with a screen's primary/secondary buttons.
|
||||
struct ZenithPlainButtonStyle: ButtonStyle {
|
||||
var color: Color = .zenithMuted
|
||||
|
||||
@Environment(\.isEnabled) private var isEnabled
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.font(.zenithBodySm)
|
||||
.foregroundStyle(configuration.isPressed ? color.opacity(0.6) : color)
|
||||
.opacity(isEnabled ? 1 : zenithDisabledOpacity)
|
||||
}
|
||||
}
|
||||
|
||||
extension ButtonStyle where Self == ZenithPrimaryButtonStyle {
|
||||
static var zenithPrimary: ZenithPrimaryButtonStyle { ZenithPrimaryButtonStyle() }
|
||||
static var zenithPrimaryDestructive: ZenithPrimaryButtonStyle { ZenithPrimaryButtonStyle(isDestructive: true) }
|
||||
}
|
||||
|
||||
extension ButtonStyle where Self == ZenithSecondaryButtonStyle {
|
||||
static var zenithSecondary: ZenithSecondaryButtonStyle { ZenithSecondaryButtonStyle() }
|
||||
static var zenithSecondaryDestructive: ZenithSecondaryButtonStyle { ZenithSecondaryButtonStyle(isDestructive: true) }
|
||||
}
|
||||
|
||||
extension ButtonStyle where Self == ZenithPlainButtonStyle {
|
||||
static var zenithPlain: ZenithPlainButtonStyle { ZenithPlainButtonStyle() }
|
||||
}
|
||||
Reference in New Issue
Block a user