208 lines
7.2 KiB
Swift
208 lines
7.2 KiB
Swift
//
|
|
// Tooltip.swift
|
|
// VDS
|
|
//
|
|
// Created by Matt Bruce on 4/13/23.
|
|
//
|
|
|
|
import Foundation
|
|
import UIKit
|
|
import VDSColorTokens
|
|
import VDSFormControlsTokens
|
|
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)
|
|
}
|
|
|
|
}
|