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 */; };
|
||||
EAB5FEED2927E1B200998C17 /* ButtonGroupPositionLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEEC2927E1B200998C17 /* ButtonGroupPositionLayout.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 */; };
|
||||
EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925822911B35300091998 /* TextLinkCaret.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -203,6 +205,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5FC35BE228D51405004EBEAC /* Button.swift */,
|
||||
EAB5FEF4292D371F00998C17 /* ButtonBase.swift */,
|
||||
);
|
||||
path = Button;
|
||||
sourceTree = "<group>";
|
||||
@ -654,6 +657,7 @@
|
||||
EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */,
|
||||
EAC925842911C63100091998 /* Colorable.swift in Sources */,
|
||||
EA3361C5289030FC0071C351 /* Accessable.swift in Sources */,
|
||||
EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */,
|
||||
EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */,
|
||||
EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */,
|
||||
EAF7F11728A1475A00B287F5 /* RadioButton.swift in Sources */,
|
||||
|
||||
@ -68,7 +68,7 @@ extension DisabledSurfaceColorable {
|
||||
/// let textColor = config.getColor(model) //returns .white
|
||||
///
|
||||
///
|
||||
final public class DisabledSurfaceColorConfiguration: DisabledSurfaceColorable {
|
||||
open class DisabledSurfaceColorConfiguration: DisabledSurfaceColorable {
|
||||
public typealias ObjectType = Surfaceable & Disabling
|
||||
public var disabled = 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 isHighlighted: Bool { didSet { updateView() } }
|
||||
|
||||
open override var isEnabled: Bool {
|
||||
get { !disabled }
|
||||
set {
|
||||
|
||||
@ -15,6 +15,8 @@ public enum BadgeFillColor: String, Codable, CaseIterable {
|
||||
case red, yellow, green, orange, blue, black, white
|
||||
}
|
||||
|
||||
|
||||
/// Badges are visual labels used to convey status or highlight supplemental information.
|
||||
@objc(VDSBadge)
|
||||
public class Badge: View, Accessable {
|
||||
|
||||
|
||||
@ -11,26 +11,14 @@ 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 }
|
||||
}
|
||||
|
||||
public enum ButtonSize: String, Codable, CaseIterable {
|
||||
case large
|
||||
case small
|
||||
}
|
||||
|
||||
@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
|
||||
//--------------------------------------------------
|
||||
@ -42,31 +30,21 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
|
||||
//--------------------------------------------------
|
||||
// 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 size: ButtonSize = .large { 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 {
|
||||
get { !disabled }
|
||||
set {
|
||||
if disabled != !newValue {
|
||||
disabled = !newValue
|
||||
}
|
||||
isUserInteractionEnabled = isEnabled
|
||||
didChange()
|
||||
}
|
||||
open override var textColor: UIColor {
|
||||
buttonTitleColorConfiguration.getColor(self)
|
||||
}
|
||||
|
||||
open override var typograpicalStyle: TypographicalStyle {
|
||||
size == .large ? TypographicalStyle.BoldBodyLarge : TypographicalStyle.BoldBodySmall
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Configuration Properties
|
||||
@ -76,23 +54,35 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
|
||||
$0.primary.enabled.darkColor = VDSColor.backgroundPrimaryLight
|
||||
$0.primary.disabled.lightColor = VDSColor.interactiveDisabledOnlight
|
||||
$0.primary.disabled.darkColor = VDSColor.interactiveDisabledOndark
|
||||
|
||||
$0.primaryHighlighted.lightColor = VDSColor.interactiveActiveOnlight
|
||||
$0.primaryHighlighted.darkColor = VDSColor.interactiveActiveOndark
|
||||
|
||||
$0.secondary.enabled.lightColor = UIColor.clear
|
||||
$0.secondary.enabled.darkColor = UIColor.clear
|
||||
$0.secondary.disabled.lightColor = UIColor.clear
|
||||
$0.secondary.disabled.darkColor = UIColor.clear
|
||||
|
||||
$0.secondaryHighlighted.lightColor = UIColor.clear
|
||||
$0.secondaryHighlighted.darkColor = UIColor.clear
|
||||
}
|
||||
|
||||
|
||||
private var buttonBorderColorConfiguration = UseableColorConfiguration().with {
|
||||
$0.primary.enabled.lightColor = VDSColor.elementsPrimaryOndark
|
||||
$0.primary.enabled.darkColor = VDSColor.elementsPrimaryOnlight
|
||||
$0.primary.disabled.lightColor = VDSColor.interactiveDisabledOnlight
|
||||
$0.primary.disabled.darkColor = VDSColor.interactiveDisabledOndark
|
||||
|
||||
|
||||
$0.primaryHighlighted.lightColor = VDSColor.elementsPrimaryOndark
|
||||
$0.primaryHighlighted.darkColor = VDSColor.elementsPrimaryOnlight
|
||||
|
||||
$0.secondary.enabled.lightColor = VDSColor.elementsPrimaryOnlight
|
||||
$0.secondary.enabled.darkColor = VDSColor.elementsPrimaryOndark
|
||||
$0.secondary.disabled.lightColor = VDSColor.interactiveDisabledOnlight
|
||||
$0.secondary.disabled.darkColor = VDSColor.interactiveDisabledOndark
|
||||
|
||||
$0.secondaryHighlighted.lightColor = VDSColor.interactiveActiveOnlight
|
||||
$0.secondaryHighlighted.darkColor = VDSColor.interactiveActiveOndark
|
||||
}
|
||||
|
||||
private var buttonTitleColorConfiguration = UseableColorConfiguration().with {
|
||||
@ -100,11 +90,17 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
|
||||
$0.primary.enabled.darkColor = VDSColor.elementsPrimaryOnlight
|
||||
$0.primary.disabled.lightColor = VDSColor.elementsPrimaryOndark
|
||||
$0.primary.disabled.darkColor = VDSColor.elementsPrimaryOnlight
|
||||
|
||||
|
||||
$0.primaryHighlighted.lightColor = VDSColor.elementsPrimaryOndark
|
||||
$0.primaryHighlighted.darkColor = VDSColor.elementsPrimaryOnlight
|
||||
|
||||
$0.secondary.enabled.lightColor = VDSColor.elementsPrimaryOnlight
|
||||
$0.secondary.enabled.darkColor = VDSColor.elementsPrimaryOndark
|
||||
$0.secondary.disabled.lightColor = VDSColor.interactiveDisabledOnlight
|
||||
$0.secondary.disabled.darkColor = VDSColor.interactiveDisabledOndark
|
||||
|
||||
$0.secondaryHighlighted.lightColor = VDSColor.interactiveActiveOnlight
|
||||
$0.secondaryHighlighted.darkColor = VDSColor.interactiveActiveOndark
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -112,91 +108,57 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
|
||||
//--------------------------------------------------
|
||||
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 override func setup() {
|
||||
super.setup()
|
||||
//only 1 of the 2 widths can be on at the same time
|
||||
widthConstraint = widthAnchor.constraint(equalToConstant: 0)
|
||||
minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: size.minimumWidth)
|
||||
|
||||
//height
|
||||
heightConstraint = heightAnchor.constraint(equalToConstant: size.height)
|
||||
heightConstraint = heightAnchor.constraint(equalToConstant: 0)
|
||||
heightConstraint?.isActive = true
|
||||
}
|
||||
|
||||
open func reset() {
|
||||
surface = .light
|
||||
disabled = false
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
use = .primary
|
||||
width = nil
|
||||
size = .large
|
||||
accessibilityCustomActions = []
|
||||
accessibilityTraits = .staticText
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// 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() {
|
||||
|
||||
open override func updateView() {
|
||||
super.updateView()
|
||||
let bgColor = buttonBackgroundColorConfiguration.getColor(self)
|
||||
let borderColor = buttonBorderColorConfiguration.getColor(self)
|
||||
let titleColor = buttonTitleColorConfiguration.getColor(self)
|
||||
let borderWidth = use == .secondary ? 1.0 : 0.0
|
||||
let buttonHeight = size.height
|
||||
let cornerRadius = buttonHeight / 2
|
||||
let cornerRadius = size.cornerRadius
|
||||
let minWidth = size.minimumWidth
|
||||
let font = size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font
|
||||
let edgeInsets = size.edgeInsets
|
||||
|
||||
setTitle(text ?? "No Text", for: .normal)
|
||||
titleLabel?.font = font
|
||||
|
||||
backgroundColor = bgColor
|
||||
setTitleColor(titleColor, for: .normal)
|
||||
layer.borderColor = borderColor.cgColor
|
||||
layer.cornerRadius = cornerRadius
|
||||
layer.borderWidth = borderWidth
|
||||
contentEdgeInsets = edgeInsets
|
||||
|
||||
|
||||
minWidthConstraint?.constant = minWidth
|
||||
heightConstraint?.constant = buttonHeight
|
||||
|
||||
@ -215,17 +177,23 @@ open class Button: UIButton, Buttonable, Handlerable, ViewProtocol, Resettable,
|
||||
//--------------------------------------------------
|
||||
|
||||
private class UseableColorConfiguration: ObjectColorable {
|
||||
typealias ObjectType = Disabling & Surfaceable & Useable
|
||||
typealias ObjectType = Buttonable & Useable
|
||||
public var primary = DisabledSurfaceColorConfiguration()
|
||||
public var secondary = DisabledSurfaceColorConfiguration()
|
||||
|
||||
public var primaryHighlighted = SurfaceColorConfiguration()
|
||||
public var secondaryHighlighted = SurfaceColorConfiguration()
|
||||
|
||||
required public init(){}
|
||||
|
||||
public func getColor(_ object: ObjectType) -> UIColor {
|
||||
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 {
|
||||
@ -239,6 +207,10 @@ extension ButtonSize {
|
||||
}
|
||||
}
|
||||
|
||||
public var cornerRadius: CGFloat {
|
||||
height / 2
|
||||
}
|
||||
|
||||
public var minimumWidth: CGFloat {
|
||||
switch self {
|
||||
case .large:
|
||||
@ -267,7 +239,6 @@ extension ButtonSize {
|
||||
}
|
||||
|
||||
extension Use {
|
||||
|
||||
public var color: UIColor {
|
||||
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.addSubview(button)
|
||||
button.pinToSuperView()
|
||||
// cell.layer.borderColor = UIColor.black.cgColor
|
||||
// cell.layer.borderWidth = 1
|
||||
return cell
|
||||
}
|
||||
|
||||
@ -156,11 +158,19 @@ open class ButtonGroup: View, UICollectionViewDataSource, UICollectionViewDelega
|
||||
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 {
|
||||
UIEdgeInsets.zero
|
||||
}
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, itemSpacingInSection section: Int) -> CGFloat {
|
||||
itemSpacing
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,27 +8,55 @@
|
||||
import Foundation
|
||||
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 {
|
||||
var attributes = [UICollectionViewLayoutAttributes]()
|
||||
var attributes = [ButtonLayoutAttributes]()
|
||||
var spacing: CGFloat = 0
|
||||
|
||||
init(spacing: CGFloat) {
|
||||
self.spacing = spacing
|
||||
}
|
||||
|
||||
func add(attribute: UICollectionViewLayoutAttributes) {
|
||||
func add(attribute: ButtonLayoutAttributes) {
|
||||
attributes.append(attribute)
|
||||
}
|
||||
|
||||
var hasButtons: Bool {
|
||||
attributes.contains(where: { $0.isButton })
|
||||
}
|
||||
|
||||
var rowWidth: CGFloat {
|
||||
return attributes.reduce(0, { result, attribute -> CGFloat in
|
||||
return result + attribute.frame.width
|
||||
}) + CGFloat(attributes.count - 1) * spacing
|
||||
return result + attribute.frame.width + attribute.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){
|
||||
var offset = 0.0
|
||||
|
||||
attributes.last?.spacing = 0
|
||||
|
||||
switch position {
|
||||
case .left:
|
||||
break
|
||||
@ -40,7 +68,7 @@ class ButtonCollectionViewRow {
|
||||
|
||||
for attribute in attributes {
|
||||
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 {
|
||||
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, itemSpacingInSection section: Int) -> CGFloat
|
||||
}
|
||||
|
||||
class ButtonGroupPositionLayout: UICollectionViewLayout {
|
||||
@ -62,58 +90,74 @@ class ButtonGroupPositionLayout: UICollectionViewLayout {
|
||||
// Total height of the content. Will be used to configure the scrollview content
|
||||
var layoutHeight: CGFloat = 0.0
|
||||
var position: ButtonPosition = .left
|
||||
private var itemCache: [UICollectionViewLayoutAttributes] = []
|
||||
private var itemCache: [ButtonLayoutAttributes] = []
|
||||
|
||||
override func prepare() {
|
||||
super.prepare()
|
||||
|
||||
|
||||
let rowSpacingButton = 12.0
|
||||
let rowSpacingTextLink = 12.0
|
||||
|
||||
itemCache.removeAll()
|
||||
layoutHeight = 0.0
|
||||
|
||||
guard let collectionView = collectionView else {
|
||||
|
||||
guard let collectionView, let delegate else {
|
||||
return
|
||||
}
|
||||
var itemSpacing = 0.0
|
||||
// Variable to track the width of the layout at the current state when the item is being drawn
|
||||
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
|
||||
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
|
||||
itemSpacing = 0.0
|
||||
|
||||
layoutHeight += insets.top
|
||||
let indexPath = IndexPath(item: item, section: section)
|
||||
|
||||
for item in 0..<collectionView.numberOfItems(inSection: section) {
|
||||
let indexPath = IndexPath(item: item, section: section)
|
||||
|
||||
itemSize = delegate?.collectionView(collectionView, sizeForItemAtIndexPath: indexPath) ?? .zero
|
||||
|
||||
if (layoutWidthIterator + itemSize.width + insets.left + insets.right) > collectionView.frame.width {
|
||||
// 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)
|
||||
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
|
||||
attributes.frame = frame
|
||||
itemCache.append(attributes)
|
||||
layoutWidthIterator = layoutWidthIterator + frame.width + interItemSpacing
|
||||
itemSize = delegate.collectionView(collectionView, sizeForItemAtIndexPath: indexPath)
|
||||
|
||||
if (layoutWidthIterator + itemSize.width + insets.left + insets.right) > collectionView.frame.width {
|
||||
// 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 + rowSpacing
|
||||
}
|
||||
|
||||
let itemButtonable = delegate.collectionView(collectionView, buttonableAtIndexPath: indexPath)
|
||||
|
||||
let nextItem = item + 1
|
||||
if nextItem < totalItems {
|
||||
let neighbor = delegate.collectionView(collectionView, buttonableAtIndexPath: IndexPath(item: nextItem, section: section))
|
||||
itemSpacing = getHorizontalSpacing(for: itemButtonable, neighboring: neighbor)
|
||||
}
|
||||
|
||||
layoutHeight += itemSize.height + insets.bottom
|
||||
layoutWidthIterator = 0.0
|
||||
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
|
||||
var rows = [ButtonCollectionViewRow]()
|
||||
var currentRowY: CGFloat = -1
|
||||
|
||||
|
||||
for attribute in itemCache {
|
||||
if currentRowY != attribute.frame.midY {
|
||||
currentRowY = attribute.frame.midY
|
||||
@ -121,12 +165,156 @@ class ButtonGroupPositionLayout: UICollectionViewLayout {
|
||||
}
|
||||
rows.last?.add(attribute: attribute)
|
||||
}
|
||||
|
||||
|
||||
//recalculate rows based off of positions
|
||||
rows.forEach { $0.layout(for: position, with: collectionView.frame.width) }
|
||||
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
|
||||
|
||||
|
||||
}
|
||||
|
||||
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]? {
|
||||
|
||||
@ -12,23 +12,40 @@ import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
@objc(VDSTextLink)
|
||||
open class TextLink: Control, Buttonable {
|
||||
open class TextLink: ButtonBase {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
//--------------------------------------------------
|
||||
private var heightConstraint: NSLayoutConstraint?
|
||||
private var label = Label()
|
||||
|
||||
private var lineHeightConstraint: NSLayoutConstraint?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
open var text: String? { 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 {
|
||||
switch size {
|
||||
case .large:
|
||||
@ -43,49 +60,42 @@ open class TextLink: Control, Buttonable {
|
||||
//--------------------------------------------------
|
||||
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()
|
||||
}
|
||||
|
||||
private var line = UIView().with {
|
||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Functions
|
||||
//--------------------------------------------------
|
||||
open override func initialSetup() {
|
||||
super.initialSetup()
|
||||
}
|
||||
|
||||
open override func setup() {
|
||||
super.setup()
|
||||
|
||||
addSubview(label)
|
||||
|
||||
//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
|
||||
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
|
||||
}
|
||||
|
||||
heightConstraint = heightAnchor.constraint(equalToConstant: height)
|
||||
heightConstraint?.isActive = true
|
||||
}
|
||||
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
label.reset()
|
||||
size = .large
|
||||
text = nil
|
||||
|
||||
size = .large
|
||||
accessibilityCustomActions = []
|
||||
accessibilityTraits = .staticText
|
||||
}
|
||||
@ -94,16 +104,36 @@ open class TextLink: Control, Buttonable {
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
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() {
|
||||
label.surface = surface
|
||||
label.disabled = disabled
|
||||
label.typograpicalStyle = size == .large ? TypographicalStyle.BodyLarge : TypographicalStyle.BodySmall
|
||||
label.text = text ?? ""
|
||||
label.attributes = [UnderlineLabelAttribute(location: 0, length: label.text!.count)]
|
||||
//need to set the properties so the super class
|
||||
//can render out the label correctly
|
||||
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,103 +16,103 @@ public enum TextLinkCaretPosition: String, CaseIterable {
|
||||
}
|
||||
|
||||
@objc(VDSTextLinkCaret)
|
||||
open class TextLinkCaret: Control, Buttonable {
|
||||
open class TextLinkCaret: ButtonBase {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
//--------------------------------------------------
|
||||
private var heightConstraint: NSLayoutConstraint?
|
||||
|
||||
private var label = Label().with {
|
||||
$0.typograpicalStyle = TypographicalStyle.BoldBodyLarge
|
||||
open override var typograpicalStyle: TypographicalStyle {
|
||||
TypographicalStyle.BoldBodyLarge
|
||||
}
|
||||
|
||||
|
||||
private var caretView = CaretView().with {
|
||||
$0.size = CaretView.CaretSize.small(.vertical)
|
||||
$0.lineWidth = 2
|
||||
}
|
||||
private var imageAttribute: ImageLabelAttribute?
|
||||
|
||||
open override var attributes: [any LabelAttributeModel]? {
|
||||
guard let imageAttribute else { return nil }
|
||||
return [imageAttribute]
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
public var availableSizes: [ButtonSize] { [.large] }
|
||||
|
||||
open var text: String? { didSet { didChange() } }
|
||||
public override var availableSizes: [ButtonSize] { [.large] }
|
||||
|
||||
open var iconPosition: TextLinkCaretPosition = .right { didSet { didChange() } }
|
||||
|
||||
private var height: CGFloat {
|
||||
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
|
||||
//--------------------------------------------------
|
||||
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 override func initialSetup() {
|
||||
super.initialSetup()
|
||||
}
|
||||
|
||||
open override func setup() {
|
||||
super.setup()
|
||||
//add tapGesture to self
|
||||
publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in
|
||||
self?.sendActions(for: .touchUpInside)
|
||||
}.store(in: &subscribers)
|
||||
|
||||
//constraints
|
||||
heightAnchor.constraint(greaterThanOrEqualToConstant: height).isActive = true
|
||||
|
||||
let size = caretView.size!.dimensions()
|
||||
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() {
|
||||
super.reset()
|
||||
label.reset()
|
||||
|
||||
label.typograpicalStyle = TypographicalStyle.BoldBodyLarge
|
||||
text = nil
|
||||
iconPosition = .right
|
||||
|
||||
accessibilityCustomActions = []
|
||||
accessibilityTraits = .staticText
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
override open var intrinsicContentSize: CGSize {
|
||||
var itemWidth = label.intrinsicContentSize.width
|
||||
var itemWidth = super.intrinsicContentSize.width
|
||||
if let caretWidth = caretView.size?.dimensions().width {
|
||||
itemWidth += caretWidth
|
||||
}
|
||||
@ -122,21 +122,18 @@ open class TextLinkCaret: Control, Buttonable {
|
||||
open override func updateView() {
|
||||
|
||||
let updatedText = text ?? ""
|
||||
|
||||
caretView.surface = surface
|
||||
caretView.disabled = disabled
|
||||
caretView.direction = iconPosition == .right ? CaretView.Direction.right : CaretView.Direction.left
|
||||
|
||||
let image = caretView.getImage()
|
||||
let location = iconPosition == .right ? updatedText.count + 1 : 0
|
||||
let textColor = label.textColorConfiguration.getColor(self)
|
||||
let imageAttribute = ImageLabelAttribute(location: location,
|
||||
let location = iconPosition == .right ? updatedText.count : 0
|
||||
|
||||
imageAttribute = ImageLabelAttribute(location: location,
|
||||
image: image,
|
||||
tintColor: textColor)
|
||||
label.surface = surface
|
||||
label.disabled = disabled
|
||||
label.text = iconPosition == .right ? "\(updatedText) " : " \(updatedText)"
|
||||
label.attributes = [imageAttribute]
|
||||
|
||||
super.updateView()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
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)
|
||||
public class Checkbox: CheckboxBase{}
|
||||
|
||||
|
||||
@ -11,10 +11,7 @@ import VDSColorTokens
|
||||
import Combine
|
||||
|
||||
@objc(VDSLabel)
|
||||
public class Label: LabelBase {}
|
||||
|
||||
@objc(VDSLabelBase)
|
||||
open class LabelBase: UILabel, Handlerable, ViewProtocol, Resettable {
|
||||
public class Label: UILabel, Handlerable, ViewProtocol, Resettable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Combine Properties
|
||||
|
||||
@ -1,13 +1,26 @@
|
||||
# ``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
|
||||
|
||||
<!--@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
|
||||
|
||||
### <!--@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