Some checks are pending
CI / test (push) Waiting to run
Tighten the inbox, detail, and watch layouts so approval actions feel denser and more direct across compact surfaces.
136 lines
5.2 KiB
Swift
136 lines
5.2 KiB
Swift
import SwiftUI
|
|
|
|
#if os(macOS)
|
|
import AppKit
|
|
#elseif canImport(UIKit)
|
|
import UIKit
|
|
#endif
|
|
|
|
public enum IdP {
|
|
public static let tint = Color("IdPTint")
|
|
public static let cardRadius: CGFloat = 12
|
|
public static let controlRadius: CGFloat = 8
|
|
public static let badgeRadius: CGFloat = 999
|
|
|
|
static func horizontalPadding(compact: Bool) -> CGFloat {
|
|
compact ? 16 : 24
|
|
}
|
|
|
|
static func verticalPadding(compact: Bool) -> CGFloat {
|
|
compact ? 16 : 24
|
|
}
|
|
}
|
|
|
|
private enum ShadcnHex {
|
|
// Light
|
|
static let bg = Color(red: 1, green: 1, blue: 1)
|
|
static let fg = Color(red: 0.039, green: 0.039, blue: 0.043)
|
|
static let muted = Color(red: 0.957, green: 0.957, blue: 0.961)
|
|
static let mutedFg = Color(red: 0.443, green: 0.443, blue: 0.478)
|
|
static let border = Color(red: 0.894, green: 0.894, blue: 0.906)
|
|
static let card = Color(red: 1, green: 1, blue: 1)
|
|
static let primary = Color(red: 0.094, green: 0.094, blue: 0.106)
|
|
static let primaryFg = Color(red: 0.980, green: 0.980, blue: 0.980)
|
|
static let accentSoft = Color(red: 0.961, green: 0.953, blue: 1.0)
|
|
static let destructive = Color(red: 0.937, green: 0.267, blue: 0.267)
|
|
static let ok = Color(red: 0.086, green: 0.639, blue: 0.290)
|
|
static let warn = Color(red: 0.918, green: 0.702, blue: 0.031)
|
|
|
|
// Dark
|
|
static let bgDark = Color(red: 0.035, green: 0.035, blue: 0.043)
|
|
static let fgDark = Color(red: 0.980, green: 0.980, blue: 0.980)
|
|
static let mutedDark = Color(red: 0.094, green: 0.094, blue: 0.106)
|
|
static let mutedFgDark = Color(red: 0.631, green: 0.631, blue: 0.667)
|
|
static let borderDark = Color(red: 0.153, green: 0.153, blue: 0.165)
|
|
static let cardDark = Color(red: 0.047, green: 0.047, blue: 0.059)
|
|
static let primaryDark = Color(red: 0.980, green: 0.980, blue: 0.980)
|
|
static let primaryFgDark = Color(red: 0.094, green: 0.094, blue: 0.106)
|
|
static let accentSoftDark = Color(red: 0.102, green: 0.086, blue: 0.180)
|
|
static let destructiveDark = Color(red: 0.498, green: 0.114, blue: 0.114)
|
|
}
|
|
|
|
private extension Color {
|
|
static func idpAdaptive(light: Color, dark: Color) -> Color {
|
|
#if os(macOS)
|
|
Color(nsColor: NSColor(name: nil) { appearance in
|
|
let matched = appearance.bestMatch(from: [.darkAqua, .vibrantDark, .aqua, .vibrantLight])
|
|
let isDark = matched == .darkAqua || matched == .vibrantDark
|
|
return NSColor(isDark ? dark : light)
|
|
})
|
|
#elseif canImport(UIKit) && !os(watchOS)
|
|
Color(uiColor: UIColor { traits in
|
|
UIColor(traits.userInterfaceStyle == .dark ? dark : light)
|
|
})
|
|
#elseif os(watchOS)
|
|
dark
|
|
#else
|
|
light
|
|
#endif
|
|
}
|
|
}
|
|
|
|
extension Color {
|
|
static let idpBackground = Color.idpAdaptive(light: ShadcnHex.bg, dark: ShadcnHex.bgDark)
|
|
static let idpForeground = Color.idpAdaptive(light: ShadcnHex.fg, dark: ShadcnHex.fgDark)
|
|
static let idpMuted = Color.idpAdaptive(light: ShadcnHex.muted, dark: ShadcnHex.mutedDark)
|
|
static let idpMutedForeground = Color.idpAdaptive(light: ShadcnHex.mutedFg, dark: ShadcnHex.mutedFgDark)
|
|
static let idpBorder = Color.idpAdaptive(light: ShadcnHex.border, dark: ShadcnHex.borderDark)
|
|
static let idpCard = Color.idpAdaptive(light: ShadcnHex.card, dark: ShadcnHex.cardDark)
|
|
static let idpPrimary = Color.idpAdaptive(light: ShadcnHex.primary, dark: ShadcnHex.primaryDark)
|
|
static let idpPrimaryForeground = Color.idpAdaptive(light: ShadcnHex.primaryFg, dark: ShadcnHex.primaryFgDark)
|
|
static let idpAccentSoft = Color.idpAdaptive(light: ShadcnHex.accentSoft, dark: ShadcnHex.accentSoftDark)
|
|
static let idpDestructive = Color.idpAdaptive(light: ShadcnHex.destructive, dark: ShadcnHex.destructiveDark)
|
|
static let idpOK = ShadcnHex.ok
|
|
static let idpWarn = ShadcnHex.warn
|
|
|
|
static var idpGroupedBackground: Color { idpBackground }
|
|
static var idpSecondaryGroupedBackground: Color { idpCard }
|
|
static var idpTertiaryFill: Color { idpMuted }
|
|
static var idpSeparator: Color { idpBorder }
|
|
}
|
|
|
|
extension View {
|
|
func idpScreenPadding(compact: Bool) -> some View {
|
|
padding(.horizontal, IdP.horizontalPadding(compact: compact))
|
|
.padding(.vertical, IdP.verticalPadding(compact: compact))
|
|
}
|
|
|
|
@ViewBuilder
|
|
func idpInlineNavigationTitle() -> some View {
|
|
#if os(macOS)
|
|
self
|
|
#else
|
|
navigationBarTitleDisplayMode(.inline)
|
|
#endif
|
|
}
|
|
|
|
@ViewBuilder
|
|
func idpTabBarChrome() -> some View {
|
|
#if os(macOS)
|
|
self
|
|
#else
|
|
toolbarBackground(.visible, for: .tabBar)
|
|
.toolbarBackground(.regularMaterial, for: .tabBar)
|
|
#endif
|
|
}
|
|
|
|
@ViewBuilder
|
|
func idpSearchable(text: Binding<String>, isPresented: Binding<Bool>) -> some View {
|
|
#if os(macOS)
|
|
searchable(text: text, isPresented: isPresented)
|
|
#else
|
|
searchable(text: text, isPresented: isPresented, placement: .navigationBarDrawer(displayMode: .automatic))
|
|
#endif
|
|
}
|
|
}
|
|
|
|
extension ToolbarItemPlacement {
|
|
static var idpTrailingToolbar: ToolbarItemPlacement {
|
|
#if os(macOS)
|
|
.primaryAction
|
|
#else
|
|
.topBarTrailing
|
|
#endif
|
|
}
|
|
}
|