258 lines
8.6 KiB
Swift
258 lines
8.6 KiB
Swift
//
|
|
// Footnote.swift
|
|
// VDS
|
|
//
|
|
// Created by Kanamarlapudi, Vasavi on 21/08/24.
|
|
//
|
|
|
|
import Foundation
|
|
import UIKit
|
|
import VDSCoreTokens
|
|
|
|
/// A footnote is text that provides supporting details, legal copy and links to related content.
|
|
/// It exists at the bottom or "foot" of a page or section.
|
|
@objcMembers
|
|
@objc(VDSFootnoteItem)
|
|
open class FootnoteItem: View {
|
|
|
|
//--------------------------------------------------
|
|
// 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 kind of component.
|
|
public enum Kind: String, DefaultValuing, CaseIterable {
|
|
case primary, secondary
|
|
|
|
/// The default kind is 'primary'.
|
|
public static var defaultValue : Self { .secondary }
|
|
|
|
/// Color configuation to Symbol and Text relative to kind.
|
|
public var colorConfiguration: SurfaceColorConfiguration {
|
|
switch self {
|
|
case .primary:
|
|
return SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark)
|
|
case .secondary:
|
|
return SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Enum that represents the size availble for component.
|
|
public enum Size: String, DefaultValuing, CaseIterable {
|
|
case micro
|
|
case small
|
|
case large
|
|
|
|
public static var defaultValue: Self { .micro }
|
|
|
|
/// TextStyle relative to Size.
|
|
public var textStyle: TextStyle.StandardStyle {
|
|
switch self {
|
|
case .micro:
|
|
return .micro
|
|
case .small:
|
|
return .bodySmall
|
|
case .large:
|
|
return .bodyLarge
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Enum used to describe the width of a fixed value or percentage of parent's width.
|
|
public enum Width {
|
|
case percentage(CGFloat)
|
|
case value(CGFloat)
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - Public Properties
|
|
//--------------------------------------------------
|
|
/// Color to the component. The default kind is Secondary.
|
|
open var kind: Kind = .defaultValue { didSet { setNeedsUpdate() } }
|
|
|
|
/// Size of the component. The default size is Micro.
|
|
open var size: Size = .defaultValue { didSet { setNeedsUpdate() } }
|
|
|
|
/// If hideSymbol true, the component will show text without symbol.
|
|
open var hideSymbol: Bool = false { didSet { setNeedsUpdate() } }
|
|
|
|
/// symbol type will be shown for the footnote item. The default symbolType is 'asterisk'.
|
|
open var symbolType: String = "*" { didSet { setNeedsUpdate() } }
|
|
|
|
/// Text of the footnote item.
|
|
open var text: String? { didSet { setNeedsUpdate() } }
|
|
|
|
open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } }
|
|
|
|
/// Any percentage or pixel value and cannot exceed container size.
|
|
/// If there is a width that is larger than container size, the footnote will resize to container's width.
|
|
open var width: Width? {
|
|
get { _width }
|
|
set {
|
|
if let newValue {
|
|
switch newValue {
|
|
case .percentage(let percentage):
|
|
if percentage <= 100.0 {
|
|
_width = newValue
|
|
}
|
|
case .value(let value):
|
|
if value > 0 {
|
|
_width = newValue
|
|
}
|
|
}
|
|
} else {
|
|
_width = nil
|
|
}
|
|
updateContainerWidth()
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - Private Properties
|
|
//--------------------------------------------------
|
|
private var _width: Width? = nil
|
|
|
|
/// To set the widest symbol width from the symbol container in the group.
|
|
internal var symbolWidth: CGFloat? { didSet { setNeedsUpdate() } }
|
|
|
|
private lazy var itemStackView = UIStackView().with {
|
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
|
$0.axis = .horizontal
|
|
$0.alignment = .leading
|
|
$0.distribution = .fill
|
|
$0.spacing = VDSLayout.space1X
|
|
$0.backgroundColor = .clear
|
|
}
|
|
|
|
internal var symbolLabel = Label().with {
|
|
$0.isAccessibilityElement = true
|
|
$0.numberOfLines = 1
|
|
$0.sizeToFit()
|
|
}
|
|
|
|
internal var textLabel = Label().with {
|
|
$0.isAccessibilityElement = true
|
|
$0.lineBreakMode = .byWordWrapping
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - Configuration Properties
|
|
//--------------------------------------------------
|
|
internal var maxWidth: CGFloat { constrainedWidth }
|
|
internal var minWidth: CGFloat { containerSize.width }
|
|
internal var containerSize: CGSize { CGSize(width: 45, height: 44) }
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - Constraints
|
|
//--------------------------------------------------
|
|
internal var symbolWidthConstraint: NSLayoutConstraint?
|
|
internal var itemWidthConstraint: NSLayoutConstraint?
|
|
|
|
//--------------------------------------------------
|
|
// 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()
|
|
|
|
// add footnote item stackview.
|
|
addSubview(itemStackView)
|
|
itemStackView.pinToSuperView()
|
|
|
|
// width constraints
|
|
itemWidthConstraint = widthAnchor.constraint(equalToConstant: 0).deactivate()
|
|
|
|
// add symbol label, text label to stack.
|
|
itemStackView.addArrangedSubview(symbolLabel)
|
|
itemStackView.addArrangedSubview(textLabel)
|
|
itemStackView.setCustomSpacing(VDSLayout.space1X, after: symbolLabel)
|
|
|
|
symbolWidthConstraint = symbolLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 0)
|
|
symbolWidthConstraint?.isActive = true
|
|
}
|
|
|
|
open override func setDefaults() {
|
|
super.setDefaults()
|
|
hideSymbol = false
|
|
text = nil
|
|
tooltipModel = nil
|
|
width = nil
|
|
}
|
|
|
|
/// Resets to default settings.
|
|
open override func reset() {
|
|
symbolLabel.reset()
|
|
textLabel.reset()
|
|
super.reset()
|
|
}
|
|
|
|
/// Used to make changes to the View based off a change events or from local properties.
|
|
open override func updateView() {
|
|
super.updateView()
|
|
|
|
// Update symbolLabel
|
|
symbolLabel.text = symbolType
|
|
symbolLabel.isHidden = hideSymbol
|
|
symbolLabel.textColor = kind.colorConfiguration.getColor(self)
|
|
symbolLabel.textStyle = size.textStyle.regular
|
|
symbolLabel.surface = surface
|
|
|
|
//Set width to the symbol label
|
|
if let symbolWidth, symbolWidth > 0 {
|
|
// Set the widest symbol width from the symbol container in the group.
|
|
symbolWidthConstraint?.constant = symbolWidth
|
|
} else {
|
|
symbolWidthConstraint?.constant = symbolLabel.intrinsicContentSize.width
|
|
}
|
|
|
|
// Update textLabel
|
|
textLabel.text = text
|
|
textLabel.textColor = kind.colorConfiguration.getColor(self)
|
|
textLabel.textStyle = size.textStyle.regular
|
|
textLabel.surface = surface
|
|
|
|
// Set the textLabel attributes
|
|
if let tooltipModel {
|
|
var attributes: [any LabelAttributeModel] = []
|
|
attributes.append(TooltipLabelAttribute(surface: surface, model: tooltipModel, presenter: self))
|
|
textLabel.attributes = attributes
|
|
}
|
|
}
|
|
|
|
/// Update container width after updating content.
|
|
internal func updateContainerWidth() {
|
|
var newWidth = 0.0
|
|
switch width {
|
|
case .percentage(let percentage):
|
|
newWidth = max(maxWidth * ((percentage) / 100), minWidth)
|
|
|
|
case .value(let value):
|
|
newWidth = value > maxWidth ? maxWidth : value
|
|
|
|
case nil:
|
|
break
|
|
}
|
|
itemWidthConstraint?.deactivate()
|
|
|
|
if newWidth > minWidth && newWidth < maxWidth {
|
|
itemWidthConstraint?.constant = newWidth
|
|
itemWidthConstraint?.activate()
|
|
}
|
|
}
|
|
}
|