193 lines
6.6 KiB
Swift
193 lines
6.6 KiB
Swift
//
|
|
// Tooltip.swift
|
|
// VDS
|
|
//
|
|
// Created by Matt Bruce on 4/13/23.
|
|
//
|
|
|
|
import Foundation
|
|
import UIKit
|
|
import VDSColorTokens
|
|
import VDSFormControlsTokens
|
|
import Combine
|
|
|
|
@objc(VDSTooltip)
|
|
open class Tooltip: Control, TooltipLaunchable {
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - Enums
|
|
//--------------------------------------------------
|
|
public enum FillColor: String, CaseIterable {
|
|
case primary, secondary, brandHighlight
|
|
}
|
|
|
|
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
|
|
//--------------------------------------------------
|
|
open var imageView = UIImageView().with {
|
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
|
$0.contentMode = .scaleAspectFill
|
|
$0.clipsToBounds = true
|
|
}
|
|
|
|
open var closeButtonText: String = "Close" { didSet { setNeedsUpdate() }}
|
|
|
|
open var fillColor: FillColor = .primary { didSet { setNeedsUpdate() }}
|
|
|
|
open var size: Size = .medium { didSet { setNeedsUpdate() }}
|
|
|
|
open var title: String? { didSet { setNeedsUpdate() }}
|
|
|
|
open var content: String? { didSet { setNeedsUpdate() }}
|
|
|
|
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: - 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: - Lifecycle
|
|
//--------------------------------------------------
|
|
|
|
open override func setup() {
|
|
super.setup()
|
|
|
|
if let image = BundleManager.shared.image(for: "info") {
|
|
infoImage = image
|
|
}
|
|
|
|
addSubview(imageView)
|
|
|
|
imageView.pinToSuperView()
|
|
heightConstraint = imageView.heightAnchor.constraint(equalToConstant: size.value.dimensions.height)
|
|
heightConstraint?.isActive = true
|
|
widthConstraint = imageView.widthAnchor.constraint(equalToConstant: size.value.dimensions.width)
|
|
widthConstraint?.isActive = true
|
|
|
|
backgroundColor = .clear
|
|
|
|
isAccessibilityElement = true
|
|
accessibilityTraits = .link
|
|
|
|
onClickSubscriber = publisher(for: .touchUpInside)
|
|
.sink(receiveValue: { [weak self] tooltip in
|
|
guard let self else { return}
|
|
self.presentTooltip(surface: tooltip.surface,
|
|
title: tooltip.title,
|
|
content: tooltip.content,
|
|
contentView: tooltip.contentView,
|
|
closeButtonText: tooltip.closeButtonText)
|
|
})
|
|
}
|
|
|
|
open override func reset() {
|
|
super.reset()
|
|
shouldUpdateView = false
|
|
size = .medium
|
|
title = ""
|
|
content = ""
|
|
fillColor = .primary
|
|
closeButtonText = "Close"
|
|
imageView.image = nil
|
|
shouldUpdateView = true
|
|
setNeedsUpdate()
|
|
}
|
|
|
|
open override func updateView() {
|
|
super.updateView()
|
|
|
|
//set the dimensions
|
|
let dimensions = size.value.dimensions
|
|
heightConstraint?.constant = dimensions.height
|
|
widthConstraint?.constant = dimensions.width
|
|
|
|
//get the color for the image
|
|
let imageColor = iconColorConfiguration.getColor(self)
|
|
imageView.image = infoImage.withTintColor(imageColor)
|
|
}
|
|
|
|
open override func updateAccessibilityLabel() {
|
|
if !accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
|
accessibilityTraits.insert(.notEnabled)
|
|
} else {
|
|
accessibilityTraits.remove(.notEnabled)
|
|
}
|
|
var label = title
|
|
if label == nil {
|
|
label = content
|
|
}
|
|
if let label {
|
|
accessibilityLabel = "Tooltip: \(label)"
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: AppleGuidlinesTouchable
|
|
extension Tooltip: AppleGuidlinesTouchable {
|
|
|
|
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
|
Self.acceptablyOutsideBounds(point: point, bounds: bounds)
|
|
}
|
|
|
|
}
|