vds_ios/VDS/Components/Tabs/Tab.swift
Matt Bruce d98b08d26e removed old frameworks to add the 1 primary VDSTokens
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2024-01-23 10:09:09 -06:00

195 lines
7.4 KiB
Swift

//
// Tab.swift
// VDS
//
// Created by Matt Bruce on 5/18/23.
//
import Foundation
import UIKit
import VDSTokens
import Combine
extension Tabs {
@objc(VDSTab)
open class Tab: Control {
//--------------------------------------------------
// 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: - Private Properties
//--------------------------------------------------
/// TextStyle used on the label.
private var textStyle: TextStyle {
if size == .medium {
return .boldBodyLarge
} else {
//specs show that the font size shouldn't change however boldTitleSmall does
//change point size between iPad/iPhone. This is a "fix" so each device will
//load the correct pointSize
return UIDevice.isIPad ? .boldTitleSmall : .boldTitleMedium
}
}
private var labelWidthConstraint: NSLayoutConstraint?
private var labelLeadingConstraint: NSLayoutConstraint?
private var labelTopConstraint: NSLayoutConstraint?
private var labelBottomConstraint: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
///position of the tab
open var index: Int = 0
///label to write out the text
open var label: Label = Label().with {
$0.isUserInteractionEnabled = false
$0.setContentCompressionResistancePriority(.required, for: .horizontal)
$0.setContentHuggingPriority(.required, for: .horizontal)
}
///orientation of the tabs
open var orientation: Tabs.Orientation = .horizontal { didSet { setNeedsUpdate() } }
///Size for tab
open var size: Tabs.Size = .medium { didSet { setNeedsUpdate() } }
///Number of lines in the Label.
open var numberOfLines: Int = 0 { didSet { setNeedsUpdate() } }
///Text position left or center
open var textAlignment: TextAlignment = .left { didSet { setNeedsUpdate() } }
///Sets the Position of the Selected/Hover Border Accent for All Tabs.
open var indicatorPosition: Tabs.IndicatorPosition = .bottom { didSet { setNeedsUpdate() } }
///If provided, it will set fixed width for this Tab.
open var width: CGFloat? { didSet { setNeedsUpdate() } }
///The text label of the tab.
open var text: String = "Tab" { didSet { setNeedsUpdate() } }
///Minimum width for the tab
open var minWidth: CGFloat = 44.0 { didSet { setNeedsUpdate() } }
open override var shouldHighlight: Bool { false }
//--------------------------------------------------
// MARK: - Configuration
//--------------------------------------------------
private var textColorConfiguration: SurfaceColorConfiguration { isSelected ? textColorSelectedConfiguration : textColorNonSelectedConfiguration }
private var textColorNonSelectedConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight , VDSColor.elementsSecondaryOndark)
private var textColorSelectedConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark)
private var indicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteRed, VDSColor.elementsPrimaryOndark)
private var indicatorWidth: CGFloat = 4.0
private var indicatorSide: UIRectEdge {
guard orientation == .horizontal else { return .left }
return indicatorPosition.value
}
private var leadingSpace: CGFloat {
guard orientation == .vertical else { return 0 }
return VDSLayout.space4X
}
private var otherSpace: CGFloat {
if orientation == .horizontal {
return size == .medium ? VDSLayout.space3X : VDSLayout.space4X
} else {
return VDSLayout.space2X
}
}
private let layoutGuide = UILayoutGuide()
private func updateWidth() {
labelWidthConstraint?.isActive = false
guard let width, width > minWidth else { return }
labelWidthConstraint?.constant = width
labelWidthConstraint?.isActive = true
}
//--------------------------------------------------
// 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()
addLayoutGuide(layoutGuide)
addSubview(label)
accessibilityTraits = .button
isAccessibilityElement = true
//pin layoutguide
layoutGuide.pinToSuperView()
//pin trailing
label.pinTrailing(layoutGuide.trailingAnchor)
//setup constraints
labelWidthConstraint = layoutGuide.width(constant: 0).with { $0.isActive = false }
layoutGuide.widthGreaterThanEqualTo(minWidth)
labelTopConstraint = label.pinTop(anchor: layoutGuide.topAnchor)
labelLeadingConstraint = label.pinLeading(anchor: layoutGuide.leadingAnchor)
labelBottomConstraint = label.pinBottom(anchor: layoutGuide.bottomAnchor, priority: .defaultHigh)
}
/// Used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
guard !text.isEmpty else { return }
accessibilityIdentifier = "VDSTab:\(text)"
//constaints
updateWidth()
labelLeadingConstraint?.constant = leadingSpace
labelTopConstraint?.constant = otherSpace
labelBottomConstraint?.constant = -otherSpace
//label properties
label.textStyle = textStyle
label.text = text
label.surface = surface
label.textAlignment = textAlignment.value
label.textColorConfiguration = textColorConfiguration.eraseToAnyColorable()
setNeedsLayout()
layoutIfNeeded()
}
/// Used to update any Accessibility properties.
open override func updateAccessibility() {
super.updateAccessibility()
accessibilityLabel = text
}
open override func layoutSubviews() {
super.layoutSubviews()
removeBorders()
if isSelected {
addBorder(side: indicatorSide, width: indicatorWidth, color: indicatorColorConfiguration.getColor(self))
}
}
}
}