updated badgeIndicator to final for this release for now

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2023-06-15 18:51:22 -05:00
parent e276087d21
commit 3dc9fdfbb7

View File

@ -10,6 +10,7 @@ import UIKit
import VDSColorTokens import VDSColorTokens
import VDSFormControlsTokens import VDSFormControlsTokens
import Combine import Combine
import VDSTypographyTokens
/// Badges are visual labels used to convey status or highlight supplemental information. /// Badges are visual labels used to convey status or highlight supplemental information.
@objc(VDSBadgeIndicator) @objc(VDSBadgeIndicator)
@ -21,6 +22,104 @@ open class BadgeIndicator: View {
case red, yellow, green, orange, blue, gray, grayLowContrast, black, white case red, yellow, green, orange, blue, gray, grayLowContrast, black, white
} }
public enum Kind: String, CaseIterable {
case simple, numbered
}
public enum MaxDigits: String, CaseIterable {
case one
case two
case three
case four
case five
case six
public var value: Int {
switch self {
case .two:
return 2
case .one:
return 1
case .three:
return 3
case .four:
return 4
case .five:
return 5
case .six:
return 6
}
}
}
public enum TextSize: String, CaseIterable {
case xxlarge = "2XLarge"
case xlarge = "XLarge"
case large = "Large"
case medium = "Medium"
case small = "Small"
public var minimumSize: CGFloat {
switch self {
case .xxlarge:
return 29
case .xlarge:
return 24
case .large:
return 20
case .medium:
return 18
case .small:
return 16
}
}
public var padding: CGFloat {
switch self {
case .xxlarge:
return 8
case .xlarge:
return 6
case .large:
return 6
case .medium:
return 6
case .small:
return 4
}
}
public var textStyle: TextStyle {
let style = TextStyle.bodySmall
var pointSize: CGFloat = VDSTypography.fontSizeBody12
switch self {
case .xxlarge:
pointSize = VDSTypography.fontSizeTitle24
case .xlarge:
pointSize = VDSTypography.fontSizeTitle20
case .large:
pointSize = VDSTypography.fontSizeBody16
case .medium:
pointSize = VDSTypography.fontSizeBody14
case .small:
pointSize = VDSTypography.fontSizeBody12
}
return TextStyle(rawValue: "\(self.rawValue)BadgeIndicator",
fontFace: style.fontFace,
pointSize: pointSize,
lineHeight: style.lineHeight,
letterSpacing: style.letterSpacing)
}
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
@ -28,24 +127,54 @@ open class BadgeIndicator: View {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.adjustsFontSizeToFitWidth = false $0.adjustsFontSizeToFitWidth = false
$0.lineBreakMode = .byTruncatingTail $0.lineBreakMode = .byTruncatingTail
$0.textPosition = .left $0.textPosition = .center
$0.textStyle = .boldBodySmall $0.numberOfLines = 1
}
open var borderColorLight: UIColor? {
didSet {
if let borderColorLight {
borderColorConfiguration.lightColor = borderColorLight
} else {
borderColorConfiguration.lightColor = VDSColor.paletteWhite
}
setNeedsUpdate()
}
}
open var borderColorDark: UIColor? {
didSet {
if let borderColorDark {
borderColorConfiguration.darkColor = borderColorDark
} else {
borderColorConfiguration.darkColor = VDSColor.paletteBlack
}
setNeedsUpdate()
}
} }
open var fillColor: FillColor = .red { didSet { setNeedsUpdate() }} open var fillColor: FillColor = .red { didSet { setNeedsUpdate() }}
open var text: String = "" { didSet { setNeedsUpdate() }} open var number: Int? { didSet { setNeedsUpdate() }}
open var maxWidth: CGFloat? { didSet { setNeedsUpdate() }} open var kind: Kind = .simple { didSet { setNeedsUpdate() }}
open var numberOfLines: Int = 1 { didSet { setNeedsUpdate() }} open var leadingCharacter: String? { didSet { setNeedsUpdate() }}
open var textSize: TextSize = .xxlarge { didSet { setNeedsUpdate() }}
open var hideDot: Bool = false { didSet { setNeedsUpdate() }}
open var hideBorder: Bool = false { didSet { setNeedsUpdate() }}
open var maxDigits: MaxDigits = .two { didSet { setNeedsUpdate() }}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Constraints // MARK: - Constraints
//-------------------------------------------------- //--------------------------------------------------
private var maxWidthConstraint: NSLayoutConstraint? private var labelWidthConstraint: NSLayoutConstraint?
private var minWidthConstraint: NSLayoutConstraint? private var labelHeightConstraint: NSLayoutConstraint?
private var defaultBadgeSize: CGFloat = 16
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
@ -54,18 +183,17 @@ open class BadgeIndicator: View {
super.setup() super.setup()
accessibilityElements = [label] accessibilityElements = [label]
layer.cornerRadius = 2
addSubview(label) addSubview(label)
label.pinToSuperView(.init(top: 2, label.pinToSuperView()
left: VDSLayout.Spacing.space1X.value,
bottom: 2,
right: VDSLayout.Spacing.space1X.value))
maxWidthConstraint = label.widthAnchor.constraint(lessThanOrEqualToConstant: 100) NSLayoutConstraint.activate([
minWidthConstraint = label.widthAnchor.constraint(greaterThanOrEqualToConstant: 23) label.centerXAnchor.constraint(equalTo: centerXAnchor),
minWidthConstraint?.isActive = true label.centerYAnchor.constraint(equalTo: centerYAnchor)
])
labelWidthConstraint = label.widthGreaterThanEqualTo(constant: defaultBadgeSize).activate()
labelHeightConstraint = label.heightGreaterThanEqualTo(constant: defaultBadgeSize).activate()
} }
open override func reset() { open override func reset() {
@ -73,12 +201,9 @@ open class BadgeIndicator: View {
shouldUpdateView = false shouldUpdateView = false
label.reset() label.reset()
label.lineBreakMode = .byTruncatingTail label.lineBreakMode = .byTruncatingTail
label.textPosition = .left label.textPosition = .center
label.textStyle = .boldBodySmall
fillColor = .red fillColor = .red
text = "" number = nil
maxWidth = nil
numberOfLines = 1
shouldUpdateView = true shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }
@ -86,6 +211,8 @@ open class BadgeIndicator: View {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Configuration // MARK: - Configuration
//-------------------------------------------------- //--------------------------------------------------
private var borderColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteBlack)
private var backgroundColorConfiguration: AnyColorable = { private var backgroundColorConfiguration: AnyColorable = {
let config = KeyedColorConfiguration<BadgeIndicator, FillColor>(keyPath: \.fillColor) let config = KeyedColorConfiguration<BadgeIndicator, FillColor>(keyPath: \.fillColor)
config.setSurfaceColors(VDSColor.backgroundBrandhighlight, VDSColor.backgroundBrandhighlight, forKey: .red) config.setSurfaceColors(VDSColor.backgroundBrandhighlight, VDSColor.backgroundBrandhighlight, forKey: .red)
@ -93,6 +220,8 @@ open class BadgeIndicator: View {
config.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen36, forKey: .green) config.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen36, forKey: .green)
config.setSurfaceColors(VDSColor.paletteOrange41, VDSColor.paletteOrange58, forKey: .orange) config.setSurfaceColors(VDSColor.paletteOrange41, VDSColor.paletteOrange58, forKey: .orange)
config.setSurfaceColors(VDSColor.paletteBlue38, VDSColor.paletteBlue46, forKey: .blue) config.setSurfaceColors(VDSColor.paletteBlue38, VDSColor.paletteBlue46, forKey: .blue)
config.setSurfaceColors(VDSColor.paletteGray44, VDSColor.paletteGray65, forKey: .gray)
config.setSurfaceColors(VDSColor.paletteGray85, VDSColor.paletteGray20, forKey: .grayLowContrast)
config.setSurfaceColors(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryDark, forKey: .black) config.setSurfaceColors(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryDark, forKey: .black)
config.setSurfaceColors(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryLight, forKey: .white) config.setSurfaceColors(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryLight, forKey: .white)
return config.eraseToAnyColorable() return config.eraseToAnyColorable()
@ -105,7 +234,7 @@ open class BadgeIndicator: View {
switch fillColor { switch fillColor {
case .red, .black: case .red, .black, .gray, .grayLowContrast:
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: false) textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: false)
textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: true) textColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOndark, forDisabled: true)
@ -127,17 +256,70 @@ open class BadgeIndicator: View {
backgroundColor = backgroundColorConfiguration.getColor(self) backgroundColor = backgroundColorConfiguration.getColor(self)
label.useAttributedText = true
label.edgeInset = .init(top: 0, left: textSize.padding, bottom: 0, right: textSize.padding)
label.font = textSize.textStyle.font
label.textColor = textColorConfiguration.getColor(self)
label.textColorConfiguration = textColorConfiguration.eraseToAnyColorable() label.textColorConfiguration = textColorConfiguration.eraseToAnyColorable()
label.numberOfLines = numberOfLines label.text = getText()
label.text = text
label.surface = surface label.surface = surface
label.disabled = disabled label.disabled = disabled
label.sizeToFit()
setNeedsLayout()
layoutIfNeeded()
}
if let maxWidth = maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth { private func getText() -> String {
maxWidthConstraint?.constant = maxWidth let badgeCount = number ?? 0
maxWidthConstraint?.isActive = true var text: String = ""
if kind == .numbered {
let maxBadgetCount = limitDigits(number: badgeCount, maxDigits: maxDigits.value)
text = "\(maxBadgetCount)"
if maxDigits.value < "\(badgeCount)".count {
text = "\(maxBadgetCount)+"
}
if let leadingCharacter {
text = "\(leadingCharacter)\(text)"
} else {
text = "\(text)"
}
}
return text
}
private func limitDigits(number: Int, maxDigits: Int) -> Int {
let maxNumber = Int(pow(10.0, Double(maxDigits))) - 1
return min(number, maxNumber)
}
open override func layoutSubviews() {
super.layoutSubviews()
labelWidthConstraint?.constant = textSize.minimumSize
labelHeightConstraint?.constant = textSize.minimumSize
layer.cornerRadius = frame.size.height / 2
if hideBorder {
layer.borderWidth = 0
} else { } else {
maxWidthConstraint?.isActive = false layer.borderColor = borderColorConfiguration.getColor(surface).cgColor
layer.borderWidth = 1
}
layer.remove(layerName: "dot")
if kind == .simple && !hideDot {
let dotSize: CGFloat = bounds.width * 0.1875
let dotLayer = CAShapeLayer()
dotLayer.name = "dot"
let centerX = (bounds.width - dotSize) / 2.0
let centerY = (bounds.width - dotSize) / 2.0
dotLayer.frame = .init(x: centerX, y: centerY, width: dotSize, height: dotSize)
dotLayer.path = UIBezierPath(ovalIn: .init(origin: .zero, size: .init(width: dotSize, height: dotSize))).cgPath
dotLayer.fillColor = textColorConfiguration.getColor(self).cgColor
layer.addSublayer(dotLayer)
} }
} }
} }