// // Button.swift // VDS // // Created by Jarrod Courtney on 9/16/22. // import Foundation import UIKit import VDSColorTokens import VDSFormControlsTokens import Combine public enum ButtonSize: String, Codable, CaseIterable { case large case small } @objc(VDSButton) open class Button: ButtonBase, Useable { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- private var minWidthConstraint: NSLayoutConstraint? private var widthConstraint: NSLayoutConstraint? private var heightConstraint: NSLayoutConstraint? private var initialSetupPerformed = false //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- open override var availableSizes: [ButtonSize] { [.large, .small] } open var use: Use = .primary { didSet { didChange() }} open var size: ButtonSize = .large { didSet { didChange() }} open var width: CGFloat? { didSet { didChange() }} open override var textColor: UIColor { buttonTitleColorConfiguration.getColor(self) } open override var typograpicalStyle: TypographicalStyle { size == .large ? TypographicalStyle.BoldBodyLarge : TypographicalStyle.BoldBodySmall } //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- private var buttonBackgroundColorConfiguration = UseableColorConfiguration().with { $0.primary.enabled.lightColor = VDSColor.backgroundPrimaryDark $0.primary.enabled.darkColor = VDSColor.backgroundPrimaryLight $0.primary.disabled.lightColor = VDSColor.interactiveDisabledOnlight $0.primary.disabled.darkColor = VDSColor.interactiveDisabledOndark $0.primaryHighlighted.lightColor = VDSColor.interactiveActiveOnlight $0.primaryHighlighted.darkColor = VDSColor.interactiveActiveOndark $0.secondary.enabled.lightColor = UIColor.clear $0.secondary.enabled.darkColor = UIColor.clear $0.secondary.disabled.lightColor = UIColor.clear $0.secondary.disabled.darkColor = UIColor.clear $0.secondaryHighlighted.lightColor = UIColor.clear $0.secondaryHighlighted.darkColor = UIColor.clear } private var buttonBorderColorConfiguration = UseableColorConfiguration().with { $0.primary.enabled.lightColor = VDSColor.elementsPrimaryOndark $0.primary.enabled.darkColor = VDSColor.elementsPrimaryOnlight $0.primary.disabled.lightColor = VDSColor.interactiveDisabledOnlight $0.primary.disabled.darkColor = VDSColor.interactiveDisabledOndark $0.primaryHighlighted.lightColor = VDSColor.elementsPrimaryOndark $0.primaryHighlighted.darkColor = VDSColor.elementsPrimaryOnlight $0.secondary.enabled.lightColor = VDSColor.elementsPrimaryOnlight $0.secondary.enabled.darkColor = VDSColor.elementsPrimaryOndark $0.secondary.disabled.lightColor = VDSColor.interactiveDisabledOnlight $0.secondary.disabled.darkColor = VDSColor.interactiveDisabledOndark $0.secondaryHighlighted.lightColor = VDSColor.interactiveActiveOnlight $0.secondaryHighlighted.darkColor = VDSColor.interactiveActiveOndark } private var buttonTitleColorConfiguration = UseableColorConfiguration().with { $0.primary.enabled.lightColor = VDSColor.elementsPrimaryOndark $0.primary.enabled.darkColor = VDSColor.elementsPrimaryOnlight $0.primary.disabled.lightColor = VDSColor.elementsPrimaryOndark $0.primary.disabled.darkColor = VDSColor.elementsPrimaryOnlight $0.primaryHighlighted.lightColor = VDSColor.elementsPrimaryOndark $0.primaryHighlighted.darkColor = VDSColor.elementsPrimaryOnlight $0.secondary.enabled.lightColor = VDSColor.elementsPrimaryOnlight $0.secondary.enabled.darkColor = VDSColor.elementsPrimaryOndark $0.secondary.disabled.lightColor = VDSColor.interactiveDisabledOnlight $0.secondary.disabled.darkColor = VDSColor.interactiveDisabledOndark $0.secondaryHighlighted.lightColor = VDSColor.interactiveActiveOnlight $0.secondaryHighlighted.darkColor = VDSColor.interactiveActiveOndark } //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- required public init() { super.init(frame: .zero) } public override init(frame: CGRect) { super.init(frame: .zero) } public required init?(coder: NSCoder) { super.init(coder: coder) } //-------------------------------------------------- // MARK: - Public Functions //-------------------------------------------------- open override func setup() { super.setup() //only 1 of the 2 widths can be on at the same time widthConstraint = widthAnchor.constraint(equalToConstant: 0) minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: size.minimumWidth) //height heightConstraint = heightAnchor.constraint(equalToConstant: 0) heightConstraint?.isActive = true } open override func reset() { super.reset() use = .primary width = nil size = .large } //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- open override var intrinsicContentSize: CGSize { guard let width, width > 0 else { return super.intrinsicContentSize } return CGSize(width: width > size.minimumWidth ? width : size.minimumWidth, height: size.height) } open override func updateView() { super.updateView() let bgColor = buttonBackgroundColorConfiguration.getColor(self) let borderColor = buttonBorderColorConfiguration.getColor(self) let borderWidth = use == .secondary ? 1.0 : 0.0 let buttonHeight = size.height let cornerRadius = size.cornerRadius let minWidth = size.minimumWidth let edgeInsets = size.edgeInsets backgroundColor = bgColor layer.borderColor = borderColor.cgColor layer.cornerRadius = cornerRadius layer.borderWidth = borderWidth contentEdgeInsets = edgeInsets minWidthConstraint?.constant = minWidth heightConstraint?.constant = buttonHeight if let width, width > minWidth { widthConstraint?.constant = width widthConstraint?.isActive = true minWidthConstraint?.isActive = false } else { widthConstraint?.isActive = false minWidthConstraint?.isActive = true } } //-------------------------------------------------- // MARK: - PRIVATE //-------------------------------------------------- private class UseableColorConfiguration: ObjectColorable { typealias ObjectType = Buttonable & Useable public var primary = DisabledSurfaceColorConfiguration() public var secondary = DisabledSurfaceColorConfiguration() public var primaryHighlighted = SurfaceColorConfiguration() public var secondaryHighlighted = SurfaceColorConfiguration() required public init(){} public func getColor(_ object: ObjectType) -> UIColor { if object.isHighlighted { return object.use == .primary ? primaryHighlighted.getColor(object) : secondaryHighlighted.getColor(object) } else { return object.use == .primary ? primary.getColor(object) : secondary.getColor(object) } } } } extension ButtonSize { public var height: CGFloat { switch self { case .large: return 44 case .small: return 32 } } public var cornerRadius: CGFloat { height / 2 } public var minimumWidth: CGFloat { switch self { case .large: return 76 case .small: return 60 } } public var edgeInsets: UIEdgeInsets { var verticalPadding = 0.0 var horizontalPadding = 0.0 switch self { case .large: verticalPadding = 12 horizontalPadding = 24 break case .small: verticalPadding = 8 horizontalPadding = 16 break } return UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding) } } extension Use { public var color: UIColor { return self == .primary ? VDSColor.backgroundPrimaryDark : .clear } }