diff --git a/Sources/OversizeUI/Controls/Badge/Bage.swift b/Sources/OversizeUI/Controls/Badge/Bage.swift index 20913c5..8b5348b 100644 --- a/Sources/OversizeUI/Controls/Badge/Bage.swift +++ b/Sources/OversizeUI/Controls/Badge/Bage.swift @@ -27,7 +27,7 @@ public struct Bage: View { .padding(.horizontal, 6) .background( RoundedRectangle(cornerRadius: controlRadius, - style: .circular) + style: .continuous) .fill(color.opacity(0.1)) .overlay( RoundedRectangle(cornerRadius: controlRadius, diff --git a/Sources/OversizeUI/Controls/Button/Button.swift b/Sources/OversizeUI/Controls/Button/Button.swift index dae168b..c818c8f 100644 --- a/Sources/OversizeUI/Controls/Button/Button.swift +++ b/Sources/OversizeUI/Controls/Button/Button.swift @@ -45,7 +45,7 @@ public struct OversizeButtonStyle: ButtonStyle { } @ViewBuilder - func background(for role: ButtonRole?) -> some View { + private func background(for role: ButtonRole?) -> some View { if type != .quaternary { switch controlBorderShape { case .capsule: @@ -72,7 +72,7 @@ public struct OversizeButtonStyle: ButtonStyle { } /// Parameters - func backgroundColor(for role: ButtonRole?) -> Color { + private func backgroundColor(for role: ButtonRole?) -> Color { switch type { case .primary: switch role { @@ -94,7 +94,7 @@ public struct OversizeButtonStyle: ButtonStyle { } } - func foregroundColor(for role: ButtonRole?) -> Color { + private func foregroundColor(for role: ButtonRole?) -> Color { switch type { case .primary: return Color.onPrimaryHighEmphasis @@ -124,7 +124,7 @@ public struct OversizeButtonStyle: ButtonStyle { } @ViewBuilder - func loadingView(for role: ButtonRole?) -> some View { + private func loadingView(for role: ButtonRole?) -> some View { if isLoading { ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: foregroundColor(for: role))) @@ -133,7 +133,7 @@ public struct OversizeButtonStyle: ButtonStyle { } } - var horizontalPadding: Space { + private var horizontalPadding: Space { switch controlSize { case .mini: return .xxSmall @@ -148,7 +148,7 @@ public struct OversizeButtonStyle: ButtonStyle { } } - var verticalPadding: Space { + private var verticalPadding: Space { switch controlSize { case .mini: return .xxSmall @@ -163,11 +163,11 @@ public struct OversizeButtonStyle: ButtonStyle { } } - var backgroundOpacity: CGFloat { + private var backgroundOpacity: CGFloat { isEnabled ? 1 : 0.5 } - var foregroundOpacity: CGFloat { + private var foregroundOpacity: CGFloat { isEnabled ? 1 : 0.7 } diff --git a/Sources/OversizeUI/Controls/Label/RowLabelStyle.swift b/Sources/OversizeUI/Controls/Label/RowLabelStyle.swift new file mode 100644 index 0000000..88210fa --- /dev/null +++ b/Sources/OversizeUI/Controls/Label/RowLabelStyle.swift @@ -0,0 +1,70 @@ +// +// Copyright © 2022 Alexander Romanov +// RowLabelStyle.swift +// + +import SwiftUI + +public struct RowLabelStyle: LabelStyle { + @Environment(\.sizeCategory) var sizeCategory + @Environment(\.multilineTextAlignment) var multilineTextAlignment + + private let subtitle: String? + + public init(subtitle: String? = nil) { + self.subtitle = subtitle + } + + public func makeBody(configuration: Configuration) -> some View { + HStack(spacing: .xSmall) { + configuration.icon + .headline() + .foregroundColor(.onSurfaceHighEmphasis) + + VStack(alignment: textAlignment, spacing: .xxxSmall) { + configuration.title + .headline(.semibold) + .foregroundColor(.onSurfaceHighEmphasis) + + if let subtitle, !subtitle.isEmpty { + Text(subtitle) + .subheadline() + .foregroundColor(.onSurfaceMediumEmphasis) + } + } + .multilineTextAlignment(multilineTextAlignment) + } + .multilineTextAlignment(multilineTextAlignment) + } + + private var textAlignment: HorizontalAlignment { + switch multilineTextAlignment { + case .leading: + return .leading + case .center: + return .center + case .trailing: + return .trailing + } + } +} + +public extension LabelStyle where Self == RowLabelStyle { + static var row: RowLabelStyle { .init() } + + static func row(_ subtitle: String?) -> RowLabelStyle { + RowLabelStyle(subtitle: subtitle) + } +} + +struct RowLabelStyle_Previews: PreviewProvider { + static var previews: some View { + VStack(spacing: .large) { + Label("Text", systemImage: "sun.max") + .labelStyle(.row) + + Label("Text", systemImage: "sun.max") + .labelStyle(.row("Subtitle")) + } + } +} diff --git a/Sources/OversizeUI/Controls/Row/Row.swift b/Sources/OversizeUI/Controls/Row/Row.swift index 5f1e79c..c8a3559 100644 --- a/Sources/OversizeUI/Controls/Row/Row.swift +++ b/Sources/OversizeUI/Controls/Row/Row.swift @@ -5,28 +5,6 @@ import SwiftUI -public enum RowTrailingType { - case radio(isOn: Binding) - case checkbox(isOn: Binding) - case toggle(isOn: Binding) - case toggleWithArrowButton(isOn: Binding, action: (() -> Void)? = nil) - @available(watchOS, unavailable) - case timePicker(date: Binding) - case arrowIcon - case text(_ text: String) - case button(_ text: String, action: () -> Void) -} - -public enum RowLeadingType { - case icon(_ name: IconsNames) - case iconOnSurface(_ name: IconsNames) - case image(_ image: Image, color: Color? = .onSurfaceHighEmphasis) - case imageOnSurface(_ image: Image, color: Color? = nil) - case systemImage(_ imageName: String) - case avatar(_ avatar: AvatarView) - case view(_ view: AnyView) -} - public enum RowClearIconStyle { case `default`, onSurface } @@ -104,7 +82,7 @@ public struct Row: View { VStack(alignment: .leading) { HStack(spacing: .zero) { if let leadingType { - leading(leadingType) + RowLeading(leadingType, isShowSubtitle: isShowSubtitle, iconBackgroundColor: iconBackgroundColor) .padding(.trailing, .small) } @@ -128,7 +106,7 @@ public struct Row: View { сlearButton if let trallingType { - tralling(trallingType) + RowTrailing(trallingType, isPremiumOption: isPremiumOption) .padding(.leading, .xxSmall) } } @@ -137,6 +115,20 @@ public struct Row: View { .padding(.horizontal, controlPadding.horizontal) } + private var text: some View { + VStack(alignment: textAlignment, spacing: .xxxSmall) { + Text(title) + .headline(.semibold) + .foregroundColor(.onSurfaceHighEmphasis) + if let subtitle, !subtitle.isEmpty { + Text(subtitle) + .subheadline() + .foregroundColor(.onSurfaceMediumEmphasis) + } + } + .multilineTextAlignment(multilineTextAlignment) + } + @ViewBuilder private var сlearButton: some View { if сlearAction != nil { @@ -155,146 +147,6 @@ public struct Row: View { } } - @ViewBuilder - private func leading(_ leadingType: RowLeadingType) -> some View { - switch leadingType { - case let .icon(icon): - Icon(icon) - - case let .image(image, color): - image - .renderingMode(color != nil ? .template : .original) - .resizable() - .scaledToFill() - .foregroundColor(color) - .frame(width: isShowSubtitle ? 48 : 24, height: isShowSubtitle ? 48 : 24) - .cornerRadius(isShowSubtitle ? 4 : 2) - - case let .avatar(avatar): - avatar - - case let .iconOnSurface(icon): - Surface { - Icon(icon) - } - .surfaceStyle(.secondary) - .surfaceBackgroundColor(iconBackgroundColor) - .controlPadding(.xxSmall) - - case let .imageOnSurface(image, color): - Surface { - image - .renderingMode(.template) - .foregroundColor(color) - } - .surfaceStyle(.secondary) - .surfaceBackgroundColor(iconBackgroundColor) - .controlPadding(.xxSmall) - - case let .systemImage(systemImage): - Image(systemName: systemImage) - .foregroundColor(Color.onBackgroundHighEmphasis) - .font(.system(size: 24)) - .frame(width: 24, height: 24, alignment: .center) - - case let .view(view): - view - } - } - - // swiftlint:disable function_body_length - @ViewBuilder - private func tralling(_ trallingType: RowTrailingType) -> some View { - switch trallingType { - case let .toggle(isOn): - Toggle(isOn: isOn) {} - .labelsHidden() - .disabled(isPremiumOption && premiumStatus == false) - - case let .radio(isOn: isOn): - ZStack { - Circle() - .stroke(Color.onSurfaceDisabled, lineWidth: 4) - .frame(width: 24, height: 24) - .cornerRadius(12) - .opacity(isOn.wrappedValue ? 0 : 1) - - Circle().fill(Color.accent) - .frame(width: 24, height: 24) - .cornerRadius(12) - .opacity(isOn.wrappedValue ? 1 : 0) - - Circle().fill(Color.white).frame(width: 8, height: 8) - .cornerRadius(4) - .opacity(isOn.wrappedValue ? 1 : 0) - } - - case let .checkbox(isOn: isOn): - ZStack { - RoundedRectangle(cornerRadius: Radius.small, style: .continuous) - .strokeBorder(Color.onSurfaceDisabled, lineWidth: 2.5) - .frame(width: 24, height: 24) - .opacity(isOn.wrappedValue ? 0 : 1) - - RoundedRectangle(cornerRadius: Radius.small, style: .continuous).fill(Color.accent) - .frame(width: 24, height: 24) - .opacity(isOn.wrappedValue ? 1 : 0) - - Image(systemName: "checkmark") - .font(.caption.weight(.black)) - .foregroundColor(.onPrimaryHighEmphasis) - .opacity(isOn.wrappedValue ? 1 : 0) - } - - case let .toggleWithArrowButton(isOn: isOn, action: action): - HStack { - Toggle(isOn: isOn) {} - .labelsHidden() - - Button(action: action ?? {}, label: { - Icon(.chevronRight, color: .onSurfaceDisabled) - }) - } - .disabled(isPremiumOption && premiumStatus == false) - - case .arrowIcon: - Icon(.chevronRight, color: .onSurfaceDisabled) - - case let .timePicker(date: date): - #if os(watchOS) - EmptyView() - #elseif os(iOS) - DatePicker("", selection: date, displayedComponents: .hourAndMinute) - .labelsHidden() - #endif - case let .text(text): - Text(text) - .subheadline() - .foregroundColor(.onSurfaceMediumEmphasis) - - case let .button(text, action: action): - Button(text, action: action) - .buttonStyle(.tertiary) - .controlBorderShape(.capsule) - .controlSize(.small) - .disabled(isPremiumOption && premiumStatus == false) - } - } - - private var text: some View { - VStack(alignment: textAlignment, spacing: .xxxSmall) { - Text(title) - .headline(.semibold) - .foregroundColor(.onSurfaceHighEmphasis) - if let subtitle, !subtitle.isEmpty { - Text(subtitle) - .subheadline() - .foregroundColor(.onSurfaceMediumEmphasis) - } - } - .multilineTextAlignment(multilineTextAlignment) - } - @ViewBuilder private var premiumLabel: some View { if isPremiumOption, premiumStatus == false { @@ -368,20 +220,6 @@ public extension View { } } -public struct RowActionButtonStyle: ButtonStyle { - public func makeBody(configuration: Self.Configuration) -> some View { - configuration.label - .background(configuration.isPressed ? Color.surfaceSecondary : Color.clear) - .contentShape(Rectangle()) - } -} - -public extension ButtonStyle where Self == RowActionButtonStyle { - static var row: RowActionButtonStyle { - RowActionButtonStyle() - } -} - // swiftlint:disable all struct ListRow_Previews: PreviewProvider { static var previews: some View { diff --git a/Sources/OversizeUI/Controls/Row/Subviews/RowButtonStyle.swift b/Sources/OversizeUI/Controls/Row/Subviews/RowButtonStyle.swift new file mode 100644 index 0000000..c9a157f --- /dev/null +++ b/Sources/OversizeUI/Controls/Row/Subviews/RowButtonStyle.swift @@ -0,0 +1,21 @@ +// +// Copyright © 2022 Alexander Romanov +// RowButtonStyle.swift +// + +import SwiftUI + +public struct RowActionButtonStyle: ButtonStyle { + public init() {} + public func makeBody(configuration: Self.Configuration) -> some View { + configuration.label + .background(configuration.isPressed ? Color.surfaceSecondary : Color.clear) + .contentShape(Rectangle()) + } +} + +public extension ButtonStyle where Self == RowActionButtonStyle { + static var row: RowActionButtonStyle { + RowActionButtonStyle() + } +} diff --git a/Sources/OversizeUI/Controls/Row/Subviews/RowLeading.swift b/Sources/OversizeUI/Controls/Row/Subviews/RowLeading.swift new file mode 100644 index 0000000..3ebdddf --- /dev/null +++ b/Sources/OversizeUI/Controls/Row/Subviews/RowLeading.swift @@ -0,0 +1,74 @@ +// +// Copyright © 2022 Alexander Romanov +// RowLeading.swift +// + +import SwiftUI + +public enum RowLeadingType { + case icon(_ name: IconsNames) + case iconOnSurface(_ name: IconsNames) + case image(_ image: Image, color: Color? = .onSurfaceHighEmphasis) + case imageOnSurface(_ image: Image, color: Color? = nil) + case systemImage(_ imageName: String) + case avatar(_ avatar: AvatarView) + case view(_ view: AnyView) +} + +internal struct RowLeading: View { + private let type: RowLeadingType + private let isShowSubtitle: Bool + private let iconBackgroundColor: Color? + + internal init(_ type: RowLeadingType, isShowSubtitle: Bool = false, iconBackgroundColor: Color? = nil) { + self.type = type + self.isShowSubtitle = isShowSubtitle + self.iconBackgroundColor = iconBackgroundColor + } + + internal var body: some View { + switch type { + case let .icon(icon): + Icon(icon) + + case let .image(image, color): + image + .renderingMode(color != nil ? .template : .original) + .resizable() + .scaledToFill() + .foregroundColor(color) + .frame(width: isShowSubtitle ? 48 : 24, height: isShowSubtitle ? 48 : 24) + .cornerRadius(isShowSubtitle ? 4 : 2) + + case let .avatar(avatar): + avatar + + case let .iconOnSurface(icon): + Surface { + Icon(icon) + } + .surfaceStyle(.secondary) + .surfaceBackgroundColor(iconBackgroundColor) + .controlPadding(.xxSmall) + + case let .imageOnSurface(image, color): + Surface { + image + .renderingMode(.template) + .foregroundColor(color) + } + .surfaceStyle(.secondary) + .surfaceBackgroundColor(iconBackgroundColor) + .controlPadding(.xxSmall) + + case let .systemImage(systemImage): + Image(systemName: systemImage) + .foregroundColor(Color.onBackgroundHighEmphasis) + .font(.system(size: 24)) + .frame(width: 24, height: 24, alignment: .center) + + case let .view(view): + view + } + } +} diff --git a/Sources/OversizeUI/Controls/Row/Subviews/RowTrailing.swift b/Sources/OversizeUI/Controls/Row/Subviews/RowTrailing.swift new file mode 100644 index 0000000..858c7b7 --- /dev/null +++ b/Sources/OversizeUI/Controls/Row/Subviews/RowTrailing.swift @@ -0,0 +1,110 @@ +// +// Copyright © 2022 Alexander Romanov +// RowTrailing.swift +// + +import SwiftUI + +import SwiftUI + +public enum RowTrailingType { + case radio(isOn: Binding) + case checkbox(isOn: Binding) + case toggle(isOn: Binding) + case toggleWithArrowButton(isOn: Binding, action: (() -> Void)? = nil) + @available(watchOS, unavailable) + case timePicker(date: Binding) + case arrowIcon + case text(_ text: String) + case button(_ text: String, action: () -> Void) +} + +internal struct RowTrailing: View { + @Environment(\.isPremium) var premiumStatus + + private let type: RowTrailingType + private let isPremiumOption: Bool + + internal init(_ type: RowTrailingType, isPremiumOption: Bool = false) { + self.type = type + self.isPremiumOption = isPremiumOption + } + + // swiftlint:disable function_body_length + internal var body: some View { + switch type { + case let .toggle(isOn): + Toggle(isOn: isOn) {} + .labelsHidden() + .disabled(isPremiumOption && premiumStatus == false) + + case let .radio(isOn: isOn): + ZStack { + Circle() + .stroke(Color.onSurfaceDisabled, lineWidth: 4) + .frame(width: 24, height: 24) + .cornerRadius(12) + .opacity(isOn.wrappedValue ? 0 : 1) + + Circle().fill(Color.accent) + .frame(width: 24, height: 24) + .cornerRadius(12) + .opacity(isOn.wrappedValue ? 1 : 0) + + Circle().fill(Color.white).frame(width: 8, height: 8) + .cornerRadius(4) + .opacity(isOn.wrappedValue ? 1 : 0) + } + + case let .checkbox(isOn: isOn): + ZStack { + RoundedRectangle(cornerRadius: Radius.small, style: .continuous) + .strokeBorder(Color.onSurfaceDisabled, lineWidth: 2.5) + .frame(width: 24, height: 24) + .opacity(isOn.wrappedValue ? 0 : 1) + + RoundedRectangle(cornerRadius: Radius.small, style: .continuous).fill(Color.accent) + .frame(width: 24, height: 24) + .opacity(isOn.wrappedValue ? 1 : 0) + + Image(systemName: "checkmark") + .font(.caption.weight(.black)) + .foregroundColor(.onPrimaryHighEmphasis) + .opacity(isOn.wrappedValue ? 1 : 0) + } + + case let .toggleWithArrowButton(isOn: isOn, action: action): + HStack { + Toggle(isOn: isOn) {} + .labelsHidden() + + Button(action: action ?? {}, label: { + Icon(.chevronRight, color: .onSurfaceDisabled) + }) + } + .disabled(isPremiumOption && premiumStatus == false) + + case .arrowIcon: + Icon(.chevronRight, color: .onSurfaceDisabled) + + case let .timePicker(date: date): + #if os(watchOS) + EmptyView() + #elseif os(iOS) + DatePicker("", selection: date, displayedComponents: .hourAndMinute) + .labelsHidden() + #endif + case let .text(text): + Text(text) + .subheadline() + .foregroundColor(.onSurfaceMediumEmphasis) + + case let .button(text, action: action): + Button(text, action: action) + .buttonStyle(.tertiary) + .controlBorderShape(.capsule) + .controlSize(.small) + .disabled(isPremiumOption && premiumStatus == false) + } + } +} diff --git a/Sources/OversizeUI/Controls/ScrollView/SectionView.swift b/Sources/OversizeUI/Controls/ScrollView/SectionView.swift index 29634dc..90f7121 100644 --- a/Sources/OversizeUI/Controls/ScrollView/SectionView.swift +++ b/Sources/OversizeUI/Controls/ScrollView/SectionView.swift @@ -37,10 +37,6 @@ public struct SectionView: View { .controlPadding(.medium) } .controlPadding(.zero) - .clipShape( - RoundedRectangle(cornerRadius: Constants.radiusMedium, - style: .circular) - ) } .paddingContent(.horizontal) .padding(.vertical, Space.small) diff --git a/Sources/OversizeUI/Controls/Surface/MaterialSurface.swift b/Sources/OversizeUI/Controls/Surface/MaterialSurface.swift index 33c4543..1755ade 100644 --- a/Sources/OversizeUI/Controls/Surface/MaterialSurface.swift +++ b/Sources/OversizeUI/Controls/Surface/MaterialSurface.swift @@ -6,19 +6,6 @@ import SwiftUI #if os(iOS) || os(tvOS) || os(macOS) - public enum SurfaceMaterialTyps { - /// A material that's somewhat translucent. - case regular - /// A material that's more opaque than translucent. - case thick - /// A material that's more translucent than opaque. - case thin - /// A mostly translucent material. - case ultraThin - /// A mostly opaque material. - case ultraThick - } - public struct MaterialSurface: View { @Environment(\.elevation) private var elevation: Elevation @Environment(\.theme) private var theme: ThemeSettings @@ -28,7 +15,7 @@ import SwiftUI private let label: Label private let action: (() -> Void)? private var border: Color? - private var material: SurfaceMaterialTyps = .regular + private var material: Material = .regular public init(action: (() -> Void)? = nil, @ViewBuilder label: () -> Label) @@ -56,22 +43,13 @@ import SwiftUI @ViewBuilder private var surface: some View { - if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { - label - .padding(.horizontal, controlPadding.horizontal) - .padding(.vertical, controlPadding.vertical) - .background(backgroundMaterial, - in: RoundedRectangle(cornerRadius: controlRadius, style: .continuous)) - .overlay(overlayView) - .shadowElevaton(elevation) - } else { - label - .padding(.horizontal, controlPadding.horizontal) - .padding(.vertical, controlPadding.vertical) - .background(legacyBackgroundView) - .overlay(overlayView) - .shadowElevaton(elevation) - } + label + .padding(.horizontal, controlPadding.horizontal) + .padding(.vertical, controlPadding.vertical) + .background(material, + in: RoundedRectangle(cornerRadius: controlRadius, style: .continuous)) + .overlay(overlayView) + .shadowElevaton(elevation) } @ViewBuilder @@ -85,44 +63,7 @@ import SwiftUI ) } - @available(iOS 15.0, macOS 12.0, tvOS 15.0, *) - private var backgroundMaterial: Material { - switch material { - case .regular: - return .regularMaterial - case .thick: - return .thickMaterial - case .thin: - return .thinMaterial - case .ultraThin: - return .ultraThinMaterial - case .ultraThick: - return .ultraThinMaterial - } - } - - private var legacyBackgroundViewColor: Color { - switch material { - case .ultraThin: - return .surfacePrimary.opacity(0.15) - case .thin: - return .surfacePrimary.opacity(0.35) - case .regular: - return .surfacePrimary.opacity(0.5) - case .thick: - return .surfacePrimary.opacity(0.75) - case .ultraThick: - return .surfacePrimary.opacity(0.95) - } - } - - private var legacyBackgroundView: some View { - RoundedRectangle(cornerRadius: controlRadius, - style: .circular) - .fill(legacyBackgroundViewColor) - } - - public func surfaceStyle(_ material: SurfaceMaterialTyps) -> MaterialSurface { + public func surfaceStyle(_ material: Material) -> MaterialSurface { var control = self control.material = material return control diff --git a/Sources/OversizeUI/Controls/Surface/Surface.swift b/Sources/OversizeUI/Controls/Surface/Surface.swift index 4dc5e5e..52655b3 100644 --- a/Sources/OversizeUI/Controls/Surface/Surface.swift +++ b/Sources/OversizeUI/Controls/Surface/Surface.swift @@ -69,7 +69,7 @@ public struct Surface: View { .overlay( RoundedRectangle(cornerRadius: controlRadius, style: .continuous) - .stroke( + .strokeBorder( border != nil ? border ?? Color.clear : theme.borderSurface ? Color.border @@ -78,6 +78,9 @@ public struct Surface: View { ) .shadowElevaton(elevation) ) + .clipShape( + RoundedRectangle(cornerRadius: controlRadius, style: .continuous) + ) } private var surfaceBackgroundColor: Color { diff --git a/Sources/OversizeUI/Controls/Toogle/CheckboxStyle.swift b/Sources/OversizeUI/Controls/Toogle/CheckboxStyle.swift new file mode 100644 index 0000000..55ff22b --- /dev/null +++ b/Sources/OversizeUI/Controls/Toogle/CheckboxStyle.swift @@ -0,0 +1,82 @@ +// +// Copyright © 2022 Alexander Romanov +// CheckboxStyle.swift +// + +import SwiftUI + +public struct CheckboxStyle: ToggleStyle { + @Environment(\.controlPadding) var controlPadding: ControlPadding + + public func makeBody(configuration: Self.Configuration) -> some View { + HStack { + configuration.label + .headline(.semibold) + .foregroundColor(.onSurfaceHighEmphasis) + + Spacer() + + ZStack { + RoundedRectangle(cornerRadius: Radius.small, style: .continuous) + .strokeBorder(Color.onSurfaceDisabled, lineWidth: 2.5) + .frame(width: 24, height: 24) + .opacity(configuration.isOn ? 0 : 1) + + RoundedRectangle(cornerRadius: Radius.small, style: .continuous).fill(Color.accent) + .frame(width: 24, height: 24) + .opacity(configuration.isOn ? 1 : 0) + + Image(systemName: "checkmark") + .font(.caption.weight(.black)) + .foregroundColor(.onPrimaryHighEmphasis) + .opacity(configuration.isOn ? 1 : 0) + } + } + .padding(.vertical, verticalPadding) + .padding(.horizontal, controlPadding.horizontal) + } + + private var verticalPadding: CGFloat { + switch controlPadding.vertical { + case .zero: + return .zero + case .xxSmall: + return .zero + default: + return controlPadding.vertical.rawValue - Space.xxSmall.rawValue + } + } +} + +public extension ToggleStyle where Self == CheckboxStyle { + static var checkboxRow: CheckboxStyle { .init() } +} + +struct Checkbox_Previews: PreviewProvider { + static var previews: some View { + SectionView { + VStack { + Toggle(isOn: .constant(true)) { + Label("Label", systemImage: "folder.fill") + .labelStyle(.row("Subtitle")) + } + .toggleStyle(.checkboxRow) + + Toggle(isOn: .constant(true)) { + Label("Label", systemImage: "folder.fill") + .labelStyle(.row) + } + .toggleStyle(.checkboxRow) + + Divider() + + Toggle("Title", isOn: .constant(true)) + .toggleStyle(.checkboxRow) + + Toggle("Title", isOn: .constant(false)) + .toggleStyle(.checkboxRow) + } + } + .background(Color.surfaceSecondary) + } +} diff --git a/Sources/OversizeUI/Controls/Toogle/RadioStyle.swift b/Sources/OversizeUI/Controls/Toogle/RadioStyle.swift new file mode 100644 index 0000000..cc3d972 --- /dev/null +++ b/Sources/OversizeUI/Controls/Toogle/RadioStyle.swift @@ -0,0 +1,83 @@ +// +// Copyright © 2022 Alexander Romanov +// RadioStyle.swift +// + +import SwiftUI + +public struct RadioStyle: ToggleStyle { + @Environment(\.controlPadding) var controlPadding: ControlPadding + + public func makeBody(configuration: Self.Configuration) -> some View { + HStack { + configuration.label + .headline(.semibold) + .foregroundColor(.onSurfaceHighEmphasis) + + Spacer() + + ZStack { + Circle() + .stroke(Color.onSurfaceDisabled, lineWidth: 4) + .frame(width: 24, height: 24) + .cornerRadius(12) + .opacity(configuration.isOn ? 0 : 1) + + Circle().fill(Color.accent) + .frame(width: 24, height: 24) + .cornerRadius(12) + .opacity(configuration.isOn ? 1 : 0) + + Circle().fill(Color.white).frame(width: 8, height: 8) + .cornerRadius(4) + .opacity(configuration.isOn ? 1 : 0) + } + } + .padding(.vertical, verticalPadding) + .padding(.horizontal, controlPadding.horizontal) + } + + private var verticalPadding: CGFloat { + switch controlPadding.vertical { + case .zero: + return .zero + case .xxSmall: + return .zero + default: + return controlPadding.vertical.rawValue - Space.xxSmall.rawValue + } + } +} + +public extension ToggleStyle where Self == RadioStyle { + static var radio: RadioStyle { .init() } +} + +struct Radio_Previews: PreviewProvider { + static var previews: some View { + SectionView { + VStack { + Toggle(isOn: .constant(true)) { + Label("Label", systemImage: "folder.fill") + .labelStyle(.row("Subtitle")) + } + .toggleStyle(.radio) + + Toggle(isOn: .constant(true)) { + Label("Label", systemImage: "folder.fill") + .labelStyle(.row) + } + .toggleStyle(.radio) + + Divider() + + Toggle("Title", isOn: .constant(true)) + .toggleStyle(.radio) + + Toggle("Title", isOn: .constant(false)) + .toggleStyle(.radio) + } + } + .background(Color.surfaceSecondary) + } +} diff --git a/Sources/OversizeUI/Controls/Toogle/Toogle.swift b/Sources/OversizeUI/Controls/Toogle/Toogle.swift deleted file mode 100644 index bf6b4a2..0000000 --- a/Sources/OversizeUI/Controls/Toogle/Toogle.swift +++ /dev/null @@ -1,6 +0,0 @@ -// -// Copyright © 2022 Alexander Romanov -// Toogle.swift -// - -//