From ced2bec5c23e8fa7b94b41bd82c54036f41d0414 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 1 Nov 2022 15:13:11 -0500 Subject: [PATCH] added TextLink/TextLinkCaret Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 24 ++++ VDS/Components/TextLink/TextLink.swift | 103 ++++++++++++++++++ .../TextLinkCaret/TextLinkCaret.swift | 98 +++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 VDS/Components/TextLink/TextLink.swift create mode 100644 VDS/Components/TextLinkCaret/TextLinkCaret.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 22a08b39..ad2df115 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -54,6 +54,8 @@ EAB1D2CF28ABEF2B00DAE764 /* Typography.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CE28ABEF2B00DAE764 /* Typography.swift */; }; EAB1D2E628AE842000DAE764 /* Publisher+Bind.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2E328AE842000DAE764 /* Publisher+Bind.swift */; }; EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2E928AE84AA00DAE764 /* UIControlPublisher.swift */; }; + EAC9257D29119B5400091998 /* TextLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9257C29119B5400091998 /* TextLink.swift */; }; + EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925822911B35300091998 /* TextLinkCaret.swift */; }; EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; }; EAF7F0952899861000B287F5 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0932899861000B287F5 /* Checkbox.swift */; }; EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0992899B17200B287F5 /* CATransaction.swift */; }; @@ -133,6 +135,8 @@ EAB1D2CE28ABEF2B00DAE764 /* Typography.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Typography.swift; sourceTree = ""; }; EAB1D2E328AE842000DAE764 /* Publisher+Bind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Publisher+Bind.swift"; sourceTree = ""; }; EAB1D2E928AE84AA00DAE764 /* UIControlPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControlPublisher.swift; sourceTree = ""; }; + EAC9257C29119B5400091998 /* TextLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLink.swift; sourceTree = ""; }; + EAC925822911B35300091998 /* TextLinkCaret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLinkCaret.swift; sourceTree = ""; }; EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Publisher.swift"; sourceTree = ""; }; EAF7F0932899861000B287F5 /* Checkbox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; EAF7F0992899B17200B287F5 /* CATransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CATransaction.swift; sourceTree = ""; }; @@ -257,6 +261,8 @@ EA89200B28B530F0006B9984 /* RadioBox */, EAF7F11428A1470D00B287F5 /* RadioButton */, EA1F265F28B945070033E859 /* RadioSwatch */, + EAC9257E29119B5D00091998 /* TextLink */, + EAC925812911B34300091998 /* TextLinkCaret */, EA3361A0288B1E6F0071C351 /* Toggle */, ); path = Components; @@ -399,6 +405,22 @@ path = Publishers; sourceTree = ""; }; + EAC9257E29119B5D00091998 /* TextLink */ = { + isa = PBXGroup; + children = ( + EAC9257C29119B5400091998 /* TextLink.swift */, + ); + path = TextLink; + sourceTree = ""; + }; + EAC925812911B34300091998 /* TextLinkCaret */ = { + isa = PBXGroup; + children = ( + EAC925822911B35300091998 /* TextLinkCaret.swift */, + ); + path = TextLinkCaret; + sourceTree = ""; + }; EAF7F092289985E200B287F5 /* Checkbox */ = { isa = PBXGroup; children = ( @@ -553,6 +575,7 @@ EA89201328B568D8006B9984 /* RadioBox.swift in Sources */, EA3362402892EF6C0071C351 /* Label.swift in Sources */, EAF7F0B3289B1ADC00B287F5 /* LabelAttributeAction.swift in Sources */, + EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */, EA33622E2891EA3C0071C351 /* DispatchQueue+Once.swift in Sources */, EA4DB2FD28D3D0CA00103EE3 /* AnyEquatable.swift in Sources */, EAF7F0AF289B144C00B287F5 /* LabelAttributeUnderline.swift in Sources */, @@ -597,6 +620,7 @@ EA3361B8288B2AAA0071C351 /* ViewProtocol.swift in Sources */, EA3361BF288B2EA60071C351 /* Handlerable.swift in Sources */, EA3361A8288B23300071C351 /* UIColor.swift in Sources */, + EAC9257D29119B5400091998 /* TextLink.swift in Sources */, EA1F266628B945070033E859 /* RadioSwatchGroup.swift in Sources */, 5FC35BE328D51405004EBEAC /* Button.swift in Sources */, ); diff --git a/VDS/Components/TextLink/TextLink.swift b/VDS/Components/TextLink/TextLink.swift new file mode 100644 index 00000000..13da46d2 --- /dev/null +++ b/VDS/Components/TextLink/TextLink.swift @@ -0,0 +1,103 @@ +// +// TextLink.swift +// VDS +// +// Created by Matt Bruce on 11/1/22. +// + +import Foundation +import UIKit +import VDSColorTokens +import VDSFormControlsTokens +import Combine + +@objc(VDSTextLink) +open class TextLink: Control { + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + private var heightConstraint: NSLayoutConstraint? + private var label = Label() + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + open var text: String? { didSet { didChange() } } + + open var size: ButtonSize = .large { didSet { didChange() }} + + private var height: CGFloat { + switch size { + case .large: + return 44 + case .small: + return 32 + } + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + initialSetup() + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + initialSetup() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + initialSetup() + } + + //-------------------------------------------------- + // MARK: - Public Functions + //-------------------------------------------------- + open override func initialSetup() { + super.initialSetup() + } + + open override func setup() { + super.setup() + + addSubview(label) + + //add tapGesture to self + publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in + self?.sendActions(for: .touchUpInside) + }.store(in: &subscribers) + + //pin stackview to edges + label.topAnchor.constraint(equalTo: topAnchor).isActive = true + label.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + label.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + label.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + + heightConstraint = heightAnchor.constraint(equalToConstant: height) + heightConstraint?.isActive = true + } + + open override func reset() { + super.reset() + size = .large + accessibilityCustomActions = [] + accessibilityTraits = .staticText + } + + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + open override func updateView() { + label.surface = surface + label.disabled = disabled + label.typograpicalStyle = size == .large ? TypographicalStyle.BodyLarge : TypographicalStyle.BodySmall + label.text = text ?? "" + label.attributes = [LabelAttributeUnderline(location: 0, length: label.text!.count)] + heightConstraint?.constant = height + } + +} diff --git a/VDS/Components/TextLinkCaret/TextLinkCaret.swift b/VDS/Components/TextLinkCaret/TextLinkCaret.swift new file mode 100644 index 00000000..9ea3f4e5 --- /dev/null +++ b/VDS/Components/TextLinkCaret/TextLinkCaret.swift @@ -0,0 +1,98 @@ +// +// TextLinkCaret.swift +// VDS +// +// Created by Matt Bruce on 11/1/22. +// + +import Foundation +import UIKit +import VDSColorTokens +import VDSFormControlsTokens +import Combine + +public enum TextLinkIconPosition: String, CaseIterable { + case left, right +} + +@objc(VDSTextLinkCaret) +open class TextLinkCaret: Control { + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + private var heightConstraint: NSLayoutConstraint? + private var label = Label() + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + open var text: String? { didSet { didChange() } } + + open var iconPosition: TextLinkIconPosition = .right { didSet { didChange() } } + + private var height: CGFloat { + 44 + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + initialSetup() + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + initialSetup() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + initialSetup() + } + + //-------------------------------------------------- + // MARK: - Public Functions + //-------------------------------------------------- + open override func initialSetup() { + super.initialSetup() + } + + open override func setup() { + super.setup() + + addSubview(label) + + //add tapGesture to self + publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in + self?.sendActions(for: .touchUpInside) + }.store(in: &subscribers) + + //pin stackview to edges + label.topAnchor.constraint(equalTo: topAnchor).isActive = true + label.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + label.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + label.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + + heightAnchor.constraint(equalToConstant: height).isActive = true + } + + open override func reset() { + super.reset() + accessibilityCustomActions = [] + accessibilityTraits = .staticText + } + + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + open override func updateView() { + label.surface = surface + label.disabled = disabled + label.typograpicalStyle = TypographicalStyle.BoldBodyLarge + label.text = text ?? "" + } + +}