// // TextLink.swift // VDS // // Created by Matt Bruce on 11/1/22. // import Foundation import UIKit import VDSCoreTokens import Combine /// A text link is an interactive element that navigates a customer to pages within an experience, like a “Bill details” page, or triggers a secondary action, /// like canceling a task. This class can be used within a ``ButtonGroup``. /// /// If you are using AutoLayoutConstraints you have a combination of Leading/Left and Trailing/Right NSLayoutConstraints, /// you need to ensure that one of these Horizontal Contraints is not constraint of "equatTo". If you are to pin the left/right edges /// to its parent this object will stretch to the parent's width. @objcMembers @objc(VDSTextLink) open class TextLink: ButtonBase { //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- required public init() { super.init(frame: .zero) } public override init(frame: CGRect) { super.init(frame: .zero) } public required init?(coder: NSCoder) { super.init(coder: coder) } //-------------------------------------------------- // MARK: - Enums //-------------------------------------------------- /// Enum used to describe the size. public enum Size: String, CaseIterable { case large case small } //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- private var lineHeightConstraint: NSLayoutConstraint? private var line = UIView().with { $0.translatesAutoresizingMaskIntoConstraints = false } //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- /// The ButtonSize available. open var size: Size = .large { didSet { setNeedsUpdate() } } open override var textStyle: TextStyle { size == .large ? TextStyle.bodyLarge : TextStyle.bodySmall } /// UIColor used on the titleLabel text. open override var textColor: UIColor { textColorConfiguration.getColor(self) } private var textColorConfiguration = ControlColorConfiguration().with { $0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal) $0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled) $0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted) } /// The natural size for the receiving view, considering only properties of the view itself. open override var intrinsicContentSize: CGSize { guard let titleLabel else { return super.intrinsicContentSize } // Calculate the titleLabel's intrinsic content size let labelSize = titleLabel.sizeThatFits(CGSize(width: self.frame.width, height: CGFloat.greatestFiniteMagnitude)) // Adjust the size if needed (add any additional padding if your design requires) let adjustedSize = CGSize(width: labelSize.width + contentEdgeInsets.left + contentEdgeInsets.right, height: labelSize.height + contentEdgeInsets.top + contentEdgeInsets.bottom) return adjustedSize } //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() isAccessibilityElement = true accessibilityTraits = .link //left align titleLabel in case this is pinned leading/trailing //default is always set to center contentHorizontalAlignment = .left if let titleLabel { addSubview(line) line.pinLeading(titleLabel.leadingAnchor) line.pinTrailing(titleLabel.trailingAnchor) line.pinTop(titleLabel.bottomAnchor) line.pinBottom(bottomAnchor, 0, .defaultHigh) lineHeightConstraint = line.height(constant: 1) lineHeightConstraint?.isActive = true } bridge_accessibilityHintBlock = { [weak self] in guard let self else { return "" } return !isEnabled ? "" : "Double tap to open." } } /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { //need to set the properties so the super class //can render out the label correctly line.backgroundColor = textColor //always call last so the label is rendered super.updateView() } /// Resets to default settings. open override func reset() { super.reset() shouldUpdateView = false text = nil size = .large accessibilityCustomActions = [] isAccessibilityElement = true accessibilityTraits = .link shouldUpdateView = true setNeedsUpdate() } }