diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 60866898..81a082b9 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -1111,7 +1111,7 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 26; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1148,7 +1148,7 @@ BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 26; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; diff --git a/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift b/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift index b061d747..067d70ed 100644 --- a/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/TooltipLabelAttribute.swift @@ -20,8 +20,9 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable 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 title: String? + public var content: String? + public var contentView: UIView? public func setAttribute(on attributedString: NSMutableAttributedString) { //update the location @@ -65,7 +66,7 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable addHandler(on: attributedString) } - public init(id: UUID = UUID(), action: PassthroughSubject = PassthroughSubject(), subscriber: AnyCancellable? = nil, surface: Surface, accessibleText: String? = nil, closeButtonText: String = "Close", title: String, content: String) { + public init(id: UUID = UUID(), action: PassthroughSubject = PassthroughSubject(), subscriber: AnyCancellable? = nil, surface: Surface, accessibleText: String? = nil, closeButtonText: String = "Close", title: String? = nil, content: String? = nil, contentView: UIView? = nil) { self.id = id self.action = action self.subscriber = subscriber @@ -74,13 +75,14 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable self.closeButtonText = closeButtonText self.title = title self.content = content - + self.contentView = contentView //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) } } diff --git a/VDS/Components/TextFields/EntryField/EntryField.swift b/VDS/Components/TextFields/EntryField/EntryField.swift index cbdc65fe..6a32bae1 100644 --- a/VDS/Components/TextFields/EntryField/EntryField.swift +++ b/VDS/Components/TextFields/EntryField/EntryField.swift @@ -151,6 +151,8 @@ open class EntryField: Control, Changeable { open var tooltipContent: String? { didSet { setNeedsUpdate() }} + open var tooltipContentView: UIView? { didSet { setNeedsUpdate() }} + open var transparentBackground: Bool = false { didSet { setNeedsUpdate() }} open var width: CGFloat? { didSet { setNeedsUpdate() }} @@ -293,7 +295,7 @@ open class EntryField: Control, Changeable { } if let tooltipTitle, let tooltipContent { - attributes.append(TooltipLabelAttribute(surface: surface, title: tooltipTitle, content: tooltipContent)) + attributes.append(TooltipLabelAttribute(surface: surface, title: tooltipTitle, content: tooltipContent, contentView: tooltipContentView)) } //set the titleLabel diff --git a/VDS/Components/TitleLockup/TitleLockupEyebrowModel.swift b/VDS/Components/TitleLockup/TitleLockupEyebrowModel.swift index 7d30e923..eaedccda 100644 --- a/VDS/Components/TitleLockup/TitleLockupEyebrowModel.swift +++ b/VDS/Components/TitleLockup/TitleLockupEyebrowModel.swift @@ -16,7 +16,7 @@ extension TitleLockup { public var numberOfLines: Int public init(text: String, - isBold: Bool = false, + isBold: Bool = true, standardStyle: OtherStandardStyle = .bodyLarge, textAttributes: [any LabelAttributeModel]? = nil, numberOfLines: Int = 0) { diff --git a/VDS/Components/Tooltip/Tooltip.swift b/VDS/Components/Tooltip/Tooltip.swift index c4a98636..f51cd36c 100644 --- a/VDS/Components/Tooltip/Tooltip.swift +++ b/VDS/Components/Tooltip/Tooltip.swift @@ -50,10 +50,12 @@ open class Tooltip: Control, TooltipLaunchable { open var size: Size = .medium { didSet { setNeedsUpdate() }} - open var title: String = "" { didSet { setNeedsUpdate() }} - - open var content: String = "" { didSet { setNeedsUpdate() }} + open var title: String? { didSet { setNeedsUpdate() }} + open var content: String? { didSet { setNeedsUpdate() }} + + open var contentView: UIView? { didSet { setNeedsUpdate() }} + //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- @@ -132,6 +134,7 @@ open class Tooltip: Control, TooltipLaunchable { self.presentTooltip(surface: tooltip.surface, title: tooltip.title, content: tooltip.content, + contentView: tooltip.contentView, closeButtonText: tooltip.closeButtonText) }) } @@ -160,8 +163,13 @@ open class Tooltip: Control, TooltipLaunchable { //get the color for the image let imageColor = iconColorConfiguration.getColor(self) imageView.image = infoImage.withTintColor(imageColor) - - accessibilityLabel = "Tooltip: \(title)" + var label = title + if label == nil { + label = content + } + if let label { + accessibilityLabel = "Tooltip: \(label)" + } } } diff --git a/VDS/Components/Tooltip/TooltipAlertViewController.swift b/VDS/Components/Tooltip/TooltipAlertViewController.swift index e8e30a94..b645c424 100644 --- a/VDS/Components/Tooltip/TooltipAlertViewController.swift +++ b/VDS/Components/Tooltip/TooltipAlertViewController.swift @@ -10,8 +10,11 @@ import UIKit import Combine import VDSColorTokens -open class TooltipAlertViewController: UIViewController, Surfaceable { +open class TooltipAlertViewController: UIViewController, Surfaceable, UIScrollViewDelegate { + /// Set of Subscribers for any Publishers for this Control + public var subscribers = Set() + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -26,15 +29,19 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { private var scrollView = UIScrollView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.backgroundColor = .clear - $0.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -5) } - private let containerView = View().with { + private let modalView = View().with { $0.layer.cornerRadius = 8 - $0.layer.shadowColor = UIColor.black.cgColor - $0.layer.shadowOpacity = 0.5 - $0.layer.shadowOffset = CGSize.zero - $0.layer.shadowRadius = 5 + } + + public var contentView: UIView? = nil + + private let contentStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .vertical + $0.distribution = .fillProportionally + $0.spacing = 0 } private var line = Line().with { instance in @@ -45,15 +52,14 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { // MARK: - Public Properties //-------------------------------------------------- open var surface: Surface = .light { didSet { updateView() }} - open var titleText: String = "" { didSet { updateView() }} + open var titleText: String? { didSet { updateView() }} open var titleLabel = Label().with { label in - //use the same font/pointsize for both Title upsizes font in iPad - label.textStyle = UIDevice.isIPad ? .boldTitleSmall : .boldTitleMedium + label.textStyle = .boldTitleMedium } - open var contentText: String = "" { didSet { updateView() }} + open var contentText: String? { didSet { updateView() }} open var contentLabel = Label().with { label in - label.textStyle = .bodyMedium + label.textStyle = .bodyLarge } open var closeButtonText: String = "Close" { didSet { updateView() }} @@ -64,10 +70,6 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { button.setTitle("Close", for: .normal) button.titleLabel?.font = TextStyle.bodyLarge.font button.translatesAutoresizingMaskIntoConstraints = false - onClickSubscriber = button.publisher(for: .touchUpInside).sink {[weak self] button in - guard let self else { return } - self.dismiss(animated: true, completion: nil) - } return button }() @@ -83,6 +85,10 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { private let closeButtonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + private let containerViewInset = VDSLayout.Spacing.space4X.value + private var containerBottomConstraint: NSLayoutConstraint? + private var containerHeightConstraint: NSLayoutConstraint? + private var contentStackViewBottomConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- @@ -93,71 +99,125 @@ open class TooltipAlertViewController: UIViewController, Surfaceable { } open func setup() { - - scrollView.addSubview(titleLabel) - scrollView.addSubview(contentLabel) - containerView.addSubview(scrollView) - containerView.addSubview(line) - containerView.addSubview(closeButton) - view.addSubview(containerView) + //left-right swipe + view.publisher(for: UISwipeGestureRecognizer().with{ $0.direction = .right }) + .sink { [weak self] swipe in + guard let self else { return } + self.dismiss(animated: true, completion: nil) + }.store(in: &subscribers) + //tapping in background + view.publisher(for: UITapGestureRecognizer().with{ $0.numberOfTapsRequired = 1 }) + .sink { [weak self] swipe in + guard let self else { return } + self.dismiss(animated: true, completion: nil) + }.store(in: &subscribers) + + //clicking button + onClickSubscriber = closeButton.publisher(for: .touchUpInside) + .sink {[weak self] button in + guard let self else { return } + self.dismiss(animated: true, completion: nil) + } + + contentStackView.addArrangedSubview(titleLabel) + contentStackView.addArrangedSubview(contentLabel) + scrollView.addSubview(contentStackView) + modalView.addSubview(scrollView) + modalView.addSubview(line) + modalView.addSubview(closeButton) + view.addSubview(modalView) + + // Activate constraints NSLayoutConstraint.activate([ - containerView.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: VDSLayout.Spacing.space8X.value), - containerView.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -VDSLayout.Spacing.space8X.value), - containerView.topAnchor.constraint(greaterThanOrEqualTo: view.topAnchor), - containerView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor), - - containerView.heightAnchor.constraint(equalToConstant: 312), - containerView.widthAnchor.constraint(equalToConstant: 296), - containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor), - containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor), - - scrollView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: VDSLayout.Spacing.space4X.value), - scrollView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: VDSLayout.Spacing.space4X.value), - scrollView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -VDSLayout.Spacing.space4X.value), - scrollView.bottomAnchor.constraint(equalTo: line.topAnchor, constant: -VDSLayout.Spacing.space4X.value), + // Constraints for the floating modal view + modalView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + modalView.centerYAnchor.constraint(equalTo: view.centerYAnchor), + modalView.widthAnchor.constraint(equalToConstant: 296), + modalView.heightAnchor.constraint(greaterThanOrEqualToConstant: 96), + modalView.heightAnchor.constraint(lessThanOrEqualToConstant: 312), - titleLabel.topAnchor.constraint(equalTo: scrollView.topAnchor), - titleLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), - titleLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), - titleLabel.widthAnchor.constraint(equalTo: scrollView.widthAnchor), - - contentLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: VDSLayout.Spacing.space1X.value), - contentLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), - contentLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), - contentLabel.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor), - contentLabel.widthAnchor.constraint(equalTo: scrollView.widthAnchor), - - line.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), - line.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), + // Constraints for the scroll view + scrollView.topAnchor.constraint(equalTo: modalView.topAnchor, constant: VDSLayout.Spacing.space4X.value), + scrollView.leadingAnchor.constraint(equalTo: modalView.leadingAnchor), + scrollView.trailingAnchor.constraint(equalTo: modalView.trailingAnchor), + scrollView.bottomAnchor.constraint(equalTo: line.topAnchor), + + line.leadingAnchor.constraint(equalTo: modalView.leadingAnchor), + line.trailingAnchor.constraint(equalTo: modalView.trailingAnchor), closeButton.topAnchor.constraint(equalTo: line.bottomAnchor), - closeButton.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), - closeButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), - closeButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), - closeButton.heightAnchor.constraint(equalToConstant: 44.0) + closeButton.leadingAnchor.constraint(equalTo: modalView.leadingAnchor), + closeButton.trailingAnchor.constraint(equalTo: modalView.trailingAnchor), + closeButton.bottomAnchor.constraint(equalTo: modalView.bottomAnchor), + closeButton.heightAnchor.constraint(equalToConstant: 44.0), + + contentStackView.topAnchor.constraint(equalTo: scrollView.topAnchor), + contentStackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: containerViewInset), + contentStackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -containerViewInset), + contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -containerViewInset), + contentStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -(containerViewInset * 2)), + contentStackView.heightAnchor.constraint(greaterThanOrEqualTo: scrollView.heightAnchor, constant: -(containerViewInset * 2)) + ]) - } open func updateView() { view.backgroundColor = backgroundColorConfiguration.getColor(self).withAlphaComponent(0.3) - containerView.backgroundColor = containerViewBackgroundColorConfiguration.getColor(self) + modalView.backgroundColor = containerViewBackgroundColorConfiguration.getColor(self) scrollView.indicatorStyle = surface == .light ? .black : .white + titleLabel.removeFromSuperview() + contentLabel.removeFromSuperview() + contentView?.removeFromSuperview() + titleLabel.surface = surface contentLabel.surface = surface line.surface = surface titleLabel.text = titleText contentLabel.text = contentText + titleLabel.sizeToFit() contentLabel.sizeToFit() - + + var addedTitle = false + + if let titleText, !titleText.isEmpty { + contentStackView.addArrangedSubview(titleLabel) + addedTitle = true + } + + var addedContent = false + if let contentText, !contentText.isEmpty { + contentStackView.addArrangedSubview(contentLabel) + addedContent = true + } else if let contentView { + contentView.translatesAutoresizingMaskIntoConstraints = false + if var surfaceable = contentView as? Surfaceable { + surfaceable.surface = surface + } + let wrapper = View() + wrapper.addSubview(contentView) + contentView.pinTop() + contentView.pinLeading() + contentView.pinBottom() + contentView.pinTrailingLessThanOrEqualTo() + contentView.setNeedsLayout() + contentStackView.addArrangedSubview(wrapper) + addedContent = true + } + + if addedTitle && addedContent { + contentStackView.setCustomSpacing(VDSLayout.Spacing.space1X.value, after: titleLabel) + } + let closeButtonTextColor = closeButtonTextColorConfiguration.getColor(self) closeButton.setTitleColor(closeButtonTextColor, for: .normal) closeButton.setTitleColor(closeButtonTextColor, for: .highlighted) closeButton.setTitle(closeButtonText, for: .normal) + + scrollView.layoutIfNeeded() + } } - diff --git a/VDS/Components/Tooltip/TooltipLaunchable.swift b/VDS/Components/Tooltip/TooltipLaunchable.swift index 5f8219b7..17c3f680 100644 --- a/VDS/Components/Tooltip/TooltipLaunchable.swift +++ b/VDS/Components/Tooltip/TooltipLaunchable.swift @@ -9,16 +9,17 @@ import Foundation import UIKit public protocol TooltipLaunchable { - func presentTooltip(surface: Surface, title: String, content: String, closeButtonText: String) + func presentTooltip(surface: Surface, title: String?, content: String?, contentView: UIView?, closeButtonText: String) } extension TooltipLaunchable { - public func presentTooltip(surface: Surface, title: String, content: String, closeButtonText: String = "Close") { + public func presentTooltip(surface: Surface, title: String?, content: String?, contentView: UIView? = nil, closeButtonText: String = "Close") { 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.modalPresentationStyle = .overCurrentContext $0.modalTransitionStyle = .crossDissolve diff --git a/VDS/Components/Tooltip/TrailingTooltipLabel.swift b/VDS/Components/Tooltip/TrailingTooltipLabel.swift index 9c7fc628..220cb2f3 100644 --- a/VDS/Components/Tooltip/TrailingTooltipLabel.swift +++ b/VDS/Components/Tooltip/TrailingTooltipLabel.swift @@ -36,10 +36,12 @@ open class TrailingTooltipLabel: View, TooltipLaunchable { open var tooltipCloseButtonText: String = "Close" { didSet { setNeedsUpdate() } } - open var tooltipTitle: String = "" { didSet { setNeedsUpdate() } } + open var tooltipTitle: String? { didSet { setNeedsUpdate() } } - open var tooltipContent: String = "" { didSet { setNeedsUpdate() } } + open var tooltipContent: String? { didSet { setNeedsUpdate() } } + open var tooltipContentView: UIView? { didSet { setNeedsUpdate() } } + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -70,8 +72,8 @@ open class TrailingTooltipLabel: View, TooltipLaunchable { label.disabled = disabled //add tooltip - if let labelText, !labelText.isEmpty, !tooltipTitle.isEmpty, !tooltipContent.isEmpty { - label.addTooltip(model: .init(surface: surface, closeButtonText: tooltipCloseButtonText, title: tooltipTitle, content: tooltipContent)) + if let labelText, !labelText.isEmpty { + label.addTooltip(model: .init(surface: surface, closeButtonText: tooltipCloseButtonText, title: tooltipTitle, content: tooltipContent, contentView: tooltipContentView)) } } @@ -95,14 +97,15 @@ extension Label { public struct TooltipModel { public var surface: Surface public var closeButtonText: String - public var title: String - public var content: String - - public init(surface: Surface = .light, closeButtonText: String = "Close", title: String, content: 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 } } @@ -121,7 +124,8 @@ extension Label { let tooltip = TooltipLabelAttribute(surface: surface, closeButtonText: model.closeButtonText, title: model.title, - content: model.content) + content: model.content, + contentView: model.contentView) newAttributes.append(tooltip) } diff --git a/VDS/SupportingFiles/ReleaseNotes.txt b/VDS/SupportingFiles/ReleaseNotes.txt index 19fde7df..b3737dfe 100644 --- a/VDS/SupportingFiles/ReleaseNotes.txt +++ b/VDS/SupportingFiles/ReleaseNotes.txt @@ -2,6 +2,11 @@ ======= - CXTDT-426626 - Toggle - Disabled "on" state - CXTDT-427165 - Text Link Caret - Container should not have left/right padding +- CXTDT-427358 - Tooltip - Body Copy incorrect style +- CXTDT-427328 - Title Lockup text style combinations do not match spec +- CXTDT-426527 - Tabs - Large Typography +- CXTDT-427362 - Tooltip - Should not be a drop shadow on modal +- CXTDT-427975 - Tooltip - Scrollbar, multiple 1.0.25 =======