updated control

and subcontrols

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2023-03-29 13:47:56 -05:00
parent 01725e634f
commit aa233515a5
13 changed files with 141 additions and 199 deletions

View File

@ -17,7 +17,7 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
//-------------------------------------------------- //--------------------------------------------------
public var subject = PassthroughSubject<Void, Never>() public var subject = PassthroughSubject<Void, Never>()
public var subscribers = Set<AnyCancellable>() public var subscribers = Set<AnyCancellable>()
open var onClickSubscriber: AnyCancellable? { internal var onClickSubscriber: AnyCancellable? {
willSet { willSet {
if let onClickSubscriber { if let onClickSubscriber {
onClickSubscriber.cancel() onClickSubscriber.cancel()
@ -27,7 +27,7 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
enabledHighlight = onClickSubscriber != nil enabledHighlight = onClickSubscriber != nil
} }
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -109,7 +109,7 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
sendActions(for: .touchUpInside) sendActions(for: .touchUpInside)
return true return true
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------

View File

@ -12,22 +12,9 @@ import VDSFormControlsTokens
import Combine import Combine
/// Checkboxes are a multi-select component through which a customer indicates a choice. If a binary choice, the component is a checkbox. If the choice has multiple options, the component is a ``CheckboxGroup``. /// Checkboxes are a multi-select component through which a customer indicates a choice. If a binary choice, the component is a checkbox. If the choice has multiple options, the component is a ``CheckboxGroup``.
@objc(VDSCheckbox)
public class Checkbox: CheckboxBase{}
@objc(VDSSoloCheckbox)
public class SoloCheckbox: CheckboxBase{
public override func initialSetup() {
super.initialSetup()
onClickSubscriber = publisher(for: .touchUpInside)
.sink { control in
control.toggle()
}
}
}
@objc(VDSCheckboxBase) @objc(VDSCheckboxBase)
open class CheckboxBase: Control, Errorable { open class Checkbox: Control, Errorable, Clickable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
@ -76,6 +63,8 @@ open class CheckboxBase: Control, Errorable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
open var onClick: ((Checkbox) -> ())? { didSet { setupOnClick() }}
open var label = Label().with { open var label = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left $0.textPosition = .left
@ -164,6 +153,13 @@ open class CheckboxBase: Control, Errorable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
open override func initialSetup() {
super.initialSetup()
onClick = { control in
control.toggle()
}
}
open override func setup() { open override func setup() {
super.setup() super.setup()

View File

@ -9,31 +9,24 @@ import Foundation
import UIKit import UIKit
@objc(VDSCheckboxGroup) @objc(VDSCheckboxGroup)
public class CheckboxGroup: CheckboxGroupBase<Checkbox> { public class CheckboxGroup: SelectorGroupHandlerBase<Checkbox> {
public override func didSelect(_ selectedControl: Checkbox) {
selectedControl.toggle()
if selectedControl.isSelected, showError{
showError.toggle()
}
valueChanged()
}
}
public class CheckboxGroupBase<HandlerType: CheckboxBase>: SelectorGroupHandlerBase<HandlerType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
public var selectedHandlers: [Checkbox]? {
public override var selectorViews: [HandlerType] { let selected = selectorViews.filter{ $0.isSelected == true }
guard selected.count > 0 else { return nil }
return selected
}
public override var selectorViews: [Checkbox] {
didSet { didSet {
for selector in selectorViews { for selector in selectorViews {
if !mainStackView.arrangedSubviews.contains(selector) { if !mainStackView.arrangedSubviews.contains(selector) {
selector selector.onClick = { [weak self] handler in
.publisher(for: .touchUpInside) self?.didSelect(handler)
.sink { [weak self] handler in }
self?.didSelect(handler)
}.store(in: &subscribers)
mainStackView.addArrangedSubview(selector) mainStackView.addArrangedSubview(selector)
} }
} }
@ -79,11 +72,13 @@ public class CheckboxGroupBase<HandlerType: CheckboxBase>: SelectorGroupHandlerB
mainStackView.pinToSuperView() mainStackView.pinToSuperView()
} }
public var selectedHandlers: [HandlerType]? { public override func didSelect(_ selectedControl: Checkbox) {
let selected = selectorViews.filter{ $0.isSelected == true } selectedControl.toggle()
guard selected.count > 0 else { return nil } if selectedControl.isSelected, showError{
return selected showError.toggle()
}
valueChanged()
} }
} }

View File

@ -106,6 +106,8 @@ public class Notification: View {
$0.use = .secondary $0.use = .secondary
} }
open var onCloseClick: ((Notification)->())?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Modal Properties // MARK: - Modal Properties
//-------------------------------------------------- //--------------------------------------------------
@ -163,7 +165,8 @@ public class Notification: View {
mainStackView.addArrangedSubview(closeButton) mainStackView.addArrangedSubview(closeButton)
closeButton.publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in closeButton.publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in
self?.didClickOnCloseButton() guard let self else { return }
self.onCloseClick?(self)
}.store(in: &subscribers) }.store(in: &subscribers)
//labels //labels
@ -227,22 +230,14 @@ public class Notification: View {
if let primaryButtonModel { if let primaryButtonModel {
primaryButton.text = primaryButtonModel.text primaryButton.text = primaryButtonModel.text
primaryButton.surface = surface primaryButton.surface = surface
primaryButton.onClickSubscriber = primaryButton primaryButton.onClick = primaryButtonModel.onClick
.publisher(for: .touchUpInside)
.sink(receiveValue: { button in
primaryButtonModel.onClick(button)
})
buttons.append(primaryButton) buttons.append(primaryButton)
} }
if let secondaryButtonModel { if let secondaryButtonModel {
secondaryButton.text = secondaryButtonModel.text secondaryButton.text = secondaryButtonModel.text
secondaryButton.surface = surface secondaryButton.surface = surface
secondaryButton.onClickSubscriber = secondaryButton secondaryButton.onClick = secondaryButtonModel.onClick
.publisher(for: .touchUpInside)
.sink(receiveValue: { button in
secondaryButtonModel.onClick(button)
})
buttons.append(secondaryButton) buttons.append(secondaryButton)
} }
@ -260,9 +255,5 @@ public class Notification: View {
.pinTrailing() .pinTrailing()
} }
} }
func didClickOnCloseButton() {
print("Notification close button clicked!!!")
}
} }

View File

@ -12,22 +12,8 @@ import VDSFormControlsTokens
import Combine import Combine
@objc(VDSRadioBox) @objc(VDSRadioBox)
public class RadioBox: RadioBoxBase{} open class RadioBox: Control, Clickable {
@objc(VDSSoloRadioBox)
public class SoloRadioBox: RadioBoxBase{
public override func initialSetup() {
super.initialSetup()
onClickSubscriber = publisher(for: .touchUpInside)
.sink { control in
control.toggle()
}
}
}
@objc(VDSRadioBoxBase)
open class RadioBoxBase: Control{
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
@ -70,6 +56,8 @@ open class RadioBoxBase: Control{
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
open var onClick: ((RadioBox) -> ())? { didSet { setupOnClick() }}
open var textLabel = Label().with { open var textLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left $0.textPosition = .left
@ -138,7 +126,13 @@ open class RadioBoxBase: Control{
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
open override func initialSetup() {
super.initialSetup()
onClick = { control in
control.toggle()
}
}
open override func setup() { open override func setup() {
super.setup() super.setup()

View File

@ -9,30 +9,18 @@ import Foundation
import UIKit import UIKit
@objc(VDSRadioBoxGroup) @objc(VDSRadioBoxGroup)
public class RadioBoxGroup: RadioBoxGroupBase<RadioBox> { public class RadioBoxGroup: SelectorGroupSelectedHandlerBase<RadioBox> {
public override func didSelect(_ selectedControl: RadioBox) {
let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first
oldSelectedControl?.toggle()
selectedControl.toggle()
valueChanged()
}
}
public class RadioBoxGroupBase<HandlerType: RadioBoxBase>: SelectorGroupSelectedHandlerBase<HandlerType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
public override var selectorViews: [HandlerType] { public override var selectorViews: [RadioBox] {
didSet { didSet {
for selector in selectorViews { for selector in selectorViews {
if !mainStackView.arrangedSubviews.contains(selector) { if !mainStackView.arrangedSubviews.contains(selector) {
selector selector.onClick = { [weak self] handler in
.publisher(for: .touchUpInside) self?.didSelect(handler)
.sink { [weak self] handler in }
self?.didSelect(handler)
}.store(in: &subscribers)
mainStackView.addArrangedSubview(selector) mainStackView.addArrangedSubview(selector)
} }
} }
@ -84,4 +72,11 @@ public class RadioBoxGroupBase<HandlerType: RadioBoxBase>: SelectorGroupSelected
} }
}.store(in: &subscribers) }.store(in: &subscribers)
} }
open override func didSelect(_ selectedControl: RadioBox) {
let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first
oldSelectedControl?.toggle()
selectedControl.toggle()
valueChanged()
}
} }

View File

@ -11,30 +11,8 @@ import VDSColorTokens
import VDSFormControlsTokens import VDSFormControlsTokens
@objc(VDSRadioButton) @objc(VDSRadioButton)
public class RadioButton: RadioButtonBase { open class RadioButton: Control, Errorable, Clickable {
//for groups allows "toggle"
open override func toggle() {
//removed error
if showError && isSelected == false {
showError.toggle()
}
isSelected.toggle()
}
}
@objc(VDSSoloRadioButton)
public class SoloRadioButton: RadioButtonBase {
public override func initialSetup() {
super.initialSetup()
onClickSubscriber = publisher(for: .touchUpInside)
.sink { control in
control.toggle()
}
}
}
@objc(VDSRadioButtonBase)
open class RadioButtonBase: Control, Errorable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
@ -83,6 +61,8 @@ open class RadioButtonBase: Control, Errorable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
open var onClick: ((RadioButton) -> ())? { didSet { setupOnClick() } }
open var label = Label().with { open var label = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textPosition = .left $0.textPosition = .left
@ -158,7 +138,13 @@ open class RadioButtonBase: Control, Errorable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
open override func initialSetup() {
super.initialSetup()
onClick = { control in
control.toggle()
}
}
open override func setup() { open override func setup() {
super.setup() super.setup()

View File

@ -9,32 +9,18 @@ import Foundation
import UIKit import UIKit
@objc(VDSRadioButtonGroup) @objc(VDSRadioButtonGroup)
public class RadioButtonGroup: RadioButtonGroupBase<RadioButton> { public class RadioButtonGroup: SelectorGroupSelectedHandlerBase<RadioButton> {
public override func didSelect(_ selectedControl: RadioButton) {
selectedHandler?.toggle()
selectedControl.toggle()
if showError {
showError = false
}
valueChanged()
}
}
public class RadioButtonGroupBase<HandlerType: RadioButtonBase>: SelectorGroupSelectedHandlerBase<HandlerType> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
public override var selectorViews: [HandlerType] { public override var selectorViews: [RadioButton] {
didSet { didSet {
for selector in selectorViews { for selector in selectorViews {
if !mainStackView.arrangedSubviews.contains(selector) { if !mainStackView.arrangedSubviews.contains(selector) {
selector selector.onClick = { [weak self] handler in
.publisher(for: .touchUpInside) self?.didSelect(handler)
.sink { [weak self] handler in }
self?.didSelect(handler)
}.store(in: &subscribers)
mainStackView.addArrangedSubview(selector) mainStackView.addArrangedSubview(selector)
} }
} }
@ -81,4 +67,22 @@ public class RadioButtonGroupBase<HandlerType: RadioButtonBase>: SelectorGroupSe
mainStackView.pinToSuperView() mainStackView.pinToSuperView()
} }
public override func didSelect(_ selectedControl: RadioButton) {
if let selectedHandler {
updateToggle(selectedHandler)
}
updateToggle(selectedControl)
if showError {
showError = false
}
valueChanged()
}
private func updateToggle(_ radioButton: RadioButton) {
if radioButton.showError && radioButton.isSelected == false {
radioButton.showError.toggle()
}
radioButton.isSelected.toggle()
}
} }

View File

@ -12,26 +12,7 @@ import VDSFormControlsTokens
import Combine import Combine
@objc(VDSRadioSwatch) @objc(VDSRadioSwatch)
public class RadioSwatch: RadioSwatchBase{ open class RadioSwatch: Control, Clickable {
//for groups allows "toggle"
open override func toggle() {
isSelected.toggle()
}
}
@objc(VDSSoloRadioSwatch)
public class SolorRadioSwatch: RadioSwatchBase{
public override func initialSetup() {
super.initialSetup()
onClickSubscriber = publisher(for: .touchUpInside)
.sink { control in
control.toggle()
}
}
}
@objc(VDSRadioSwatchBase)
open class RadioSwatchBase: Control {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
@ -51,6 +32,8 @@ open class RadioSwatchBase: Control {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
open var onClick: ((RadioSwatch) -> ())? { didSet { setupOnClick() } }
public var selectorView = UIView().with { public var selectorView = UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false $0.translatesAutoresizingMaskIntoConstraints = false
} }
@ -78,7 +61,13 @@ open class RadioSwatchBase: Control {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
open override func initialSetup() {
super.initialSetup()
onClick = { control in
control.toggle()
}
}
open override func setup() { open override func setup() {
super.setup() super.setup()

View File

@ -10,24 +10,12 @@ import UIKit
import Combine import Combine
@objc(VDSRadioSwatchGroup) @objc(VDSRadioSwatchGroup)
public class RadioSwatchGroup: RadioSwatchGroupBase<RadioSwatch> { public class RadioSwatchGroup: SelectorGroupSelectedHandlerBase<RadioSwatch>, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
public override func didSelect(selector: RadioSwatch) {
selectedHandler?.toggle()
selector.toggle()
label.text = selector.text
didChange()
valueChanged()
}
}
public class RadioSwatchGroupBase<HandlerType: RadioSwatchBase>: SelectorGroupSelectedHandlerBase<HandlerType>, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
public override var selectorViews: [HandlerType] { public override var selectorViews: [RadioSwatch] {
didSet { didSet {
collectionView.reloadData() collectionView.reloadData()
} }
@ -160,18 +148,20 @@ public class RadioSwatchGroupBase<HandlerType: RadioSwatchBase>: SelectorGroupSe
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath)
let handler = selectorViews[indexPath.row] let handler = selectorViews[indexPath.row]
handler.subscribers.forEach{ $0.cancel() } handler.onClick = { [weak self] handler in
handler.subscribers.removeAll()
handler.publisher(for: .touchUpInside).sink { [weak self] handler in
self?.didSelect(selector: handler) self?.didSelect(selector: handler)
}.store(in: &handler.subscribers) }
cell.subviews.forEach { $0.removeFromSuperview() } cell.subviews.forEach { $0.removeFromSuperview() }
cell.addSubview(handler) cell.addSubview(handler)
handler.pinToSuperView() handler.pinToSuperView()
return cell return cell
} }
open func didSelect(selector: HandlerType) { public func didSelect(selector: RadioSwatch) {
fatalError("Must override didSelect") selectedHandler?.toggle()
selector.toggle()
label.text = selector.text
didChange()
valueChanged()
} }
} }

View File

@ -11,7 +11,12 @@ import VDSFormControlsTokens
import UIKit import UIKit
@objc(VDSTileContainer) @objc(VDSTileContainer)
open class TileContainer: Control { open class TileContainer: TileContainerBase, Clickable {
public var onClick: ((TileContainer) -> ())? { didSet { setupOnClick() } }
}
@objc(VDSTileContainerBase)
open class TileContainerBase: Control {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
@ -310,13 +315,13 @@ open class TileContainer: Control {
} }
} }
extension TileContainer { extension TileContainerBase {
class BackgroundColorConfiguration: ObjectColorable { class BackgroundColorConfiguration: ObjectColorable {
typealias ObjectType = TileContainer typealias ObjectType = TileContainerBase
required init() { } required init() { }
func getColor(_ object: TileContainer) -> UIColor { func getColor(_ object: TileContainerBase) -> UIColor {
switch object.color { switch object.color {
case .white: case .white:

View File

@ -12,7 +12,8 @@ import UIKit
import Combine import Combine
@objc(VDSTilelet) @objc(VDSTilelet)
open class Tilelet: TileContainer { open class Tilelet: TileContainerBase, Clickable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Enums // MARK: - Enums
//-------------------------------------------------- //--------------------------------------------------
@ -62,7 +63,9 @@ open class Tilelet: TileContainer {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
open override var onClickSubscriber: AnyCancellable? { open var onClick: ((Tilelet) -> ())? { didSet { setupOnClick() } }
internal override var onClickSubscriber: AnyCancellable? {
didSet { didSet {
isAccessibilityElement = onClickSubscriber != nil isAccessibilityElement = onClickSubscriber != nil
} }

View File

@ -18,18 +18,7 @@ import Combine
Knob: The circular indicator that slides on the container. Knob: The circular indicator that slides on the container.
*/ */
@objc(VDSToggle) @objc(VDSToggle)
public class Toggle: ToggleBase{ open class Toggle: Control, Clickable {
public override func initialSetup() {
super.initialSetup()
publisher(for: .touchUpInside)
.sink { control in
control.toggle()
}.store(in: &subscribers)
}
}
@objc(VDSToggleBase)
open class ToggleBase: Control {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Enums // MARK: - Enums
//-------------------------------------------------- //--------------------------------------------------
@ -64,6 +53,7 @@ open class ToggleBase: Control {
// MARK: - Private Properties // MARK: - Private Properties
//-------------------------------------------------- //--------------------------------------------------
private var stackView = UIStackView().with { private var stackView = UIStackView().with {
$0.isUserInteractionEnabled = false
$0.translatesAutoresizingMaskIntoConstraints = false $0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .horizontal $0.axis = .horizontal
$0.distribution = .fill $0.distribution = .fill
@ -81,6 +71,8 @@ open class ToggleBase: Control {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Configuration Properties // MARK: - Configuration Properties
//-------------------------------------------------- //--------------------------------------------------
open var onClick: ((Toggle) -> ())? { didSet { setupOnClick() } }
// Sizes are from InVision design specs. // Sizes are from InVision design specs.
public let toggleSize = CGSize(width: 52, height: 24) public let toggleSize = CGSize(width: 52, height: 24)
public let toggleContainerSize = CGSize(width: 52, height: 44) public let toggleContainerSize = CGSize(width: 52, height: 44)
@ -223,14 +215,16 @@ open class ToggleBase: Control {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
open override func initialSetup() {
super.initialSetup()
onClick = { control in
control.toggle()
}
}
open override func setup() { open override func setup() {
super.setup() super.setup()
//add tapGesture to self
publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in
self?.sendActions(for: .touchUpInside)
}.store(in: &subscribers)
isAccessibilityElement = true isAccessibilityElement = true
accessibilityTraits = .button accessibilityTraits = .button
addSubview(stackView) addSubview(stackView)
@ -288,7 +282,7 @@ open class ToggleBase: Control {
toggleView.backgroundColor = toggleColorConfiguration.getColor(self) toggleView.backgroundColor = toggleColorConfiguration.getColor(self)
knobView.backgroundColor = knobColorConfiguration.getColor(self) knobView.backgroundColor = knobColorConfiguration.getColor(self)
} }
/// This will toggle the state of the Toggle and execute the actionBlock if provided. /// This will toggle the state of the Toggle and execute the actionBlock if provided.
open func toggle() { open func toggle() {
isOn.toggle() isOn.toggle()