// // 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, 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 { textColorConfiguration.getColor(self) } open override var textStyle: TextStyle { size == .large ? TextStyle.boldBodyLarge : TextStyle.boldBodySmall } //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- // Background Color Config private var primaryBackgroundColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryLight, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) } private var secondaryBackgroundColorConfiguration = ControlColorConfiguration() private var backgroundColorConfiguration: ControlColorConfiguration{ use == .primary ? primaryBackgroundColorConfiguration : secondaryBackgroundColorConfiguration } // Border Color Config private var primaryBorderColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .normal) $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .highlighted) $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) } private var secondaryBorderColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) } private var borderColorConfiguration: ControlColorConfiguration { use == .primary ? primaryBorderColorConfiguration : secondaryBorderColorConfiguration } // Text Color Config private var primaryTextColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .normal) $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .highlighted) $0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .disabled) } private var secondaryTextColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) } private var textColorConfiguration: ControlColorConfiguration { use == .primary ? primaryTextColorConfiguration : secondaryTextColorConfiguration } //-------------------------------------------------- // 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 = backgroundColorConfiguration.getColor(self) let borderColor = borderColorConfiguration.getColor(self) let borderWidth = use == .secondary ? VDSFormControls.widthBorder : 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 } } } internal extension ButtonSize { var height: CGFloat { switch self { case .large: return 44 case .small: return 32 } } var cornerRadius: CGFloat { height / 2 } var minimumWidth: CGFloat { switch self { case .large: return 76 case .small: return 60 } } 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 } }