Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify background configuration components #695

Merged
merged 11 commits into from
Oct 18, 2023
Binary file modified Snapshots/iPad/TagTests/testTags.1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Snapshots/iPhone/TagTests/testTags.1.png
sjavora marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
127 changes: 0 additions & 127 deletions Sources/Orbit/Components/Alert.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,133 +108,6 @@ public extension Alert {
}
}

// MARK: - Types

/// Button style matching Orbit ``Alert`` component.
public struct AlertButtonStyle: PrimitiveButtonStyle {

@Environment(\.buttonPriority) private var buttonPriority
@Environment(\.isSubtle) private var isSubtle
@Environment(\.status) private var status

public init() {}

public func makeBody(configuration: Configuration) -> some View {
OrbitCustomButtonContent(
configuration: configuration,
textActiveColor: textActiveColor,
horizontalPadding: .small,
verticalPadding: .xSmall, // = 32 height @ normal size
cornerRadius: BorderRadius.desktop,
hapticFeedback: resolvedStatus.defaultHapticFeedback
) {
EmptyView()
} disclosureIcon: {
EmptyView()
} background: {
background
} backgroundActive: {
backgroundActive
}
.textFontWeight(.medium)
.textSize(.small)
.textColor(textColor)
}

var textColor: Color {
switch (resolvedPriority, isSubtle) {
case (.primary, _): return .whiteNormal
case (.secondary, true): return .inkDark
case (.secondary, false): return resolvedStatus.darkColor
}
}

var textActiveColor: Color {
switch (resolvedPriority, isSubtle) {
case (.primary, _): return .whiteNormal
case (.secondary, true): return .inkDarkActive
case (.secondary, false): return resolvedStatus.darkActiveColor
}
}

@ViewBuilder var background: some View {
switch (resolvedPriority, isSubtle) {
case (.primary, _): resolvedStatus.color
case (.secondary, true): Color.inkDark.opacity(0.1)
case (.secondary, false): resolvedStatus.darkColor.opacity(0.12)
}
}

@ViewBuilder var backgroundActive: some View {
switch (resolvedPriority, isSubtle) {
case (.primary, _): resolvedStatus.activeColor
case (.secondary, true): Color.inkDark.opacity(0.2)
case (.secondary, false): resolvedStatus.darkColor.opacity(0.24)
}
}

var resolvedPriority: ButtonPriority {
buttonPriority ?? .primary
}

var resolvedStatus: Status {
status ?? .info
}
}

struct IsSubtleKey: EnvironmentKey {
static let defaultValue = false
}

extension EnvironmentValues {
var isSubtle: Bool {
get { self[IsSubtleKey.self] }
set { self[IsSubtleKey.self] = newValue }
}
}

/// A builder that constructs buttons for the ``Alert`` component.
@resultBuilder
public enum AlertButtonsBuilder {

public static func buildBlock(_ empty: EmptyView) -> EmptyView {
empty
}

public static func buildBlock(_ primary: some View) -> some View {
primary
.suppressButtonStyle()
.buttonStyle(AlertButtonStyle())
.buttonSize(.compact)
.accessibility(.alertButtonPrimary)
}

public static func buildBlock(_ primary: some View, _ secondary: some View) -> some View {
HStack(alignment: .top, spacing: .xSmall) {
primary
.accessibility(.alertButtonPrimary)

secondary
.accessibility(.alertButtonSecondary)
.buttonPriority(.secondary)
}
.suppressButtonStyle()
.buttonStyle(AlertButtonStyle())
}

public static func buildOptional<V: View>(_ component: V?) -> V? {
component
}

public static func buildEither<T: View, F: View>(first view: T) -> _ConditionalContent<T, F> {
.init(content: .trueView(view))
}

public static func buildEither<T: View, F: View>(second view: F) -> _ConditionalContent<T, F> {
.init(content: .falseView(view))
}
}

// MARK: - Previews
struct AlertPreviews: PreviewProvider {

Expand Down
35 changes: 0 additions & 35 deletions Sources/Orbit/Components/AlertInline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,41 +54,6 @@ public extension AlertInline {
}
}

// MARK: - Types
struct AlertInlineButtonStyle: PrimitiveButtonStyle {

@Environment(\.status) private var status

func makeBody(configuration: Configuration) -> some View {
OrbitCustomButtonContent(
configuration: configuration,
textActiveColor: resolvedStatus.darkHoverColor,
horizontalPadding: 0,
horizontalLabelPadding: 0,
verticalPadding: 6, // = 32 height @ normal size
horizontalBackgroundPadding: .xSmall,
verticalBackgroundPadding: .xxxSmall,
spacing: .xSmall,
hapticFeedback: resolvedStatus.defaultHapticFeedback
) {
EmptyView()
} disclosureIcon: {
EmptyView()
} background: {
Color.clear
} backgroundActive: {
resolvedStatus.color.opacity(0.24)
}
.textFontWeight(.medium)
.textColor(resolvedStatus.darkColor)
.idealSize()
}

var resolvedStatus: Status {
status ?? .info
}
}

/// A builder that constructs buttons for the ``AlertInline`` component.
@resultBuilder
public enum AlertInlineButtonsBuilder {
Expand Down
90 changes: 47 additions & 43 deletions Sources/Orbit/Components/Badge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import SwiftUI
/// - Note: [Orbit definition](https://orbit.kiwi/components/badge/)
public struct Badge<LeadingIcon: View, TrailingIcon: View>: View, PotentiallyEmptyView {

@Environment(\.status) private var status
@Environment(\.backgroundColor) private var backgroundColor
@Environment(\.sizeCategory) private var sizeCategory
@Environment(\.status) private var status
@Environment(\.textColor) private var textColor

private let label: String
private let leadingIcon: LeadingIcon
Expand All @@ -22,64 +24,68 @@ public struct Badge<LeadingIcon: View, TrailingIcon: View>: View, PotentiallyEmp
leadingIcon
.iconSize(.small)
.font(.system(size: Icon.Size.small.value))
.foregroundColor(labelColor)
.foregroundColor(resolvedLabelColor)

Text(label)
.textSize(.small)
.fontWeight(.medium)
.textLinkColor(.custom(labelColor))
.textLinkColor(.custom(resolvedLabelColor))
.frame(minWidth: minTextWidth)

trailingIcon
.iconSize(.small)
.font(.system(size: Icon.Size.small.value))
.foregroundColor(labelColor)
.foregroundColor(resolvedLabelColor)
}
.textColor(labelColor)
.textColor(resolvedLabelColor)
.padding(.vertical, .xxSmall) // = 24 height @ normal size
.padding(.horizontal, .xSmall)
.background(
background
.clipShape(shape)
resolvedBackground
.clipShape(Capsule())
)
}
}

@ViewBuilder var background: some View {
switch type {
case .light: Color.whiteDarker
case .lightInverted: Color.inkDark
case .neutral: Color.cloudLight
case .status(let status, true): (status ?? defaultStatus).color
case .status(let status, false): (status ?? defaultStatus).lightColor
case .custom(_, _, let backgroundColor): backgroundColor
case .gradient(let gradient): gradient.background
@ViewBuilder var resolvedBackground: some View {
if let backgroundColor {
backgroundColor.inactiveView
} else {
defaultBackgroundColor
}
}

var minTextWidth: CGFloat {
Text.Size.small.lineHeight * sizeCategory.ratio - .xSmall
}

var shape: some InsettableShape {
Capsule()
}

var isEmpty: Bool {
leadingIcon.isEmpty && label.isEmpty && trailingIcon.isEmpty

var resolvedLabelColor: Color {
textColor ?? labelColor
}

var labelColor: Color {
switch type {
case .light: return .inkDark
case .lightInverted: return .whiteNormal
case .neutral: return .inkDark
case .status(let status, false): return (status ?? defaultStatus).darkColor
case .status(_, true): return .whiteNormal
case .custom(let labelColor, _, _): return labelColor
case .gradient: return .whiteNormal
}
}

var defaultBackgroundColor: Color {
switch type {
case .light: return .whiteDarker
case .lightInverted: return .inkDark
case .neutral: return .cloudLight
case .status(let status, true): return (status ?? defaultStatus).color
case .status(let status, false): return (status ?? defaultStatus).lightColor
}
}

var minTextWidth: CGFloat {
Text.Size.small.lineHeight * sizeCategory.ratio - .xSmall
}

var isEmpty: Bool {
leadingIcon.isEmpty && label.isEmpty && trailingIcon.isEmpty
}

var defaultStatus: Status {
status ?? .info
Expand All @@ -91,6 +97,8 @@ public extension Badge {

/// Creates Orbit Badge component.
///
/// Custom background color be specified using `.backgroundColor()` modifier.
///
/// - Parameters:
/// - type: A visual style of component. A `status` style can be optionally modified using `status()` modifier when `nil` value is provided.
init(
Expand All @@ -108,6 +116,8 @@ public extension Badge {

/// Creates Orbit Badge component with custom icons.
///
/// Custom background color be specified using `.backgroundColor()` modifier.
///
/// - Parameters:
/// - style: A visual style of component. A `status` style can be optionally modified using `status()` modifier when `nil` value is provided.
init(
Expand All @@ -130,8 +140,6 @@ public enum BadgeType {
case lightInverted
case neutral
case status(_ status: Status? = nil, inverted: Bool = false)
case custom(labelColor: Color, outlineColor: Color, backgroundColor: Color)
case gradient(Gradient)

public static var status: Self {
.status(nil)
Expand Down Expand Up @@ -213,16 +221,10 @@ struct BadgePreviews: PreviewProvider {
static var mix: some View {
VStack(alignment: .leading, spacing: .xLarge) {
HStack(spacing: .small) {
Badge(
"Custom",
icon: .airplane,
type: .custom(
labelColor: .blueDark,
outlineColor: .blueDark,
backgroundColor: .whiteHover
)
)
.iconColor(.pink)
Badge("Custom", icon: .airplane)
.iconColor(.pink)
.textColor(.blueDark)
.backgroundColor(.whiteHover)

Badge("Flag") {
CountryFlag("us")
Expand Down Expand Up @@ -266,7 +268,9 @@ struct BadgePreviews: PreviewProvider {
}

static func gradientBadge(_ gradient: Gradient) -> some View {
badges(.gradient(gradient))
badges(.neutral)
.textColor(.whiteNormal)
.backgroundColor(gradient.background)
.previewDisplayName("\(String(describing: gradient).titleCased)")
}
}
Loading
Loading