Merge branch 'feature/buttonGroupUpdate' into 'develop'
created basebutton See merge request BPHV_MIPS/vds_ios!17
This commit is contained in:
commit
f852ae5084
@ -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 */,
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|
||||||
|
|||||||
@ -11,25 +11,13 @@ 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,9 +30,7 @@ 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() }}
|
||||||
|
|
||||||
@ -52,21 +38,13 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
|
|||||||
|
|
||||||
open var width: CGFloat? { didSet { didChange() }}
|
open var width: CGFloat? { didSet { didChange() }}
|
||||||
|
|
||||||
open var surface: Surface = .light { didSet { didChange() }}
|
open override var textColor: UIColor {
|
||||||
|
buttonTitleColorConfiguration.getColor(self)
|
||||||
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
|
|
||||||
|
|
||||||
open override var isEnabled: Bool {
|
|
||||||
get { !disabled }
|
|
||||||
set {
|
|
||||||
if disabled != !newValue {
|
|
||||||
disabled = !newValue
|
|
||||||
}
|
|
||||||
isUserInteractionEnabled = isEnabled
|
|
||||||
didChange()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override var typograpicalStyle: TypographicalStyle {
|
||||||
|
size == .large ? TypographicalStyle.BoldBodyLarge : TypographicalStyle.BoldBodySmall
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Configuration Properties
|
// MARK: - Configuration Properties
|
||||||
@ -77,10 +55,16 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
|
|||||||
$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 {
|
||||||
@ -89,10 +73,16 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
|
|||||||
$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 {
|
||||||
@ -101,10 +91,16 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
|
|||||||
$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,86 +108,52 @@ 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() {
|
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
titleLabel?.adjustsFontSizeToFitWidth = false
|
|
||||||
titleLabel?.lineBreakMode = .byTruncatingTail
|
|
||||||
|
|
||||||
|
open override func setup() {
|
||||||
|
super.setup()
|
||||||
//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
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
182
VDS/Components/Buttons/Button/ButtonBase.swift
Normal file
182
VDS/Components/Buttons/Button/ButtonBase.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,53 +90,69 @@ 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
|
// Get the necessary data (if implemented) from the delegates else provide default values
|
||||||
let insets: UIEdgeInsets = delegate?.collectionView(collectionView, insetsForItemsInSection: section) ?? UIEdgeInsets.zero
|
let insets: UIEdgeInsets = delegate.collectionView(collectionView, insetsForItemsInSection: section)
|
||||||
let interItemSpacing: CGFloat = delegate?.collectionView(collectionView, itemSpacingInSection: section) ?? 0.0
|
let rowSpacing: CGFloat = rowSpacingButton
|
||||||
itemSpacing = interItemSpacing
|
// Variables to track individual item width and cumultative height of all items as they are being laid out.
|
||||||
// Variables to track individual item width and cumultative height of all items as they are being laid out.
|
var itemSize: CGSize = .zero
|
||||||
var itemSize: CGSize = .zero
|
|
||||||
|
|
||||||
layoutHeight += insets.top
|
layoutHeight += insets.top
|
||||||
|
let totalItems = collectionView.numberOfItems(inSection: section)
|
||||||
|
for item in 0..<totalItems {
|
||||||
|
|
||||||
for item in 0..<collectionView.numberOfItems(inSection: section) {
|
itemSpacing = 0.0
|
||||||
let indexPath = IndexPath(item: item, section: section)
|
|
||||||
|
|
||||||
itemSize = delegate?.collectionView(collectionView, sizeForItemAtIndexPath: indexPath) ?? .zero
|
let indexPath = IndexPath(item: item, section: section)
|
||||||
|
|
||||||
if (layoutWidthIterator + itemSize.width + insets.left + insets.right) > collectionView.frame.width {
|
itemSize = delegate.collectionView(collectionView, sizeForItemAtIndexPath: indexPath)
|
||||||
// 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 frame = CGRect(x: layoutWidthIterator + insets.left, y: layoutHeight, width: itemSize.width, height: itemSize.height)
|
if (layoutWidthIterator + itemSize.width + insets.left + insets.right) > collectionView.frame.width {
|
||||||
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
|
// 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
|
||||||
attributes.frame = frame
|
layoutWidthIterator = 0.0
|
||||||
itemCache.append(attributes)
|
layoutHeight += itemSize.height + rowSpacing
|
||||||
layoutWidthIterator = layoutWidthIterator + frame.width + interItemSpacing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutHeight += itemSize.height + insets.bottom
|
let itemButtonable = delegate.collectionView(collectionView, buttonableAtIndexPath: indexPath)
|
||||||
layoutWidthIterator = 0.0
|
|
||||||
|
let nextItem = item + 1
|
||||||
|
if nextItem < totalItems {
|
||||||
|
let neighbor = delegate.collectionView(collectionView, buttonableAtIndexPath: IndexPath(item: nextItem, section: section))
|
||||||
|
itemSpacing = getHorizontalSpacing(for: itemButtonable, neighboring: neighbor)
|
||||||
|
}
|
||||||
|
|
||||||
|
let frame = CGRect(x: layoutWidthIterator + insets.left, y: layoutHeight, width: itemSize.width, height: itemSize.height)
|
||||||
|
//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]()
|
||||||
@ -125,10 +169,154 @@ class ButtonGroupPositionLayout: UICollectionViewLayout {
|
|||||||
//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]? {
|
||||||
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
|
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
|
||||||
|
|
||||||
|
|||||||
@ -12,22 +12,39 @@ 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 {
|
||||||
@ -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)
|
||||||
|
line.pinLeading(titleLabel.leadingAnchor)
|
||||||
|
line.pinTrailing(titleLabel.trailingAnchor)
|
||||||
|
line.pinTop(titleLabel.bottomAnchor, 1)
|
||||||
|
lineHeightConstraint = line.heightAnchor.constraint(equalToConstant: 1.0)
|
||||||
|
lineHeightConstraint?.isActive = true
|
||||||
|
}
|
||||||
|
|
||||||
//add tapGesture to self
|
|
||||||
publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in
|
|
||||||
self?.sendActions(for: .touchUpInside)
|
|
||||||
}.store(in: &subscribers)
|
|
||||||
|
|
||||||
//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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -16,28 +16,32 @@ 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() } }
|
||||||
|
|
||||||
@ -45,74 +49,70 @@ open class TextLinkCaret: Control, Buttonable {
|
|||||||
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]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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{}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -1,13 +1,26 @@
|
|||||||
# ``VDS``
|
# ``VDS``
|
||||||
|
|
||||||
<!--@START_MENU_TOKEN@-->Summary<!--@END_MENU_TOKEN@-->
|
The Verizon Design System is the single source of truth for Verizon’s 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``
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user