vds_ios/VDS/Components/Tooltip/Tooltip.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

207 lines
7.2 KiB
Swift

//
// Tooltip.swift
// VDS
//
// Created by Matt Bruce on 4/13/23.
//
import Foundation
import UIKit
import VDSTokens
import Combine
/// A tooltip is an overlay that clarifies another component or content
/// element. It is triggered when a customer hovers, clicks or taps
/// the tooltip icon.
@objc(VDSTooltip)
open class Tooltip: Control, TooltipLaunchable {
//--------------------------------------------------
// 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: - Enums
//--------------------------------------------------
/// Enum used to describe the color of the tooltip.
public enum FillColor: String, CaseIterable {
case primary, secondary, brandHighlight
}
/// Enum used to describe the size of the icon.
public enum Size: String, EnumSubset {
case small
case medium
public var defaultValue: Icon.Size { .small }
}
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var widthConstraint: NSLayoutConstraint?
private var heightConstraint: NSLayoutConstraint?
private var infoImage = UIImage()
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
/// Icon used to render the tooltip image.
open var icon = Icon().with {
$0.name = .info
$0.size = .small
$0.isUserInteractionEnabled = false
}
/// Will render the text for Close button for tooltip dialog when on mobile devices
open var closeButtonText: String = "Close" { didSet { setNeedsUpdate() } }
/// Will render icon in brand colors.
open var fillColor: FillColor = .primary { didSet { setNeedsUpdate() } }
/// Size of the icon
open var size: Size = .medium { didSet { setNeedsUpdate() } }
/// Text rendered for the title of the tooltip
open var title: String? { didSet { setNeedsUpdate() } }
/// Text rendered for the content of the tooltip
open var content: String? { didSet { setNeedsUpdate() } }
/// UIView rendered for the content area of the tooltip
open var contentView: UIView? { didSet { setNeedsUpdate() } }
//--------------------------------------------------
// MARK: - Configuration
//--------------------------------------------------
private var iconColorConfiguration: AnyColorable {
switch fillColor {
case .primary:
return primaryColorConfiguration.eraseToAnyColorable()
case .secondary:
return secondaryColorConfiguration.eraseToAnyColorable()
case .brandHighlight:
return brandHighlightColorConfiguration.eraseToAnyColorable()
}
}
private var primaryColorConfiguration = ControlColorConfiguration().with {
$0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal)
$0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted)
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled)
}
private var secondaryColorConfiguration = ControlColorConfiguration().with {
$0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forState: .normal)
$0.setSurfaceColors(VDSColor.paletteGray65, VDSColor.paletteGray65, forState: .highlighted)
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled)
}
private var brandHighlightColorConfiguration = ControlColorConfiguration().with {
$0.setSurfaceColors(VDSColor.elementsBrandhighlight, VDSColor.elementsBrandhighlight, forState: .normal)
$0.setSurfaceColors(VDSColor.elementsBrandhighlight, VDSColor.elementsBrandhighlight, forState: .highlighted)
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled)
}
//--------------------------------------------------
// 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()
addSubview(icon)
icon.pinToSuperView()
backgroundColor = .clear
isAccessibilityElement = true
accessibilityTraits = .button
onClick = { [weak self] tooltip in
guard let self else { return}
self.presentTooltip(surface: tooltip.surface,
tooltipModel: .init(closeButtonText: tooltip.closeButtonText,
title: tooltip.title,
content: tooltip.content,
contentView: tooltip.contentView),
presenter: self)
}
}
/// Resets to default settings.
open override func reset() {
super.reset()
shouldUpdateView = false
size = .medium
title = ""
content = ""
fillColor = .primary
closeButtonText = "Close"
shouldUpdateView = true
setNeedsUpdate()
}
/// Used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
//get the size
icon.size = size.value
//get the color for the image
icon.color = iconColorConfiguration.getColor(self)
}
/// Used to update any Accessibility properties.
open override func updateAccessibility() {
super.updateAccessibility()
var label = title
if label == nil {
label = content
}
if let label, !label.isEmpty {
accessibilityLabel = label
} else {
accessibilityLabel = "Modal"
}
accessibilityHint = isEnabled ? "Double tap to open." : ""
}
public static func accessibleText(for title: String?, content: String?, closeButtonText: String) -> String {
var label = ""
if let title {
label = title
}
if let content {
if !label.isEmpty {
label += ","
}
label += content
}
return label
}
}
// MARK: AppleGuidelinesTouchable
extension Tooltip: AppleGuidelinesTouchable {
/// Overrides to ensure that the touch point meets a minimum of the minimumTappableArea.
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
Self.acceptablyOutsideBounds(point: point, bounds: bounds)
}
}