From 00edeac169f8d5f97cc7896b7a1c5dcf34e68d2e Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 15 Jun 2023 13:21:09 -0500 Subject: [PATCH] initial component just copied badge for ease Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 12 ++ .../BadgeIndicator/BadgeIndicator.swift | 144 ++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 VDS/Components/BadgeIndicator/BadgeIndicator.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index d5893104..1f086e68 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -109,6 +109,7 @@ EAD062A72A3B67770015965D /* UIView+CALayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD062A62A3B67770015965D /* UIView+CALayer.swift */; }; EAD062A92A3B67B10015965D /* NSLayoutAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD062A82A3B67B10015965D /* NSLayoutAnchor.swift */; }; EAD062AB2A3B67D00015965D /* NSLayoutDimension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD062AA2A3B67D00015965D /* NSLayoutDimension.swift */; }; + EAD062B02A3B873E0015965D /* BadgeIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD062AF2A3B873E0015965D /* BadgeIndicator.swift */; }; EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; }; EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9829D4850E00101452 /* Clickable.swift */; }; EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9A29DB1A6000101452 /* Changeable.swift */; }; @@ -243,6 +244,7 @@ EAD062A62A3B67770015965D /* UIView+CALayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+CALayer.swift"; sourceTree = ""; }; EAD062A82A3B67B10015965D /* NSLayoutAnchor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSLayoutAnchor.swift; sourceTree = ""; }; EAD062AA2A3B67D00015965D /* NSLayoutDimension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSLayoutDimension.swift; sourceTree = ""; }; + EAD062AF2A3B873E0015965D /* BadgeIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeIndicator.swift; sourceTree = ""; }; EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Publisher.swift"; sourceTree = ""; }; EAF1FE9829D4850E00101452 /* Clickable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clickable.swift; sourceTree = ""; }; EAF1FE9A29DB1A6000101452 /* Changeable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Changeable.swift; sourceTree = ""; }; @@ -400,6 +402,7 @@ EA33619D288B1E330071C351 /* Components */ = { isa = PBXGroup; children = ( + EAD062AE2A3B87210015965D /* BadgeIndicator */, EA4DB2FE28DCBC1900103EE3 /* Badge */, EA0FC2BE2912D18200DF80B4 /* Buttons */, EAF7F092289985E200B287F5 /* Checkbox */, @@ -695,6 +698,14 @@ path = EntryField; sourceTree = ""; }; + EAD062AE2A3B87210015965D /* BadgeIndicator */ = { + isa = PBXGroup; + children = ( + EAD062AF2A3B873E0015965D /* BadgeIndicator.swift */, + ); + path = BadgeIndicator; + sourceTree = ""; + }; EAF7F092289985E200B287F5 /* Checkbox */ = { isa = PBXGroup; children = ( @@ -927,6 +938,7 @@ EA336171288B19200071C351 /* VDS.docc in Sources */, EA985BF02968A93600F2FF2E /* TitleLockupEyebrowModel.swift in Sources */, EA5E30532950DDA60082B959 /* TitleLockup.swift in Sources */, + EAD062B02A3B873E0015965D /* BadgeIndicator.swift in Sources */, EAA5EEB528ECBFB4003B3210 /* ImageLabelAttribute.swift in Sources */, EAB5FF0129424ACB00998C17 /* UIControl.swift in Sources */, EA985BF5296C60C000F2FF2E /* Icon.swift in Sources */, diff --git a/VDS/Components/BadgeIndicator/BadgeIndicator.swift b/VDS/Components/BadgeIndicator/BadgeIndicator.swift new file mode 100644 index 00000000..68af0d22 --- /dev/null +++ b/VDS/Components/BadgeIndicator/BadgeIndicator.swift @@ -0,0 +1,144 @@ +// +// Badge.swift +// VDS +// +// Created by Matt Bruce on 9/22/22. +// + +import Foundation +import UIKit +import VDSColorTokens +import VDSFormControlsTokens +import Combine + +/// Badges are visual labels used to convey status or highlight supplemental information. +@objc(VDSBadgeIndicator) +open class BadgeIndicator: View { + //-------------------------------------------------- + // MARK: - Enums + //-------------------------------------------------- + public enum FillColor: String, CaseIterable { + case red, yellow, green, orange, blue, gray, grayLowContrast, black, white + } + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + open var label = Label().with { + $0.setContentCompressionResistancePriority(.required, for: .vertical) + $0.adjustsFontSizeToFitWidth = false + $0.lineBreakMode = .byTruncatingTail + $0.textPosition = .left + $0.textStyle = .boldBodySmall + } + + open var fillColor: FillColor = .red { didSet { setNeedsUpdate() }} + + open var text: String = "" { didSet { setNeedsUpdate() }} + + open var maxWidth: CGFloat? { didSet { setNeedsUpdate() }} + + open var numberOfLines: Int = 1 { didSet { setNeedsUpdate() }} + + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + private var maxWidthConstraint: NSLayoutConstraint? + private var minWidthConstraint: NSLayoutConstraint? + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + open override func setup() { + super.setup() + + accessibilityElements = [label] + layer.cornerRadius = 2 + + addSubview(label) + label.pinToSuperView(.init(top: 2, + left: VDSLayout.Spacing.space1X.value, + bottom: 2, + right: VDSLayout.Spacing.space1X.value)) + + maxWidthConstraint = label.widthAnchor.constraint(lessThanOrEqualToConstant: 100) + minWidthConstraint = label.widthAnchor.constraint(greaterThanOrEqualToConstant: 23) + minWidthConstraint?.isActive = true + + } + + open override func reset() { + super.reset() + shouldUpdateView = false + label.reset() + label.lineBreakMode = .byTruncatingTail + label.textPosition = .left + label.textStyle = .boldBodySmall + fillColor = .red + text = "" + maxWidth = nil + numberOfLines = 1 + shouldUpdateView = true + setNeedsUpdate() + } + + //-------------------------------------------------- + // MARK: - Configuration + //-------------------------------------------------- + private var backgroundColorConfiguration: AnyColorable = { + let config = KeyedColorConfiguration(keyPath: \.fillColor) + config.setSurfaceColors(VDSColor.backgroundBrandhighlight, VDSColor.backgroundBrandhighlight, forKey: .red) + config.setSurfaceColors(VDSColor.paletteYellow53, VDSColor.paletteYellow53, forKey: .yellow) + config.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen36, forKey: .green) + config.setSurfaceColors(VDSColor.paletteOrange41, VDSColor.paletteOrange58, forKey: .orange) + config.setSurfaceColors(VDSColor.paletteBlue38, VDSColor.paletteBlue46, forKey: .blue) + config.setSurfaceColors(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryDark, forKey: .black) + config.setSurfaceColors(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryLight, forKey: .white) + return config.eraseToAnyColorable() + }() + + private var textColorConfiguration = ViewColorConfiguration() + + public func updateTextColorConfig() { + textColorConfiguration.reset() + + switch fillColor { + + case .red, .black: + textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: false) + textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: true) + + case .yellow, .white: + textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forDisabled: false) + textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOnlight, forDisabled: true) + + case .orange, .green, .blue: + textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forDisabled: false) + textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forDisabled: true) + } + } + + //-------------------------------------------------- + // MARK: - State + //-------------------------------------------------- + open override func updateView() { + updateTextColorConfig() + + backgroundColor = backgroundColorConfiguration.getColor(self) + + label.textColorConfiguration = textColorConfiguration.eraseToAnyColorable() + label.numberOfLines = numberOfLines + label.text = text + label.surface = surface + label.disabled = disabled + + if let maxWidth = maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth { + maxWidthConstraint?.constant = maxWidth + maxWidthConstraint?.isActive = true + } else { + maxWidthConstraint?.isActive = false + } + } +} +