Merge branch 'feature/buttonGroupUpdate' into 'develop'

created basebutton

See merge request BPHV_MIPS/vds_ios!17
This commit is contained in:
Bruce, Matt R 2022-12-01 18:04:42 +00:00
commit f852ae5084
13 changed files with 610 additions and 213 deletions

View File

@ -61,6 +61,7 @@
EAB5FED429267EB300998C17 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FED329267EB300998C17 /* UIView.swift */; }; EAB5FED429267EB300998C17 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FED329267EB300998C17 /* UIView.swift */; };
EAB5FEED2927E1B200998C17 /* ButtonGroupPositionLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEEC2927E1B200998C17 /* ButtonGroupPositionLayout.swift */; }; EAB5FEED2927E1B200998C17 /* ButtonGroupPositionLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEEC2927E1B200998C17 /* ButtonGroupPositionLayout.swift */; };
EAB5FEF12927F4AA00998C17 /* SelfSizingCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */; }; EAB5FEF12927F4AA00998C17 /* SelfSizingCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */; };
EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF4292D371F00998C17 /* ButtonBase.swift */; };
EAC9257D29119B5400091998 /* TextLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9257C29119B5400091998 /* TextLink.swift */; }; EAC9257D29119B5400091998 /* TextLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9257C29119B5400091998 /* TextLink.swift */; };
EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925822911B35300091998 /* TextLinkCaret.swift */; }; EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925822911B35300091998 /* TextLinkCaret.swift */; };
EAC925842911C63100091998 /* Colorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEDF28F49DB3003B3210 /* Colorable.swift */; }; EAC925842911C63100091998 /* Colorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEDF28F49DB3003B3210 /* Colorable.swift */; };
@ -153,6 +154,7 @@
EAB5FED329267EB300998C17 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; }; EAB5FED329267EB300998C17 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
EAB5FEEC2927E1B200998C17 /* ButtonGroupPositionLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupPositionLayout.swift; sourceTree = "<group>"; }; EAB5FEEC2927E1B200998C17 /* ButtonGroupPositionLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupPositionLayout.swift; sourceTree = "<group>"; };
EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfSizingCollectionView.swift; sourceTree = "<group>"; }; EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfSizingCollectionView.swift; sourceTree = "<group>"; };
EAB5FEF4292D371F00998C17 /* ButtonBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonBase.swift; sourceTree = "<group>"; };
EAC9257C29119B5400091998 /* TextLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLink.swift; sourceTree = "<group>"; }; EAC9257C29119B5400091998 /* TextLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLink.swift; sourceTree = "<group>"; };
EAC925822911B35300091998 /* TextLinkCaret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLinkCaret.swift; sourceTree = "<group>"; }; EAC925822911B35300091998 /* TextLinkCaret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLinkCaret.swift; sourceTree = "<group>"; };
EAC925872911C9DE00091998 /* TextEntryField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = "<group>"; }; EAC925872911C9DE00091998 /* TextEntryField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = "<group>"; };
@ -203,6 +205,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5FC35BE228D51405004EBEAC /* Button.swift */, 5FC35BE228D51405004EBEAC /* Button.swift */,
EAB5FEF4292D371F00998C17 /* ButtonBase.swift */,
); );
path = Button; path = Button;
sourceTree = "<group>"; sourceTree = "<group>";
@ -654,6 +657,7 @@
EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */, EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */,
EAC925842911C63100091998 /* Colorable.swift in Sources */, EAC925842911C63100091998 /* Colorable.swift in Sources */,
EA3361C5289030FC0071C351 /* Accessable.swift in Sources */, EA3361C5289030FC0071C351 /* Accessable.swift in Sources */,
EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */,
EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */, EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */,
EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */,
EAF7F11728A1475A00B287F5 /* RadioButton.swift in Sources */, EAF7F11728A1475A00B287F5 /* RadioButton.swift in Sources */,

View File

@ -68,7 +68,7 @@ extension DisabledSurfaceColorable {
/// let textColor = config.getColor(model) //returns .white /// let textColor = config.getColor(model) //returns .white
/// ///
/// ///
final public class DisabledSurfaceColorConfiguration: DisabledSurfaceColorable { open class DisabledSurfaceColorConfiguration: DisabledSurfaceColorable {
public typealias ObjectType = Surfaceable & Disabling public typealias ObjectType = Surfaceable & Disabling
public var disabled = SurfaceColorConfiguration() public var disabled = SurfaceColorConfiguration()
public var enabled = SurfaceColorConfiguration() public var enabled = SurfaceColorConfiguration()

View File

@ -29,6 +29,8 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable {
open override var isSelected: Bool { didSet { didChange() } } open override var isSelected: Bool { didSet { didChange() } }
open override var isHighlighted: Bool { didSet { updateView() } }
open override var isEnabled: Bool { open override var isEnabled: Bool {
get { !disabled } get { !disabled }
set { set {

View File

@ -15,6 +15,8 @@ public enum BadgeFillColor: String, Codable, CaseIterable {
case red, yellow, green, orange, blue, black, white case red, yellow, green, orange, blue, black, white
} }
/// Badges are visual labels used to convey status or highlight supplemental information.
@objc(VDSBadge) @objc(VDSBadge)
public class Badge: View, Accessable { public class Badge: View, Accessable {

View File

@ -11,26 +11,14 @@ import VDSColorTokens
import VDSFormControlsTokens import VDSFormControlsTokens
import Combine import Combine
public protocol Buttonable: UIControl, Surfaceable, Disabling {
var availableSizes: [ButtonSize] { get }
var text: String? { get set }
var intrinsicContentSize: CGSize { get }
}
public enum ButtonSize: String, Codable, CaseIterable { public enum ButtonSize: String, Codable, CaseIterable {
case large case large
case small case small
} }
@objc(VDSButton) @objc(VDSButton)
open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable, Useable { open class Button: ButtonBase, Useable {
//--------------------------------------------------
// MARK: - Combine Properties
//--------------------------------------------------
public var subject = PassthroughSubject<Void, Never>()
public var subscribers = Set<AnyCancellable>()
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
//-------------------------------------------------- //--------------------------------------------------
@ -42,31 +30,21 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public var availableSizes: [ButtonSize] { [.large, .small] } open override var availableSizes: [ButtonSize] { [.large, .small] }
open var text: String? { didSet { didChange() } }
open var use: Use = .primary { didSet { didChange() }} open var use: Use = .primary { didSet { didChange() }}
open var size: ButtonSize = .large { didSet { didChange() }} open var size: ButtonSize = .large { didSet { didChange() }}
open var width: CGFloat? { didSet { didChange() }} open var width: CGFloat? { didSet { didChange() }}
open var surface: Surface = .light { didSet { didChange() }}
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
open override var isEnabled: Bool { open override var textColor: UIColor {
get { !disabled } buttonTitleColorConfiguration.getColor(self)
set { }
if disabled != !newValue {
disabled = !newValue open override var typograpicalStyle: TypographicalStyle {
} size == .large ? TypographicalStyle.BoldBodyLarge : TypographicalStyle.BoldBodySmall
isUserInteractionEnabled = isEnabled
didChange()
}
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Configuration Properties // MARK: - Configuration Properties
@ -76,23 +54,35 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
$0.primary.enabled.darkColor = VDSColor.backgroundPrimaryLight $0.primary.enabled.darkColor = VDSColor.backgroundPrimaryLight
$0.primary.disabled.lightColor = VDSColor.interactiveDisabledOnlight $0.primary.disabled.lightColor = VDSColor.interactiveDisabledOnlight
$0.primary.disabled.darkColor = VDSColor.interactiveDisabledOndark $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.lightColor = UIColor.clear
$0.secondary.enabled.darkColor = UIColor.clear $0.secondary.enabled.darkColor = UIColor.clear
$0.secondary.disabled.lightColor = UIColor.clear $0.secondary.disabled.lightColor = UIColor.clear
$0.secondary.disabled.darkColor = UIColor.clear $0.secondary.disabled.darkColor = UIColor.clear
$0.secondaryHighlighted.lightColor = UIColor.clear
$0.secondaryHighlighted.darkColor = UIColor.clear
} }
private var buttonBorderColorConfiguration = UseableColorConfiguration().with { private var buttonBorderColorConfiguration = UseableColorConfiguration().with {
$0.primary.enabled.lightColor = VDSColor.elementsPrimaryOndark $0.primary.enabled.lightColor = VDSColor.elementsPrimaryOndark
$0.primary.enabled.darkColor = VDSColor.elementsPrimaryOnlight $0.primary.enabled.darkColor = VDSColor.elementsPrimaryOnlight
$0.primary.disabled.lightColor = VDSColor.interactiveDisabledOnlight $0.primary.disabled.lightColor = VDSColor.interactiveDisabledOnlight
$0.primary.disabled.darkColor = VDSColor.interactiveDisabledOndark $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.lightColor = VDSColor.elementsPrimaryOnlight
$0.secondary.enabled.darkColor = VDSColor.elementsPrimaryOndark $0.secondary.enabled.darkColor = VDSColor.elementsPrimaryOndark
$0.secondary.disabled.lightColor = VDSColor.interactiveDisabledOnlight $0.secondary.disabled.lightColor = VDSColor.interactiveDisabledOnlight
$0.secondary.disabled.darkColor = VDSColor.interactiveDisabledOndark $0.secondary.disabled.darkColor = VDSColor.interactiveDisabledOndark
$0.secondaryHighlighted.lightColor = VDSColor.interactiveActiveOnlight
$0.secondaryHighlighted.darkColor = VDSColor.interactiveActiveOndark
} }
private var buttonTitleColorConfiguration = UseableColorConfiguration().with { private var buttonTitleColorConfiguration = UseableColorConfiguration().with {
@ -100,11 +90,17 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
$0.primary.enabled.darkColor = VDSColor.elementsPrimaryOnlight $0.primary.enabled.darkColor = VDSColor.elementsPrimaryOnlight
$0.primary.disabled.lightColor = VDSColor.elementsPrimaryOndark $0.primary.disabled.lightColor = VDSColor.elementsPrimaryOndark
$0.primary.disabled.darkColor = VDSColor.elementsPrimaryOnlight $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.lightColor = VDSColor.elementsPrimaryOnlight
$0.secondary.enabled.darkColor = VDSColor.elementsPrimaryOndark $0.secondary.enabled.darkColor = VDSColor.elementsPrimaryOndark
$0.secondary.disabled.lightColor = VDSColor.interactiveDisabledOnlight $0.secondary.disabled.lightColor = VDSColor.interactiveDisabledOnlight
$0.secondary.disabled.darkColor = VDSColor.interactiveDisabledOndark $0.secondary.disabled.darkColor = VDSColor.interactiveDisabledOndark
$0.secondaryHighlighted.lightColor = VDSColor.interactiveActiveOnlight
$0.secondaryHighlighted.darkColor = VDSColor.interactiveActiveOndark
} }
//-------------------------------------------------- //--------------------------------------------------
@ -112,91 +108,57 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
//-------------------------------------------------- //--------------------------------------------------
required public init() { required public init() {
super.init(frame: .zero) super.init(frame: .zero)
initialSetup()
} }
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: .zero) super.init(frame: .zero)
initialSetup()
} }
public required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
initialSetup()
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Functions // MARK: - Public Functions
//-------------------------------------------------- //--------------------------------------------------
open func initialSetup() {
if !initialSetupPerformed {
backgroundColor = .clear
translatesAutoresizingMaskIntoConstraints = false
accessibilityCustomActions = []
accessibilityTraits = .staticText
setup()
setupDidChangeEvent()
updateView()
}
}
open func setup() { open override func setup() {
translatesAutoresizingMaskIntoConstraints = false super.setup()
titleLabel?.adjustsFontSizeToFitWidth = false
titleLabel?.lineBreakMode = .byTruncatingTail
//only 1 of the 2 widths can be on at the same time //only 1 of the 2 widths can be on at the same time
widthConstraint = widthAnchor.constraint(equalToConstant: 0) widthConstraint = widthAnchor.constraint(equalToConstant: 0)
minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: size.minimumWidth) minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: size.minimumWidth)
//height //height
heightConstraint = heightAnchor.constraint(equalToConstant: size.height) heightConstraint = heightAnchor.constraint(equalToConstant: 0)
heightConstraint?.isActive = true heightConstraint?.isActive = true
} }
open func reset() { open override func reset() {
surface = .light super.reset()
disabled = false
use = .primary use = .primary
width = nil width = nil
size = .large size = .large
accessibilityCustomActions = []
accessibilityTraits = .staticText
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
override open var intrinsicContentSize: CGSize { open override func updateView() {
let intrinsicContentSize = super.intrinsicContentSize super.updateView()
let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right
let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom
return CGSize(width: adjustedWidth, height: adjustedHeight)
}
open func updateView() {
let bgColor = buttonBackgroundColorConfiguration.getColor(self) let bgColor = buttonBackgroundColorConfiguration.getColor(self)
let borderColor = buttonBorderColorConfiguration.getColor(self) let borderColor = buttonBorderColorConfiguration.getColor(self)
let titleColor = buttonTitleColorConfiguration.getColor(self)
let borderWidth = use == .secondary ? 1.0 : 0.0 let borderWidth = use == .secondary ? 1.0 : 0.0
let buttonHeight = size.height let buttonHeight = size.height
let cornerRadius = buttonHeight / 2 let cornerRadius = size.cornerRadius
let minWidth = size.minimumWidth let minWidth = size.minimumWidth
let font = size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font
let edgeInsets = size.edgeInsets let edgeInsets = size.edgeInsets
setTitle(text ?? "No Text", for: .normal)
titleLabel?.font = font
backgroundColor = bgColor backgroundColor = bgColor
setTitleColor(titleColor, for: .normal)
layer.borderColor = borderColor.cgColor layer.borderColor = borderColor.cgColor
layer.cornerRadius = cornerRadius layer.cornerRadius = cornerRadius
layer.borderWidth = borderWidth layer.borderWidth = borderWidth
contentEdgeInsets = edgeInsets contentEdgeInsets = edgeInsets
minWidthConstraint?.constant = minWidth minWidthConstraint?.constant = minWidth
heightConstraint?.constant = buttonHeight heightConstraint?.constant = buttonHeight
@ -215,17 +177,23 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
//-------------------------------------------------- //--------------------------------------------------
private class UseableColorConfiguration: ObjectColorable { private class UseableColorConfiguration: ObjectColorable {
typealias ObjectType = Disabling & Surfaceable & Useable typealias ObjectType = Buttonable & Useable
public var primary = DisabledSurfaceColorConfiguration() public var primary = DisabledSurfaceColorConfiguration()
public var secondary = DisabledSurfaceColorConfiguration() public var secondary = DisabledSurfaceColorConfiguration()
public var primaryHighlighted = SurfaceColorConfiguration()
public var secondaryHighlighted = SurfaceColorConfiguration()
required public init(){} required public init(){}
public func getColor(_ object: ObjectType) -> UIColor { public func getColor(_ object: ObjectType) -> UIColor {
return object.use == .primary ? primary.getColor(object) : secondary.getColor(object) 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 { extension ButtonSize {
@ -239,6 +207,10 @@ extension ButtonSize {
} }
} }
public var cornerRadius: CGFloat {
height / 2
}
public var minimumWidth: CGFloat { public var minimumWidth: CGFloat {
switch self { switch self {
case .large: case .large:
@ -267,7 +239,6 @@ extension ButtonSize {
} }
extension Use { extension Use {
public var color: UIColor { public var color: UIColor {
return self == .primary ? VDSColor.backgroundPrimaryDark : .clear return self == .primary ? VDSColor.backgroundPrimaryDark : .clear
} }

View File

@ -0,0 +1,182 @@
//
// BaseButton.swift
// VDS
//
// Created by Matt Bruce on 11/22/22.
//
import Foundation
import UIKit
import VDSColorTokens
import VDSFormControlsTokens
import Combine
public protocol Buttonable: UIControl, Surfaceable, Disabling {
var availableSizes: [ButtonSize] { get }
var text: String? { get set }
var intrinsicContentSize: CGSize { get }
}
@objc(VDSButtonBase)
open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable {
//--------------------------------------------------
// MARK: - Combine Properties
//--------------------------------------------------
public var subject = PassthroughSubject<Void, Never>()
public var subscribers = Set<AnyCancellable>()
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var initialSetupPerformed = false
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
open var availableSizes: [ButtonSize] { [] }
open var text: String? { didSet { didChange() } }
open var attributes: [any LabelAttributeModel]? { nil }
open var surface: Surface = .light { didSet { didChange() }}
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
open override var isHighlighted: Bool { didSet { if isHighlighted != oldValue { updateView() } } }
open var typograpicalStyle: TypographicalStyle { .defaultStyle }
open var textColor: UIColor { .black }
open override var isEnabled: Bool {
get { !disabled }
set {
if disabled != !newValue {
disabled = !newValue
}
isUserInteractionEnabled = isEnabled
didChange()
}
}
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
required public init() {
super.init(frame: .zero)
initialSetup()
}
public override init(frame: CGRect) {
super.init(frame: .zero)
initialSetup()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
initialSetup()
}
//--------------------------------------------------
// MARK: - Public Functions
//--------------------------------------------------
open func initialSetup() {
if !initialSetupPerformed {
backgroundColor = .clear
translatesAutoresizingMaskIntoConstraints = false
accessibilityCustomActions = []
accessibilityTraits = .staticText
setup()
setupDidChangeEvent()
updateView()
}
}
open func setup() {
translatesAutoresizingMaskIntoConstraints = false
titleLabel?.adjustsFontSizeToFitWidth = false
titleLabel?.lineBreakMode = .byTruncatingTail
}
open func reset() {
surface = .light
disabled = false
text = nil
accessibilityCustomActions = []
accessibilityTraits = .button
}
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
override open var intrinsicContentSize: CGSize {
let intrinsicContentSize = super.intrinsicContentSize
let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right
let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom
return CGSize(width: adjustedWidth, height: adjustedHeight)
}
open func updateView() {
updateLabel()
}
//--------------------------------------------------
// MARK: - PRIVATE
//--------------------------------------------------
private func updateLabel() {
let font = typograpicalStyle.font
//clear the arrays holding actions
accessibilityCustomActions = []
//create the primary string
let startingAttributes = [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: textColor]
let mutableText = NSMutableAttributedString(string: text ?? "No Text", attributes: startingAttributes)
//set the local lineHeight/lineSpacing attributes
//get the range
let entireRange = NSRange(location: 0, length: mutableText.length)
//set letterSpacing
if typograpicalStyle.letterSpacing > 0.0 {
mutableText.addAttribute(.kern, value: typograpicalStyle.letterSpacing, range: entireRange)
}
let paragraph = NSMutableParagraphStyle().with {
$0.alignment = titleLabel?.textAlignment ?? .center
$0.lineBreakMode = titleLabel?.lineBreakMode ?? .byTruncatingTail
}
//set lineHeight
if typograpicalStyle.lineHeight > 0.0 {
let lineHeight = typograpicalStyle.lineHeight
let adjustment = lineHeight > font.lineHeight ? 2.0 : 1.0
let baselineOffset = (lineHeight - font.lineHeight) / 2.0 / adjustment
paragraph.maximumLineHeight = lineHeight
paragraph.minimumLineHeight = lineHeight
mutableText.addAttribute(.baselineOffset, value: baselineOffset, range: entireRange)
mutableText.addAttribute( .paragraphStyle, value: paragraph, range: entireRange)
} else {
mutableText.addAttribute( .paragraphStyle, value: paragraph, range: entireRange)
}
if let attributes = attributes {
//loop through the models attributes
for attribute in attributes {
//add attribute on the string
attribute.setAttribute(on: mutableText)
}
}
//set the attributed text
setAttributedTitle(mutableText, for: .normal)
setAttributedTitle(mutableText, for: .highlighted)
}
}

View File

@ -149,6 +149,8 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega
cell.subviews.forEach { $0.removeFromSuperview() } cell.subviews.forEach { $0.removeFromSuperview() }
cell.addSubview(button) cell.addSubview(button)
button.pinToSuperView() button.pinToSuperView()
// cell.layer.borderColor = UIColor.black.cgColor
// cell.layer.borderWidth = 1
return cell return cell
} }
@ -156,11 +158,19 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega
buttons[indexPath.row].intrinsicContentSize buttons[indexPath.row].intrinsicContentSize
} }
public func collectionView(_ collectionView: UICollectionView, isButtonTypeForItemAtIndexPath indexPath: IndexPath) -> Bool {
if let _ = buttons[indexPath.row] as? Button {
return true
} else {
return false
}
}
public func collectionView(_ collectionView: UICollectionView, buttonableAtIndexPath indexPath: IndexPath) -> Buttonable {
buttons[indexPath.row]
}
public func collectionView(_ collectionView: UICollectionView, insetsForItemsInSection section: Int) -> UIEdgeInsets { public func collectionView(_ collectionView: UICollectionView, insetsForItemsInSection section: Int) -> UIEdgeInsets {
UIEdgeInsets.zero UIEdgeInsets.zero
} }
public func collectionView(_ collectionView: UICollectionView, itemSpacingInSection section: Int) -> CGFloat {
itemSpacing
}
} }

View File

@ -8,27 +8,55 @@
import Foundation import Foundation
import UIKit import UIKit
class ButtonLayoutAttributes: UICollectionViewLayoutAttributes{
var spacing: CGFloat = 0
var isButton: Bool = false
convenience init(spacing: CGFloat,
forCellWith indexPath: IndexPath) {
self.init(forCellWith: indexPath)
self.spacing = spacing
}
}
class ButtonCollectionViewRow { class ButtonCollectionViewRow {
var attributes = [UICollectionViewLayoutAttributes]() var attributes = [ButtonLayoutAttributes]()
var spacing: CGFloat = 0 var spacing: CGFloat = 0
init(spacing: CGFloat) { init(spacing: CGFloat) {
self.spacing = spacing self.spacing = spacing
} }
func add(attribute: UICollectionViewLayoutAttributes) { func add(attribute: ButtonLayoutAttributes) {
attributes.append(attribute) attributes.append(attribute)
} }
var hasButtons: Bool {
attributes.contains(where: { $0.isButton })
}
var rowWidth: CGFloat { var rowWidth: CGFloat {
return attributes.reduce(0, { result, attribute -> CGFloat in return attributes.reduce(0, { result, attribute -> CGFloat in
return result + attribute.frame.width return result + attribute.frame.width + attribute.spacing
}) + CGFloat(attributes.count - 1) * spacing })
}
var rowHeight: CGFloat {
attributes.compactMap{$0.frame.height}.max() ?? 0
}
var rowY: CGFloat = 0 {
didSet {
for attribute in attributes {
attribute.frame.origin.y = rowY
}
}
} }
func layout(for position: ButtonPosition, with collectionViewWidth: CGFloat){ func layout(for position: ButtonPosition, with collectionViewWidth: CGFloat){
var offset = 0.0 var offset = 0.0
attributes.last?.spacing = 0
switch position { switch position {
case .left: case .left:
break break
@ -40,7 +68,7 @@ class ButtonCollectionViewRow {
for attribute in attributes { for attribute in attributes {
attribute.frame.origin.x = offset attribute.frame.origin.x = offset
offset += attribute.frame.width + spacing offset += attribute.frame.width + attribute.spacing
} }
} }
} }
@ -51,8 +79,8 @@ public enum ButtonPosition: String, CaseIterable {
protocol ButtongGroupPositionLayoutDelegate: AnyObject { protocol ButtongGroupPositionLayoutDelegate: AnyObject {
func collectionView(_ collectionView: UICollectionView, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize func collectionView(_ collectionView: UICollectionView, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize
func collectionView(_ collectionView: UICollectionView, buttonableAtIndexPath indexPath: IndexPath) -> any Buttonable
func collectionView(_ collectionView: UICollectionView, insetsForItemsInSection section: Int) -> UIEdgeInsets func collectionView(_ collectionView: UICollectionView, insetsForItemsInSection section: Int) -> UIEdgeInsets
func collectionView(_ collectionView: UICollectionView, itemSpacingInSection section: Int) -> CGFloat
} }
class ButtonGroupPositionLayout: UICollectionViewLayout { class ButtonGroupPositionLayout: UICollectionViewLayout {
@ -62,58 +90,74 @@ class ButtonGroupPositionLayout: UICollectionViewLayout {
// Total height of the content. Will be used to configure the scrollview content // Total height of the content. Will be used to configure the scrollview content
var layoutHeight: CGFloat = 0.0 var layoutHeight: CGFloat = 0.0
var position: ButtonPosition = .left var position: ButtonPosition = .left
private var itemCache: [UICollectionViewLayoutAttributes] = [] private var itemCache: [ButtonLayoutAttributes] = []
override func prepare() { override func prepare() {
super.prepare() super.prepare()
let rowSpacingButton = 12.0
let rowSpacingTextLink = 12.0
itemCache.removeAll() itemCache.removeAll()
layoutHeight = 0.0 layoutHeight = 0.0
guard let collectionView = collectionView else { guard let collectionView, let delegate else {
return return
} }
var itemSpacing = 0.0 var itemSpacing = 0.0
// Variable to track the width of the layout at the current state when the item is being drawn // Variable to track the width of the layout at the current state when the item is being drawn
var layoutWidthIterator: CGFloat = 0.0 var layoutWidthIterator: CGFloat = 0.0
for section in 0..<collectionView.numberOfSections { let section = 0
// Get the necessary data (if implemented) from the delegates else provide default values
let insets: UIEdgeInsets = delegate.collectionView(collectionView, insetsForItemsInSection: section)
let rowSpacing: CGFloat = rowSpacingButton
// Variables to track individual item width and cumultative height of all items as they are being laid out.
var itemSize: CGSize = .zero
layoutHeight += insets.top
let totalItems = collectionView.numberOfItems(inSection: section)
for item in 0..<totalItems {
// Get the necessary data (if implemented) from the delegates else provide default values itemSpacing = 0.0
let insets: UIEdgeInsets = delegate?.collectionView(collectionView, insetsForItemsInSection: section) ?? UIEdgeInsets.zero
let interItemSpacing: CGFloat = delegate?.collectionView(collectionView, itemSpacingInSection: section) ?? 0.0
itemSpacing = interItemSpacing
// Variables to track individual item width and cumultative height of all items as they are being laid out.
var itemSize: CGSize = .zero
layoutHeight += insets.top let indexPath = IndexPath(item: item, section: section)
for item in 0..<collectionView.numberOfItems(inSection: section) { itemSize = delegate.collectionView(collectionView, sizeForItemAtIndexPath: indexPath)
let indexPath = IndexPath(item: item, section: section)
if (layoutWidthIterator + itemSize.width + insets.left + insets.right) > collectionView.frame.width {
itemSize = delegate?.collectionView(collectionView, sizeForItemAtIndexPath: indexPath) ?? .zero // If the current row width (after this item being laid out) is exceeding the width of the collection view content, put it in the next line
layoutWidthIterator = 0.0
if (layoutWidthIterator + itemSize.width + insets.left + insets.right) > collectionView.frame.width { layoutHeight += itemSize.height + rowSpacing
// If the current row width (after this item being laid out) is exceeding the width of the collection view content, put it in the next line }
layoutWidthIterator = 0.0
layoutHeight += itemSize.height + interItemSpacing let itemButtonable = delegate.collectionView(collectionView, buttonableAtIndexPath: indexPath)
}
let nextItem = item + 1
let frame = CGRect(x: layoutWidthIterator + insets.left, y: layoutHeight, width: itemSize.width, height: itemSize.height) if nextItem < totalItems {
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath) let neighbor = delegate.collectionView(collectionView, buttonableAtIndexPath: IndexPath(item: nextItem, section: section))
attributes.frame = frame itemSpacing = getHorizontalSpacing(for: itemButtonable, neighboring: neighbor)
itemCache.append(attributes)
layoutWidthIterator = layoutWidthIterator + frame.width + interItemSpacing
} }
layoutHeight += itemSize.height + insets.bottom let frame = CGRect(x: layoutWidthIterator + insets.left, y: layoutHeight, width: itemSize.width, height: itemSize.height)
layoutWidthIterator = 0.0 //print(frame)
let attributes = ButtonLayoutAttributes(spacing: itemSpacing, forCellWith: indexPath)
attributes.frame = frame
attributes.isButton = isButton(buttonable: itemButtonable)
itemCache.append(attributes)
layoutWidthIterator = layoutWidthIterator + frame.width + itemSpacing
} }
//print("*******")
layoutHeight += itemSize.height + insets.bottom
layoutWidthIterator = 0.0
//Turn into rows and re-calculate //Turn into rows and re-calculate
var rows = [ButtonCollectionViewRow]() var rows = [ButtonCollectionViewRow]()
var currentRowY: CGFloat = -1 var currentRowY: CGFloat = -1
for attribute in itemCache { for attribute in itemCache {
if currentRowY != attribute.frame.midY { if currentRowY != attribute.frame.midY {
currentRowY = attribute.frame.midY currentRowY = attribute.frame.midY
@ -121,12 +165,156 @@ class ButtonGroupPositionLayout: UICollectionViewLayout {
} }
rows.last?.add(attribute: attribute) rows.last?.add(attribute: attribute)
} }
//recalculate rows based off of positions //recalculate rows based off of positions
rows.forEach { $0.layout(for: position, with: collectionView.frame.width) } rows.forEach { $0.layout(for: position, with: collectionView.frame.width) }
let rowAttributes = rows.flatMap { $0.attributes } let rowAttributes = rows.flatMap { $0.attributes }
layoutHeight = insets.top
for item in 0..<rows.count {
let row = rows[item]
var rowSpacing = 0.0
if item > 0 && item < rows.count {
rowSpacing = row.hasButtons ? rowSpacingButton : rowSpacingTextLink
}
if item > 0 {
row.rowY = layoutHeight + rowSpacing
layoutHeight += rowSpacing
}
layoutHeight += row.rowHeight
}
layoutHeight += insets.bottom
itemCache = rowAttributes itemCache = rowAttributes
}
func isButton(buttonable: Buttonable) -> Bool{
if let _ = buttonable as? Button {
return true
} else {
return false
}
}
func getHorizontalSpacing(for primary: Buttonable, neighboring: Buttonable) -> CGFloat {
let defaultSpace = 12.0
//large button
if let button = primary as? Button, button.size == .large {
if let neighboringButton = neighboring as? Button, neighboringButton.size == .large {
return 12.0
} else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .large {
return 16.0
} else if let _ = neighboring as? TextLinkCaret {
return 24.0
} else {
return defaultSpace
}
}
//large text link
else if let textLink = primary as? TextLink, textLink.size == .large {
if let neighboringButton = neighboring as? Button, neighboringButton.size == .large {
return 16.0
} else if let _ = neighboring as? TextLinkCaret {
return 24.0
} else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .large {
return 16.0
} else {
return defaultSpace
}
}
//text link caret
else if let _ = primary as? TextLinkCaret {
if let _ = neighboring as? TextLinkCaret {
return 24.0
} else {
return defaultSpace
}
}
//small button
else if let button = primary as? Button, button.size == .small {
if let neighboringButton = neighboring as? Button, neighboringButton.size == .small {
return 12.0
} else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .small {
return 16.0
} else {
return defaultSpace
}
}
//small text link
else if let textLink = primary as? TextLink, textLink.size == .small {
if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .small {
return 16.0
} else {
return defaultSpace
}
}
//return defaultSpace
else {
return defaultSpace
}
}
func getVerticalSpacing(for primary: Buttonable, neighboring: Buttonable) -> CGFloat {
let defaultSpace = 12.0
//large button
if let button = primary as? Button, button.size == .large {
if let neighboringButton = neighboring as? Button, neighboringButton.size == .large {
return 12.0
} else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .large {
return 16.0
} else if let _ = neighboring as? TextLinkCaret {
return 24.0
} else {
return defaultSpace
}
}
//large text link
else if let textLink = primary as? TextLink, textLink.size == .large {
if let neighboringButton = neighboring as? Button, neighboringButton.size == .large {
return 16.0
} else if let _ = neighboring as? TextLinkCaret {
return 24.0
} else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .large {
return 24.0
} else {
return defaultSpace
}
}
//text link caret
else if let _ = primary as? TextLinkCaret {
if let _ = neighboring as? TextLinkCaret {
return 24.0
} else {
return defaultSpace
}
}
//small button
else if let button = primary as? Button, button.size == .small {
if let neighboringButton = neighboring as? Button, neighboringButton.size == .small {
return 12.0
} else if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .small {
return 24.0
} else {
return defaultSpace
}
}
//small text link
else if let textLink = primary as? TextLink, textLink.size == .small {
if let neighboringTextLink = neighboring as? TextLink, neighboringTextLink.size == .small {
return 32.0
} else {
return defaultSpace
}
}
//return defaultSpace
else {
return defaultSpace
}
} }
override func layoutAttributesForElements(in rect: CGRect)-> [UICollectionViewLayoutAttributes]? { override func layoutAttributesForElements(in rect: CGRect)-> [UICollectionViewLayoutAttributes]? {

View File

@ -12,23 +12,40 @@ import VDSFormControlsTokens
import Combine import Combine
@objc(VDSTextLink) @objc(VDSTextLink)
open class TextLink: Control, Buttonable { open class TextLink: ButtonBase {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
//-------------------------------------------------- //--------------------------------------------------
private var heightConstraint: NSLayoutConstraint? private var heightConstraint: NSLayoutConstraint?
private var label = Label() private var lineHeightConstraint: NSLayoutConstraint?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
open var text: String? { didSet { didChange() } }
open var size: ButtonSize = .large { didSet { didChange() }} open var size: ButtonSize = .large { didSet { didChange() }}
public var availableSizes: [ButtonSize] { [.large, .small] } open override var availableSizes: [ButtonSize] { [.large, .small] }
open override var typograpicalStyle: TypographicalStyle {
size == .large ? TypographicalStyle.BodyLarge : TypographicalStyle.BodySmall
}
open override var textColor: UIColor {
textColorConfiguration.getColor(self)
}
private var textColorConfiguration = HighlightDisabledSurfaceColorConfiguration().with {
$0.disabled.lightColor = VDSColor.elementsSecondaryOnlight
$0.disabled.darkColor = VDSColor.elementsSecondaryOndark
$0.enabled.lightColor = VDSColor.elementsPrimaryOnlight
$0.enabled.darkColor = VDSColor.elementsPrimaryOndark
$0.highlighted.lightColor = VDSColor.interactiveActiveOnlight
$0.highlighted.darkColor = VDSColor.interactiveActiveOndark
}
private var height: CGFloat { private var height: CGFloat {
switch size { switch size {
case .large: case .large:
@ -43,49 +60,42 @@ open class TextLink: Control, Buttonable {
//-------------------------------------------------- //--------------------------------------------------
required public init() { required public init() {
super.init(frame: .zero) super.init(frame: .zero)
initialSetup()
} }
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: .zero) super.init(frame: .zero)
initialSetup()
} }
public required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
initialSetup() }
private var line = UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Functions // MARK: - Public Functions
//-------------------------------------------------- //--------------------------------------------------
open override func initialSetup() {
super.initialSetup()
}
open override func setup() { open override func setup() {
super.setup() super.setup()
addSubview(label) if let titleLabel {
addSubview(line)
//add tapGesture to self line.pinLeading(titleLabel.leadingAnchor)
publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in line.pinTrailing(titleLabel.trailingAnchor)
self?.sendActions(for: .touchUpInside) line.pinTop(titleLabel.bottomAnchor, 1)
}.store(in: &subscribers) lineHeightConstraint = line.heightAnchor.constraint(equalToConstant: 1.0)
lineHeightConstraint?.isActive = true
//pin stackview to edges }
label.pinToSuperView()
label.numberOfLines = 1
heightConstraint = heightAnchor.constraint(equalToConstant: height) heightConstraint = heightAnchor.constraint(equalToConstant: height)
heightConstraint?.isActive = true heightConstraint?.isActive = true
} }
open override func reset() { open override func reset() {
super.reset() super.reset()
label.reset() size = .large
size = .large
text = nil
accessibilityCustomActions = [] accessibilityCustomActions = []
accessibilityTraits = .staticText accessibilityTraits = .staticText
} }
@ -94,16 +104,36 @@ open class TextLink: Control, Buttonable {
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
override open var intrinsicContentSize: CGSize { override open var intrinsicContentSize: CGSize {
return CGSize(width: label.intrinsicContentSize.width, height: height) var itemWidth = super.intrinsicContentSize.width
return CGSize(width: itemWidth, height: height)
} }
open override func updateView() { open override func updateView() {
label.surface = surface //need to set the properties so the super class
label.disabled = disabled //can render out the label correctly
label.typograpicalStyle = size == .large ? TypographicalStyle.BodyLarge : TypographicalStyle.BodySmall
label.text = text ?? ""
label.attributes = [UnderlineLabelAttribute(location: 0, length: label.text!.count)]
heightConstraint?.constant = height heightConstraint?.constant = height
lineHeightConstraint?.constant = isHighlighted ? 2.0 : 1.0
line.backgroundColor = textColor
//always call last so the label is rendered
super.updateView()
} }
} }
class HighlightDisabledSurfaceColorConfiguration: ObjectColorable {
typealias ObjectType = Buttonable
public var highlighted = SurfaceColorConfiguration()
public var disabled = SurfaceColorConfiguration()
public var enabled = SurfaceColorConfiguration()
required public init(){}
public func getColor(_ object: any ObjectType) -> UIColor {
if object.isHighlighted {
return highlighted.getColor(object)
} else {
return object.disabled ? disabled.getColor(object) : enabled.getColor(object)
}
}
}

View File

@ -16,103 +16,103 @@ public enum TextLinkCaretPosition: String, CaseIterable {
} }
@objc(VDSTextLinkCaret) @objc(VDSTextLinkCaret)
open class TextLinkCaret: Control, Buttonable { open class TextLinkCaret: ButtonBase {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
//-------------------------------------------------- //--------------------------------------------------
private var heightConstraint: NSLayoutConstraint? private var heightConstraint: NSLayoutConstraint?
private var label = Label().with { open override var typograpicalStyle: TypographicalStyle {
$0.typograpicalStyle = TypographicalStyle.BoldBodyLarge TypographicalStyle.BoldBodyLarge
} }
private var caretView = CaretView().with { private var caretView = CaretView().with {
$0.size = CaretView.CaretSize.small(.vertical) $0.size = CaretView.CaretSize.small(.vertical)
$0.lineWidth = 2 $0.lineWidth = 2
} }
private var imageAttribute: ImageLabelAttribute?
open override var attributes: [any LabelAttributeModel]? {
guard let imageAttribute else { return nil }
return [imageAttribute]
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
public var availableSizes: [ButtonSize] { [.large] } public override var availableSizes: [ButtonSize] { [.large] }
open var text: String? { didSet { didChange() } }
open var iconPosition: TextLinkCaretPosition = .right { didSet { didChange() } } open var iconPosition: TextLinkCaretPosition = .right { didSet { didChange() } }
private var height: CGFloat { private var height: CGFloat {
44 44
} }
private var _text: String?
open override var text: String? {
get{ _text }
set {
var updatedText = newValue ?? ""
updatedText = iconPosition == .right ? "\(updatedText) " : " \(updatedText)"
_text = updatedText
didChange()
}
}
open override var textColor: UIColor {
textColorConfiguration.getColor(self)
}
private var textColorConfiguration = HighlightDisabledSurfaceColorConfiguration().with {
$0.disabled.lightColor = VDSColor.elementsSecondaryOnlight
$0.disabled.darkColor = VDSColor.elementsSecondaryOndark
$0.enabled.lightColor = VDSColor.elementsPrimaryOnlight
$0.enabled.darkColor = VDSColor.elementsPrimaryOndark
$0.highlighted.lightColor = VDSColor.interactiveActiveOnlight
$0.highlighted.darkColor = VDSColor.interactiveActiveOndark
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
required public init() { required public init() {
super.init(frame: .zero) super.init(frame: .zero)
initialSetup()
} }
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: .zero) super.init(frame: .zero)
initialSetup()
} }
public required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
initialSetup()
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Functions // MARK: - Public Functions
//-------------------------------------------------- //--------------------------------------------------
open override func initialSetup() {
super.initialSetup()
}
open override func setup() { open override func setup() {
super.setup() super.setup()
//add tapGesture to self
publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in
self?.sendActions(for: .touchUpInside)
}.store(in: &subscribers)
//constraints //constraints
heightAnchor.constraint(greaterThanOrEqualToConstant: height).isActive = true heightAnchor.constraint(greaterThanOrEqualToConstant: height).isActive = true
let size = caretView.size!.dimensions() let size = caretView.size!.dimensions()
caretView.frame = .init(x: 0, y: 0, width: size.width, height: size.height) caretView.frame = .init(x: 0, y: 0, width: size.width, height: size.height)
addSubview(label)
label.pinToSuperView()
label.numberOfLines = 1
} }
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
private var caretLeadingConstraint: NSLayoutConstraint?
private var caretTrailingConstraint: NSLayoutConstraint?
private var labelConstraint: NSLayoutConstraint?
open override func reset() { open override func reset() {
super.reset() super.reset()
label.reset()
label.typograpicalStyle = TypographicalStyle.BoldBodyLarge
text = nil
iconPosition = .right iconPosition = .right
accessibilityCustomActions = []
accessibilityTraits = .staticText
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
override open var intrinsicContentSize: CGSize { override open var intrinsicContentSize: CGSize {
var itemWidth = label.intrinsicContentSize.width var itemWidth = super.intrinsicContentSize.width
if let caretWidth = caretView.size?.dimensions().width { if let caretWidth = caretView.size?.dimensions().width {
itemWidth += caretWidth itemWidth += caretWidth
} }
@ -122,21 +122,18 @@ open class TextLinkCaret: Control, Buttonable {
open override func updateView() { open override func updateView() {
let updatedText = text ?? "" let updatedText = text ?? ""
caretView.surface = surface caretView.surface = surface
caretView.disabled = disabled caretView.disabled = disabled
caretView.direction = iconPosition == .right ? CaretView.Direction.right : CaretView.Direction.left caretView.direction = iconPosition == .right ? CaretView.Direction.right : CaretView.Direction.left
let image = caretView.getImage() let image = caretView.getImage()
let location = iconPosition == .right ? updatedText.count + 1 : 0 let location = iconPosition == .right ? updatedText.count : 0
let textColor = label.textColorConfiguration.getColor(self)
let imageAttribute = ImageLabelAttribute(location: location, imageAttribute = ImageLabelAttribute(location: location,
image: image, image: image,
tintColor: textColor) tintColor: textColor)
label.surface = surface
label.disabled = disabled super.updateView()
label.text = iconPosition == .right ? "\(updatedText) " : " \(updatedText)"
label.attributes = [imageAttribute]
} }
} }

View File

@ -11,6 +11,7 @@ import VDSColorTokens
import VDSFormControlsTokens import VDSFormControlsTokens
import Combine import Combine
/// Checkboxes are a multi-select component through which a customer indicates a choice. If a binary choice, the component is a checkbox. If the choice has multiple options, the component is a ``CheckboxGroup``.
@objc(VDSCheckbox) @objc(VDSCheckbox)
public class Checkbox: CheckboxBase{} public class Checkbox: CheckboxBase{}

View File

@ -11,10 +11,7 @@ import VDSColorTokens
import Combine import Combine
@objc(VDSLabel) @objc(VDSLabel)
public class Label: LabelBase {} public class Label: UILabel, Handlerable, ViewProtocol, Resettable {
@objc(VDSLabelBase)
open class LabelBase: UILabel, Handlerable, ViewProtocol, Resettable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Combine Properties // MARK: - Combine Properties

View File

@ -1,13 +1,26 @@
# ``VDS`` # ``VDS``
<!--@START_MENU_TOKEN@-->Summary<!--@END_MENU_TOKEN@--> The Verizon Design System is the single source of truth for Verizons digital experiences. It aligns design and code resources to give designers and developers consistent, detailed documentation and standardized libraries of symbols and coded components.
## Overview ## Overview
<!--@START_MENU_TOKEN@-->Text<!--@END_MENU_TOKEN@--> Using the system allows designers and developers to collaborate more easily and efficiently on creating on-brand and accessible digital experiences. Spend more time improving our digital products for customers and less time redrawing or rebuilding basic user interface elements.
## Topics ## Topics
### <!--@START_MENU_TOKEN@-->Group<!--@END_MENU_TOKEN@--> ### Components
- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@--> - ``Badge``
- ``Button``
- ``TextLink``
- ``TextLinkCaret``
- ``CheckboxGroup``
- ``Checkbox``
- ``Label``
- ``RadioBoxGroup``
- ``RadioBox``
- ``RadioButtonGroup``
- ``RadioButton``
- ``RadioSwatchGroup``
- ``RadioSwatch``
- ``Toggle``