diff --git a/Sources/Orbit/Components/Tag.swift b/Sources/Orbit/Components/Tag.swift index 6bb14f7aa8b..7ded0c059cb 100644 --- a/Sources/Orbit/Components/Tag.swift +++ b/Sources/Orbit/Components/Tag.swift @@ -6,7 +6,9 @@ import SwiftUI /// /// ```swift /// Tag("Sorting", icon: .sort, isSelected: $isSortingApplied) -/// Tag("Filters", style: .removable(action: { /* Tap action */ }), isSelected: $isFiltersApplied) +/// Tag("Filters", isSelected: $isFiltersApplied) { +/// // Tap action +/// } /// ``` /// /// ### Customizing appearance @@ -34,10 +36,10 @@ public struct Tag: View, PotentiallyEmptyView { @Environment(\.isHapticsEnabled) private var isHapticsEnabled private let label: String - private let style: TagStyle private let isFocused: Bool @Binding private var isSelected: Bool @ViewBuilder private let icon: Icon + private let removeAction: (() -> Void)? public var body: some View { if isEmpty == false { @@ -67,11 +69,7 @@ public struct Tag: View, PotentiallyEmptyView { } } .buttonStyle( - TagButtonStyle( - style: style, - isFocused: isFocused, - isSelected: isSelected - ) + TagButtonStyle(isFocused: isFocused, isSelected: isSelected, removeAction: removeAction) ) .accessibility(addTraits: isSelected ? .isSelected : []) } @@ -88,52 +86,36 @@ public extension Tag { /// Creates Orbit ``Tag`` component with custom leading icon. init( _ label: String = "", - style: TagStyle = .default, isFocused: Bool = true, isSelected: Binding, - @ViewBuilder icon: () -> Icon + @ViewBuilder icon: () -> Icon, + removeAction: (() -> Void)? = nil ) { self.label = label - self.style = style self.isFocused = isFocused self._isSelected = isSelected self.icon = icon() + self.removeAction = removeAction } /// Creates Orbit ``Tag`` component. init( _ label: String = "", icon: Icon.Symbol? = nil, - style: TagStyle = .default, isFocused: Bool = true, - isSelected: Binding + isSelected: Binding, + removeAction: (() -> Void)? = nil ) where Icon == Orbit.Icon { self.init( label, - style: style, isFocused: isFocused, - isSelected: isSelected - ) { - Icon(icon) - .iconSize(textSize: .normal) - } - } -} - -// MARK: - Types - -/// Orbit ``Tag`` style. -public enum TagStyle: Equatable { - - case `default` - case removable(action: () -> Void) - - public static func == (lhs: TagStyle, rhs: TagStyle) -> Bool { - switch (lhs, rhs) { - case (.default, .default): return true - case (.removable, .removable): return true - default: return false - } + isSelected: isSelected, + icon: { + Icon(icon) + .iconSize(textSize: .normal) + }, + removeAction: removeAction + ) } } @@ -154,7 +136,7 @@ struct TagPreviews: PreviewProvider { static var standalone: some View { StateWrapper(true) { isSelected in - Tag(label, icon: .grid, style: .removable(action: {}), isSelected: isSelected) + Tag(label, icon: .grid, isSelected: isSelected, removeAction: {}) Tag("", isSelected: .constant(false)) // EmptyView } .padding(.medium) @@ -163,7 +145,7 @@ struct TagPreviews: PreviewProvider { static var standaloneExpanding: some View { StateWrapper(true) { isSelected in - Tag(label, icon: .grid, style: .removable(action: {}), isSelected: isSelected) + Tag(label, icon: .grid, isSelected: isSelected, removeAction: {}) .idealSize(horizontal: false) } .padding(.medium) @@ -172,17 +154,17 @@ struct TagPreviews: PreviewProvider { static var styles: some View { VStack(alignment: .leading, spacing: .large) { - stack(style: .default, isFocused: true) - stack(style: .default, isFocused: false) - stack(style: .removable(action: {}), isFocused: true) - stack(style: .removable(action: {}), isFocused: false) + stack(isRemovable: false, isFocused: true) + stack(isRemovable: false, isFocused: false) + stack(isRemovable: true, isFocused: true) + stack(isRemovable: true, isFocused: false) Separator() - stack(style: .default, isFocused: false, idealWidth: false) - stack(style: .removable(action: {}), isFocused: true, idealWidth: false) + stack(isRemovable: false, isFocused: false, idealWidth: false) + stack(isRemovable: true, isFocused: true, idealWidth: false) Separator() HStack(spacing: .medium) { StateWrapper(true) { isSelected in - Tag(label, icon: .sort, style: .removable(action: {}), isSelected: isSelected) + Tag(label, icon: .sort, isSelected: isSelected, removeAction: {}) } StateWrapper(false) { isSelected in Tag(icon: .notificationAdd, isFocused: false, isSelected: isSelected) @@ -206,23 +188,23 @@ struct TagPreviews: PreviewProvider { .previewDisplayName() } - static func stack(style: TagStyle, isFocused: Bool, idealWidth: Bool? = nil) -> some View { + static func stack(isRemovable: Bool, isFocused: Bool, idealWidth: Bool? = nil) -> some View { HStack(spacing: .medium) { VStack(spacing: .small) { - tag(style: style, isFocused: isFocused, isSelected: false) - tag(style: style, isFocused: isFocused, isSelected: true) + tag(isRemovable: isRemovable, isFocused: isFocused, isSelected: false) + tag(isRemovable: isRemovable, isFocused: isFocused, isSelected: true) } } .idealSize(horizontal: idealWidth) } - static func tag(style: TagStyle, isFocused: Bool, isSelected: Bool) -> some View { - StateWrapper((style, isSelected, true)) { state in + static func tag(isRemovable: Bool, isFocused: Bool, isSelected: Bool) -> some View { + StateWrapper((isRemovable, isSelected, true)) { state in Tag( label, - style: style == .default ? .default : .removable(action: { state.wrappedValue.2 = false }), isFocused: isFocused, - isSelected: state.1 + isSelected: state.1, + removeAction: state.0.wrappedValue ? { state.wrappedValue.2 = false } : nil ) .opacity(state.wrappedValue.2 ? 1 : 0) } diff --git a/Sources/Orbit/Support/ButtonStyles/TagButtonStyle.swift b/Sources/Orbit/Support/ButtonStyles/TagButtonStyle.swift index 30deb05a306..17d9717a61d 100644 --- a/Sources/Orbit/Support/ButtonStyles/TagButtonStyle.swift +++ b/Sources/Orbit/Support/ButtonStyles/TagButtonStyle.swift @@ -9,10 +9,10 @@ public struct TagButtonStyle: ButtonStyle { @Environment(\.backgroundShape) private var backgroundShape @Environment(\.iconColor) private var iconColor @Environment(\.textColor) private var textColor - - private let style: TagStyle + private let isFocused: Bool private let isSelected: Bool + private let removeAction: (() -> Void)? public func makeBody(configuration: Configuration) -> some View { HStack(spacing: .xSmall) { @@ -21,7 +21,7 @@ public struct TagButtonStyle: ButtonStyle { .textColor(resolvedTextColor) .lineLimit(1) - if case .removable(let removeAction) = style { + if let removeAction { Icon(.closeCircle) .iconSize(.small) .iconColor(resolvedIconColor(isPressed: configuration.isPressed)) @@ -110,13 +110,13 @@ public struct TagButtonStyle: ButtonStyle { /// Create button style for Orbit ``Tag`` component. public init( - style: TagStyle, isFocused: Bool, - isSelected: Bool + isSelected: Bool, + removeAction: (() -> Void)? ) { - self.style = style self.isFocused = isFocused self.isSelected = isSelected + self.removeAction = removeAction } } @@ -134,25 +134,25 @@ struct TagButtonStylePreviews: PreviewProvider { static var styles: some View { VStack(alignment: .leading, spacing: .medium) { button - .buttonStyle(TagButtonStyle(style: .default, isFocused: false, isSelected: false)) - + .buttonStyle(TagButtonStyle(isFocused: false, isSelected: false, removeAction: nil)) + button - .buttonStyle(TagButtonStyle(style: .default, isFocused: true, isSelected: false)) - + .buttonStyle(TagButtonStyle(isFocused: true, isSelected: false, removeAction: nil)) + button - .buttonStyle(TagButtonStyle(style: .default, isFocused: true, isSelected: true)) - + .buttonStyle(TagButtonStyle(isFocused: true, isSelected: true, removeAction: nil)) + button - .buttonStyle(TagButtonStyle(style: .removable(action: {}), isFocused: true, isSelected: false)) - + .buttonStyle(TagButtonStyle(isFocused: true, isSelected: false, removeAction: {})) + button - .buttonStyle(TagButtonStyle(style: .removable(action: {}), isFocused: true, isSelected: true)) - + .buttonStyle(TagButtonStyle(isFocused: true, isSelected: true, removeAction: {})) + button - .buttonStyle(TagButtonStyle(style: .removable(action: {}), isFocused: false, isSelected: false)) - + .buttonStyle(TagButtonStyle(isFocused: false, isSelected: false, removeAction: {})) + button - .buttonStyle(TagButtonStyle(style: .removable(action: {}), isFocused: false, isSelected: true)) + .buttonStyle(TagButtonStyle(isFocused: false, isSelected: true, removeAction: {})) } .previewDisplayName() } @@ -171,11 +171,11 @@ struct TagButtonStylePreviews: PreviewProvider { .backgroundStyle(Gradient.bundleBasic.background, active: .redLight) button - .buttonStyle(TagButtonStyle(style: .removable(action: {}), isFocused: true, isSelected: true)) + .buttonStyle(TagButtonStyle(isFocused: true, isSelected: true, removeAction: {})) .iconColor(.blueLight) .textColor(.blueLight) } - .buttonStyle(TagButtonStyle(style: .default, isFocused: false, isSelected: false)) + .buttonStyle(TagButtonStyle(isFocused: false, isSelected: false, removeAction: nil)) .previewDisplayName() } diff --git a/Sources/OrbitStorybook/Detail/Items/StorybookTag.swift b/Sources/OrbitStorybook/Detail/Items/StorybookTag.swift index 3f43c159b05..6eacd21bbf1 100644 --- a/Sources/OrbitStorybook/Detail/Items/StorybookTag.swift +++ b/Sources/OrbitStorybook/Detail/Items/StorybookTag.swift @@ -7,17 +7,17 @@ struct StorybookTag { @ViewBuilder static var basic: some View { VStack(alignment: .leading, spacing: .large) { - stack(style: .default, isFocused: true) - stack(style: .default, isFocused: false) - stack(style: .removable(action: {}), isFocused: true) - stack(style: .removable(action: {}), isFocused: false) + stack(isRemovable: false, isFocused: true) + stack(isRemovable: false, isFocused: false) + stack(isRemovable: true, isFocused: true) + stack(isRemovable: true, isFocused: false) Separator() - stack(style: .default, isFocused: false, idealWidth: false) - stack(style: .removable(action: {}), isFocused: true, idealWidth: false) + stack(isRemovable: false, isFocused: false, idealWidth: false) + stack(isRemovable: true, isFocused: true, idealWidth: false) Separator() HStack(spacing: .medium) { StateWrapper(true) { isSelected in - Tag(label, icon: .sort, style: .removable(action: {}), isSelected: isSelected) + Tag(label, icon: .sort, isSelected: isSelected, removeAction: {}) } StateWrapper(false) { isSelected in Tag(icon: .notificationAdd, isFocused: false, isSelected: isSelected) @@ -27,23 +27,23 @@ struct StorybookTag { .previewDisplayName() } - @ViewBuilder static func stack(style: TagStyle, isFocused: Bool, idealWidth: Bool? = nil) -> some View { + static func stack(isRemovable: Bool, isFocused: Bool, idealWidth: Bool? = nil) -> some View { HStack(spacing: .medium) { VStack(spacing: .small) { - tag(style: style, isFocused: isFocused, isSelected: false) - tag(style: style, isFocused: isFocused, isSelected: true) + tag(isRemovable: isRemovable, isFocused: isFocused, isSelected: false) + tag(isRemovable: isRemovable, isFocused: isFocused, isSelected: true) } } .idealSize(horizontal: idealWidth) } - @ViewBuilder static func tag(style: TagStyle, isFocused: Bool, isSelected: Bool) -> some View { - StateWrapper((style, isSelected, true)) { state in + static func tag(isRemovable: Bool, isFocused: Bool, isSelected: Bool) -> some View { + StateWrapper((isRemovable, isSelected, true)) { state in Tag( label, - style: style == .default ? .default : .removable(action: { state.wrappedValue.2 = false }), isFocused: isFocused, - isSelected: state.1 + isSelected: state.1, + removeAction: state.0.wrappedValue ? { state.wrappedValue.2 = false } : nil ) .opacity(state.wrappedValue.2 ? 1 : 0) }