diff --git a/SDDSComponents/SDDSComponents.xcodeproj/project.pbxproj b/SDDSComponents/SDDSComponents.xcodeproj/project.pbxproj index e2a48308..75db9479 100644 --- a/SDDSComponents/SDDSComponents.xcodeproj/project.pbxproj +++ b/SDDSComponents/SDDSComponents.xcodeproj/project.pbxproj @@ -7,6 +7,22 @@ objects = { /* Begin PBXBuildFile section */ + 6950A1282D1D4C47001695C5 /* SegmentElementAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6950A1272D1D4C28001695C5 /* SegmentElementAppearance.swift */; }; + 6950A12A2D1D4D56001695C5 /* SegmentElementSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6950A1292D1D4D4E001695C5 /* SegmentElementSize.swift */; }; + 69573D6A2D1D756000359D53 /* SegmentElementAppearance+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69573D692D1D754400359D53 /* SegmentElementAppearance+Extensions.swift */; }; + 69573D6C2D1D7C5D00359D53 /* SegmentElementAppearance+Variation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69573D6B2D1D7C4300359D53 /* SegmentElementAppearance+Variation.swift */; }; + 696843432D1EA12E002F8C7A /* SegmentElementAppearance+ButtonAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 696843422D1EA11A002F8C7A /* SegmentElementAppearance+ButtonAppearance.swift */; }; + 696843452D1EA35B002F8C7A /* SegmentElementAccessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 696843442D1EA348002F8C7A /* SegmentElementAccessibility.swift */; }; + 6969EADE2D1D5B8E008536E5 /* SegmentElementAppearanceVariations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6969EADD2D1D5B73008536E5 /* SegmentElementAppearanceVariations.swift */; }; + 697736FF2D26C23F0077D1A4 /* CounterAppearance+Variation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697736FE2D26C2310077D1A4 /* CounterAppearance+Variation.swift */; }; + 697737852D2EB8030077D1A4 /* View+ErrorString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697737842D2EB7F80077D1A4 /* View+ErrorString.swift */; }; + 6987445F2D1C367700559ABA /* SDDSCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6987445D2D1C367700559ABA /* SDDSCounter.swift */; }; + 698744602D1C367700559ABA /* CounterAppearance+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6987445A2D1C367700559ABA /* CounterAppearance+Extensions.swift */; }; + 698744612D1C367700559ABA /* CounterSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698744582D1C367700559ABA /* CounterSize.swift */; }; + 698744622D1C367700559ABA /* CounterColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698744572D1C367700559ABA /* CounterColor.swift */; }; + 698744632D1C367700559ABA /* CounterData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6987445C2D1C367700559ABA /* CounterData.swift */; }; + 698744642D1C367700559ABA /* CounterAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698744562D1C367700559ABA /* CounterAppearance.swift */; }; + 698744672D1C368E00559ABA /* SDDSSegmentElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698744652D1C368E00559ABA /* SDDSSegmentElement.swift */; }; 8102BA2F2CBE9B3300C589D3 /* SDDSTextArea.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8102BA2A2CBE9B3300C589D3 /* SDDSTextArea.swift */; }; 8102BA302CBE9B3300C589D3 /* TextAreaAccessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8102BA2B2CBE9B3300C589D3 /* TextAreaAccessibility.swift */; }; 8102BA312CBE9B3300C589D3 /* TextAreaAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8102BA2C2CBE9B3300C589D3 /* TextAreaAppearance.swift */; }; @@ -146,6 +162,22 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 6950A1272D1D4C28001695C5 /* SegmentElementAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentElementAppearance.swift; sourceTree = ""; }; + 6950A1292D1D4D4E001695C5 /* SegmentElementSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentElementSize.swift; sourceTree = ""; }; + 69573D692D1D754400359D53 /* SegmentElementAppearance+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SegmentElementAppearance+Extensions.swift"; sourceTree = ""; }; + 69573D6B2D1D7C4300359D53 /* SegmentElementAppearance+Variation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SegmentElementAppearance+Variation.swift"; sourceTree = ""; }; + 696843422D1EA11A002F8C7A /* SegmentElementAppearance+ButtonAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SegmentElementAppearance+ButtonAppearance.swift"; sourceTree = ""; }; + 696843442D1EA348002F8C7A /* SegmentElementAccessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentElementAccessibility.swift; sourceTree = ""; }; + 6969EADD2D1D5B73008536E5 /* SegmentElementAppearanceVariations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentElementAppearanceVariations.swift; sourceTree = ""; }; + 697736FE2D26C2310077D1A4 /* CounterAppearance+Variation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CounterAppearance+Variation.swift"; sourceTree = ""; }; + 697737842D2EB7F80077D1A4 /* View+ErrorString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+ErrorString.swift"; sourceTree = ""; }; + 698744562D1C367700559ABA /* CounterAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CounterAppearance.swift; sourceTree = ""; }; + 698744572D1C367700559ABA /* CounterColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CounterColor.swift; sourceTree = ""; }; + 698744582D1C367700559ABA /* CounterSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CounterSize.swift; sourceTree = ""; }; + 6987445A2D1C367700559ABA /* CounterAppearance+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CounterAppearance+Extensions.swift"; sourceTree = ""; }; + 6987445C2D1C367700559ABA /* CounterData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CounterData.swift; sourceTree = ""; }; + 6987445D2D1C367700559ABA /* SDDSCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDDSCounter.swift; sourceTree = ""; }; + 698744652D1C368E00559ABA /* SDDSSegmentElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDDSSegmentElement.swift; sourceTree = ""; }; 8102BA2A2CBE9B3300C589D3 /* SDDSTextArea.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SDDSTextArea.swift; sourceTree = ""; }; 8102BA2B2CBE9B3300C589D3 /* TextAreaAccessibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextAreaAccessibility.swift; sourceTree = ""; }; 8102BA2C2CBE9B3300C589D3 /* TextAreaAppearance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextAreaAppearance.swift; sourceTree = ""; }; @@ -324,6 +356,59 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 6950A1262D1D4BE1001695C5 /* SegmentAppearance */ = { + isa = PBXGroup; + children = ( + 696843442D1EA348002F8C7A /* SegmentElementAccessibility.swift */, + 696843422D1EA11A002F8C7A /* SegmentElementAppearance+ButtonAppearance.swift */, + 69573D6B2D1D7C4300359D53 /* SegmentElementAppearance+Variation.swift */, + 69573D692D1D754400359D53 /* SegmentElementAppearance+Extensions.swift */, + 6969EADD2D1D5B73008536E5 /* SegmentElementAppearanceVariations.swift */, + 6950A1292D1D4D4E001695C5 /* SegmentElementSize.swift */, + 6950A1272D1D4C28001695C5 /* SegmentElementAppearance.swift */, + ); + path = SegmentAppearance; + sourceTree = ""; + }; + 698744592D1C367700559ABA /* CounterAppearance */ = { + isa = PBXGroup; + children = ( + 697736FE2D26C2310077D1A4 /* CounterAppearance+Variation.swift */, + 698744562D1C367700559ABA /* CounterAppearance.swift */, + 698744572D1C367700559ABA /* CounterColor.swift */, + 698744582D1C367700559ABA /* CounterSize.swift */, + ); + path = CounterAppearance; + sourceTree = ""; + }; + 6987445B2D1C367700559ABA /* CounterAppearance+Extensions */ = { + isa = PBXGroup; + children = ( + 6987445A2D1C367700559ABA /* CounterAppearance+Extensions.swift */, + ); + path = "CounterAppearance+Extensions"; + sourceTree = ""; + }; + 6987445E2D1C367700559ABA /* SDDSCounter */ = { + isa = PBXGroup; + children = ( + 698744592D1C367700559ABA /* CounterAppearance */, + 6987445B2D1C367700559ABA /* CounterAppearance+Extensions */, + 6987445C2D1C367700559ABA /* CounterData.swift */, + 6987445D2D1C367700559ABA /* SDDSCounter.swift */, + ); + path = SDDSCounter; + sourceTree = ""; + }; + 698744662D1C368E00559ABA /* SDDSSegmentElement */ = { + isa = PBXGroup; + children = ( + 6950A1262D1D4BE1001695C5 /* SegmentAppearance */, + 698744652D1C368E00559ABA /* SDDSSegmentElement.swift */, + ); + path = SDDSSegmentElement; + sourceTree = ""; + }; 8102BA282CBE9B3300C589D3 /* SDDSTextArea */ = { isa = PBXGroup; children = ( @@ -611,6 +696,8 @@ 818C03AF2C418705002C6D0A /* Components */ = { isa = PBXGroup; children = ( + 698744662D1C368E00559ABA /* SDDSSegmentElement */, + 6987445E2D1C367700559ABA /* SDDSCounter */, 8102BA282CBE9B3300C589D3 /* SDDSTextArea */, 81E9FA8D2C92B12C0041B5FF /* SDDSTextField */, 81CBC09B2C82326600FBDAC8 /* SDDSAvatarGroup */, @@ -728,6 +815,7 @@ 81998FF72C355033009074B7 /* Extensions */ = { isa = PBXGroup; children = ( + 697737842D2EB7F80077D1A4 /* View+ErrorString.swift */, 81998FF82C35503D009074B7 /* View+Modifiers.swift */, ); path = Extensions; @@ -1145,6 +1233,12 @@ 818C03BF2C43C371002C6D0A /* DebugModifier.swift in Sources */, 817339D72D033C0A0092608A /* ProgressBarAccessibility.swift in Sources */, 814E30392C99AFB3004601F7 /* SDDSAvatarGroup.swift in Sources */, + 6987445F2D1C367700559ABA /* SDDSCounter.swift in Sources */, + 698744602D1C367700559ABA /* CounterAppearance+Extensions.swift in Sources */, + 698744612D1C367700559ABA /* CounterSize.swift in Sources */, + 698744622D1C367700559ABA /* CounterColor.swift in Sources */, + 698744632D1C367700559ABA /* CounterData.swift in Sources */, + 698744642D1C367700559ABA /* CounterAppearance.swift in Sources */, 814E307E2C99CEE1004601F7 /* View+DebugModifiers.swift in Sources */, 817339D52D033BF50092608A /* ProgressBarSizeConfiguration.swift in Sources */, 818FE9312C3C1CDC00F64958 /* Components.swift in Sources */, @@ -1162,6 +1256,7 @@ 814E303A2C99AFBD004601F7 /* BackportAsyncImage.swift in Sources */, 81CF12192C6E686D0074174F /* SDDSRadioboxGroup.swift in Sources */, 814185CA2C34260300D8E524 /* ButtonSize.swift in Sources */, + 69573D6A2D1D756000359D53 /* SegmentElementAppearance+Extensions.swift in Sources */, 817339ED2D033EC90092608A /* AvatarSizeConfiguration.swift in Sources */, 81733A3A2D0733460092608A /* ChipAppearance+Extensions.swift in Sources */, 81F753422CF505DE000156D9 /* IconButton+Extensions.swift in Sources */, @@ -1169,6 +1264,7 @@ 814E30412C99B090004601F7 /* Text+FillModifier.swift in Sources */, 817339E62D033E650092608A /* RadioboxData.swift in Sources */, 816C62A22CB80EC400352891 /* Opacity.swift in Sources */, + 697736FF2D26C23F0077D1A4 /* CounterAppearance+Variation.swift in Sources */, 814E30382C99AFB0004601F7 /* SDDSAvatarModifiers.swift in Sources */, 81D2B1992C32B3E400CAA7FD /* SDDSButton.swift in Sources */, 817339E22D033DA30092608A /* ChipBorderStyle.swift in Sources */, @@ -1176,7 +1272,10 @@ 81F753262CF467F6000156D9 /* ButtonIconAttributes.swift in Sources */, 817339EB2D033EB90092608A /* AvatarStatus.swift in Sources */, 81F7532C2CF46885000156D9 /* ButtonShapeStyle.swift in Sources */, + 6950A12A2D1D4D56001695C5 /* SegmentElementSize.swift in Sources */, 81A901532CF6EAA800992B04 /* LinkButton.swift in Sources */, + 69573D6C2D1D7C5D00359D53 /* SegmentElementAppearance+Variation.swift in Sources */, + 697737852D2EB8030077D1A4 /* View+ErrorString.swift in Sources */, 81F7531C2CF46755000156D9 /* ButtonStyle.swift in Sources */, 81F753282CF46831000156D9 /* ButtonColor.swift in Sources */, 811DE1582C5017C3000DD354 /* ProgressBarAppearance.swift in Sources */, @@ -1187,11 +1286,14 @@ 818C03C92C451424002C6D0A /* RadioboxAppearance.swift in Sources */, 8102BA312CBE9B3300C589D3 /* TextAreaAppearance.swift in Sources */, 8159F7302C5D1CFE00622836 /* FillStyle.swift in Sources */, + 696843432D1EA12E002F8C7A /* SegmentElementAppearance+ButtonAppearance.swift in Sources */, 81F753242CF467E5000156D9 /* ButtonContentAlignment.swift in Sources */, + 6969EADE2D1D5B8E008536E5 /* SegmentElementAppearanceVariations.swift in Sources */, 816AA9B02C97424000C3347C /* TextFieldSizeConfiguration.swift in Sources */, 818C03CE2C4515ED002C6D0A /* SDDSRadiobox.swift in Sources */, 81998FF92C35503D009074B7 /* View+Modifiers.swift in Sources */, 816AA9A72C97280400C3347C /* PlaceholderTextField.swift in Sources */, + 698744672D1C368E00559ABA /* SDDSSegmentElement.swift in Sources */, 811DE1542C50098D000DD354 /* SDDSChip.swift in Sources */, 8102BA3C2CBEB32700C589D3 /* ExpandingTextEditor.swift in Sources */, 81F7531E2CF46793000156D9 /* ButtonAlignment.swift in Sources */, @@ -1199,7 +1301,9 @@ 81F7532E2CF4689C000156D9 /* ButtonAccessibility.swift in Sources */, 81685C2B2CFF898D00278446 /* SwitchAppearance+Extensions.swift in Sources */, 818C03B92C43B99B002C6D0A /* ColorToken+Extensions.swift in Sources */, + 696843452D1EA35B002F8C7A /* SegmentElementAccessibility.swift in Sources */, 817339E92D033E8E0092608A /* AvatarAppearance.swift in Sources */, + 6950A1282D1D4C47001695C5 /* SegmentElementAppearance.swift in Sources */, 811DE1712C5783B6000DD354 /* HierarchicalList.swift in Sources */, 818C03D02C451651002C6D0A /* SelectionControlAppearance.swift in Sources */, 814E30362C99AFAB004601F7 /* SDDSAvatar.swift in Sources */, diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/BasicButton.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/BasicButton.swift index b293583c..3acc4cea 100644 --- a/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/BasicButton.swift +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/BasicButton.swift @@ -49,6 +49,7 @@ public struct BasicButton: View { appearance: appearance, layoutMode: layoutMode, accessibility: accessibility, + counterView: nil, action: action ) } diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/ButtonAppearance/ButtonSize.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/ButtonAppearance/ButtonSize.swift index bfe3dd0d..c5b53267 100644 --- a/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/ButtonAppearance/ButtonSize.swift +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/ButtonAppearance/ButtonSize.swift @@ -27,13 +27,18 @@ public protocol ButtonSizeConfiguration: SizeConfiguration, CustomDebugStringCon /** Внутренние отступы кнопки. */ - var paddings: EdgeInsets { get } + func paddings(style: ComponentShapeStyle) -> EdgeInsets /** Размер иконки, отображаемой в кнопке. */ var iconSize: CGSize { get } + /** + Размер счетчика, отображаемой в кнопке. + */ + var counterSize: CounterSizeConfiguration { get } + /** Размер спиннера загрузки в кнопке. */ @@ -54,8 +59,9 @@ public protocol ButtonSizeConfiguration: SizeConfiguration, CustomDebugStringCon public struct DefaultButtonSize: ButtonSizeConfiguration { public var height: CGFloat = 0 public func cornerRadius(style: ComponentShapeStyle) -> CGFloat { 0 } - public var paddings: EdgeInsets = .init() + public func paddings(style: ComponentShapeStyle) -> EdgeInsets { .init() } public var iconSize: CGSize = .zero + public var counterSize: CounterSizeConfiguration = DefaultCounterSize() public var spinnerSize: CGSize = .zero public var iconHorizontalGap: CGFloat = 0 public var titleHorizontalGap: CGFloat = 0 diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/IconButton.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/IconButton.swift index eebd8ca5..9bee6a4e 100644 --- a/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/IconButton.swift +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/IconButton.swift @@ -43,6 +43,7 @@ public struct IconButton: View { appearance: appearance, layoutMode: layoutMode, accessibility: accessibility, + counterView: nil, action: action ) } diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/LinkButton.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/LinkButton.swift index a9a82872..fd444805 100644 --- a/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/LinkButton.swift +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/LinkButton.swift @@ -46,6 +46,7 @@ public struct LinkButton: View { appearance: appearance, layoutMode: layoutMode, accessibility: accessibility, + counterView: nil, action: action ) } diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/SDDSButton.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/SDDSButton.swift index c8eef7ea..23427f50 100644 --- a/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/SDDSButton.swift +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSButton/SDDSButton.swift @@ -9,11 +9,12 @@ public struct SDDSButton: View { public let iconAttributes: ButtonIconAttributes? public let isDisabled: Bool public let isLoading: Bool - public let spinnerImage: Image + public let spinnerImage: Image? public let buttonStyle: SDDSComponents.ButtonStyle public let appearance: ButtonAppearance public let layoutMode: ButtonLayoutMode public let accessibility: ButtonAccessibility + public let counterView: SDDSCounter? @Environment(\.colorScheme) var colorScheme @State private var isAnimating: Bool = false @@ -28,11 +29,12 @@ public struct SDDSButton: View { iconAttributes: ButtonIconAttributes? = nil, isDisabled: Bool = false, isLoading: Bool = false, - spinnerImage: Image = Image("spinner", bundle: Bundle(for: Components.self)), + spinnerImage: Image? = Image("spinner", bundle: Bundle(for: Components.self)), buttonStyle: SDDSComponents.ButtonStyle = .basic, appearance: ButtonAppearance, layoutMode: ButtonLayoutMode = .wrapContent, accessibility: ButtonAccessibility = ButtonAccessibility(), + counterView: SDDSCounter? = nil, action: @escaping () -> Void ) { self.title = title @@ -45,6 +47,7 @@ public struct SDDSButton: View { self.appearance = appearance self.layoutMode = layoutMode self.accessibility = accessibility + self.counterView = counterView self.action = action } @@ -106,7 +109,7 @@ public struct SDDSButton: View { if isSideBySide { Spacer() } - if !subtitle.isEmpty { + if !subtitle.isEmpty && counterView == nil { if !title.isEmpty { Spacer().frame(width: appearance.size.titleHorizontalGap) } @@ -121,13 +124,14 @@ public struct SDDSButton: View { } icon } + counter if isCentered { Spacer() } } .frame(height: appearance.size.height) - .padding(.leading, appearance.size.paddings.leading) - .padding(.trailing, appearance.size.paddings.trailing) + .padding(.leading, appearance.size.paddings(style: appearance.shapeStyle).leading) + .padding(.trailing, appearance.size.paddings(style: appearance.shapeStyle).trailing) } @ViewBuilder @@ -161,7 +165,7 @@ public struct SDDSButton: View { @ViewBuilder private var spinner: some View { - if isLoading { + if let spinnerImage = spinnerImage, isLoading { SpinnerView( image: spinnerImage, foregroundColor: currentColor(for: appearance.spinnerColor) @@ -170,6 +174,21 @@ public struct SDDSButton: View { EmptyView() } } + + @ViewBuilder + private var counter: some View { + if let counter = counterView { + SDDSCounter( + data: counter.data, + appearance: counter.appearance, + isAnimating: isAnimating, + isHighlighted: isHighlighted, + isHovered: isHovered + ) + } else { + EmptyView() + } + } } private extension SDDSButton { diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSChip/ChipAppearance/ChipAppearance+Extensions.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSChip/ChipAppearance/ChipAppearance+Extensions.swift index 0eb75750..a172980b 100644 --- a/SDDSComponents/Sources/SDDSComponents/Components/SDDSChip/ChipAppearance/ChipAppearance+Extensions.swift +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSChip/ChipAppearance/ChipAppearance+Extensions.swift @@ -14,6 +14,19 @@ public extension ChipAppearance { ) } + func shapeStyle(_ shapeStyle: ComponentShapeStyle) -> ChipAppearance { + return ChipAppearance( + size: self.size, + titleColor: self.titleColor, + titleTypography: self.titleTypography, + imageTintColor: self.imageTintColor, + buttonTintColor: self.buttonTintColor, + backgroundColor: self.backgroundColor, + shapeStyle: shapeStyle, + disabledAlpha: self.disabledAlpha + ) + } + func titleColor(_ titleColor: ColorToken) -> ChipAppearance { return ChipAppearance( size: self.size, diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance+Extensions/CounterAppearance+Extensions.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance+Extensions/CounterAppearance+Extensions.swift new file mode 100644 index 00000000..16e06b55 --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance+Extensions/CounterAppearance+Extensions.swift @@ -0,0 +1,70 @@ +import Foundation +import SDDSThemeCore + +public extension CounterAppearance { + func size(_ size: CounterSizeConfiguration) -> CounterAppearance { + return CounterAppearance( + size: size, + dataTypography: self.dataTypography, + dataColor: self.dataColor, + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha, + loadingAlpha: self.loadingAlpha + ) + } + + func dataTypography(_ dataTypography: TypographyConfiguration) -> CounterAppearance { + return CounterAppearance( + size: self.size, + dataTypography: dataTypography, + dataColor: self.dataColor, + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha, + loadingAlpha: self.loadingAlpha + ) + } + + func dataColor(_ dataColor: CounterColor) -> CounterAppearance { + return CounterAppearance( + size: self.size, + dataTypography: self.dataTypography, + dataColor: dataColor, + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha, + loadingAlpha: self.loadingAlpha + ) + } + + func backgroundColor(_ backgroundColor: CounterColor) -> CounterAppearance { + return CounterAppearance( + size: self.size, + dataTypography: self.dataTypography, + dataColor: self.dataColor, + backgroundColor: backgroundColor, + disabledAlpha: self.disabledAlpha, + loadingAlpha: self.loadingAlpha + ) + } + + func disabledAlpha(_ disabledAlpha: CGFloat) -> CounterAppearance { + return CounterAppearance( + size: self.size, + dataTypography: self.dataTypography, + dataColor: dataColor, + backgroundColor: self.backgroundColor, + disabledAlpha: disabledAlpha, + loadingAlpha: self.loadingAlpha + ) + } + + func loadingAlpha(_ loadingAlpha: CGFloat) -> CounterAppearance { + return CounterAppearance( + size: self.size, + dataTypography: self.dataTypography, + dataColor: dataColor, + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha, + loadingAlpha: loadingAlpha + ) + } +} diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance/CounterAppearance+Variation.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance/CounterAppearance+Variation.swift new file mode 100644 index 00000000..6b6b0782 --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance/CounterAppearance+Variation.swift @@ -0,0 +1,14 @@ +import Foundation + +public extension CounterAppearance { + func applyColorVariation(variation: CounterAppearance) -> CounterAppearance { + CounterAppearance( + size: size, + dataTypography: dataTypography, + dataColor: variation.dataColor, + backgroundColor: variation.backgroundColor, + disabledAlpha: variation.disabledAlpha, + loadingAlpha: variation.loadingAlpha + ) + } +} diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance/CounterAppearance.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance/CounterAppearance.swift new file mode 100644 index 00000000..847e50ff --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance/CounterAppearance.swift @@ -0,0 +1,28 @@ +import Foundation +@_exported import SDDSThemeCore + +public struct CounterAppearance { + public let size: CounterSizeConfiguration + public let dataTypography: TypographyConfiguration + public let dataColor: CounterColor + public let backgroundColor: CounterColor + public let disabledAlpha: CGFloat + public let loadingAlpha: CGFloat + + public init( + size: CounterSizeConfiguration = DefaultCounterSize(), + dataTypography: TypographyConfiguration = .default, + dataColor: CounterColor = CounterColor(), + backgroundColor: CounterColor = CounterColor(), + disabledAlpha: CGFloat = 0, + loadingAlpha: CGFloat = 0 + ) { + self.size = size + self.dataTypography = dataTypography + self.dataColor = dataColor + self.backgroundColor = backgroundColor + self.disabledAlpha = disabledAlpha + self.loadingAlpha = loadingAlpha + } +} + diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance/CounterColor.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance/CounterColor.swift new file mode 100644 index 00000000..742e2862 --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance/CounterColor.swift @@ -0,0 +1,18 @@ +import Foundation +@_exported import SDDSThemeCore + +public struct CounterColor { + public let defaultColor: ColorToken + public let highlightedColor: ColorToken + public let hoveredColor: ColorToken + + public init( + defaultColor: ColorToken = .clearColor, + highlightedColor: ColorToken = .clearColor, + hoveredColor: ColorToken = .clearColor + ) { + self.defaultColor = defaultColor + self.highlightedColor = highlightedColor + self.hoveredColor = hoveredColor + } +} diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance/CounterSize.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance/CounterSize.swift new file mode 100644 index 00000000..09c19379 --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterAppearance/CounterSize.swift @@ -0,0 +1,19 @@ +import Foundation +import SwiftUI + +public struct DefaultCounterSize: CounterSizeConfiguration { + public var height: CGFloat = 0 + public var width: CGFloat = 0 + public var paddings: EdgeInsets = .init() + public var debugDescription: String { + return "DefaultCounterSize" + } + + public init() {} +} + +public protocol CounterSizeConfiguration: SizeConfiguration, CustomDebugStringConvertible { + var height: CGFloat { get } + var width: CGFloat { get } + var paddings: EdgeInsets { get } +} diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterData.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterData.swift new file mode 100644 index 00000000..18484b39 --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/CounterData.swift @@ -0,0 +1,9 @@ +import Foundation + +public struct CounterData { + public var value: String + + public init(value: String = "") { + self.value = value + } +} diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/SDDSCounter.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/SDDSCounter.swift new file mode 100644 index 00000000..11a9928e --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSCounter/SDDSCounter.swift @@ -0,0 +1,90 @@ +import Foundation +import SwiftUI +@_exported import SDDSThemeCore + +public struct SDDSCounter: View { + public let data: CounterData + public let appearance: CounterAppearance + + @Environment(\.colorScheme) var colorScheme + + var isAnimating: Bool + var isHighlighted: Bool + var isHovered: Bool + + public init( + data: CounterData, + appearance: CounterAppearance, + isAnimating: Bool, + isHighlighted: Bool, + isHovered: Bool + ) { + self.data = data + self.appearance = appearance + self.isAnimating = isAnimating + self.isHighlighted = isHighlighted + self.isHovered = isHovered + } + + public var body: some View { + ZStack { + if isAuto { + counterAutomationSize + .padding(appearance.size.paddings) + } else { + counterMinimumSize + } + } + .background(currentColor(for: appearance.backgroundColor)) + .cornerRadius(cornerRadius) + .frame(height: appearance.size.height) + } + + public var counterAutomationSize: some View { + Text(text) + .typography(dataTypography) + .foregroundColor(currentColor(for: appearance.dataColor)) + .fixedSize() + } + + @ViewBuilder + public var counterMinimumSize: some View { + Circle() + .fill(currentColor(for: appearance.backgroundColor)) + Text(text) + .typography(dataTypography) + .foregroundColor(currentColor(for: appearance.dataColor)) + } +} + +private extension SDDSCounter { + var cornerRadius: CGFloat { + return appearance.size.height / 2 + } + + var dataTypography: TypographyToken { + if let typography = appearance.dataTypography.typography(with: appearance.size) { + return typography + } else { + fatalError(undefinedTypographyErrorText(sizeDescription: appearance.size.debugDescription)) + } + } + + func currentColor(for counterColor: CounterColor) -> Color { + if isHighlighted { + return counterColor.highlightedColor.color(for: colorScheme) + } else if isHovered { + return counterColor.hoveredColor.color(for: colorScheme) + } else { + return counterColor.defaultColor.color(for: colorScheme) + } + } + + var isAuto: Bool { + data.value.count > 1 + } + + var text: String { + data.value + } +} diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SDDSSegmentElement.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SDDSSegmentElement.swift new file mode 100644 index 00000000..80d2c85d --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SDDSSegmentElement.swift @@ -0,0 +1,116 @@ +import Foundation +import SwiftUI + +public struct SDDSSegmentElement: View { + public let title: String + public let subtitle: String + public let iconAttributes: ButtonIconAttributes? + public let isDisabled: Bool + public let appearance: SegmentElementAppearance + public let accessibility: SegmentElementAccessibility + public let counter: SDDSCounter? + public var action: () -> Void + + public init( + title: String, + subtitle: String, + iconAttributes: ButtonIconAttributes?, + isDisabled: Bool = false, + appearance: SegmentElementAppearance, + accessibility: SegmentElementAccessibility = SegmentElementAccessibility(), + counter: SDDSCounter? = nil, + action: @escaping () -> Void + ) { + self.title = title + self.subtitle = subtitle + self.iconAttributes = iconAttributes + self.isDisabled = isDisabled + self.appearance = appearance + self.accessibility = accessibility + self.counter = counter + self.action = action + } + + @ViewBuilder + public var body: some View { + if hasTitle && !hasCounter && !hasSubtitle { + SDDSButton( + title: title, + subtitle: "", + iconAttributes: iconAttributes, + isDisabled: isDisabled, + isLoading: false, + spinnerImage: nil, + buttonStyle: .basic, + appearance: appearance.buttonAppearance, + counterView: nil, + action: action + ) + } else if hasTitle && !hasCounter && hasSubtitle { + SDDSButton( + title: title, + subtitle: subtitle, + iconAttributes: iconAttributes, + isDisabled: isDisabled, + isLoading: false, + spinnerImage: nil, + buttonStyle: .basic, + appearance: appearance.buttonAppearance, + counterView: nil, + action: action + ) + } else if hasTitle && hasCounter && !hasSubtitle && isIconLeading { + SDDSButton( + title: title, + subtitle: "", + iconAttributes: iconAttributes, + isDisabled: isDisabled, + isLoading: false, + spinnerImage: nil, + buttonStyle: .basic, + appearance: appearance.buttonAppearance, + counterView: counter, + action: action + ) + } else { + SDDSButton( + title: title, + subtitle: "", + iconAttributes: iconAttributes, + isDisabled: isDisabled, + isLoading: false, + spinnerImage: nil, + buttonStyle: .basic, + appearance: appearance.buttonAppearance, + counterView: nil, + action: action + ) + } + } +} + + +extension SDDSSegmentElement { + var hasIconAttributes: Bool { + iconAttributes != nil + } + + var isIconLeading: Bool { + if let iconAttributes = iconAttributes, iconAttributes.alignment == .leading { + return true + } + return false + } + + var hasTitle: Bool { + !title.isEmpty + } + + var hasSubtitle: Bool { + !subtitle.isEmpty + } + + var hasCounter: Bool { + counter != nil + } +} diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAccessibility.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAccessibility.swift new file mode 100644 index 00000000..590c11ad --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAccessibility.swift @@ -0,0 +1,40 @@ +import Foundation + +public struct SegmentElementAccessibility { + public var label: String + public var hint: String + public var value: String + + /** + Инициализатор для создания параметров доступности кнопки. + + - Parameters: + - label: Метка доступности для кнопки. + - hint: Подсказка доступности для кнопки. + - value: Значение доступности для кнопки. + */ + public init(label: String, hint: String, value: String) { + self.label = label + self.hint = hint + self.value = value + } + + /** + Инициализатор для создания параметров доступности кнопки с пустыми значениями. + */ + public init() { + self.label = "" + self.hint = "" + self.value = "" + } +} + +extension SegmentElementAccessibility { + var buttonAccessibility: ButtonAccessibility { + ButtonAccessibility( + label: label, + hint: hint, + value: value + ) + } +} diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearance+ButtonAppearance.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearance+ButtonAppearance.swift new file mode 100644 index 00000000..c8e60d50 --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearance+ButtonAppearance.swift @@ -0,0 +1,19 @@ +import Foundation + +extension SegmentElementAppearance { + var buttonAppearance: ButtonAppearance { + .init( + size: self.size, + shapeStyle: self.shapeStyle, + titleTypography: self.titleTypography, + titleColor: self.titleColor, + subtitleTypography: self.subtitleTypography, + subtitleColor: self.subtitleColor, + iconColor: self.iconColor, + spinnerColor: ButtonColor(), + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha, + loadingAlpha: 0 + ) + } +} diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearance+Extensions.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearance+Extensions.swift new file mode 100644 index 00000000..08caba4e --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearance+Extensions.swift @@ -0,0 +1,143 @@ +import Foundation + +public extension SegmentElementAppearance { + func size(_ size: SegmentElementSizeConfiguration) -> SegmentElementAppearance { + return SegmentElementAppearance( + size: size, + shapeStyle: self.shapeStyle, + titleTypography: self.titleTypography, + titleColor: self.titleColor, + subtitleTypography: self.subtitleTypography, + subtitleColor: self.subtitleColor, + iconColor: self.iconColor, + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha + ) + } + + func shapeStyle(_ shapeStyle: ComponentShapeStyle) -> SegmentElementAppearance { + return SegmentElementAppearance( + size: self.size, + shapeStyle: shapeStyle, + titleTypography: self.titleTypography, + titleColor: self.titleColor, + subtitleTypography: self.subtitleTypography, + subtitleColor: self.subtitleColor, + iconColor: self.iconColor, + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha + ) + } + + func titleTypography(_ titleTypography: TypographyConfiguration) -> SegmentElementAppearance { + return SegmentElementAppearance( + size: self.size, + shapeStyle: self.shapeStyle, + titleTypography: titleTypography, + titleColor: self.titleColor, + subtitleTypography: self.subtitleTypography, + subtitleColor: self.subtitleColor, + iconColor: self.iconColor, + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha + ) + } + + func titleColor(_ titleColor: ButtonColor) -> SegmentElementAppearance { + return SegmentElementAppearance( + size: self.size, + shapeStyle: self.shapeStyle, + titleTypography: self.titleTypography, + titleColor: titleColor, + subtitleTypography: self.subtitleTypography, + subtitleColor: self.subtitleColor, + iconColor: self.iconColor, + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha + ) + } + + func subtitleTypography(_ subtitleTypography: TypographyConfiguration) -> SegmentElementAppearance { + return SegmentElementAppearance( + size: self.size, + shapeStyle: self.shapeStyle, + titleTypography: self.titleTypography, + titleColor: self.titleColor, + subtitleTypography: subtitleTypography, + subtitleColor: self.subtitleColor, + iconColor: self.iconColor, + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha + ) + } + + func subtitleColor(_ subtitleColor: ButtonColor) -> SegmentElementAppearance { + return SegmentElementAppearance( + size: self.size, + shapeStyle: self.shapeStyle, + titleTypography: self.titleTypography, + titleColor: self.titleColor, + subtitleTypography: self.subtitleTypography, + subtitleColor: subtitleColor, + iconColor: self.iconColor, + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha + ) + } + + func iconColor(_ iconColor: ButtonColor) -> SegmentElementAppearance { + return SegmentElementAppearance( + size: self.size, + shapeStyle: self.shapeStyle, + titleTypography: self.titleTypography, + titleColor: self.titleColor, + subtitleTypography: self.subtitleTypography, + subtitleColor: self.subtitleColor, + iconColor: iconColor, + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha + ) + } + + func backgroundColor(_ backgroundColor: ButtonColor) -> SegmentElementAppearance { + return SegmentElementAppearance( + size: self.size, + shapeStyle: self.shapeStyle, + titleTypography: self.titleTypography, + titleColor: self.titleColor, + subtitleTypography: self.subtitleTypography, + subtitleColor: self.subtitleColor, + iconColor: self.iconColor, + backgroundColor: backgroundColor, + disabledAlpha: self.disabledAlpha + ) + } + + func disabledAlpha(_ disabledAlpha: CGFloat) -> SegmentElementAppearance { + return SegmentElementAppearance( + size: self.size, + shapeStyle: self.shapeStyle, + titleTypography: self.titleTypography, + titleColor: self.titleColor, + subtitleTypography: self.subtitleTypography, + subtitleColor: self.subtitleColor, + iconColor: self.iconColor, + backgroundColor: self.backgroundColor, + disabledAlpha: disabledAlpha + ) + } + + func loadingAlpha(_ loadingAlpha: CGFloat) -> SegmentElementAppearance { + return SegmentElementAppearance( + size: self.size, + shapeStyle: self.shapeStyle, + titleTypography: self.titleTypography, + titleColor: self.titleColor, + subtitleTypography: self.subtitleTypography, + subtitleColor: self.subtitleColor, + iconColor: self.iconColor, + backgroundColor: self.backgroundColor, + disabledAlpha: self.disabledAlpha + ) + } +} diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearance+Variation.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearance+Variation.swift new file mode 100644 index 00000000..78d894db --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearance+Variation.swift @@ -0,0 +1,19 @@ +import Foundation + +public extension SegmentElementAppearance { + func applyColorVariation(variation: SegmentElementAppearance) -> SegmentElementAppearance { + SegmentElementAppearance( + size: size, + shapeStyle: shapeStyle, + titleTypography: titleTypography, + titleColor: variation.titleColor, + subtitleTypography: subtitleTypography, + subtitleColor: variation.subtitleColor, + iconColor: variation.iconColor, + backgroundColor: variation.backgroundColor, + disabledAlpha: variation.disabledAlpha + ) + } +} + + diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearance.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearance.swift new file mode 100644 index 00000000..cac556ae --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearance.swift @@ -0,0 +1,44 @@ +import Foundation +@_exported import SDDSThemeCore + +public struct SegmentElementAppearance { + public let size: SegmentElementSizeConfiguration + + public let shapeStyle: ComponentShapeStyle + + public let titleTypography: TypographyConfiguration + + public let titleColor: ButtonColor + + public let subtitleTypography: TypographyConfiguration + + public let subtitleColor: ButtonColor + + public let iconColor: ButtonColor + + public let backgroundColor: ButtonColor + + public let disabledAlpha: CGFloat + + public init( + size: SegmentElementSizeConfiguration = DefaultSegmentElementSize(), + shapeStyle: ComponentShapeStyle = .cornered, + titleTypography: TypographyConfiguration = .default, + titleColor: ButtonColor = ButtonColor(), + subtitleTypography: TypographyConfiguration = .default, + subtitleColor: ButtonColor = ButtonColor(), + iconColor: ButtonColor = ButtonColor(), + backgroundColor: ButtonColor = ButtonColor(), + disabledAlpha: CGFloat = 0 + ) { + self.size = size + self.shapeStyle = shapeStyle + self.titleTypography = titleTypography + self.titleColor = titleColor + self.subtitleTypography = subtitleTypography + self.subtitleColor = subtitleColor + self.iconColor = iconColor + self.backgroundColor = backgroundColor + self.disabledAlpha = disabledAlpha + } +} diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearanceVariations.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearanceVariations.swift new file mode 100644 index 00000000..2e19889e --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementAppearanceVariations.swift @@ -0,0 +1,19 @@ +import Foundation + +public struct SegmentElementAppearanceVariation: Hashable { + public let name: String + public let appearance: SegmentElementAppearance + + public init(name: String = "", appearance: SegmentElementAppearance = SegmentElementAppearance()) { + self.name = name + self.appearance = appearance + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(name) + } + + public static func == (lhs: SegmentElementAppearanceVariation, rhs: SegmentElementAppearanceVariation) -> Bool { + return lhs.name == rhs.name + } +} diff --git a/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementSize.swift b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementSize.swift new file mode 100644 index 00000000..d2e98f87 --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Components/SDDSSegmentElement/SegmentAppearance/SegmentElementSize.swift @@ -0,0 +1,21 @@ +import Foundation +import SwiftUI + +public protocol SegmentElementSizeConfiguration: ButtonSizeConfiguration, CustomDebugStringConvertible {} + +public struct DefaultSegmentElementSize: SegmentElementSizeConfiguration { + public var height: CGFloat = 0 + public func cornerRadius(style: ComponentShapeStyle) -> CGFloat { 0 } + public func paddings(style: ComponentShapeStyle) -> EdgeInsets { .init() } + public var iconSize: CGSize = .zero + public var counterSize: CounterSizeConfiguration = DefaultCounterSize() + public var iconHorizontalGap: CGFloat = 0 + public var titleHorizontalGap: CGFloat = 0 + public var spinnerSize: CGSize = .zero + + public var debugDescription: String { + return "Default SegmentElement Size" + } + + public init() {} +} diff --git a/SDDSComponents/Sources/SDDSComponents/Extensions/View+ErrorString.swift b/SDDSComponents/Sources/SDDSComponents/Extensions/View+ErrorString.swift new file mode 100644 index 00000000..c62572bc --- /dev/null +++ b/SDDSComponents/Sources/SDDSComponents/Extensions/View+ErrorString.swift @@ -0,0 +1,7 @@ +import SwiftUI + +public extension View { + func undefinedTypographyErrorText(sizeDescription: String) -> String { + "Undefined \(Self.self) Typography for size \(sizeDescription). Using a default value." + } +} diff --git a/SDDSDemoApp/SDDSDemoApp.xcodeproj/project.pbxproj b/SDDSDemoApp/SDDSDemoApp.xcodeproj/project.pbxproj index 56ea845c..ff482de4 100644 --- a/SDDSDemoApp/SDDSDemoApp.xcodeproj/project.pbxproj +++ b/SDDSDemoApp/SDDSDemoApp.xcodeproj/project.pbxproj @@ -13,6 +13,10 @@ 24C154172BB3020B00963FAA /* PlasmaDemoAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24C154162BB3020B00963FAA /* PlasmaDemoAppTests.swift */; }; 24C154212BB3020B00963FAA /* PlasmaDemoAppUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24C154202BB3020B00963FAA /* PlasmaDemoAppUITests.swift */; }; 24C154232BB3020B00963FAA /* PlasmaDemoAppUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24C154222BB3020B00963FAA /* PlasmaDemoAppUITestsLaunchTests.swift */; }; + 6987446B2D1C3B6400559ABA /* CounterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698744692D1C3B6400559ABA /* CounterView.swift */; }; + 6987446C2D1C3B6400559ABA /* CounterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6987446A2D1C3B6400559ABA /* CounterViewModel.swift */; }; + 698744742D1C6EC300559ABA /* SegmentElementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698744732D1C6EB800559ABA /* SegmentElementView.swift */; }; + 698744762D1C6F0100559ABA /* SegmentElementViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698744752D1C6EC600559ABA /* SegmentElementViewModel.swift */; }; 8104243D2CFDB83F0009B6E9 /* SDDSServTheme in Frameworks */ = {isa = PBXBuildFile; productRef = 8104243C2CFDB83F0009B6E9 /* SDDSServTheme */; }; 810424402CFDB8E10009B6E9 /* SDDSComponents in Frameworks */ = {isa = PBXBuildFile; productRef = 8104243F2CFDB8E10009B6E9 /* SDDSComponents */; }; 810424422CFDB8FD0009B6E9 /* SDDSComponentsPreview in Frameworks */ = {isa = PBXBuildFile; productRef = 810424412CFDB8FD0009B6E9 /* SDDSComponentsPreview */; }; @@ -87,6 +91,10 @@ 24C1541C2BB3020B00963FAA /* SDDSDemoAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SDDSDemoAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 24C154202BB3020B00963FAA /* PlasmaDemoAppUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlasmaDemoAppUITests.swift; sourceTree = ""; }; 24C154222BB3020B00963FAA /* PlasmaDemoAppUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlasmaDemoAppUITestsLaunchTests.swift; sourceTree = ""; }; + 698744692D1C3B6400559ABA /* CounterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CounterView.swift; sourceTree = ""; }; + 6987446A2D1C3B6400559ABA /* CounterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CounterViewModel.swift; sourceTree = ""; }; + 698744732D1C6EB800559ABA /* SegmentElementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentElementView.swift; sourceTree = ""; }; + 698744752D1C6EC600559ABA /* SegmentElementViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentElementViewModel.swift; sourceTree = ""; }; 811C684C2CC8024A00480F89 /* TextAreaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextAreaView.swift; sourceTree = ""; }; 811C684E2CC8027600480F89 /* TextAreaViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextAreaViewModel.swift; sourceTree = ""; }; 811C68502CC8059800480F89 /* ChipGroupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChipGroupViewModel.swift; sourceTree = ""; }; @@ -234,6 +242,24 @@ name = Frameworks; sourceTree = ""; }; + 698744682D1C38A200559ABA /* CounterView */ = { + isa = PBXGroup; + children = ( + 698744692D1C3B6400559ABA /* CounterView.swift */, + 6987446A2D1C3B6400559ABA /* CounterViewModel.swift */, + ); + path = CounterView; + sourceTree = ""; + }; + 698744722D1C6E9C00559ABA /* SegmentElementView */ = { + isa = PBXGroup; + children = ( + 698744732D1C6EB800559ABA /* SegmentElementView.swift */, + 698744752D1C6EC600559ABA /* SegmentElementViewModel.swift */, + ); + path = SegmentElementView; + sourceTree = ""; + }; 811C684B2CC8023600480F89 /* TextAreaView */ = { isa = PBXGroup; children = ( @@ -254,6 +280,8 @@ 811DE1362C4D0EB2000DD354 /* Views */ = { isa = PBXGroup; children = ( + 698744722D1C6E9C00559ABA /* SegmentElementView */, + 698744682D1C38A200559ABA /* CounterView */, 811C684B2CC8023600480F89 /* TextAreaView */, 819A382D2CC6927700A1377F /* ChipGroupView */, 8199E5A12CBD088000AF3D22 /* TextFieldView */, @@ -585,10 +613,13 @@ 819A382F2CC6928700A1377F /* ChipGroupView.swift in Sources */, 811DE1352C4D0C7F000DD354 /* ComponentsView.swift in Sources */, 811C684F2CC8027600480F89 /* TextAreaViewModel.swift in Sources */, + 6987446B2D1C3B6400559ABA /* CounterView.swift in Sources */, + 6987446C2D1C3B6400559ABA /* CounterViewModel.swift in Sources */, 816243572CB940F400506E1C /* ChipViewModel.swift in Sources */, 817580EC2C383BA500E45207 /* ButtonViewModel.swift in Sources */, 811DE1422C4D15FA000DD354 /* RadioboxView.swift in Sources */, 811C68512CC8059800480F89 /* ChipGroupViewModel.swift in Sources */, + 698744762D1C6F0100559ABA /* SegmentElementViewModel.swift in Sources */, 811DE1472C4D19D3000DD354 /* ProgressBarView.swift in Sources */, 8162435B2CB9418B00506E1C /* RadioboxGroupView.swift in Sources */, 816243512CB93EC800506E1C /* AvatarGroupViewModel.swift in Sources */, @@ -612,6 +643,7 @@ 811DE14E2C4D1DAD000DD354 /* SwitchViewModel.swift in Sources */, 8199A1722C3BF94E00AD650A /* Spacing.swift in Sources */, 816243492CB90FA800506E1C /* GradientPickerView.swift in Sources */, + 698744742D1C6EC300559ABA /* SegmentElementView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SDDSDemoApp/SDDSDemoApp/Views/ButtonView/ButtonViewModel.swift b/SDDSDemoApp/SDDSDemoApp/Views/ButtonView/ButtonViewModel.swift index b1ab857d..8321e59d 100644 --- a/SDDSDemoApp/SDDSDemoApp/Views/ButtonView/ButtonViewModel.swift +++ b/SDDSDemoApp/SDDSDemoApp/Views/ButtonView/ButtonViewModel.swift @@ -57,7 +57,7 @@ final class ButtonViewModel: ObservableObject { if value { self.appearance = self.appearance.shapeStyle(.pilled) } else { - self.appearance = self.appearance.shapeStyle(.default) + self.appearance = self.appearance.shapeStyle(.cornered) } } .store(in: &cancellables) @@ -114,5 +114,4 @@ final class ButtonViewModel: ObservableObject { private func iconAttributes(with alignment: SDDSComponents.ButtonAlignment) -> ButtonIconAttributes { .init(image: Image("buttonIcon"), alignment: alignment) } - } diff --git a/SDDSDemoApp/SDDSDemoApp/Views/ChipGroupView/ChipGroupViewModel.swift b/SDDSDemoApp/SDDSDemoApp/Views/ChipGroupView/ChipGroupViewModel.swift index e317d2b8..bc29c96a 100644 --- a/SDDSDemoApp/SDDSDemoApp/Views/ChipGroupView/ChipGroupViewModel.swift +++ b/SDDSDemoApp/SDDSDemoApp/Views/ChipGroupView/ChipGroupViewModel.swift @@ -6,7 +6,7 @@ import SDDSServTheme final class ChipGroupViewModel: ObservableObject { @Published var chipTitle: String = "" - @Published var chipSize: SDDSChipSize = .medium(.pilled) + @Published var chipSize: SDDSChipSize = .medium @Published var chipGroupSize: DefaultChipGroupSize = .init(alignment: .left) @Published var chips: [ChipData] = [] @Published var appearance: ChipAppearance = SDDSChip.default.appearance { diff --git a/SDDSDemoApp/SDDSDemoApp/Views/ChipView/ChipView.swift b/SDDSDemoApp/SDDSDemoApp/Views/ChipView/ChipView.swift index 8c593209..ce8d0256 100644 --- a/SDDSDemoApp/SDDSDemoApp/Views/ChipView/ChipView.swift +++ b/SDDSDemoApp/SDDSDemoApp/Views/ChipView/ChipView.swift @@ -55,8 +55,8 @@ struct ChipView: View { } Picker("Border Style", selection: $viewModel.borderStyle) { - Text("Default").tag(ChipBorderStyle.default(viewModel.size.shapeToken.cornerRadius)) - Text("Pilled").tag(ChipBorderStyle.pilled) + Text("Default").tag(ComponentShapeStyle.cornered) + Text("Pilled").tag(ComponentShapeStyle.pilled) } HStack { diff --git a/SDDSDemoApp/SDDSDemoApp/Views/ChipView/ChipViewModel.swift b/SDDSDemoApp/SDDSDemoApp/Views/ChipView/ChipViewModel.swift index e64f6141..68d9a5f0 100644 --- a/SDDSDemoApp/SDDSDemoApp/Views/ChipView/ChipViewModel.swift +++ b/SDDSDemoApp/SDDSDemoApp/Views/ChipView/ChipViewModel.swift @@ -9,8 +9,8 @@ final class ChipViewModel: ObservableObject { @Published var isEnabled: Bool = true @Published var iconImageEnabled: Bool = true @Published var buttomImageEnabled: Bool = true - @Published var size: SDDSChipSize = .medium(.default(8)) - @Published var borderStyle: ChipBorderStyle = .default(8) + @Published var size: SDDSChipSize = .medium + @Published var borderStyle: ComponentShapeStyle = .cornered @Published var appearance: ChipAppearance = SDDSChip.accent.appearance @Published var variationName: String = SDDSChip.accent.name @Published var iconImage: Image? = nil @@ -21,7 +21,6 @@ final class ChipViewModel: ObservableObject { init() { setIconImage() setButtonImage() - observeSizeChange() } @@ -48,17 +47,8 @@ final class ChipViewModel: ObservableObject { buttonImage = Image.image("chipClose") } - func updateBorderStyle(borderStyle: ChipBorderStyle) { - switch size { - case .small: - size = .small(borderStyle) - case .medium: - size = .medium(borderStyle) - case .large: - size = .large(borderStyle) - case .extraSmall: - size = .extraSmall(borderStyle) - } + func updateBorderStyle(borderStyle: ComponentShapeStyle) { + appearance = appearance.shapeStyle(borderStyle) } } @@ -66,7 +56,7 @@ final class ChipViewModel: ObservableObject { extension SDDSChipSize: Hashable, CaseIterable { public static var allCases: [SDDSChipSize] { - [.large(.default(8)), .medium(.default(8)), .small(.default(8)), .extraSmall(.default(8))] + [.large, .medium, .small, .extraSmall] } public var debugDescription: String { diff --git a/SDDSDemoApp/SDDSDemoApp/Views/Components/ComponentsView.swift b/SDDSDemoApp/SDDSDemoApp/Views/Components/ComponentsView.swift index ff335d6c..a930cc26 100644 --- a/SDDSDemoApp/SDDSDemoApp/Views/Components/ComponentsView.swift +++ b/SDDSDemoApp/SDDSDemoApp/Views/Components/ComponentsView.swift @@ -14,7 +14,9 @@ struct ComponentsView: View { ("SDDSRadioboxGroup", AnyView(RadioboxGroupView())), ("SDDSSwitch", AnyView(SwitchView())), ("SDDSTextArea", AnyView(TextAreaView())), - ("SDDSTextField", AnyView(TextFieldView())) + ("SDDSTextField", AnyView(TextFieldView())), + ("SDDSCounter", AnyView(CounterView())), + ("SDDSSegmentElement", AnyView(SegmentElementView())) ] var body: some View { diff --git a/SDDSDemoApp/SDDSDemoApp/Views/CounterView/CounterView.swift b/SDDSDemoApp/SDDSDemoApp/Views/CounterView/CounterView.swift new file mode 100644 index 00000000..063684d7 --- /dev/null +++ b/SDDSDemoApp/SDDSDemoApp/Views/CounterView/CounterView.swift @@ -0,0 +1,88 @@ +import Foundation +import SwiftUI +import Combine +import SDDSComponents +import SDDSComponentsPreview +import SDDSServTheme + +public struct CounterView: View { + @ObservedObject var viewModel: CounterViewModel + + init(viewModel: CounterViewModel = CounterViewModel()) { + self.viewModel = viewModel + } + + public var body: some View { + List { + counterView + Section { + dataTextField + appearance + size + } + } + } + + public var counterView: some View { + Section { + HStack { + Spacer() + SDDSCounter( + data: viewModel.data, + appearance: viewModel.appearance, + isAnimating: false, + isHighlighted: false, + isHovered: false + ) + Spacer() + } + } + } + + public var dataTextField: some View { + HStack { + Text("Data") + TextField("data", text: $viewModel.data.value) + .multilineTextAlignment(.trailing) + } + } + + public var appearance: some View { + HStack { + Text("Appearance") + Spacer() + Menu { + ForEach(SDDSCounter.all, id: \.self) { variation in + Button(variation.name) { + viewModel.appearance = variation.appearance.size(viewModel.size) + viewModel.variationName = variation.name + } + } + } label: { + Text(viewModel.variationName.capitalized) + } + } + } + + public var size: some View { + HStack { + Text("Size") + Spacer() + Menu { + ForEach(CounterSize.allCases, id: \.self) { size in + Button(size.rawValue) { + viewModel.size = size + } + } + } label: { + if let size = viewModel.size as? CounterSize { + Text(size.rawValue) + } + } + } + } +} + +#Preview { + CounterView(viewModel: CounterViewModel()) +} diff --git a/SDDSDemoApp/SDDSDemoApp/Views/CounterView/CounterViewModel.swift b/SDDSDemoApp/SDDSDemoApp/Views/CounterView/CounterViewModel.swift new file mode 100644 index 00000000..539df646 --- /dev/null +++ b/SDDSDemoApp/SDDSDemoApp/Views/CounterView/CounterViewModel.swift @@ -0,0 +1,31 @@ +import Foundation +import SwiftUI +import Combine +import SDDSComponents +import SDDSComponentsPreview +import SDDSServTheme + +final class CounterViewModel: ObservableObject { + @Published var data: CounterData = CounterData(value: "1") + @Published var appearance: CounterAppearance = SDDSCounter.black.appearance + @Published var size: CounterSizeConfiguration = CounterSize.medium + + @Published var variationName: String = SDDSCounter.black.name + + private var cancellables: Set = [] + + init() { + observeSizeChange() + } + + private func observeSizeChange() { + $size + .sink { [weak self] value in + guard let self = self else { + return + } + self.appearance = self.appearance.size(value) + } + .store(in: &cancellables) + } +} diff --git a/SDDSDemoApp/SDDSDemoApp/Views/SegmentElementView/SegmentElementView.swift b/SDDSDemoApp/SDDSDemoApp/Views/SegmentElementView/SegmentElementView.swift new file mode 100644 index 00000000..f566b711 --- /dev/null +++ b/SDDSDemoApp/SDDSDemoApp/Views/SegmentElementView/SegmentElementView.swift @@ -0,0 +1,188 @@ +import Foundation +import SwiftUI +import Combine +import SDDSComponents +import SDDSComponentsPreview +import SDDSServTheme + +public struct SegmentElementView: View { + @ObservedObject private var viewModel: SegmentElementViewModel + + init(viewModel: SegmentElementViewModel = SegmentElementViewModel()) { + self.viewModel = viewModel + } + + public var body: some View { + List { + segmentView + Section { + segmentElementType + title + subtitle + size + appearance + alignment + disabled + shapeStyle + } + + Section { + counter + } + } + } + + public var segmentView: some View { + HStack { + Spacer() + SDDSSegmentElement( + title: viewModel.title, + subtitle: viewModel.subtitle, + iconAttributes: viewModel.iconAttributes, + isDisabled: viewModel.isDisabled, + appearance: viewModel.appearance, + counter: viewModel.counter, + action: {} + ) + Spacer() + } + } + + public var segmentElementType: some View { + HStack { + Text("Content Type") + Spacer() + Menu { + ForEach(SegmentElementContentType.allCases, id: \.self) { content in + Button { + viewModel.contentType = content + } label: { + Text(content.rawValue.capitalized) + } + } + } label: { + Text(viewModel.contentType.rawValue.capitalized) + } + } + } + + public var title: some View { + HStack { + Text("Title") + TextField("Title", text: $viewModel.title) + .multilineTextAlignment(.trailing) + } + } + + public var subtitle: some View { + HStack { + Text("Value") + TextField("Subtitle", text: $viewModel.subtitle) + .multilineTextAlignment(.trailing) + } + } + + public var size: some View { + HStack { + Text("Size") + Spacer() + Menu { + ForEach(SegmentElementSize.allCases, id: \.self) { size in + Button(size.rawValue) { + viewModel.size = size + viewModel.counterSize = size.counterSize + } + } + } label: { + if let size = viewModel.size as? SegmentElementSize { + Text(size.rawValue) + } + } + } + } + + public var appearance: some View { + HStack { + Text("Appearance") + Spacer() + Menu { + ForEach(SDDSSegmentElement.all, id: \.self) { variation in + Button(variation.name) { + viewModel.appearance = variation.appearance.size(viewModel.size) + viewModel.variationName = variation.name + } + } + } label: { + Text(viewModel.variationName.capitalized) + } + } + } + + public var shapeStyle: some View { + HStack { + Toggle("Pilled", isOn: $viewModel.isPilled) + } + } + + public var alignment: some View { + HStack { + Text("icon Alignment") + Spacer() + Menu { + ForEach(ButtonAlignment.allCases, id: \.self) { alignment in + Button(alignment.rawValue) { + viewModel.alignment = alignment + } + } + } label: { + Text(viewModel.alignment.rawValue) + } + } + } + + public var disabled: some View { + HStack { + Toggle("Disabled", isOn: $viewModel.isDisabled) + } + } + + public var counter: some View { + VStack { + HStack { + Text("Appearance") + Spacer() + Menu { + ForEach(SDDSCounter.all, id: \.self) { variation in + Button(variation.name) { + viewModel.counterAppearance = variation.appearance.size(viewModel.appearance.size.counterSize) + viewModel.counterVariationName = variation.name + } + } + } label: { + Text(viewModel.counterVariationName.capitalized) + } + } + Divider() + HStack { + Text("Counter data") + TextField("Number", text: $viewModel.counterData.value) + .multilineTextAlignment(.trailing) + } + Divider() + HStack { + Text("Current Size") + Spacer() + if let currentSize = viewModel.size.counterSize as? CounterSize, viewModel.isCounterVisible { + Text(currentSize.rawValue) + } else { + Text("none") + .foregroundColor(Color.gray.opacity(0.5)) + } + } + } + } +} + +#Preview { + SegmentElementView(viewModel: SegmentElementViewModel()) +} diff --git a/SDDSDemoApp/SDDSDemoApp/Views/SegmentElementView/SegmentElementViewModel.swift b/SDDSDemoApp/SDDSDemoApp/Views/SegmentElementView/SegmentElementViewModel.swift new file mode 100644 index 00000000..51491fdf --- /dev/null +++ b/SDDSDemoApp/SDDSDemoApp/Views/SegmentElementView/SegmentElementViewModel.swift @@ -0,0 +1,191 @@ +import Foundation +import SwiftUI +import Combine +import SDDSComponents +import SDDSComponentsPreview +import SDDSServTheme + +public enum SegmentElementContentType: String, CaseIterable { + case icon + case counter + case helpText + case none +} + +final class SegmentElementViewModel: ObservableObject { + @Published var title: String = "" + @Published var subtitle: String = "" + @Published var size: SegmentElementSizeConfiguration = SegmentElementSize.medium + @Published var iconAttributes: ButtonIconAttributes? = nil + @Published var isDisabled: Bool = false + @Published var appearance: SegmentElementAppearance = SDDSSegmentElement.default.appearance + @Published var variationName: String = SDDSSegmentElement.default.name + @Published var isPilled: Bool = false + + @Published var contentType: SegmentElementContentType = .none + + @Published var isIconVisible: Bool = false + @Published var alignment: SDDSComponents.ButtonAlignment = .leading + + @Published var isCounterVisible: Bool = false + @Published var counter: SDDSCounter? = nil + @Published var counterData: CounterData = CounterData(value: "") + @Published var counterSize: CounterSizeConfiguration = CounterSize.medium + @Published var counterAppearance: CounterAppearance = SDDSCounter.accent.appearance + @Published var counterVariationName: String = SDDSCounter.accent.name + + var cancellables: Set = [] + + init() { + observeIcon() + observeSizeChange() + observeCounter() + observeContentType() + } + + private func observeIcon() { + $isIconVisible + .sink { [weak self] value in + guard let self = self else { + return + } + if value { + self.setIconAttributes(alignment: self.alignment) + } else { + self.iconAttributes = nil + } + } + .store(in: &cancellables) + + $alignment + .sink { [weak self] value in + guard let self = self else { + return + } + setIconAttributes(alignment: value) + } + .store(in: &cancellables) + + $isPilled + .sink { [weak self] value in + guard let self = self else { + return + } + if value { + self.appearance = self.appearance.shapeStyle(.pilled) + } else { + self.appearance = self.appearance.shapeStyle(.cornered) + } + } + .store(in: &cancellables) + } + + private func observeSizeChange() { + $size + .sink { [weak self] value in + guard let self = self else { + return + } + self.appearance = self.appearance.size(value) + self.counterAppearance = self.counterAppearance.size(value.counterSize) + } + .store(in: &cancellables) + } + + private func observeCounter() { + $isCounterVisible + .sink { [weak self] value in + guard let self = self else { + return + } + if value { + setCounter() + } else { + self.counter = nil + } + } + .store(in: &cancellables) + + $counterData + .sink { [weak self] _ in + guard let self = self else { + return + } + setCounter() + } + .store(in: &cancellables) + + $counterVariationName + .sink { [weak self] _ in + guard let self = self else { + return + } + if self.isCounterVisible { + setCounter() + } + } + .store(in: &cancellables) + + $counterSize + .sink { [weak self] _ in + guard let self = self else { + return + } + if self.isCounterVisible { + setCounter() + } + } + .store(in: &cancellables) + } + + private func observeContentType() { + $contentType + .sink { [weak self] value in + guard let self = self else { + return + } + switch value { + case .icon: + title = "Label" + subtitle = "" + isIconVisible = true + isCounterVisible = false + case .helpText: + title = "Label" + subtitle = "Help Text" + isIconVisible = true + isCounterVisible = false + case .counter: + title = "Label" + subtitle = "" + isIconVisible = true + isCounterVisible = true + alignment = .leading + case .none: + title = "" + subtitle = "" + iconAttributes = nil + counter = nil + } + } + .store(in: &cancellables) + } + + private func setIconAttributes(alignment: SDDSComponents.ButtonAlignment) { + iconAttributes = iconAttributes(with: alignment) + } + + private func iconAttributes(with alignment: SDDSComponents.ButtonAlignment) -> ButtonIconAttributes { + .init(image: Image("buttonIcon"), alignment: alignment) + } + + private func setCounter() { + self.counter = SDDSCounter( + data: self.counterData, + appearance: self.counterAppearance, + isAnimating: false, + isHighlighted: false, + isHovered: false + ) + } +} diff --git a/Themes/SDDSservTheme/SDDSButton/BasicButtonSize+ButtonSizeConfiguration.swift b/Themes/SDDSservTheme/SDDSButton/BasicButtonSize+ButtonSizeConfiguration.swift index bb7d044b..554483c1 100644 --- a/Themes/SDDSservTheme/SDDSButton/BasicButtonSize+ButtonSizeConfiguration.swift +++ b/Themes/SDDSservTheme/SDDSButton/BasicButtonSize+ButtonSizeConfiguration.swift @@ -34,8 +34,8 @@ extension BasicButtonSize: ButtonSizeConfiguration { case .extraSmall: return 32 } } - - public var paddings: EdgeInsets { + + public func paddings(style: ComponentShapeStyle) -> EdgeInsets { switch self { case .large: return EdgeInsets(top: 0, leading: 24, bottom: 0, trailing: 24) case .medium: return EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20) @@ -43,7 +43,7 @@ extension BasicButtonSize: ButtonSizeConfiguration { case .extraSmall: return EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12) } } - + public var iconSize: CGSize { switch self { case .large: return CGSize(width: 24, height: 24) @@ -52,7 +52,16 @@ extension BasicButtonSize: ButtonSizeConfiguration { case .extraSmall: return CGSize(width: 16, height: 16) } } - + + public var counterSize: CounterSizeConfiguration { + switch self { + case .large: return CounterSize.small + case .medium: return CounterSize.extraSmall + case .small: return CounterSize.extraSmall + case .extraSmall: return CounterSize.extraExtraSmall + } + } + public var spinnerSize: CGSize { switch self { case .large: return CGSize(width: 22, height: 22) @@ -61,7 +70,7 @@ extension BasicButtonSize: ButtonSizeConfiguration { case .extraSmall: return CGSize(width: 16, height: 16) } } - + public var titleHorizontalGap: CGFloat { switch self { case .large: return 4 @@ -70,7 +79,7 @@ extension BasicButtonSize: ButtonSizeConfiguration { case .extraSmall: return 2 } } - + public var iconHorizontalGap: CGFloat { switch self { case .large: return 8 @@ -79,7 +88,7 @@ extension BasicButtonSize: ButtonSizeConfiguration { case .extraSmall: return 4 } } - + public var debugDescription: String { return "BasicButtonSize" } diff --git a/Themes/SDDSservTheme/SDDSButton/IconButtonSize+ButtonSizeConfiguration.swift b/Themes/SDDSservTheme/SDDSButton/IconButtonSize+ButtonSizeConfiguration.swift index 883730ff..f4700017 100644 --- a/Themes/SDDSservTheme/SDDSButton/IconButtonSize+ButtonSizeConfiguration.swift +++ b/Themes/SDDSservTheme/SDDSButton/IconButtonSize+ButtonSizeConfiguration.swift @@ -12,7 +12,7 @@ public enum IconButtonSize: String, CaseIterable { } extension IconButtonSize: ButtonSizeConfiguration { - + public var height: CGFloat { switch self { case .large: return 56 @@ -21,7 +21,7 @@ extension IconButtonSize: ButtonSizeConfiguration { case .extraSmall: return 32 } } - + public func cornerRadius(style: SDDSComponents.ComponentShapeStyle) -> CGFloat { switch style { case .cornered: @@ -35,8 +35,8 @@ extension IconButtonSize: ButtonSizeConfiguration { return height / 2 } } - - public var paddings: EdgeInsets { + + public func paddings(style: ComponentShapeStyle) -> EdgeInsets { switch self { case .large: return EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16) case .medium: return EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12) @@ -44,7 +44,7 @@ extension IconButtonSize: ButtonSizeConfiguration { case .extraSmall: return EdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8) } } - + public var iconSize: CGSize { switch self { case .large: return CGSize(width: 24, height: 24) @@ -53,7 +53,16 @@ extension IconButtonSize: ButtonSizeConfiguration { case .extraSmall: return CGSize(width: 16, height: 16) } } - + + public var counterSize: CounterSizeConfiguration { + switch self { + case .large: return CounterSize.small + case .medium: return CounterSize.extraSmall + case .small: return CounterSize.extraSmall + case .extraSmall: return CounterSize.extraExtraSmall + } + } + public var spinnerSize: CGSize { switch self { case .large: return CGSize(width: 22, height: 22) @@ -62,7 +71,7 @@ extension IconButtonSize: ButtonSizeConfiguration { case .extraSmall: return CGSize(width: 16, height: 16) } } - + public var titleHorizontalGap: CGFloat { switch self { case .large: return 0 @@ -71,7 +80,7 @@ extension IconButtonSize: ButtonSizeConfiguration { case .extraSmall: return 0 } } - + public var iconHorizontalGap: CGFloat { switch self { case .large: return 0 @@ -80,7 +89,7 @@ extension IconButtonSize: ButtonSizeConfiguration { case .extraSmall: return 0 } } - + public var debugDescription: String { return "IconButtonSize" } diff --git a/Themes/SDDSservTheme/SDDSButton/LinkButtonSize+ButtonSizeConfiguration.swift b/Themes/SDDSservTheme/SDDSButton/LinkButtonSize+ButtonSizeConfiguration.swift index 6f628aff..2e99156a 100644 --- a/Themes/SDDSservTheme/SDDSButton/LinkButtonSize+ButtonSizeConfiguration.swift +++ b/Themes/SDDSservTheme/SDDSButton/LinkButtonSize+ButtonSizeConfiguration.swift @@ -12,7 +12,7 @@ public enum LinkButtonSize: String, CaseIterable { } extension LinkButtonSize: ButtonSizeConfiguration { - + public var height: CGFloat { switch self { case .large: return 56 @@ -21,12 +21,12 @@ extension LinkButtonSize: ButtonSizeConfiguration { case .extraSmall: return 32 } } - + public func cornerRadius(style: SDDSComponents.ComponentShapeStyle) -> CGFloat { 0 } - - public var paddings: EdgeInsets { + + public func paddings(style: ComponentShapeStyle) -> EdgeInsets { switch self { case .large: return EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0) case .medium: return EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0) @@ -34,7 +34,7 @@ extension LinkButtonSize: ButtonSizeConfiguration { case .extraSmall: return EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0) } } - + public var iconSize: CGSize { switch self { case .large: return CGSize(width: 24, height: 24) @@ -43,7 +43,16 @@ extension LinkButtonSize: ButtonSizeConfiguration { case .extraSmall: return CGSize(width: 16, height: 16) } } - + + public var counterSize: CounterSizeConfiguration { + switch self { + case .large: return CounterSize.small + case .medium: return CounterSize.extraSmall + case .small: return CounterSize.extraSmall + case .extraSmall: return CounterSize.extraExtraSmall + } + } + public var spinnerSize: CGSize { switch self { case .large: return CGSize(width: 22, height: 22) @@ -52,7 +61,7 @@ extension LinkButtonSize: ButtonSizeConfiguration { case .extraSmall: return CGSize(width: 16, height: 16) } } - + public var titleHorizontalGap: CGFloat { switch self { case .large: return 4 @@ -61,7 +70,7 @@ extension LinkButtonSize: ButtonSizeConfiguration { case .extraSmall: return 2 } } - + public var iconHorizontalGap: CGFloat { switch self { case .large: return 8 @@ -70,7 +79,7 @@ extension LinkButtonSize: ButtonSizeConfiguration { case .extraSmall: return 4 } } - + public var debugDescription: String { return "LinkButtonSize" } diff --git a/Themes/SDDSservTheme/SDDSCounter/SDDSCounter+ColorVariations.swift b/Themes/SDDSservTheme/SDDSCounter/SDDSCounter+ColorVariations.swift new file mode 100644 index 00000000..0bcc0f82 --- /dev/null +++ b/Themes/SDDSservTheme/SDDSCounter/SDDSCounter+ColorVariations.swift @@ -0,0 +1,225 @@ +import Foundation +import SDDSComponents +import SDDSThemeCore + +public struct CounterAppearanceVariation: Hashable { + public let name: String + public let appearance: CounterAppearance + + public init(name: String = "", appearance: CounterAppearance = CounterAppearance()) { + self.name = name + self.appearance = appearance + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(name) + } + + public static func == (lhs: CounterAppearanceVariation, rhs: CounterAppearanceVariation) -> Bool { + return lhs.name == rhs.name + } +} + +public extension CounterAppearanceVariation { + var black: Self { + .init( + appearance: appearance.applyColorVariation(variation: SDDSCounter.black.appearance) + ) + } + var negative: Self { + .init( + appearance: appearance.applyColorVariation(variation: SDDSCounter.negative.appearance) + ) + } + + var warning: Self { + .init( + appearance: appearance.applyColorVariation(variation: SDDSCounter.warning.appearance) + ) + } + + var positive: Self { + .init( + appearance: appearance.applyColorVariation(variation: SDDSCounter.positive.appearance) + ) + } + + var accent: Self { + .init( + appearance: appearance.applyColorVariation(variation: SDDSCounter.accent.appearance) + ) + } + + var white: Self { + .init( + appearance: appearance.applyColorVariation(variation: SDDSCounter.white.appearance) + ) + } + + var `default`: Self { + .init( + appearance: appearance.applyColorVariation(variation: SDDSCounter.default.appearance) + ) + } +} + +public extension SDDSCounter { + static var black: CounterAppearanceVariation { + .init( + name: "black", + appearance: CounterAppearance( + dataTypography: SDDSCounter.dataTypography, + dataColor: CounterColor( + defaultColor: .textOnDarkPrimary, + highlightedColor: .textOnDarkPrimaryActive, + hoveredColor: .textOnDarkPrimaryHover + ), + backgroundColor: CounterColor( + defaultColor: .surfaceOnLightSolidDefault, + highlightedColor: .surfaceOnLightSolidDefaultActive, + hoveredColor: .surfaceOnLightSolidDefaultHover + ), + disabledAlpha: 0.4, + loadingAlpha: 0 + ) + ) + } + static var negative: CounterAppearanceVariation { + .init( + name: "negative", + appearance: CounterAppearance( + dataTypography: SDDSCounter.dataTypography, + dataColor: CounterColor( + defaultColor: .textOnDarkPrimary, + highlightedColor: .textOnDarkPrimaryActive, + hoveredColor: .textOnDarkPrimaryHover + ), + backgroundColor: CounterColor( + defaultColor: .surfaceDefaultNegative, + highlightedColor: .surfaceDefaultNegativeActive, + hoveredColor: .surfaceDefaultNegativeHover + ), + disabledAlpha: 0.4, + loadingAlpha: 0 + ) + ) + } + static var warning: CounterAppearanceVariation { + .init( + name: "warning", + appearance: CounterAppearance( + dataTypography: SDDSCounter.dataTypography, + dataColor: CounterColor( + defaultColor: .textOnDarkPrimary, + highlightedColor: .textOnDarkPrimaryActive, + hoveredColor: .textOnDarkPrimaryHover + ), + backgroundColor: CounterColor( + defaultColor: .surfaceDefaultWarning, + highlightedColor: .surfaceDefaultWarningActive, + hoveredColor: .surfaceDefaultWarningHover + ), + disabledAlpha: 0.4, + loadingAlpha: 0 + ) + ) + } + static var positive: CounterAppearanceVariation { + .init( + name: "positive", + appearance: CounterAppearance( + dataTypography: SDDSCounter.dataTypography, + dataColor: CounterColor( + defaultColor: .textOnDarkPrimary, + highlightedColor: .textOnDarkPrimaryActive, + hoveredColor: .textOnDarkPrimaryHover + ), + backgroundColor: CounterColor( + defaultColor: .surfaceDefaultPositive, + highlightedColor: .surfaceDefaultPositiveActive, + hoveredColor: .surfaceDefaultPositiveHover + ), + disabledAlpha: 0.4, + loadingAlpha: 0 + ) + ) + } + static var accent: CounterAppearanceVariation { + .init( + name: "accent", + appearance: CounterAppearance( + dataTypography: SDDSCounter.dataTypography, + dataColor: CounterColor( + defaultColor: .textOnDarkPrimary, + highlightedColor: .textOnDarkPrimaryActive, + hoveredColor: .textOnDarkPrimaryHover + ), + backgroundColor: CounterColor( + defaultColor: .surfaceDefaultAccent, + highlightedColor: .surfaceDefaultAccentActive, + hoveredColor: .surfaceDefaultAccentHover + ), + disabledAlpha: 0.4, + loadingAlpha: 0 + ) + ) + } + static var white: CounterAppearanceVariation { + .init( + name: "white", + appearance: CounterAppearance( + dataTypography: SDDSCounter.dataTypography, + dataColor: CounterColor( + defaultColor: .textOnLightPrimary, + highlightedColor: .textOnLightPrimaryActive, + hoveredColor: .textOnLightPrimaryActive + ), + backgroundColor: CounterColor( + defaultColor: .backgroundLightPrimary, + highlightedColor: .backgroundLightPrimary, + hoveredColor: .backgroundLightPrimary + ), + disabledAlpha: 0.4, + loadingAlpha: 0 + ) + ) + } + static var `default`: CounterAppearanceVariation { + .init( + name: "default", + appearance: CounterAppearance( + dataTypography: SDDSCounter.dataTypography, + dataColor: CounterColor( + defaultColor: .textInversePrimary, + highlightedColor: .textInversePrimaryActive, + hoveredColor: .textInversePrimaryHover + ), + backgroundColor: CounterColor( + defaultColor: .surfaceDefaultSolidDefault, + highlightedColor: .surfaceDefaultSolidDefaultActive, + hoveredColor: .surfaceDefaultSolidDefaultHover + ), + disabledAlpha: 0.4, + loadingAlpha: 0 + ) + ) + } + static var all: [CounterAppearanceVariation] { + [ + SDDSCounter.black, + + SDDSCounter.negative, + + SDDSCounter.warning, + + SDDSCounter.positive, + + SDDSCounter.accent, + + SDDSCounter.white, + + SDDSCounter.default + ] + } +} + diff --git a/Themes/SDDSservTheme/SDDSCounter/SDDSCounter+SizeConfiguration.swift b/Themes/SDDSservTheme/SDDSCounter/SDDSCounter+SizeConfiguration.swift new file mode 100644 index 00000000..34f780a8 --- /dev/null +++ b/Themes/SDDSservTheme/SDDSCounter/SDDSCounter+SizeConfiguration.swift @@ -0,0 +1,63 @@ +import Foundation +import SDDSComponents +import SwiftUI + +public enum CounterSize: String, CaseIterable { + case large + case medium + case small + case extraSmall + case extraExtraSmall +} + +extension CounterSize: CounterSizeConfiguration { + + public var height: CGFloat { + switch self { + case .large: + return 28 + case .medium: + return 24 + case .small: + return 20 + case .extraSmall: + return 16 + case .extraExtraSmall: + return 12 + } + } + + public var width: CGFloat { + switch self { + case .large: + return 28 + case .medium: + return 24 + case .small: + return 20 + case .extraSmall: + return 16 + case .extraExtraSmall: + return 12 + } + } + + public var paddings: EdgeInsets { + switch self { + case .large: + return EdgeInsets(top: 5, leading: 10, bottom: 5, trailing: 10) + case .medium: + return EdgeInsets(top: 5, leading: 8, bottom: 5, trailing: 8) + case .small: + return EdgeInsets(top: 4, leading: 6, bottom: 4, trailing: 6) + case .extraSmall: + return EdgeInsets(top: 1.5, leading: 4, bottom: 2.5, trailing: 4) + case .extraExtraSmall: + return EdgeInsets(top: 0, leading: 2, bottom: 0, trailing: 2) + } + } + + public var debugDescription: String { + "CounterSize" + } +} diff --git a/Themes/SDDSservTheme/SDDSCounter/SDDSCounter+Typography.swift b/Themes/SDDSservTheme/SDDSCounter/SDDSCounter+Typography.swift new file mode 100644 index 00000000..75a532c0 --- /dev/null +++ b/Themes/SDDSservTheme/SDDSCounter/SDDSCounter+Typography.swift @@ -0,0 +1,17 @@ +import Foundation +import SDDSComponents +import SDDSThemeCore + +public extension SDDSCounter { + static var dataTypography: TypographyConfiguration { + CounterTypography( + large: AdaptiveTypographyToken.bodySNormal.typography, + medium: AdaptiveTypographyToken.bodyXsNormal.typography, + small: AdaptiveTypographyToken.bodyXxsNormal.typography, + extraSmall: AdaptiveTypographyToken.bodyXxsNormal.typography, + extraExtraSmall: AdaptiveTypographyToken.bodyXxsNormal.typography + ) + .asContainer + } +} + diff --git a/Themes/SDDSservTheme/SDDSCounter/SDDSCounterSize+CounterSizeVariations.swift b/Themes/SDDSservTheme/SDDSCounter/SDDSCounterSize+CounterSizeVariations.swift new file mode 100644 index 00000000..bbf17b95 --- /dev/null +++ b/Themes/SDDSservTheme/SDDSCounter/SDDSCounterSize+CounterSizeVariations.swift @@ -0,0 +1,60 @@ +import Foundation +import SDDSComponents +import SDDSThemeCore + +public extension CounterAppearanceVariation { + var large: Self { + .init( + appearance: appearance + .size(CounterSize.large) + .dataTypography(SDDSCounter.dataTypography) + ) + } + var medium: Self { + .init( + appearance: appearance + .size(CounterSize.medium) + .dataTypography(SDDSCounter.dataTypography) + ) + } + var small: Self { + .init( + appearance: appearance + .size(CounterSize.small) + .dataTypography(SDDSCounter.dataTypography) + ) + } + var extraSmall: Self { + .init( + appearance: appearance + .size(CounterSize.extraSmall) + .dataTypography(SDDSCounter.dataTypography) + ) + } + var extraExtraSmall: Self { + .init( + appearance: appearance + .size(CounterSize.extraExtraSmall) + .dataTypography(SDDSCounter.dataTypography) + ) + } +} + +public extension SDDSCounter { + static var large: CounterAppearanceVariation { + return CounterAppearanceVariation().large + } + static var medium: CounterAppearanceVariation { + return CounterAppearanceVariation().medium + } + static var small: CounterAppearanceVariation { + return CounterAppearanceVariation().small + } + static var extraSmall: CounterAppearanceVariation { + return CounterAppearanceVariation().extraSmall + } + static var extraExtraSmall: CounterAppearanceVariation { + return CounterAppearanceVariation().extraExtraSmall + } +} + diff --git a/Themes/SDDSservTheme/SDDSCounter/SDDSCounterTypography.swift b/Themes/SDDSservTheme/SDDSCounter/SDDSCounterTypography.swift new file mode 100644 index 00000000..9b418665 --- /dev/null +++ b/Themes/SDDSservTheme/SDDSCounter/SDDSCounterTypography.swift @@ -0,0 +1,37 @@ +import Foundation +import SDDSComponents + +public struct CounterTypography: GeneralTypographyConfiguration { + typealias S = CounterSizeConfiguration + + let large: TypographyToken? + let medium: TypographyToken? + let small: TypographyToken? + let extraSmall: TypographyToken? + let extraExtraSmall: TypographyToken? + + init(large: TypographyToken?, + medium: TypographyToken?, + small: TypographyToken?, + extraSmall: TypographyToken?, + extraExtraSmall: TypographyToken? + ) { + self.large = large + self.medium = medium + self.small = small + self.extraSmall = extraSmall + self.extraExtraSmall = extraExtraSmall + } + + public func typography(with size: CounterSizeConfiguration) -> TypographyToken? { + switch size as? CounterSize { + case .large: return large + case .medium: return medium + case .small: return small + case .extraSmall: return extraSmall + case .extraExtraSmall: return extraExtraSmall + case .none: return nil + } + } +} + diff --git a/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElement+ButtonSizeConfiguration.swift b/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElement+ButtonSizeConfiguration.swift new file mode 100644 index 00000000..d32d3ae1 --- /dev/null +++ b/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElement+ButtonSizeConfiguration.swift @@ -0,0 +1,106 @@ +import Foundation +import SDDSComponents +import SwiftUI + +public enum SegmentElementSize: String, CaseIterable { + case large + case medium + case small + case extraSmall +} + +extension SegmentElementSize: SegmentElementSizeConfiguration { + public func cornerRadius(style: ComponentShapeStyle) -> CGFloat { + switch style { + case .cornered: + switch self { + case .large: return ShapeToken.roundL.cornerRadius - 2.0 + case .medium: return ShapeToken.roundM.cornerRadius + case .small: return ShapeToken.roundM.cornerRadius - 2.0 + case .extraSmall: return ShapeToken.roundS.cornerRadius + } + case .pilled: + return height / 2 + } + } + + public var height: CGFloat { + switch self { + case .large: return 56 + case .medium: return 48 + case .small: return 40 + case .extraSmall: return 32 + } + } + + public func paddings(style: ComponentShapeStyle) -> EdgeInsets { + switch style { + case .cornered: + switch self { + case .large: + return EdgeInsets(top: 0, leading: 24, bottom: 0, trailing: 24) + case .medium: + return EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20) + case .small: + return EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16) + case .extraSmall: + return EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12) + } + case .pilled: + switch self { + case .large: + return EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16) + case .medium: + return EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12) + case .small: + return EdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8) + case .extraSmall: + return EdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8) + } + } + } + + public var iconSize: CGSize { + switch self { + case .large: return CGSize(width: 24, height: 24) + case .medium: return CGSize(width: 24, height: 24) + case .small: return CGSize(width: 24, height: 24) + case .extraSmall: return CGSize(width: 16, height: 16) + } + } + + public var counterSize: CounterSizeConfiguration { + switch self { + case .large: return CounterSize.small + case .medium: return CounterSize.extraSmall + case .small: return CounterSize.extraSmall + case .extraSmall: return CounterSize.extraExtraSmall + } + } + + public var spinnerSize: CGSize { + CGSize(width: 0, height: 0) + } + + public var titleHorizontalGap: CGFloat { + switch self { + case .large: return 6 + case .medium: return 4 + case .small: return 2 + case .extraSmall: return 2 + } + } + + public var iconHorizontalGap: CGFloat { + switch self { + case .large: return 6 + case .medium: return 4 + case .small: return 2 + case .extraSmall: return 2 + } + } + + public var debugDescription: String { + return "SegmentElementSize" + } +} diff --git a/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElement+ColorVariations.swift b/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElement+ColorVariations.swift new file mode 100644 index 00000000..a1d095a4 --- /dev/null +++ b/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElement+ColorVariations.swift @@ -0,0 +1,125 @@ +import Foundation +import SDDSComponents +import SDDSThemeCore + +public extension SegmentElementAppearanceVariation { + var clear: Self { + .init( + appearance: appearance.applyColorVariation(variation: SDDSSegmentElement.clear.appearance) + ) + } + var `default`: Self { + .init( + appearance: appearance.applyColorVariation(variation: SDDSSegmentElement.default.appearance) + ) + } + var white: Self { + .init( + appearance: appearance.applyColorVariation(variation: SDDSSegmentElement.white.appearance) + ) + } +} + +public extension SDDSSegmentElement { + static var clear: SegmentElementAppearanceVariation { + .init( + name: "clear", + appearance: SegmentElementAppearance( + titleTypography: SDDSSegmentElement.titleTypography, + titleColor: ButtonColor( + defaultColor: .textDefaultPrimary, + highlightedColor: .textDefaultPrimaryActive, + hoveredColor: .textDefaultPrimaryHover + ), + subtitleTypography: SDDSSegmentElement.subtitleTypography, + subtitleColor: ButtonColor( + defaultColor: .textDefaultSecondary, + highlightedColor: .textDefaultSecondaryActive, + hoveredColor: .textDefaultSecondaryHover + ), + iconColor: ButtonColor( + defaultColor: .textDefaultPrimary, + highlightedColor: .textDefaultPrimaryActive, + hoveredColor: .textDefaultPrimaryHover + ), + backgroundColor: ButtonColor( + defaultColor: .surfaceDefaultClear, + highlightedColor: .surfaceDefaultClearActive, + hoveredColor: .surfaceDefaultClearHover + ), + disabledAlpha: 0.4 + ) + ) + } + static var `default`: SegmentElementAppearanceVariation { + .init( + name: "default", + appearance: SegmentElementAppearance( + titleTypography: SDDSSegmentElement.titleTypography, + titleColor: ButtonColor( + defaultColor: .textInversePrimary, + highlightedColor: .textInversePrimaryActive, + hoveredColor: .textInversePrimaryHover + ), + subtitleTypography: SDDSSegmentElement.subtitleTypography, + subtitleColor: ButtonColor( + defaultColor: .textInverseSecondary, + highlightedColor: .textInverseSecondaryActive, + hoveredColor: .textInverseSecondaryHover + ), + iconColor: ButtonColor( + defaultColor: .textInversePrimary, + highlightedColor: .textInversePrimaryActive, + hoveredColor: .textInversePrimaryHover + ), + backgroundColor: ButtonColor( + defaultColor: .surfaceDefaultSolidDefault, + highlightedColor: .surfaceDefaultSolidDefaultActive, + hoveredColor: .surfaceDefaultSolidDefaultHover + ), + disabledAlpha: 0.4 + ) + ) + } + static var white: SegmentElementAppearanceVariation { + .init( + name: "white", + appearance: SegmentElementAppearance( + titleTypography: SDDSSegmentElement.titleTypography, + titleColor: ButtonColor( + defaultColor: .textOnLightPrimary, + highlightedColor: .textOnLightPrimaryActive, + hoveredColor: .textOnLightPrimaryHover + ), + subtitleTypography: SDDSSegmentElement.subtitleTypography, + subtitleColor: ButtonColor( + defaultColor: .textOnLightSecondary, + highlightedColor: .textOnLightSecondaryActive, + hoveredColor: .textOnLightSecondaryHover + ), + iconColor: ButtonColor( + defaultColor: .textOnLightPrimary, + highlightedColor: .textOnLightPrimaryActive, + hoveredColor: .textOnLightPrimaryHover + ), + backgroundColor: ButtonColor( + defaultColor: .surfaceDefaultSolidCard, + highlightedColor: .surfaceDefaultSolidCardActive, + hoveredColor: .surfaceDefaultSolidCardHover + ), + disabledAlpha: 0.4 + ) + ) + } + static var all: [SegmentElementAppearanceVariation] { + [ + + SDDSSegmentElement.clear, + + SDDSSegmentElement.default, + + SDDSSegmentElement.white + + ] + } +} diff --git a/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElement+SizeVariations.swift b/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElement+SizeVariations.swift new file mode 100644 index 00000000..2c27a5b6 --- /dev/null +++ b/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElement+SizeVariations.swift @@ -0,0 +1,53 @@ +import Foundation +import SDDSComponents +import SDDSThemeCore + +public extension SegmentElementAppearanceVariation { + var large: Self { + .init( + appearance: appearance + .size(SegmentElementSize.large) + .titleTypography(SDDSSegmentElement.titleTypography) + .subtitleTypography(SDDSSegmentElement.subtitleTypography) + ) + } + var medium: Self { + .init( + appearance: appearance + .size(SegmentElementSize.medium) + .titleTypography(SDDSSegmentElement.titleTypography) + .subtitleTypography(SDDSSegmentElement.subtitleTypography) + ) + } + var small: Self { + .init( + appearance: appearance + .size(SegmentElementSize.small) + .titleTypography(SDDSSegmentElement.titleTypography) + .subtitleTypography(SDDSSegmentElement.subtitleTypography) + ) + } + var extraSmall: Self { + .init( + appearance: appearance + .size(SegmentElementSize.extraSmall) + .titleTypography(SDDSSegmentElement.titleTypography) + .subtitleTypography(SDDSSegmentElement.subtitleTypography) + ) + } +} + +public extension SDDSSegmentElement { + static var large: SegmentElementAppearanceVariation { + return SegmentElementAppearanceVariation().large + } + static var medium: SegmentElementAppearanceVariation { + return SegmentElementAppearanceVariation().medium + } + static var small: SegmentElementAppearanceVariation { + return SegmentElementAppearanceVariation().small + } + static var extraSmall: SegmentElementAppearanceVariation { + return SegmentElementAppearanceVariation().extraSmall + } +} diff --git a/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElement+Typography.swift b/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElement+Typography.swift new file mode 100644 index 00000000..0c466daf --- /dev/null +++ b/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElement+Typography.swift @@ -0,0 +1,26 @@ +import Foundation +import SDDSServTheme +import SDDSComponents +import SDDSThemeCore + + +public extension SDDSSegmentElement { + static var titleTypography: TypographyConfiguration { + SegmentElementTypography( + large: AdaptiveTypographyToken.bodyLBold.typography, + medium: AdaptiveTypographyToken.bodyMBold.typography, + small: AdaptiveTypographyToken.bodySBold.typography, + extraSmall: AdaptiveTypographyToken.bodyXsBold.typography + ) + .asContainer + } + static var subtitleTypography: TypographyConfiguration { + SegmentElementTypography( + large: AdaptiveTypographyToken.bodyLBold.typography, + medium: AdaptiveTypographyToken.bodyMBold.typography, + small: AdaptiveTypographyToken.bodySBold.typography, + extraSmall: AdaptiveTypographyToken.bodyXsBold.typography + ) + .asContainer + } +} diff --git a/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElementTypography.swift b/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElementTypography.swift new file mode 100644 index 00000000..22dbff9c --- /dev/null +++ b/Themes/SDDSservTheme/SDDSSegmentElement/SDDSSegmentElementTypography.swift @@ -0,0 +1,39 @@ +import Foundation +import SDDSComponents + +public struct SegmentElementTypography: GeneralTypographyConfiguration { + typealias S = SegmentElementSizeConfiguration + + let large: TypographyToken? + let medium: TypographyToken? + let small: TypographyToken? + let extraSmall: TypographyToken? + + init(large: TypographyToken?, medium: TypographyToken?, small: TypographyToken?, extraSmall: TypographyToken?) { + self.large = large + self.medium = medium + self.small = small + self.extraSmall = extraSmall + } + + /** + Возвращает типографику для заданного размера кнопки. + + - Parameter size: Размер кнопки. + - Returns: Типографика для заданного размера кнопки или nil, если не задана. + */ + public func typography(with size: SegmentElementSizeConfiguration) -> TypographyToken? { + switch size as? SegmentElementSize { + case .large: + return large + case .medium: + return medium + case .small: + return small + case .extraSmall: + return extraSmall + case .none: + return medium + } + } +}