vds_ios/VDS/Components/Tabs/Tab.swift
Matt Bruce 3b264d2355 refactored code logic into properties and separate functions
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-06-08 13:51:39 -05:00

181 lines
7.2 KiB
Swift

//
// Tab.swift
// VDS
//
// Created by Matt Bruce on 5/18/23.
//
import Foundation
import VDSColorTokens
import Combine
extension Tabs {
@objc(VDSTab)
open class Tab: View {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
///position of the tab
open var index: Int = 0
///label to write out the text
open var label: Label = Label()
///orientation of the tabs
open var orientation: Tabs.Orientation = .horizontal { didSet { setNeedsUpdate() } }
///Size for tab
open var size: Tabs.Size = .medium { didSet { setNeedsUpdate() } }
///Text position left or center
open var textPosition: TextPosition = .left { didSet { setNeedsUpdate() } }
///Sets the Position of the Selected/Hover Border Accent for All Tabs.
open var indicatorPosition: Tabs.IndicatorPosition = .bottom { didSet { setNeedsUpdate() } }
///An optional callback that is called when this Tab is clicked. Passes parameters (tabIndex).
open var onClick: ((Int) -> Void)? { didSet { setNeedsUpdate() } }
///If provided, it will set fixed width for this Tab.
open var width: CGFloat? { didSet { setNeedsUpdate() } }
///If provided, it will set this Tab to the Active Tab on render.
open var selected: Bool = false { 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() } }
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var labelWidthConstraint: NSLayoutConstraint?
private var labelLeadingConstraint: NSLayoutConstraint?
private var labelTopConstraint: NSLayoutConstraint?
private var labelBottomConstraint: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Configuration
//--------------------------------------------------
private var textColorConfiguration: SurfaceColorConfiguration { selected ? textColorSelectedConfiguration : textColorNonSelectedConfiguration }
private var textColorNonSelectedConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight , VDSColor.elementsSecondaryOnlight)
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 size == .medium ? VDSLayout.Spacing.space4X.value : VDSLayout.Spacing.space6X.value
}
private var otherSpace: CGFloat {
if orientation == .horizontal {
return size == .medium ? VDSLayout.Spacing.space3X.value : VDSLayout.Spacing.space4X.value
} else {
return VDSLayout.Spacing.space2X.value
}
}
private var widthConstraint: NSLayoutConstraint {
if let width, orientation == .vertical {
return label.widthAnchor.constraint(equalToConstant: width)
} else {
return label.widthAnchor.constraint(greaterThanOrEqualToConstant: minWidth)
}
}
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
public override init(frame: CGRect) {
super.init(frame: frame)
}
public required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required public convenience init() {
self.init(frame: .zero)
}
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
open override func setup() {
super.setup()
addSubview(label)
accessibilityTraits = .button
label.translatesAutoresizingMaskIntoConstraints = false
label.pinTrailing()
labelTopConstraint = label.topAnchor.constraint(equalTo: topAnchor)
labelTopConstraint?.isActive = true
labelBottomConstraint = label.bottomAnchor.constraint(equalTo: bottomAnchor)
labelBottomConstraint?.isActive = true
labelLeadingConstraint = label.leadingAnchor.constraint(equalTo: leadingAnchor)
labelLeadingConstraint?.isActive = true
let layoutGuide = UILayoutGuide()
addLayoutGuide(layoutGuide)
labelWidthConstraint = layoutGuide.widthAnchor.constraint(greaterThanOrEqualToConstant: minWidth)
labelWidthConstraint?.isActive = true
//activate the constraints
NSLayoutConstraint.activate([layoutGuide.topAnchor.constraint(equalTo: topAnchor),
layoutGuide.bottomAnchor.constraint(equalTo: bottomAnchor),
layoutGuide.leadingAnchor.constraint(equalTo: leadingAnchor),
layoutGuide.trailingAnchor.constraint(equalTo: trailingAnchor)])
publisher(for: UITapGestureRecognizer())
.sink { [weak self] _ in
guard let self else { return }
self.onClick?(self.index)
}.store(in: &subscribers)
}
open override func updateView() {
//label properties
label.text = text
label.textPosition = textPosition
label.textStyle = size.textStyle
label.textColor = textColorConfiguration.getColor(self)
//constaints
labelWidthConstraint?.isActive = false
labelWidthConstraint = widthConstraint
labelWidthConstraint?.isActive = true
labelLeadingConstraint?.constant = leadingSpace
labelTopConstraint?.constant = otherSpace
labelBottomConstraint?.constant = -otherSpace
setNeedsLayout()
}
open override func layoutSubviews() {
super.layoutSubviews()
removeBorders()
if selected {
addBorder(side: indicatorSide, width: indicatorWidth, color: indicatorColorConfiguration.getColor(self))
}
}
}
}