diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 4661e23d..e3948c92 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -92,6 +92,7 @@ EAA5EEEF28F5C908003B3210 /* VDSTypographyTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEEC28F5C908003B3210 /* VDSTypographyTokens.xcframework */; }; EAA5EEF128F5C909003B3210 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */; }; EAA5EEF328F5C909003B3210 /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */; }; + EAA7456C2AB23E2000C1841F /* TooltipModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA7456B2AB23E2000C1841F /* TooltipModel.swift */; }; EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */; }; EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CC28ABE76000DAE764 /* Withable.swift */; }; EAB1D2CF28ABEF2B00DAE764 /* Typography+Base.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CE28ABEF2B00DAE764 /* Typography+Base.swift */; }; @@ -240,6 +241,7 @@ EAA5EEEC28F5C908003B3210 /* VDSTypographyTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSTypographyTokens.xcframework; path = ../SharedFrameworks/VDSTypographyTokens.xcframework; sourceTree = ""; }; EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; + EAA7456B2AB23E2000C1841F /* TooltipModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipModel.swift; sourceTree = ""; }; EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = ""; }; EAB1D2CC28ABE76000DAE764 /* Withable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Withable.swift; sourceTree = ""; }; EAB1D2CE28ABEF2B00DAE764 /* Typography+Base.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Typography+Base.swift"; sourceTree = ""; }; @@ -691,6 +693,7 @@ children = ( EAB2375C29E8789100AABE9A /* Tooltip.swift */, EA8E40922A82889500934ED3 /* TooltipDialog.swift */, + EAA7456B2AB23E2000C1841F /* TooltipModel.swift */, EAB2376729E9992800AABE9A /* TooltipAlertViewController.swift */, EAB2376929E9E59100AABE9A /* TooltipLaunchable.swift */, EAB2376129E9880400AABE9A /* TrailingTooltipLabel.swift */, @@ -979,6 +982,7 @@ EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */, EAD0688E2A55F819002E3A2D /* Loader.swift in Sources */, EAB5FEF829393A7200998C17 /* ButtonGroupConstants.swift in Sources */, + EAA7456C2AB23E2000C1841F /* TooltipModel.swift in Sources */, EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */, EA513A952A4E1F82002A4DFF /* TitleLockupStyleConfiguration.swift in Sources */, 44604AD729CE196600E62B51 /* Line.swift in Sources */, diff --git a/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift b/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift index 26867749..a98024b1 100644 --- a/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift @@ -20,10 +20,7 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable /// Current Surface and this is used to pass down to child objects that implement Surfacable public var surface: Surface = .light public var accessibleText: String? = "Tool Tip" - public var closeButtonText: String = "Close" - public var title: String? - public var content: String? - public var contentView: UIView? + public var model: Tooltip.TooltipModel public var presenter: UIView? public func setAttribute(on attributedString: NSMutableAttributedString) { @@ -67,27 +64,24 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable attributedString.append(NSAttributedString(attachment: tooltip)) addHandler(on: attributedString) } - - public init(id: UUID = UUID(), subscriber: AnyCancellable? = nil, surface: Surface, accessibleText: String? = nil, closeButtonText: String = "Close", title: String? = nil, content: String? = nil, contentView: UIView? = nil, presenter: UIView? = nil) { + + public init(id: UUID = UUID(), + subscriber: AnyCancellable? = nil, + accessibleText: String? = nil, + surface: Surface = .light, + model: Tooltip.TooltipModel, + presenter: UIView? = nil) { self.id = id self.subscriber = subscriber self.surface = surface + self.model = model self.accessibleText = accessibleText - self.closeButtonText = closeButtonText - self.title = title - self.content = content - self.contentView = contentView self.presenter = presenter //create the tooltip click event self.subscriber = action.sink { [weak self] in guard let self else { return } - self.presentTooltip(surface: self.surface, - title: self.title, - content: self.content, - contentView: contentView, - closeButtonText: self.closeButtonText, - presenter: self.presenter) + self.presentTooltip(surface: surface, model: model, presenter: self.presenter) } } diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index f62e0694..aaf03256 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -124,7 +124,6 @@ open class EntryFieldBase: Control, Changeable { $0.textStyle = .bodySmall } - open var tooltipView: UIView? open var icon: Icon = Icon().with { $0.size = .small } @@ -149,12 +148,8 @@ open class EntryFieldBase: Control, Changeable { open var errorText: String? { didSet { setNeedsUpdate() } } - open var tooltipTitle: String? { didSet { setNeedsUpdate() } } - - open var tooltipContent: String? { didSet { setNeedsUpdate() } } - - open var tooltipContentView: UIView? { didSet { setNeedsUpdate() } } - + open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } } + open var transparentBackground: Bool = false { didSet { setNeedsUpdate() } } open var width: CGFloat? { didSet { setNeedsUpdate() } } @@ -244,8 +239,7 @@ open class EntryFieldBase: Control, Changeable { helperText = nil showError = false errorText = nil - tooltipTitle = nil - tooltipContent = nil + tooltipModel = nil transparentBackground = false width = nil maxLength = nil @@ -299,8 +293,8 @@ open class EntryFieldBase: Control, Changeable { updatedLabelText = "\(oldText) Optional" } - if let tooltipTitle, let tooltipContent { - attributes.append(TooltipLabelAttribute(surface: surface, title: tooltipTitle, content: tooltipContent, contentView: tooltipContentView, presenter: self)) + if let tooltipModel { + attributes.append(TooltipLabelAttribute(surface: surface, model: tooltipModel, presenter: self)) } //set the titleLabel diff --git a/VDS/Components/Tooltip/Tooltip.swift b/VDS/Components/Tooltip/Tooltip.swift index 679c260e..1333cc7e 100644 --- a/VDS/Components/Tooltip/Tooltip.swift +++ b/VDS/Components/Tooltip/Tooltip.swift @@ -134,10 +134,10 @@ open class Tooltip: Control, TooltipLaunchable { .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, + model: .init(closeButtonText: tooltip.closeButtonText, + title: tooltip.title, + content: tooltip.content, + contentView: tooltip.contentView), presenter: self) }) } diff --git a/VDS/Components/Tooltip/TooltipAlertViewController.swift b/VDS/Components/Tooltip/TooltipAlertViewController.swift index 29a9aba7..b71d92cf 100644 --- a/VDS/Components/Tooltip/TooltipAlertViewController.swift +++ b/VDS/Components/Tooltip/TooltipAlertViewController.swift @@ -33,10 +33,7 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { //-------------------------------------------------- /// Current Surface and this is used to pass down to child objects that implement Surfacable open var surface: Surface = .light { didSet { updateView() }} - open var titleText: String? { didSet { updateView() }} - open var contentText: String? { didSet { updateView() }} - open var contentView: UIView? { didSet { updateView() }} - open var closeButtonText: String = "Close" { didSet { updateView() }} + open var model = Tooltip.TooltipModel() { didSet { updateView() }} open var presenter: UIView? { didSet { updateView() }} //-------------------------------------------------- @@ -106,9 +103,6 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { open func updateView() { view.backgroundColor = backgroundColorConfiguration.getColor(self).withAlphaComponent(0.3) tooltipDialog.surface = surface - tooltipDialog.titleText = titleText - tooltipDialog.contentText = contentText - tooltipDialog.contentView = contentView - tooltipDialog.closeButtonText = closeButtonText + tooltipDialog.model = model } } diff --git a/VDS/Components/Tooltip/TooltipDialog.swift b/VDS/Components/Tooltip/TooltipDialog.swift index b79c453e..fa15325e 100644 --- a/VDS/Components/Tooltip/TooltipDialog.swift +++ b/VDS/Components/Tooltip/TooltipDialog.swift @@ -55,22 +55,18 @@ open class TooltipDialog: View, UIScrollViewDelegate { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - open var titleText: String? { didSet { setNeedsUpdate() } } + open var model = Tooltip.TooltipModel() { didSet { setNeedsUpdate() } } + open var titleLabel = Label().with { label in label.isAccessibilityElement = true label.textStyle = .boldTitleMedium } - open var contentText: String? { didSet { setNeedsUpdate() } } open var contentLabel = Label().with { label in label.isAccessibilityElement = true label.textStyle = .bodyLarge } - - open var contentView: UIView? = nil - - open var closeButtonText: String = "Close" { didSet { setNeedsUpdate() } } - + open lazy var closeButton: UIButton = { let button = UIButton(type: .system) button.isAccessibilityElement = true @@ -146,7 +142,7 @@ open class TooltipDialog: View, UIScrollViewDelegate { /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() - + backgroundColor = backgroundColorConfiguration.getColor(self) scrollView.indicatorStyle = surface == .light ? .black : .white @@ -156,24 +152,24 @@ open class TooltipDialog: View, UIScrollViewDelegate { contentLabel.surface = surface line.surface = surface - titleLabel.text = titleText - contentLabel.text = contentText + titleLabel.text = model.title + contentLabel.text = model.content titleLabel.sizeToFit() contentLabel.sizeToFit() var addedTitle = false - if let titleText, !titleText.isEmpty { + if let titleText = model.title, !titleText.isEmpty { contentStackView.addArrangedSubview(titleLabel) addedTitle = true } var addedContent = false - if let contentText, !contentText.isEmpty { + if let contentText = model.content, !contentText.isEmpty { contentStackView.addArrangedSubview(contentLabel) addedContent = true - } else if let contentView { + } else if let contentView = model.contentView { contentView.translatesAutoresizingMaskIntoConstraints = false if var surfaceable = contentView as? Surfaceable { surfaceable.surface = surface @@ -189,8 +185,8 @@ open class TooltipDialog: View, UIScrollViewDelegate { let closeButtonTextColor = closeButtonTextColorConfiguration.getColor(self) closeButton.setTitleColor(closeButtonTextColor, for: .normal) closeButton.setTitleColor(closeButtonTextColor, for: .highlighted) - closeButton.setTitle(closeButtonText, for: .normal) - closeButton.accessibilityLabel = closeButtonText + closeButton.setTitle(model.closeButtonText, for: .normal) + closeButton.accessibilityLabel = model.closeButtonText contentStackView.setNeedsLayout() contentStackView.layoutIfNeeded() @@ -224,7 +220,7 @@ open class TooltipDialog: View, UIScrollViewDelegate { open override func updateAccessibility() { super.updateAccessibility() - primaryAccessibilityElement.accessibilityHint = "Double tap on the \(closeButtonText) button to close." + primaryAccessibilityElement.accessibilityHint = "Double tap on the \(model.closeButtonText) button to close." var elements: [Any] = [primaryAccessibilityElement] contentStackView.arrangedSubviews.forEach{ elements.append($0) } diff --git a/VDS/Components/Tooltip/TooltipLaunchable.swift b/VDS/Components/Tooltip/TooltipLaunchable.swift index 33d09b16..514110b4 100644 --- a/VDS/Components/Tooltip/TooltipLaunchable.swift +++ b/VDS/Components/Tooltip/TooltipLaunchable.swift @@ -9,18 +9,15 @@ import Foundation import UIKit public protocol TooltipLaunchable { - func presentTooltip(surface: Surface, title: String?, content: String?, contentView: UIView?, closeButtonText: String, presenter: UIView?) + func presentTooltip(surface: Surface, model: Tooltip.TooltipModel, presenter: UIView?) } extension TooltipLaunchable { - public func presentTooltip(surface: Surface, title: String?, content: String?, contentView: UIView? = nil, closeButtonText: String = "Close", presenter: UIView? = nil) { + public func presentTooltip(surface: Surface, model: Tooltip.TooltipModel, presenter: UIView? = nil) { if let presenting = UIApplication.topViewController() { let tooltipViewController = TooltipAlertViewController(nibName: nil, bundle: nil).with { $0.surface = surface - $0.titleText = title - $0.contentText = content - $0.contentView = contentView - $0.closeButtonText = closeButtonText + $0.model = model $0.presenter = presenter $0.modalPresentationStyle = .overCurrentContext $0.modalTransitionStyle = .crossDissolve diff --git a/VDS/Components/Tooltip/TooltipModel.swift b/VDS/Components/Tooltip/TooltipModel.swift new file mode 100644 index 00000000..ee690d74 --- /dev/null +++ b/VDS/Components/Tooltip/TooltipModel.swift @@ -0,0 +1,33 @@ +// +// TooltipModel.swift +// VDS +// +// Created by Matt Bruce on 9/13/23. +// + +import Foundation +import UIKit + +extension Tooltip { + + /// Model used to represent the tooltip. + public struct TooltipModel { + /// Current Surface and this is used to pass down to child objects that implement Surfacable + public var closeButtonText: String + public var title: String? + public var content: String? + public var contentView: UIView? + public var contentViewAlignment: UIStackView.Alignment? + public init(closeButtonText: String = "Close", + title: String? = nil, + content: String? = nil, + contentView: UIView? = nil, + contentViewAlignment: UIStackView.Alignment = .leading) { + self.closeButtonText = closeButtonText + self.title = title + self.content = content + self.contentView = contentView + self.contentViewAlignment = contentViewAlignment + } + } +} diff --git a/VDS/Components/Tooltip/TrailingTooltipLabel.swift b/VDS/Components/Tooltip/TrailingTooltipLabel.swift index 4ea09a90..f4701b24 100644 --- a/VDS/Components/Tooltip/TrailingTooltipLabel.swift +++ b/VDS/Components/Tooltip/TrailingTooltipLabel.swift @@ -29,11 +29,6 @@ open class TrailingTooltipLabel: View, TooltipLaunchable { super.init(coder: coder) } - //-------------------------------------------------- - // MARK: - Private Properties - //-------------------------------------------------- - private let tooltipAction = PassthroughSubject() - //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @@ -58,16 +53,7 @@ open class TrailingTooltipLabel: View, TooltipLaunchable { }() { didSet { setNeedsUpdate() } } /// Will render the text for Close button for tooltip dialog when on mobile devices - open var tooltipCloseButtonText: String = "Close" { didSet { setNeedsUpdate() } } - - /// Text rendered for the title of the tooltip - open var tooltipTitle: String? { didSet { setNeedsUpdate() } } - - /// Text rendered for the content of the tooltip - open var tooltipContent: String? { didSet { setNeedsUpdate() } } - - /// UIView rendered for the content area of the tooltip - open var tooltipContentView: UIView? { didSet { setNeedsUpdate() } } + open var tooltipModel: Tooltip.TooltipModel? { didSet { setNeedsUpdate() } } //-------------------------------------------------- // MARK: - Overrides @@ -79,15 +65,6 @@ open class TrailingTooltipLabel: View, TooltipLaunchable { addSubview(label) label.pinToSuperView() - //create the tooltip click event - tooltipAction.sink { [weak self] in - guard let self else { return } - self.presentTooltip(surface: self.surface, - title: self.tooltipTitle, - content: self.tooltipContent, - closeButtonText: self.tooltipCloseButtonText, - presenter: self) - }.store(in: &subscribers) } /// Used to make changes to the View based off a change events or from local properties. @@ -102,8 +79,8 @@ open class TrailingTooltipLabel: View, TooltipLaunchable { label.isEnabled = isEnabled //add tooltip - if let labelText, !labelText.isEmpty { - label.addTooltip(model: .init(surface: surface, closeButtonText: tooltipCloseButtonText, title: tooltipTitle, content: tooltipContent, contentView: tooltipContentView)) + if let labelText, let tooltipModel, !labelText.isEmpty { + label.addTooltip(model: tooltipModel) } } @@ -115,9 +92,7 @@ open class TrailingTooltipLabel: View, TooltipLaunchable { labelAttributes = nil labelTextStyle = .defaultStyle labelTextAlignment = .left - tooltipCloseButtonText = "Close" - tooltipTitle = "" - tooltipContent = "" + tooltipModel = nil shouldUpdateView = true setNeedsUpdate() } @@ -125,25 +100,9 @@ open class TrailingTooltipLabel: View, TooltipLaunchable { extension Label { - /// Model used to represent the tooltip. - public struct TooltipModel { - /// Current Surface and this is used to pass down to child objects that implement Surfacable - public var surface: Surface - public var closeButtonText: String - public var title: String? - public var content: String? - public var contentView: UIView? - public init(surface: Surface = .light, closeButtonText: String = "Close", title: String?, content: String?, contentView: UIView?) { - self.surface = surface - self.closeButtonText = closeButtonText - self.title = title - self.content = content - self.contentView = contentView - } - } /// Helper to add a tool tip attribute to an existing label. - public func addTooltip(model: TooltipModel) { + public func addTooltip(model: Tooltip.TooltipModel) { var newAttributes: [any LabelAttributeModel] = [] if let attributes { @@ -155,12 +114,7 @@ extension Label { } if let text = text, !text.isEmpty { - let tooltip = TooltipLabelAttribute(surface: surface, - closeButtonText: model.closeButtonText, - title: model.title, - content: model.content, - contentView: model.contentView, - presenter: self) + let tooltip = TooltipLabelAttribute(surface: surface, model: model, presenter: self) newAttributes.append(tooltip) }