// // Tab.swift // VDS // // Created by Matt Bruce on 5/18/23. // import Foundation import VDSColorTokens import Combine extension Tabs { open class TabItem: 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? { 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 //-------------------------------------------------- // 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) backgroundColor = .clear label.backgroundColor = .clear 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: 44.0) 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.text = text label.textPosition = textPosition label.textStyle = size.textStyle label.textColor = textColorConfiguration.getColor(self) labelWidthConstraint?.isActive = false if let width { labelWidthConstraint = label.widthAnchor.constraint(equalToConstant: width) } else { labelWidthConstraint = label.widthAnchor.constraint(greaterThanOrEqualToConstant: 44.0) } labelWidthConstraint?.isActive = true var leadingSpace: CGFloat if orientation == .horizontal { leadingSpace = 0 } else { leadingSpace = size == .medium ? VDSLayout.Spacing.space4X.value : VDSLayout.Spacing.space6X.value } labelLeadingConstraint?.constant = leadingSpace var otherSpace: CGFloat if orientation == .horizontal { otherSpace = size == .medium ? VDSLayout.Spacing.space3X.value : VDSLayout.Spacing.space4X.value } else { otherSpace = VDSLayout.Spacing.space2X.value } labelTopConstraint?.constant = otherSpace labelBottomConstraint?.constant = -otherSpace setNeedsLayout() } open override func layoutSubviews() { super.layoutSubviews() removeBorders() if selected { var indicator: UIRectEdge = .left if orientation == .horizontal { indicator = indicatorPosition.value } addBorder(side: indicator, width: indicatorWidth, color: indicatorColorConfiguration.getColor(self), offset: 1) } } } }