refactored to remove models
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
parent
dead6947f1
commit
4bf9ce83b5
@ -9,13 +9,12 @@ import Foundation
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
open class CollectionView<ModelType: Modelable>: UICollectionView, ModelHandlerable, ViewProtocol, Resettable {
|
||||
open class CollectionView: UICollectionView, ModelHandlerable, ViewProtocol, Resettable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Combine Properties
|
||||
//--------------------------------------------------
|
||||
@Published public var model: ModelType = ModelType()
|
||||
public var modelPublisher: Published<ModelType>.Publisher { $model }
|
||||
public var subject = PassthroughSubject<Void, Never>()
|
||||
public var subscribers = Set<AnyCancellable>()
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -23,24 +22,18 @@ open class CollectionView<ModelType: Modelable>: UICollectionView, ModelHandlera
|
||||
//--------------------------------------------------
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
@Proxy(\.model.surface)
|
||||
open var surface: Surface
|
||||
|
||||
@Proxy(\.model.disabled)
|
||||
open var disabled: Bool {
|
||||
didSet {
|
||||
self.isEnabled = !disabled
|
||||
}
|
||||
}
|
||||
open var surface: Surface = .light { didSet { subject.send() }}
|
||||
|
||||
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
|
||||
|
||||
open var isEnabled: Bool {
|
||||
get { !model.disabled }
|
||||
get { !disabled }
|
||||
set {
|
||||
//create local vars for clear coding
|
||||
let disabled = !newValue
|
||||
if model.disabled != disabled {
|
||||
model.disabled = disabled
|
||||
if disabled != !newValue {
|
||||
disabled = !newValue
|
||||
}
|
||||
isUserInteractionEnabled = isEnabled
|
||||
subject.send()
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,22 +45,14 @@ open class CollectionView<ModelType: Modelable>: UICollectionView, ModelHandlera
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init(with model: ModelType) {
|
||||
super.init(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public required init(with model: ModelType, collectionViewLayout layout: UICollectionViewLayout) {
|
||||
public required init(collectionViewLayout layout: UICollectionViewLayout) {
|
||||
super.init(frame: .zero, collectionViewLayout: layout)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
|
||||
super.init(frame: frame, collectionViewLayout: layout)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
@ -90,15 +75,14 @@ open class CollectionView<ModelType: Modelable>: UICollectionView, ModelHandlera
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
open func updateView(viewModel: ModelType) {
|
||||
open func updateView() {
|
||||
fatalError("Implement updateView")
|
||||
}
|
||||
|
||||
open func reset() {
|
||||
backgroundColor = .clear
|
||||
if let model = model as? Resettable {
|
||||
model.reset()
|
||||
}
|
||||
surface = .light
|
||||
disabled = false
|
||||
}
|
||||
|
||||
// MARK: - ViewProtocol
|
||||
|
||||
@ -10,7 +10,6 @@ import UIKit
|
||||
import Combine
|
||||
|
||||
open class CollectionViewCell<ModelHandlerType: ModelHandlerable & UIView>: UICollectionViewCell, ViewProtocol {
|
||||
public typealias ModelType = ModelHandlerType.ModelType
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
@ -18,10 +17,10 @@ open class CollectionViewCell<ModelHandlerType: ModelHandlerable & UIView>: UICo
|
||||
|
||||
public var modelHandler: ModelHandlerType = ModelHandlerType()
|
||||
|
||||
@Proxy(\.modelHandler.model.surface)
|
||||
open var surface: Surface
|
||||
@Proxy(\.modelHandler.surface)
|
||||
open var surface: Surface
|
||||
|
||||
@Proxy(\.modelHandler.model.disabled)
|
||||
@Proxy(\.modelHandler.disabled)
|
||||
open var disabled: Bool
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -32,12 +31,6 @@ open class CollectionViewCell<ModelHandlerType: ModelHandlerable & UIView>: UICo
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init(with model: ModelType) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
initialSetup()
|
||||
@ -58,22 +51,7 @@ open class CollectionViewCell<ModelHandlerType: ModelHandlerable & UIView>: UICo
|
||||
setup()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
open func shouldUpdateView(viewModel: ModelType) -> Bool {
|
||||
return modelHandler.shouldUpdateView(viewModel: viewModel)
|
||||
}
|
||||
|
||||
open func updateView(viewModel: ModelType) {
|
||||
modelHandler.updateView(viewModel: viewModel)
|
||||
}
|
||||
|
||||
public func set(with model: ModelType) {
|
||||
modelHandler.set(with: model)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ViewProtocol
|
||||
/// Will be called only once.
|
||||
open func setup() {
|
||||
|
||||
@ -87,6 +87,10 @@ public protocol BinaryColorable{
|
||||
var userTrueColor: Bool { get }
|
||||
}
|
||||
|
||||
extension BinaryColorable where Self: Control {
|
||||
public var userTrueColor: Bool { return isSelected }
|
||||
}
|
||||
|
||||
extension BinaryColorable where Self: Selectable {
|
||||
public var userTrueColor: Bool { return selected }
|
||||
}
|
||||
|
||||
@ -10,13 +10,12 @@ import UIKit
|
||||
import Combine
|
||||
|
||||
|
||||
open class Control<ModelType: Modelable>: UIControl, ModelHandlerable, ViewProtocol, Resettable {
|
||||
open class Control: UIControl, ModelHandlerable, ViewProtocol, Resettable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Combine Properties
|
||||
//--------------------------------------------------
|
||||
@Published public var model: ModelType = ModelType()
|
||||
public var modelPublisher: Published<ModelType>.Publisher { $model }
|
||||
public var subject = PassthroughSubject<Void, Never>()
|
||||
public var subscribers = Set<AnyCancellable>()
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -24,25 +23,18 @@ open class Control<ModelType: Modelable>: UIControl, ModelHandlerable, ViewProto
|
||||
//--------------------------------------------------
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
@Proxy(\.model.surface)
|
||||
open var surface: Surface
|
||||
|
||||
@Proxy(\.model.disabled)
|
||||
open var disabled: Bool {
|
||||
didSet {
|
||||
self.isEnabled = !disabled
|
||||
}
|
||||
}
|
||||
open var surface: Surface = .light { didSet { subject.send() }}
|
||||
|
||||
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
|
||||
|
||||
open override var isEnabled: Bool {
|
||||
get { !model.disabled }
|
||||
get { !disabled }
|
||||
set {
|
||||
//create local vars for clear coding
|
||||
let disabled = !newValue
|
||||
if model.disabled != disabled {
|
||||
model.disabled = disabled
|
||||
if disabled != !newValue {
|
||||
disabled = !newValue
|
||||
}
|
||||
isUserInteractionEnabled = isEnabled
|
||||
subject.send()
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,12 +46,6 @@ open class Control<ModelType: Modelable>: UIControl, ModelHandlerable, ViewProto
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init(with model: ModelType) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
@ -92,15 +78,14 @@ open class Control<ModelType: Modelable>: UIControl, ModelHandlerable, ViewProto
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
open func updateView(viewModel: ModelType) {
|
||||
open func updateView() {
|
||||
fatalError("Implement updateView")
|
||||
}
|
||||
|
||||
open func reset() {
|
||||
backgroundColor = .clear
|
||||
if let model = model as? Resettable {
|
||||
model.reset()
|
||||
}
|
||||
surface = .light
|
||||
disabled = false
|
||||
}
|
||||
|
||||
// MARK: - ViewProtocol
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public class SelectorGroupHandlerBase<GroupModelType: SelectorGroupModelable, ModelHandlerType: Control<GroupModelType.SelectorModelType>>: Control<GroupModelType> {
|
||||
public class SelectorGroupHandlerBase<ModelHandlerType: Control>: Control {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
@ -33,42 +33,10 @@ public class SelectorGroupHandlerBase<GroupModelType: SelectorGroupModelable, Mo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func findSelectorView(id: UUID?) -> ModelHandlerType? {
|
||||
return selectorViews.first(where: { existingSelectorView in
|
||||
return existingSelectorView.model.id == id
|
||||
})
|
||||
}
|
||||
|
||||
public func getCachedSelector(viewModel: ModelHandlerType.ModelType) -> ModelHandlerType.ModelType? {
|
||||
if let index = model.selectors.firstIndex(where: { element in
|
||||
return element.id == viewModel.id
|
||||
}) {
|
||||
return model.selectors[index]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public func replace(viewModel: ModelHandlerType.ModelType){
|
||||
if let index = model.selectors.firstIndex(where: { element in
|
||||
return element.id == viewModel.id
|
||||
}) {
|
||||
model.selectors[index] = viewModel
|
||||
}
|
||||
}
|
||||
|
||||
public func createModelHandler(selector: ModelHandlerType.ModelType) -> ModelHandlerType {
|
||||
|
||||
public func createModelHandler() -> ModelHandlerType {
|
||||
//create view
|
||||
let newSelectorView = ModelHandlerType(with: selector)
|
||||
|
||||
//add model update to the subscribers
|
||||
newSelectorView
|
||||
.modelPublisher
|
||||
.sink { [weak self] model in
|
||||
self?.replace(viewModel: model)
|
||||
}
|
||||
.store(in: &subscribers)
|
||||
let newSelectorView = ModelHandlerType()
|
||||
|
||||
//add the selectedPublisher for the change
|
||||
newSelectorView
|
||||
@ -93,9 +61,8 @@ public class SelectorGroupHandlerBase<GroupModelType: SelectorGroupModelable, Mo
|
||||
}
|
||||
}
|
||||
|
||||
public class SelectorGroupSelectedHandlerBase<GroupModelType: SelectorGroupSelectedModelable, ModelHandlerType: Control<GroupModelType.SelectorModelType>>: SelectorGroupHandlerBase<GroupModelType, ModelHandlerType> where GroupModelType.SelectorModelType == ModelHandlerType.ModelType {
|
||||
|
||||
public var selectedModel: ModelHandlerType.ModelType? {
|
||||
return model.selectedModel
|
||||
public class SelectorGroupSelectedHandlerBase<ModelHandlerType: Control>: SelectorGroupHandlerBase<ModelHandlerType>{
|
||||
public var selectedHandler: ModelHandlerType? {
|
||||
return selectorViews.filter { $0.isSelected == true }.first
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,39 +10,31 @@ import UIKit
|
||||
import Combine
|
||||
|
||||
|
||||
open class View<ModelType: Modelable>: UIView, ModelHandlerable, ViewProtocol, Resettable {
|
||||
open class View: UIView, ModelHandlerable, ViewProtocol, Resettable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Combine Properties
|
||||
//--------------------------------------------------
|
||||
@Published public var model: ModelType = ModelType()
|
||||
public var modelPublisher: Published<ModelType>.Publisher { $model }
|
||||
public var subject = PassthroughSubject<Void, Never>()
|
||||
public var subscribers = Set<AnyCancellable>()
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
@Proxy(\.model.surface)
|
||||
open var surface: Surface
|
||||
|
||||
@Proxy(\.model.disabled)
|
||||
open var disabled: Bool {
|
||||
didSet {
|
||||
self.isEnabled = !disabled
|
||||
}
|
||||
}
|
||||
open var surface: Surface = .light { didSet { subject.send() }}
|
||||
|
||||
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
|
||||
|
||||
open var isEnabled: Bool {
|
||||
get { !model.disabled }
|
||||
get { !disabled }
|
||||
set {
|
||||
//create local vars for clear coding
|
||||
let disabled = !newValue
|
||||
if model.disabled != disabled {
|
||||
model.disabled = disabled
|
||||
if disabled != !newValue {
|
||||
disabled = !newValue
|
||||
}
|
||||
isUserInteractionEnabled = isEnabled
|
||||
subject.send()
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,12 +45,6 @@ open class View<ModelType: Modelable>: UIView, ModelHandlerable, ViewProtocol, R
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init(with model: ModelType) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
@ -85,15 +71,14 @@ open class View<ModelType: Modelable>: UIView, ModelHandlerable, ViewProtocol, R
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
open func updateView(viewModel: ModelType) {
|
||||
open func updateView() {
|
||||
fatalError("Implement updateView")
|
||||
}
|
||||
|
||||
open func reset() {
|
||||
backgroundColor = .clear
|
||||
if let model = model as? Resettable {
|
||||
model.reset()
|
||||
}
|
||||
surface = .light
|
||||
disabled = false
|
||||
}
|
||||
|
||||
// MARK: - ViewProtocol
|
||||
|
||||
@ -11,44 +11,34 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
public class Badge: BadgeBase<DefaultBadgeModel>{}
|
||||
public class Badge: BadgeBase{}
|
||||
|
||||
open class BadgeBase<ModelType: BadgeModel>: View<ModelType> {
|
||||
open class BadgeBase: View, Accessable {
|
||||
|
||||
private var label = Label()
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
@Proxy(\.model.fillColor)
|
||||
open var fillColor: BadgeFillColor
|
||||
open var fillColor: BadgeFillColor = .red { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.text)
|
||||
open var text: String
|
||||
open var text: String = "" { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.maxWidth)
|
||||
open var maxWidth: CGFloat?
|
||||
open var maxWidth: CGFloat? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.numberOfLines)
|
||||
open var numberOfLines: Int
|
||||
open var numberOfLines: Int = 1 { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintEnabled)
|
||||
open var accessibilityHintEnabled: String?
|
||||
open var accessibilityHintEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintDisabled)
|
||||
open var accessibilityHintDisabled: String?
|
||||
open var accessibilityHintDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueEnabled)
|
||||
open var accessibilityValueEnabled: String?
|
||||
open var accessibilityValueEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueDisabled)
|
||||
open var accessibilityValueDisabled: String?
|
||||
open var accessibilityValueDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelEnabled)
|
||||
open var accessibilityLabelEnabled: String?
|
||||
open var accessibilityLabelEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelDisabled)
|
||||
open var accessibilityLabelDisabled: String?
|
||||
open var accessibilityLabelDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
@ -90,9 +80,9 @@ open class BadgeBase<ModelType: BadgeModel>: View<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Configuration
|
||||
//--------------------------------------------------
|
||||
public func backgroundColor(for fillColor: BadgeFillColor) -> UIColor {
|
||||
public func backgroundColor() -> UIColor {
|
||||
var config: SurfaceColorConfiguration
|
||||
switch model.fillColor {
|
||||
switch fillColor {
|
||||
case .red:
|
||||
config = SurfaceColorConfiguration().with {
|
||||
$0.lightColor = VDSColor.backgroundBrandhighlight
|
||||
@ -130,10 +120,10 @@ open class BadgeBase<ModelType: BadgeModel>: View<ModelType> {
|
||||
}
|
||||
}
|
||||
|
||||
return config.getColor(model)
|
||||
return config.getColor(self)
|
||||
}
|
||||
|
||||
public func textColorConfiguration(for fillColor: BadgeFillColor) -> AnyColorable {
|
||||
public func textColorConfiguration() -> AnyColorable {
|
||||
|
||||
switch fillColor {
|
||||
|
||||
@ -165,19 +155,23 @@ open class BadgeBase<ModelType: BadgeModel>: View<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - State
|
||||
//--------------------------------------------------
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
backgroundColor = backgroundColor(for: viewModel.fillColor)
|
||||
open override func updateView() {
|
||||
backgroundColor = backgroundColor()
|
||||
label.textColorConfiguration = textColorConfiguration()
|
||||
label.numberOfLines = numberOfLines
|
||||
label.textPosition = .left
|
||||
label.typograpicalStyle = .BoldBodySmall
|
||||
label.text = text
|
||||
label.surface = surface
|
||||
label.disabled = disabled
|
||||
|
||||
label.textColorConfiguration = textColorConfiguration(for: viewModel.fillColor)
|
||||
label.numberOfLines = viewModel.numberOfLines
|
||||
|
||||
if let maxWidth = viewModel.maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth {
|
||||
if let maxWidth = maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth {
|
||||
maxWidthConstraint?.constant = maxWidth
|
||||
maxWidthConstraint?.isActive = true
|
||||
} else {
|
||||
maxWidthConstraint?.isActive = false
|
||||
}
|
||||
label.set(with: viewModel.label)
|
||||
|
||||
setAccessibilityLabel()
|
||||
}
|
||||
|
||||
|
||||
@ -11,15 +11,12 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
public class Button:ButtonBase<DefaultButtonModel>{}
|
||||
|
||||
open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewProtocol, Resettable {
|
||||
open class Button: UIButton, ModelHandlerable, ViewProtocol, Resettable, Useable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Combine Properties
|
||||
//--------------------------------------------------
|
||||
@Published public var model: ModelType = ModelType()
|
||||
public var modelPublisher: Published<ModelType>.Publisher { $model }
|
||||
public var subject = PassthroughSubject<Void, Never>()
|
||||
public var subscribers = Set<AnyCancellable>()
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -32,39 +29,29 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
@Proxy(\.model.surface)
|
||||
open var surface: Surface
|
||||
open var text: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.disabled)
|
||||
open var disabled: Bool {
|
||||
didSet {
|
||||
isEnabled = !disabled
|
||||
}
|
||||
}
|
||||
open var use: Use = .primary { didSet { subject.send() }}
|
||||
|
||||
open var size: ButtonSize = .large { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.text)
|
||||
open var text: String?
|
||||
open var width: CGFloat? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.use)
|
||||
open var use: Use
|
||||
|
||||
@Proxy(\.model.size)
|
||||
open var size: ButtonSize
|
||||
|
||||
@Proxy(\.model.width)
|
||||
open var width: CGFloat?
|
||||
open var surface: Surface = .light { didSet { subject.send() }}
|
||||
|
||||
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
|
||||
|
||||
open override var isEnabled: Bool {
|
||||
get { !model.disabled }
|
||||
get { !disabled }
|
||||
set {
|
||||
//create local vars for clear coding
|
||||
let disabled = !newValue
|
||||
if model.disabled != disabled {
|
||||
model.disabled = disabled
|
||||
if disabled != !newValue {
|
||||
disabled = !newValue
|
||||
}
|
||||
isUserInteractionEnabled = isEnabled
|
||||
subject.send()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Configuration Properties
|
||||
@ -113,16 +100,9 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init(with model: ModelType) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
@ -149,15 +129,19 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
|
||||
|
||||
//only 1 of the 2 widths can be on at the same time
|
||||
widthConstraint = widthAnchor.constraint(equalToConstant: 0)
|
||||
minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: model.size.minimumWidth)
|
||||
minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: size.minimumWidth)
|
||||
|
||||
//height
|
||||
heightConstraint = heightAnchor.constraint(equalToConstant: model.size.height)
|
||||
heightConstraint = heightAnchor.constraint(equalToConstant: size.height)
|
||||
heightConstraint?.isActive = true
|
||||
}
|
||||
|
||||
open func reset() {
|
||||
model = ModelType()
|
||||
surface = .light
|
||||
disabled = false
|
||||
use = .primary
|
||||
width = nil
|
||||
size = .large
|
||||
accessibilityCustomActions = []
|
||||
accessibilityTraits = .staticText
|
||||
}
|
||||
@ -165,19 +149,19 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
open func updateView(viewModel: ModelType) {
|
||||
open func updateView() {
|
||||
|
||||
let bgColor = buttonBackgroundColorConfiguration.getColor(viewModel)
|
||||
let borderColor = buttonBorderColorConfiguration.getColor(viewModel)
|
||||
let titleColor = buttonTitleColorConfiguration.getColor(viewModel)
|
||||
let borderWidth = viewModel.use == .secondary ? 1.0 : 0.0
|
||||
let buttonHeight = viewModel.size.height
|
||||
let bgColor = buttonBackgroundColorConfiguration.getColor(self)
|
||||
let borderColor = buttonBorderColorConfiguration.getColor(self)
|
||||
let titleColor = buttonTitleColorConfiguration.getColor(self)
|
||||
let borderWidth = use == .secondary ? 1.0 : 0.0
|
||||
let buttonHeight = size.height
|
||||
let cornerRadius = buttonHeight / 2
|
||||
let minWidth = viewModel.size.minimumWidth
|
||||
let font = viewModel.size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font
|
||||
let edgeInsets = viewModel.size.edgeInsets
|
||||
let minWidth = size.minimumWidth
|
||||
let font = size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font
|
||||
let edgeInsets = size.edgeInsets
|
||||
|
||||
if let text = viewModel.text {
|
||||
if let text = text {
|
||||
setTitle(text, for: .normal)
|
||||
} else {
|
||||
setTitle("No ViewModel Text", for: .normal)
|
||||
@ -193,7 +177,7 @@ open class ButtonBase<ModelType: ButtonModel>: UIButton, ModelHandlerable, ViewP
|
||||
minWidthConstraint?.constant = minWidth
|
||||
heightConstraint?.constant = buttonHeight
|
||||
|
||||
if let width = viewModel.width, width > minWidth {
|
||||
if let width, width > minWidth {
|
||||
widthConstraint?.constant = width
|
||||
widthConstraint?.isActive = true
|
||||
minWidthConstraint?.isActive = false
|
||||
|
||||
@ -24,7 +24,6 @@ public struct DefaultButtonModel: ButtonModel {
|
||||
|
||||
public var id = UUID()
|
||||
public var text: String?
|
||||
public var typograpicalStyle: TypographicalStyle = .BoldBodyLarge
|
||||
public var surface: Surface = .light
|
||||
public var use: Use = .primary
|
||||
public var disabled: Bool = false
|
||||
|
||||
@ -11,9 +11,9 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
public class Checkbox: CheckboxBase<DefaultCheckboxModel>{}
|
||||
public class Checkbox: CheckboxBase{}
|
||||
|
||||
public class SoloCheckbox: CheckboxBase<DefaultCheckboxModel>{
|
||||
public class SoloCheckbox: CheckboxBase{
|
||||
public override func initialSetup() {
|
||||
super.initialSetup()
|
||||
publisher(for: .touchUpInside)
|
||||
@ -23,11 +23,39 @@ public class SoloCheckbox: CheckboxBase<DefaultCheckboxModel>{
|
||||
}
|
||||
}
|
||||
|
||||
open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
open class CheckboxBase: Control, Accessable, BinaryColorable, Errorable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// 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: - Private Properties
|
||||
//--------------------------------------------------
|
||||
private var shouldShowError: Bool {
|
||||
guard showError && !disabled && errorText?.isEmpty == false else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
private var shouldShowLabels: Bool {
|
||||
guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
private var mainStackView: UIStackView = {
|
||||
return UIStackView().with {
|
||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||
@ -67,60 +95,77 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
}()
|
||||
|
||||
//can't bind to @Proxy
|
||||
open override var isSelected: Bool {
|
||||
get { model.selected }
|
||||
set {
|
||||
if model.selected != newValue {
|
||||
model.selected = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
open override var isSelected: Bool { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.labelText)
|
||||
open var labelText: String?
|
||||
open var labelText: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.childText)
|
||||
open var childText: String?
|
||||
open var labelTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.showError)
|
||||
open var showError: Bool
|
||||
|
||||
@Proxy(\.model.errorText)
|
||||
open var errorText: String?
|
||||
open var childText: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.inputId)
|
||||
open var inputId: String?
|
||||
open var childTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.value)
|
||||
open var value: AnyHashable?
|
||||
open var showError: Bool = false { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataAnalyticsTrack)
|
||||
open var dataAnalyticsTrack: String?
|
||||
open var errorText: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataClickStream)
|
||||
open var dataClickStream: String?
|
||||
open var inputId: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataTrack)
|
||||
open var dataTrack: String?
|
||||
open var value: AnyHashable? { didSet { subject.send() }}
|
||||
|
||||
open var dataAnalyticsTrack: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintEnabled)
|
||||
open var accessibilityHintEnabled: String?
|
||||
open var dataClickStream: String? { didSet { subject.send() }}
|
||||
|
||||
open var dataTrack: String? { didSet { subject.send() }}
|
||||
|
||||
open var accessibilityHintEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintDisabled)
|
||||
open var accessibilityHintDisabled: String?
|
||||
open var accessibilityHintDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueEnabled)
|
||||
open var accessibilityValueEnabled: String?
|
||||
open var accessibilityValueEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueDisabled)
|
||||
open var accessibilityValueDisabled: String?
|
||||
open var accessibilityValueDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelEnabled)
|
||||
open var accessibilityLabelEnabled: String?
|
||||
open var accessibilityLabelEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelDisabled)
|
||||
open var accessibilityLabelDisabled: String?
|
||||
open var accessibilityLabelDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
private var labelModel: DefaultLabelModel? {
|
||||
guard let labelText = labelText else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BoldBodyLarge
|
||||
model.text = labelText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
model.attributes = labelTextAttributes
|
||||
return model
|
||||
}
|
||||
|
||||
private var childModel: DefaultLabelModel? {
|
||||
guard let childText = childText else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodyLarge
|
||||
model.text = childText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
model.attributes = childTextAttributes
|
||||
return model
|
||||
}
|
||||
|
||||
private var errorModel: DefaultLabelModel? {
|
||||
guard let errorText = errorText, showError else { return nil }
|
||||
var model = DefaultLabelModel()
|
||||
model.textPosition = .left
|
||||
model.typograpicalStyle = .BodyMedium
|
||||
model.text = errorText
|
||||
model.surface = surface
|
||||
model.disabled = disabled
|
||||
return model
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
//--------------------------------------------------
|
||||
@ -159,7 +204,7 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width)
|
||||
selectorWidthConstraint?.isActive = true
|
||||
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
|
||||
mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||
@ -168,22 +213,32 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
|
||||
}
|
||||
|
||||
func updateLabels(_ viewModel: ModelType) {
|
||||
func updateLabels() {
|
||||
|
||||
//deal with labels
|
||||
if viewModel.shouldShowLabels {
|
||||
if shouldShowLabels {
|
||||
//add the stackview to hold the 2 labels
|
||||
//top label
|
||||
if let labelModel = viewModel.labelModel {
|
||||
primaryLabel.set(with: labelModel)
|
||||
if let labelText {
|
||||
primaryLabel.textPosition = .left
|
||||
primaryLabel.typograpicalStyle = .BoldBodyLarge
|
||||
primaryLabel.text = labelText
|
||||
primaryLabel.surface = surface
|
||||
primaryLabel.disabled = disabled
|
||||
primaryLabel.attributes = labelTextAttributes
|
||||
primaryLabel.isHidden = false
|
||||
} else {
|
||||
primaryLabel.isHidden = true
|
||||
}
|
||||
|
||||
//bottom label
|
||||
if let childModel = viewModel.childModel {
|
||||
secondaryLabel.set(with: childModel)
|
||||
if let childText {
|
||||
secondaryLabel.textPosition = .left
|
||||
secondaryLabel.typograpicalStyle = .BodyLarge
|
||||
secondaryLabel.text = childText
|
||||
secondaryLabel.surface = surface
|
||||
secondaryLabel.disabled = disabled
|
||||
secondaryLabel.attributes = childTextAttributes
|
||||
secondaryLabel.isHidden = false
|
||||
} else {
|
||||
secondaryLabel.isHidden = true
|
||||
@ -199,20 +254,23 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
}
|
||||
|
||||
//either add/remove the error from the main stack
|
||||
if let errorModel = model.errorModel, model.shouldShowError {
|
||||
errorLabel.set(with: errorModel)
|
||||
if let errorText, shouldShowError {
|
||||
errorLabel.textPosition = .left
|
||||
errorLabel.typograpicalStyle = .BodyMedium
|
||||
errorLabel.text = errorText
|
||||
errorLabel.surface = surface
|
||||
errorLabel.disabled = disabled
|
||||
mainStackView.spacing = 8
|
||||
errorLabel.isHidden = false
|
||||
} else {
|
||||
mainStackView.spacing = 0
|
||||
errorLabel.isHidden = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override func reset() {
|
||||
super.reset()
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
setAccessibilityLabel()
|
||||
}
|
||||
|
||||
@ -229,12 +287,12 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - State
|
||||
//--------------------------------------------------
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
updateLabels(viewModel)
|
||||
updateSelector(viewModel)
|
||||
open override func updateView() {
|
||||
updateLabels()
|
||||
updateSelector()
|
||||
setAccessibilityHint()
|
||||
setAccessibilityValue(viewModel.selected)
|
||||
setAccessibilityLabel(viewModel.selected)
|
||||
setAccessibilityValue(isSelected)
|
||||
setAccessibilityLabel(isSelected)
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
}
|
||||
@ -294,11 +352,11 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
return checkboxSize
|
||||
}
|
||||
|
||||
open func updateSelector(_ viewModel: ModelType) {
|
||||
open func updateSelector() {
|
||||
//get the colors
|
||||
let backgroundColor = checkboxBackgroundColorConfiguration.getColor(viewModel)
|
||||
let borderColor = checkboxBorderColorConfiguration.getColor(viewModel)
|
||||
let checkColor = checkboxCheckColorConfiguration.getColor(viewModel)
|
||||
let backgroundColor = checkboxBackgroundColorConfiguration.getColor(self)
|
||||
let borderColor = checkboxBorderColorConfiguration.getColor(self)
|
||||
let checkColor = checkboxCheckColorConfiguration.getColor(self)
|
||||
|
||||
if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) {
|
||||
shapeLayer.removeFromSuperlayer()
|
||||
@ -340,7 +398,7 @@ open class CheckboxBase<ModelType: CheckboxModel>: Control<ModelType> {
|
||||
shapeLayer.lineJoin = .miter
|
||||
shapeLayer.lineWidth = 2
|
||||
CATransaction.withDisabledAnimations {
|
||||
shapeLayer.strokeEnd = model.selected ? 1 : 0
|
||||
shapeLayer.strokeEnd = isSelected ? 1 : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public class CheckboxGroup: CheckboxGroupBase<DefaultCheckboxGroupModel, Checkbox> {
|
||||
public class CheckboxGroup: CheckboxGroupBase<Checkbox> {
|
||||
public override func didSelect(_ selectedControl: Checkbox) {
|
||||
selectedControl.toggle()
|
||||
if selectedControl.isSelected, showError{
|
||||
@ -18,13 +18,25 @@ public class CheckboxGroup: CheckboxGroupBase<DefaultCheckboxGroupModel, Checkbo
|
||||
}
|
||||
}
|
||||
|
||||
public class CheckboxGroupBase<GroupModelType: CheckboxGroupModel, ModelHandlerType: CheckboxBase<GroupModelType.SelectorModelType>>: SelectorGroupHandlerBase<GroupModelType, ModelHandlerType> {
|
||||
public class CheckboxGroupBase<ModelHandlerType: CheckboxBase>: SelectorGroupHandlerBase<ModelHandlerType> {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override var selectorViews: [ModelHandlerType] {
|
||||
didSet {
|
||||
for selector in selectorViews {
|
||||
if !mainStackView.arrangedSubviews.contains(selector) {
|
||||
mainStackView.addArrangedSubview(selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _showError: Bool = false
|
||||
public var showError: Bool {
|
||||
get { model.showError }
|
||||
get { _showError }
|
||||
set {
|
||||
var newShowError = newValue
|
||||
let anySelected = selectorViews.filter { $0.isSelected == true }.count > 0
|
||||
@ -34,8 +46,7 @@ public class CheckboxGroupBase<GroupModelType: CheckboxGroupModel, ModelHandlerT
|
||||
selectorViews.forEach { handler in
|
||||
handler.showError = newShowError
|
||||
}
|
||||
model.showError = newShowError
|
||||
|
||||
_showError = newShowError
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,22 +78,6 @@ public class CheckboxGroupBase<GroupModelType: CheckboxGroupModel, ModelHandlerT
|
||||
mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
||||
mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
||||
}
|
||||
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
for selectorModel in viewModel.selectors {
|
||||
//see if view is there for the model
|
||||
if let foundSelectorView = findSelectorView(id: selectorModel.id) {
|
||||
foundSelectorView.set(with: selectorModel)
|
||||
} else {
|
||||
|
||||
//create view
|
||||
let newSelectorView = createModelHandler(selector: selectorModel)
|
||||
|
||||
self.selectorViews.append(newSelectorView)
|
||||
mainStackView.addArrangedSubview(newSelectorView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var selectedModelHandlers: [ModelHandlerType]? {
|
||||
let selected = selectorViews.filter{ $0.isSelected == true }
|
||||
|
||||
@ -10,57 +10,43 @@ import UIKit
|
||||
import VDSColorTokens
|
||||
import Combine
|
||||
|
||||
public class Label:LabelBase<DefaultLabelModel>{}
|
||||
public class Label: LabelBase {}
|
||||
|
||||
open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProtocol, Resettable {
|
||||
open class LabelBase: UILabel, ModelHandlerable, ViewProtocol, Resettable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Combine Properties
|
||||
//--------------------------------------------------
|
||||
@Published public var model: ModelType = ModelType()
|
||||
public var modelPublisher: Published<ModelType>.Publisher { $model }
|
||||
public var subject = PassthroughSubject<Void, Never>()
|
||||
public var subscribers = Set<AnyCancellable>()
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
@Proxy(\.model.surface)
|
||||
open var surface: Surface
|
||||
open var surface: Surface = .light { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.disabled)
|
||||
open var disabled: Bool {
|
||||
didSet {
|
||||
self.isEnabled = !disabled
|
||||
}
|
||||
}
|
||||
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
|
||||
|
||||
open var attributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||
|
||||
open var typograpicalStyle: TypographicalStyle = .defaultStyle { didSet { subject.send() }}
|
||||
|
||||
open var textPosition: TextPosition = .left { didSet { subject.send() }}
|
||||
|
||||
open override var isEnabled: Bool {
|
||||
get { !model.disabled }
|
||||
get { !disabled }
|
||||
set {
|
||||
//create local vars for clear coding
|
||||
let disabled = !newValue
|
||||
if model.disabled != disabled {
|
||||
model.disabled = disabled
|
||||
if disabled != !newValue {
|
||||
disabled = !newValue
|
||||
}
|
||||
isUserInteractionEnabled = isEnabled
|
||||
subject.send()
|
||||
}
|
||||
}
|
||||
|
||||
@Proxy(\.model.attributes)
|
||||
open var attributes: [any LabelAttributeModel]?
|
||||
|
||||
@Proxy(\.model.typograpicalStyle)
|
||||
open var typograpicalStyle: TypographicalStyle
|
||||
|
||||
@Proxy(\.model.textPosition)
|
||||
open var textPosition: TextPosition
|
||||
|
||||
//can't use @Proxy here
|
||||
override open var text: String? {
|
||||
didSet {
|
||||
if model.text != oldValue {
|
||||
model.text = text
|
||||
}
|
||||
subject.send()
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,17 +67,10 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
}
|
||||
|
||||
public required init(with model: ModelType) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
initialSetup()
|
||||
set(with: model)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
@ -116,11 +95,13 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
|
||||
open func setup() {}
|
||||
|
||||
open func reset() {
|
||||
surface = .light
|
||||
disabled = false
|
||||
attributes = nil
|
||||
typograpicalStyle = .defaultStyle
|
||||
textPosition = .left
|
||||
text = nil
|
||||
attributedText = nil
|
||||
textColor = .black
|
||||
font = TypographicalStyle.BodyLarge.font
|
||||
textAlignment = .left
|
||||
accessibilityCustomActions = []
|
||||
accessibilityTraits = .staticText
|
||||
numberOfLines = 0
|
||||
@ -129,17 +110,12 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
open func updateView(viewModel: ModelType) {
|
||||
textAlignment = viewModel.textPosition.textAlignment
|
||||
textColor = textColorConfiguration.getColor(viewModel)
|
||||
open func updateView() {
|
||||
textAlignment = textPosition.textAlignment
|
||||
textColor = textColorConfiguration.getColor(self)
|
||||
font = typograpicalStyle.font
|
||||
|
||||
if let vdsFont = viewModel.font {
|
||||
font = vdsFont
|
||||
} else {
|
||||
font = TypographicalStyle.defaultStyle.font
|
||||
}
|
||||
|
||||
if let text = viewModel.text, let font = font, let textColor = textColor {
|
||||
if let text = text, let font = font, let textColor = textColor {
|
||||
//clear the arrays holding actions
|
||||
accessibilityCustomActions = []
|
||||
actions = []
|
||||
@ -149,9 +125,9 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
|
||||
let mutableText = NSMutableAttributedString(string: text, attributes: startingAttributes)
|
||||
|
||||
//set the local lineHeight/lineSpacing attributes
|
||||
setStyleAttributes(viewModel: viewModel, attributedString: mutableText)
|
||||
setStyleAttributes(attributedString: mutableText)
|
||||
|
||||
if let attributes = viewModel.attributes {
|
||||
if let attributes = attributes {
|
||||
//loop through the models attributes
|
||||
for attribute in attributes {
|
||||
|
||||
@ -170,42 +146,40 @@ open class LabelBase<ModelType: LabelModel>: UILabel, ModelHandlerable, ViewProt
|
||||
}
|
||||
|
||||
//only enabled if enabled and has actions
|
||||
isUserInteractionEnabled = !viewModel.disabled && !actions.isEmpty
|
||||
isUserInteractionEnabled = !disabled && !actions.isEmpty
|
||||
|
||||
//set the attributed text
|
||||
attributedText = mutableText
|
||||
} else {
|
||||
text = viewModel.text
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private Attributes
|
||||
private func setStyleAttributes(viewModel: ModelType, attributedString: NSMutableAttributedString) {
|
||||
private func setStyleAttributes(attributedString: NSMutableAttributedString) {
|
||||
//get the range
|
||||
let entireRange = NSRange(location: 0, length: attributedString.length)
|
||||
|
||||
//set letterSpacing
|
||||
if viewModel.typograpicalStyle.letterSpacing > 0.0 {
|
||||
attributedString.addAttribute(.kern, value: viewModel.typograpicalStyle.letterSpacing, range: entireRange)
|
||||
if typograpicalStyle.letterSpacing > 0.0 {
|
||||
attributedString.addAttribute(.kern, value: typograpicalStyle.letterSpacing, range: entireRange)
|
||||
}
|
||||
|
||||
//set lineHeight
|
||||
if viewModel.typograpicalStyle.lineHeight > 0.0 {
|
||||
let lineHeight = viewModel.typograpicalStyle.lineHeight
|
||||
if typograpicalStyle.lineHeight > 0.0 {
|
||||
let lineHeight = typograpicalStyle.lineHeight
|
||||
let adjustment = lineHeight > font.lineHeight ? 2.0 : 1.0
|
||||
let baselineOffset = (lineHeight - font.lineHeight) / 2.0 / adjustment
|
||||
let paragraph = NSMutableParagraphStyle().with {
|
||||
$0.maximumLineHeight = lineHeight
|
||||
$0.minimumLineHeight = lineHeight
|
||||
$0.alignment = viewModel.textPosition.textAlignment
|
||||
$0.alignment = textPosition.textAlignment
|
||||
$0.lineBreakMode = lineBreakMode
|
||||
}
|
||||
attributedString.addAttribute(.baselineOffset, value: baselineOffset, range: entireRange)
|
||||
attributedString.addAttribute( .paragraphStyle, value: paragraph, range: entireRange)
|
||||
|
||||
} else if viewModel.textPosition != .left {
|
||||
} else if textPosition != .left {
|
||||
let paragraph = NSMutableParagraphStyle().with {
|
||||
$0.alignment = viewModel.textPosition.textAlignment
|
||||
$0.alignment = textPosition.textAlignment
|
||||
$0.lineBreakMode = lineBreakMode
|
||||
}
|
||||
attributedString.addAttribute( .paragraphStyle, value: paragraph, range: entireRange)
|
||||
|
||||
@ -11,9 +11,9 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
public class RadioBox: RadioBoxBase<DefaultRadioBoxModel>{}
|
||||
public class RadioBox: RadioBoxBase{}
|
||||
|
||||
public class SolorRadioBox: RadioBoxBase<DefaultRadioBoxModel>{
|
||||
public class SolorRadioBox: RadioBoxBase{
|
||||
|
||||
public override func initialSetup() {
|
||||
super.initialSetup()
|
||||
@ -24,8 +24,25 @@ public class SolorRadioBox: RadioBoxBase<DefaultRadioBoxModel>{
|
||||
}
|
||||
}
|
||||
|
||||
open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
open class RadioBoxBase: Control, BinaryColorable, Accessable {
|
||||
//--------------------------------------------------
|
||||
// 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: - Private Properties
|
||||
//--------------------------------------------------
|
||||
@ -67,61 +84,42 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
}()
|
||||
|
||||
//can't bind to @Proxy
|
||||
open override var isSelected: Bool {
|
||||
get { model.selected }
|
||||
set {
|
||||
if model.selected != newValue {
|
||||
model.selected = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Proxy(\.model.text)
|
||||
open var text: String
|
||||
open var text: String = "Default Text" { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.subText)
|
||||
open var subText: String?
|
||||
open var textAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||
|
||||
open var subText: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.subTextRight)
|
||||
open var subTextRight: String?
|
||||
open var subTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.strikethrough)
|
||||
open var strikethrough: Bool
|
||||
open var subTextRight: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.inputId)
|
||||
open var inputId: String?
|
||||
open var subTextRightAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.value)
|
||||
open var value: AnyHashable?
|
||||
open var strikethrough: Bool = false { didSet { subject.send() }}
|
||||
|
||||
open var inputId: String? { didSet { subject.send() }}
|
||||
|
||||
open var value: AnyHashable? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataAnalyticsTrack)
|
||||
open var dataAnalyticsTrack: String?
|
||||
open var dataAnalyticsTrack: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataClickStream)
|
||||
open var dataClickStream: String?
|
||||
open var dataClickStream: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataTrack)
|
||||
open var dataTrack: String?
|
||||
open var dataTrack: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintEnabled)
|
||||
open var accessibilityHintEnabled: String?
|
||||
open var accessibilityHintEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintDisabled)
|
||||
open var accessibilityHintDisabled: String?
|
||||
open var accessibilityHintDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueEnabled)
|
||||
open var accessibilityValueEnabled: String?
|
||||
open var accessibilityValueEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueDisabled)
|
||||
open var accessibilityValueDisabled: String?
|
||||
open var accessibilityValueDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelEnabled)
|
||||
open var accessibilityLabelEnabled: String?
|
||||
open var accessibilityLabelEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelDisabled)
|
||||
open var accessibilityLabelDisabled: String?
|
||||
open var accessibilityLabelDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
//functions
|
||||
//--------------------------------------------------
|
||||
@ -153,7 +151,7 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
selectorLeftLabelStackView.spacing = 4
|
||||
selectorLeftLabelStackView.isHidden = false
|
||||
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
|
||||
selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
selectorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||
@ -167,23 +165,38 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
|
||||
}
|
||||
|
||||
func updateLabels(_ viewModel: ModelType) {
|
||||
func updateLabels() {
|
||||
|
||||
//add the stackview to hold the 2 labels
|
||||
//text label
|
||||
textLabel.set(with: viewModel.textModel)
|
||||
|
||||
textLabel.textPosition = .left
|
||||
textLabel.typograpicalStyle = .BoldBodyLarge
|
||||
textLabel.text = text
|
||||
textLabel.surface = surface
|
||||
textLabel.disabled = disabled
|
||||
textLabel.attributes = textAttributes
|
||||
|
||||
//subText label
|
||||
if let subTextModel = viewModel.subTextModel {
|
||||
subTextLabel.set(with: subTextModel)
|
||||
if let subText {
|
||||
subTextLabel.textPosition = .left
|
||||
subTextLabel.typograpicalStyle = .BodyLarge
|
||||
subTextLabel.text = subText
|
||||
subTextLabel.surface = surface
|
||||
subTextLabel.disabled = disabled
|
||||
subTextLabel.attributes = subTextAttributes
|
||||
subTextLabel.isHidden = false
|
||||
} else {
|
||||
subTextLabel.isHidden = true
|
||||
}
|
||||
|
||||
//subTextRight label
|
||||
if let subTextRightModel = viewModel.subTextRightModel {
|
||||
subTextRightLabel.set(with: subTextRightModel)
|
||||
if let subTextRight {
|
||||
subTextRightLabel.textPosition = .right
|
||||
subTextRightLabel.typograpicalStyle = .BodyLarge
|
||||
subTextRightLabel.text = subTextRight
|
||||
subTextRightLabel.surface = surface
|
||||
subTextRightLabel.disabled = disabled
|
||||
subTextRightLabel.attributes = subTextRightAttributes
|
||||
subTextRightLabel.isHidden = false
|
||||
} else {
|
||||
subTextRightLabel.isHidden = true
|
||||
@ -192,7 +205,7 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
|
||||
public override func reset() {
|
||||
super.reset()
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
setAccessibilityLabel()
|
||||
}
|
||||
|
||||
@ -206,12 +219,12 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - State
|
||||
//--------------------------------------------------
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
updateLabels(viewModel)
|
||||
updateSelector(viewModel)
|
||||
open override func updateView() {
|
||||
updateLabels()
|
||||
updateSelector()
|
||||
setAccessibilityHint()
|
||||
setAccessibilityValue(viewModel.selected)
|
||||
setAccessibilityLabel(viewModel.selected)
|
||||
setAccessibilityValue(isSelected)
|
||||
setAccessibilityLabel(isSelected)
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
}
|
||||
@ -256,11 +269,11 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
|
||||
private var shapeLayer: CAShapeLayer?
|
||||
|
||||
open func updateSelector(_ viewModel: ModelType) {
|
||||
open func updateSelector() {
|
||||
//get the colors
|
||||
let backgroundColor = radioBoxBackgroundColorConfiguration.getColor(viewModel)
|
||||
let borderColor = radioBoxBorderColorConfiguration.getColor(viewModel)
|
||||
let borderWidth = viewModel.selected ? selectorBorderWidthSelected : selectorBorderWidth
|
||||
let backgroundColor = radioBoxBackgroundColorConfiguration.getColor(self)
|
||||
let borderColor = radioBoxBorderColorConfiguration.getColor(self)
|
||||
let borderWidth = isSelected ? selectorBorderWidthSelected : selectorBorderWidth
|
||||
|
||||
selectorView.backgroundColor = backgroundColor
|
||||
selectorView.layer.borderColor = borderColor.cgColor
|
||||
@ -278,12 +291,12 @@ open class RadioBoxBase<ModelType: RadioBoxModel>: Control<ModelType> {
|
||||
|
||||
open override func draw(_ layer: CALayer, in ctx: CGContext) {
|
||||
|
||||
let borderColor = radioBoxBorderColorConfiguration.getColor(model)
|
||||
let borderColor = radioBoxBorderColorConfiguration.getColor(self)
|
||||
|
||||
shapeLayer?.removeFromSuperlayer()
|
||||
shapeLayer = nil
|
||||
|
||||
if model.strikethrough {
|
||||
if strikethrough {
|
||||
let bounds = selectorView.bounds
|
||||
let length = max(bounds.size.height, bounds.size.width)
|
||||
guard length > 0.0, shapeLayer == nil else { return }
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public class RadioBoxGroup: RadioBoxGroupBase<DefaultRadioBoxGroupModel, RadioBox> {
|
||||
public class RadioBoxGroup: RadioBoxGroupBase<RadioBox> {
|
||||
|
||||
public override func didSelect(_ selectedControl: RadioBox) {
|
||||
let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first
|
||||
@ -18,8 +18,21 @@ public class RadioBoxGroup: RadioBoxGroupBase<DefaultRadioBoxGroupModel, RadioBo
|
||||
}
|
||||
}
|
||||
|
||||
public class RadioBoxGroupBase<GroupModelType: RadioBoxGroupModel, ModelHandlerType: RadioBoxBase<GroupModelType.SelectorModelType>>: SelectorGroupSelectedHandlerBase<GroupModelType, ModelHandlerType> {
|
||||
|
||||
public class RadioBoxGroupBase<ModelHandlerType: RadioBoxBase>: SelectorGroupSelectedHandlerBase<ModelHandlerType> {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
public override var selectorViews: [ModelHandlerType] {
|
||||
didSet {
|
||||
for selector in selectorViews {
|
||||
if !mainStackView.arrangedSubviews.contains(selector) {
|
||||
mainStackView.addArrangedSubview(selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
//--------------------------------------------------
|
||||
@ -68,22 +81,6 @@ public class RadioBoxGroupBase<GroupModelType: RadioBoxGroupModel, ModelHandlerT
|
||||
}
|
||||
}.store(in: &subscribers)
|
||||
}
|
||||
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
for selectorModel in viewModel.selectors {
|
||||
//see if view is there for the model
|
||||
if let foundSelectorView = findSelectorView(id: selectorModel.id) {
|
||||
foundSelectorView.set(with: selectorModel)
|
||||
} else {
|
||||
|
||||
//create view
|
||||
let newSelectorView = createModelHandler(selector: selectorModel)
|
||||
|
||||
self.selectorViews.append(newSelectorView)
|
||||
mainStackView.addArrangedSubview(newSelectorView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var selectedModelHandler: ModelHandlerType? {
|
||||
if let index = selectorViews.firstIndex(where: { element in
|
||||
|
||||
@ -10,7 +10,7 @@ import UIKit
|
||||
import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
|
||||
public class RadioButton: RadioButtonBase<DefaultRadioButtonModel>{
|
||||
public class RadioButton: RadioButtonBase {
|
||||
//for groups allows "toggle"
|
||||
open override func toggle() {
|
||||
//removed error
|
||||
@ -21,7 +21,7 @@ public class RadioButton: RadioButtonBase<DefaultRadioButtonModel>{
|
||||
}
|
||||
}
|
||||
|
||||
public class SoloRadioButton: RadioButtonBase<DefaultRadioButtonModel>{
|
||||
public class SoloRadioButton: RadioButtonBase {
|
||||
public override func initialSetup() {
|
||||
super.initialSetup()
|
||||
publisher(for: .touchUpInside)
|
||||
@ -31,11 +31,38 @@ public class SoloRadioButton: RadioButtonBase<DefaultRadioButtonModel>{
|
||||
}
|
||||
}
|
||||
|
||||
open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
open class RadioButtonBase: Control, Accessable, BinaryColorable, Errorable {
|
||||
//--------------------------------------------------
|
||||
// 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: - Private Properties
|
||||
//--------------------------------------------------
|
||||
private var shouldShowError: Bool {
|
||||
guard showError && !disabled && errorText?.isEmpty == false else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
private var shouldShowLabels: Bool {
|
||||
guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
private var mainStackView: UIStackView = {
|
||||
return UIStackView().with {
|
||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||
@ -73,61 +100,40 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
}()
|
||||
|
||||
//can't bind to @Proxy
|
||||
open override var isSelected: Bool {
|
||||
get { model.selected }
|
||||
set {
|
||||
if model.selected != newValue {
|
||||
model.selected = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Proxy(\.model.labelText)
|
||||
open var labelText: String?
|
||||
open var labelText: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.childText)
|
||||
open var childText: String?
|
||||
open var labelTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||
|
||||
open var childText: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.showError)
|
||||
open var showError: Bool
|
||||
|
||||
@Proxy(\.model.errorText)
|
||||
open var errorText: String?
|
||||
open var childTextAttributes: [any LabelAttributeModel]? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.inputId)
|
||||
open var inputId: String?
|
||||
open var showError: Bool = false { didSet { subject.send() }}
|
||||
|
||||
open var errorText: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.value)
|
||||
open var value: AnyHashable?
|
||||
|
||||
@Proxy(\.model.dataAnalyticsTrack)
|
||||
open var dataAnalyticsTrack: String?
|
||||
open var inputId: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataClickStream)
|
||||
open var dataClickStream: String?
|
||||
open var value: AnyHashable? { didSet { subject.send() }}
|
||||
|
||||
open var dataAnalyticsTrack: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataTrack)
|
||||
open var dataTrack: String?
|
||||
open var dataClickStream: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintEnabled)
|
||||
open var accessibilityHintEnabled: String?
|
||||
open var dataTrack: String? { didSet { subject.send() }}
|
||||
|
||||
open var accessibilityHintEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintDisabled)
|
||||
open var accessibilityHintDisabled: String?
|
||||
open var accessibilityHintDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueEnabled)
|
||||
open var accessibilityValueEnabled: String?
|
||||
open var accessibilityValueEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueDisabled)
|
||||
open var accessibilityValueDisabled: String?
|
||||
open var accessibilityValueDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelEnabled)
|
||||
open var accessibilityLabelEnabled: String?
|
||||
open var accessibilityLabelEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelDisabled)
|
||||
open var accessibilityLabelDisabled: String?
|
||||
open var accessibilityLabelDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
@ -167,7 +173,7 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width)
|
||||
selectorWidthConstraint?.isActive = true
|
||||
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
|
||||
mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||
@ -176,22 +182,32 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
|
||||
}
|
||||
|
||||
func updateLabels(_ viewModel: ModelType) {
|
||||
func updateLabels() {
|
||||
|
||||
//deal with labels
|
||||
if viewModel.shouldShowLabels {
|
||||
if shouldShowLabels {
|
||||
//add the stackview to hold the 2 labels
|
||||
//top label
|
||||
if let labelModel = viewModel.labelModel {
|
||||
primaryLabel.set(with: labelModel)
|
||||
if let labelText {
|
||||
primaryLabel.textPosition = .left
|
||||
primaryLabel.typograpicalStyle = .BoldBodyLarge
|
||||
primaryLabel.text = labelText
|
||||
primaryLabel.surface = surface
|
||||
primaryLabel.disabled = disabled
|
||||
primaryLabel.attributes = labelTextAttributes
|
||||
primaryLabel.isHidden = false
|
||||
} else {
|
||||
primaryLabel.isHidden = true
|
||||
}
|
||||
|
||||
//bottom label
|
||||
if let childModel = viewModel.childModel {
|
||||
secondaryLabel.set(with: childModel)
|
||||
if let childText {
|
||||
secondaryLabel.textPosition = .left
|
||||
secondaryLabel.typograpicalStyle = .BodyLarge
|
||||
secondaryLabel.text = childText
|
||||
secondaryLabel.surface = surface
|
||||
secondaryLabel.disabled = disabled
|
||||
secondaryLabel.attributes = childTextAttributes
|
||||
secondaryLabel.isHidden = false
|
||||
} else {
|
||||
secondaryLabel.isHidden = true
|
||||
@ -207,8 +223,12 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
}
|
||||
|
||||
//either add/remove the error from the main stack
|
||||
if let errorModel = model.errorModel, model.shouldShowError {
|
||||
errorLabel.set(with: errorModel)
|
||||
if let errorText, shouldShowError {
|
||||
errorLabel.textPosition = .left
|
||||
errorLabel.typograpicalStyle = .BodyMedium
|
||||
errorLabel.text = errorText
|
||||
errorLabel.surface = surface
|
||||
errorLabel.disabled = disabled
|
||||
mainStackView.spacing = 8
|
||||
errorLabel.isHidden = false
|
||||
} else {
|
||||
@ -220,7 +240,7 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
|
||||
public override func reset() {
|
||||
super.reset()
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
setAccessibilityLabel()
|
||||
}
|
||||
|
||||
@ -239,12 +259,12 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - State
|
||||
//--------------------------------------------------
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
updateLabels(viewModel)
|
||||
updateSelector(viewModel)
|
||||
open override func updateView() {
|
||||
updateLabels()
|
||||
updateSelector()
|
||||
setAccessibilityHint()
|
||||
setAccessibilityValue(viewModel.selected)
|
||||
setAccessibilityLabel(viewModel.selected)
|
||||
setAccessibilityValue(isSelected)
|
||||
setAccessibilityLabel(isSelected)
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
}
|
||||
@ -295,7 +315,7 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
radioButtonSize
|
||||
}
|
||||
|
||||
open func updateSelector(_ viewModel: ModelType) {
|
||||
open func updateSelector() {
|
||||
|
||||
if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) {
|
||||
shapeLayer.removeFromSuperlayer()
|
||||
@ -307,9 +327,9 @@ open class RadioButtonBase<ModelType: RadioButtonModel>: Control<ModelType> {
|
||||
guard length > 0.0, shapeLayer == nil else { return }
|
||||
|
||||
//get the colors
|
||||
let backgroundColor = radioButtonBackgroundColorConfiguration.getColor(viewModel)
|
||||
let borderColor = radioButtonBorderColorConfiguration.getColor(viewModel)
|
||||
let radioSelectedColor = radioButtonCheckColorConfiguration.getColor(viewModel)
|
||||
let backgroundColor = radioButtonBackgroundColorConfiguration.getColor(self)
|
||||
let borderColor = radioButtonBorderColorConfiguration.getColor(self)
|
||||
let radioSelectedColor = radioButtonCheckColorConfiguration.getColor(self)
|
||||
|
||||
selectorView.backgroundColor = backgroundColor
|
||||
selectorView.layer.borderColor = borderColor.cgColor
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public class RadioButtonGroup: RadioButtonGroupBase<DefaultRadioButtonGroupModel, RadioButton> {
|
||||
public class RadioButtonGroup: RadioButtonGroupBase<RadioButton> {
|
||||
|
||||
public override func didSelect(_ selectedControl: RadioButton) {
|
||||
let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first
|
||||
@ -21,22 +21,33 @@ public class RadioButtonGroup: RadioButtonGroupBase<DefaultRadioButtonGroupModel
|
||||
}
|
||||
}
|
||||
|
||||
public class RadioButtonGroupBase<GroupModelType: RadioButtonGroupModel, ModelHandlerType: RadioButtonBase<GroupModelType.SelectorModelType>>: SelectorGroupSelectedHandlerBase<GroupModelType, ModelHandlerType> {
|
||||
public class RadioButtonGroupBase<ModelHandlerType: RadioButtonBase>: SelectorGroupSelectedHandlerBase<ModelHandlerType> {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
public override var selectorViews: [ModelHandlerType] {
|
||||
didSet {
|
||||
for selector in selectorViews {
|
||||
if !mainStackView.arrangedSubviews.contains(selector) {
|
||||
mainStackView.addArrangedSubview(selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var _showError: Bool = false
|
||||
public var showError: Bool {
|
||||
get { model.showError }
|
||||
get { _showError }
|
||||
set {
|
||||
var newShowError = newValue
|
||||
if selectedModel != nil, newShowError {
|
||||
if selectedModelHandler != nil, newShowError {
|
||||
newShowError = false
|
||||
}
|
||||
selectorViews.forEach { handler in
|
||||
handler.showError = newShowError
|
||||
}
|
||||
model.showError = newShowError
|
||||
_showError = newShowError
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,22 +79,6 @@ public class RadioButtonGroupBase<GroupModelType: RadioButtonGroupModel, ModelHa
|
||||
mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
||||
mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
||||
}
|
||||
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
for selectorModel in viewModel.selectors {
|
||||
//see if view is there for the model
|
||||
if let foundSelectorView = findSelectorView(id: selectorModel.id) {
|
||||
foundSelectorView.set(with: selectorModel)
|
||||
} else {
|
||||
|
||||
//create view
|
||||
let newSelectorView = createModelHandler(selector: selectorModel)
|
||||
|
||||
self.selectorViews.append(newSelectorView)
|
||||
mainStackView.addArrangedSubview(newSelectorView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var selectedModelHandler: ModelHandlerType? {
|
||||
if let index = selectorViews.firstIndex(where: { element in
|
||||
|
||||
@ -11,7 +11,7 @@ import VDSColorTokens
|
||||
import VDSFormControlsTokens
|
||||
import Combine
|
||||
|
||||
public class RadioSwatch: RadioSwatchBase<DefaultRadioSwatchModel>{
|
||||
public class RadioSwatch: RadioSwatchBase{
|
||||
public override func initialSetup() {
|
||||
super.initialSetup()
|
||||
publisher(for: .touchUpInside)
|
||||
@ -21,8 +21,26 @@ public class RadioSwatch: RadioSwatchBase<DefaultRadioSwatchModel>{
|
||||
}
|
||||
}
|
||||
|
||||
open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
|
||||
open class RadioSwatchBase: Control, Accessable, DataTrackable, BinaryColorable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// 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 Properties
|
||||
//--------------------------------------------------
|
||||
@ -38,55 +56,38 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
$0.contentMode = .scaleAspectFit
|
||||
}
|
||||
}()
|
||||
|
||||
open var fillImage: UIImage? { didSet { subject.send() }}
|
||||
|
||||
open var text: String = "" { didSet { subject.send() }}
|
||||
|
||||
open var primaryColor: UIColor? { didSet { subject.send() }}
|
||||
|
||||
open var secondaryColor: UIColor? { didSet { subject.send() }}
|
||||
|
||||
open var strikethrough: Bool = false { didSet { subject.send() }}
|
||||
|
||||
//can't bind to @Proxy
|
||||
open override var isSelected: Bool {
|
||||
get { model.selected }
|
||||
set {
|
||||
if model.selected != newValue {
|
||||
model.selected = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Proxy(\.model.text)
|
||||
open var text: String
|
||||
|
||||
@Proxy(\.model.strikethrough)
|
||||
open var strikethrough: Bool
|
||||
open var inputId: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.inputId)
|
||||
open var inputId: String?
|
||||
open var value: AnyHashable? { didSet { subject.send() }}
|
||||
|
||||
open var dataAnalyticsTrack: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.value)
|
||||
open var value: AnyHashable?
|
||||
|
||||
@Proxy(\.model.dataAnalyticsTrack)
|
||||
open var dataAnalyticsTrack: String?
|
||||
open var dataClickStream: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataClickStream)
|
||||
open var dataClickStream: String?
|
||||
open var dataTrack: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataTrack)
|
||||
open var dataTrack: String?
|
||||
|
||||
@Proxy(\.model.accessibilityHintEnabled)
|
||||
open var accessibilityHintEnabled: String?
|
||||
open var accessibilityHintEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintDisabled)
|
||||
open var accessibilityHintDisabled: String?
|
||||
open var accessibilityHintDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueEnabled)
|
||||
open var accessibilityValueEnabled: String?
|
||||
open var accessibilityValueEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueDisabled)
|
||||
open var accessibilityValueDisabled: String?
|
||||
open var accessibilityValueDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelEnabled)
|
||||
open var accessibilityLabelEnabled: String?
|
||||
open var accessibilityLabelEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelDisabled)
|
||||
open var accessibilityLabelDisabled: String?
|
||||
open var accessibilityLabelDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
//functions
|
||||
//--------------------------------------------------
|
||||
@ -107,7 +108,7 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
|
||||
selectorView.addSubview(fillView)
|
||||
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
|
||||
selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
selectorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||
@ -128,7 +129,7 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
|
||||
public override func reset() {
|
||||
super.reset()
|
||||
updateSelector(model)
|
||||
updateSelector()
|
||||
setAccessibilityLabel()
|
||||
}
|
||||
|
||||
@ -140,11 +141,11 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - State
|
||||
//--------------------------------------------------
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
updateSelector(viewModel)
|
||||
open override func updateView() {
|
||||
updateSelector()
|
||||
setAccessibilityHint()
|
||||
setAccessibilityValue(viewModel.selected)
|
||||
setAccessibilityLabel(viewModel.selected)
|
||||
setAccessibilityValue(isSelected)
|
||||
setAccessibilityLabel(isSelected)
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
}
|
||||
@ -199,15 +200,15 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
return swatchSize
|
||||
}
|
||||
|
||||
open func updateSelector(_ viewModel: ModelType) {
|
||||
open func updateSelector() {
|
||||
//get the colors
|
||||
let backgroundColor = radioSwatchBackgroundColorConfiguration.getColor(viewModel)
|
||||
let borderColor = viewModel.selected ? radioSwatchBorderColorConfiguration.getColor(viewModel) : .clear
|
||||
let fillBorderColor = radioSwatchFillBorderColorConfiguration.getColor(viewModel)
|
||||
let backgroundColor = radioSwatchBackgroundColorConfiguration.getColor(self)
|
||||
let borderColor = isSelected ? radioSwatchBorderColorConfiguration.getColor(self) : .clear
|
||||
let fillBorderColor = radioSwatchFillBorderColorConfiguration.getColor(self)
|
||||
selectorView.backgroundColor = backgroundColor
|
||||
selectorView.layer.borderColor = borderColor.cgColor
|
||||
selectorView.layer.cornerRadius = selectorView.bounds.width * 0.5
|
||||
selectorView.layer.borderWidth = viewModel.selected ? selectorBorderWidth : 0
|
||||
selectorView.layer.borderWidth = isSelected ? selectorBorderWidth : 0
|
||||
selectorView.layer.masksToBounds = true
|
||||
|
||||
gradientLayer?.removeFromSuperlayer()
|
||||
@ -215,14 +216,14 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
|
||||
var fillColorBackground: UIColor = .clear
|
||||
|
||||
if let fillImage = viewModel.fillImage {
|
||||
fillView.image = viewModel.disabled ? fillImage.image(alpha: disabledAlpha) : fillImage
|
||||
if let fillImage {
|
||||
fillView.image = disabled ? fillImage.image(alpha: disabledAlpha) : fillImage
|
||||
|
||||
} else {
|
||||
fillView.image = nil
|
||||
if let primary = viewModel.primaryColor, let secondary = viewModel.secondaryColor {
|
||||
let firstColor = viewModel.disabled ? primary.withAlphaComponent(disabledAlpha) : primary
|
||||
let secondColor = viewModel.disabled ? secondary.withAlphaComponent(disabledAlpha) : secondary
|
||||
if let primary = primaryColor, let secondary = secondaryColor {
|
||||
let firstColor = disabled ? primary.withAlphaComponent(disabledAlpha) : primary
|
||||
let secondColor = disabled ? secondary.withAlphaComponent(disabledAlpha) : secondary
|
||||
let gradient = CAGradientLayer()
|
||||
gradientLayer = gradient
|
||||
gradient.frame = fillView.bounds
|
||||
@ -231,10 +232,11 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
gradient.transform = CATransform3DMakeRotation(135.0 / 180.0 * .pi, 0.0, 0.0, 1.0)
|
||||
fillView.layer.addSublayer(gradient)
|
||||
} else {
|
||||
fillColorBackground = viewModel.primaryColor ?? .white
|
||||
fillColorBackground = primaryColor ?? .white
|
||||
}
|
||||
}
|
||||
fillView.backgroundColor = viewModel.disabled ? fillColorBackground.withAlphaComponent(disabledAlpha) : fillColorBackground
|
||||
|
||||
fillView.backgroundColor = disabled ? fillColorBackground.withAlphaComponent(disabledAlpha) : fillColorBackground
|
||||
fillView.layer.borderColor = fillBorderColor.cgColor
|
||||
fillView.layer.cornerRadius = fillView.bounds.width * 0.5
|
||||
fillView.layer.borderWidth = selectorBorderWidth
|
||||
@ -250,12 +252,12 @@ open class RadioSwatchBase<ModelType: RadioSwatchModel>: Control<ModelType> {
|
||||
|
||||
open override func draw(_ layer: CALayer, in ctx: CGContext) {
|
||||
|
||||
let borderColor = radioSwatchBorderColorConfiguration.getColor(model)
|
||||
let borderColor = radioSwatchBorderColorConfiguration.getColor(self)
|
||||
|
||||
shapeLayer?.removeFromSuperlayer()
|
||||
shapeLayer = nil
|
||||
|
||||
if model.strikethrough {
|
||||
if strikethrough {
|
||||
let bounds = selectorView.bounds
|
||||
let length = max(bounds.size.height, bounds.size.width)
|
||||
guard length > 0.0, shapeLayer == nil else { return }
|
||||
|
||||
@ -9,29 +9,31 @@ import Foundation
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
public class RadioSwatchGroup: RadioSwatchGroupBase<DefaultRadioSwatchGroupModel, RadioSwatch> {
|
||||
public class RadioSwatchGroup: RadioSwatchGroupBase<RadioSwatch> {
|
||||
|
||||
public override func didSelect(selector: RadioSwatch) {
|
||||
if let index = model.selectors.firstIndex(where: {$0.selected == true }),
|
||||
if let index = selectorViews.firstIndex(where: {$0.isSelected == true }),
|
||||
let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0)) as? CollectionViewCell<RadioSwatch> {
|
||||
cell.modelHandler.toggle()
|
||||
}
|
||||
selector.toggle()
|
||||
label.text = selector.model.text
|
||||
label.text = selector.text
|
||||
valueChanged()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHandlerType: RadioSwatchBase<GroupModelType.SelectorModelType>>: Control<GroupModelType>, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
|
||||
public class RadioSwatchGroupBase<ModelHandlerType: RadioSwatchBase>: SelectorGroupSelectedHandlerBase<ModelHandlerType>, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
public var selectedModel: ModelHandlerType.ModelType? {
|
||||
return model.selectedModel
|
||||
public override var selectorViews: [ModelHandlerType] {
|
||||
didSet {
|
||||
collectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private Properties
|
||||
//--------------------------------------------------
|
||||
@ -109,16 +111,14 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
|
||||
}
|
||||
|
||||
open func setHeight() {
|
||||
let swatches = model.selectors
|
||||
|
||||
guard swatches.count > 0 else {
|
||||
guard selectorViews.count > 0 else {
|
||||
collectionViewHeight?.constant = 0
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate the height
|
||||
let swatchesInRow = floor(CGFloat(collectionView.bounds.width/(cellSize + itemSpacing)))
|
||||
let numberOfRows = ceil(CGFloat(swatches.count)/swatchesInRow)
|
||||
let numberOfRows = ceil(CGFloat(selectorViews.count)/swatchesInRow)
|
||||
let height = (numberOfRows * cellSize) + (itemSpacing * (numberOfRows-1))
|
||||
|
||||
collectionViewHeight?.constant = CGFloat(height)
|
||||
@ -130,31 +130,23 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
|
||||
collectionView.dataSource = self
|
||||
}
|
||||
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
label.set(with: viewModel.labelModel)
|
||||
open override func updateView() {
|
||||
label.textPosition = .left
|
||||
label.typograpicalStyle = .BodySmall
|
||||
label.text = selectedHandler?.text ?? " "
|
||||
label.surface = surface
|
||||
label.disabled = disabled
|
||||
collectionView.reloadData()
|
||||
setNeedsLayout()
|
||||
}
|
||||
|
||||
//Refactor into new CollectionView Selector protocol
|
||||
public func updateSelectors(){
|
||||
let selectors = model.selectors.compactMap { existing in
|
||||
return existing.copyWith {
|
||||
$0.disabled = disabled
|
||||
$0.surface = surface
|
||||
}
|
||||
}
|
||||
model.selectors = selectors
|
||||
}
|
||||
|
||||
public func replace(viewModel: ModelHandlerType.ModelType){
|
||||
if let index = model.selectors.firstIndex(where: { element in
|
||||
return element.id == viewModel.id
|
||||
}) {
|
||||
model.selectors[index] = viewModel
|
||||
}
|
||||
}
|
||||
|
||||
private func updateSelectors() {
|
||||
for selector in selectorViews {
|
||||
selector.surface = surface
|
||||
selector.disabled = disabled
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - UICollectionViewDelegateFlowLayout
|
||||
//--------------------------------------------------
|
||||
@ -166,7 +158,7 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
|
||||
// MARK: - UICollectionViewDelegate
|
||||
//--------------------------------------------------
|
||||
open func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
|
||||
return !model.selectors[indexPath.row].disabled
|
||||
return !selectorViews[indexPath.row].disabled
|
||||
}
|
||||
|
||||
open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
@ -182,34 +174,16 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
|
||||
}
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return model.selectors.count
|
||||
return selectorViews.count
|
||||
}
|
||||
|
||||
var cellsubs: [Int: AnyCancellable] = [:]
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as? CollectionViewCell<ModelHandlerType> else { return UICollectionViewCell() }
|
||||
|
||||
let model = model.selectors[indexPath.row]
|
||||
let model = selectorViews[indexPath.row]
|
||||
cell.modelHandler = selectorViews[indexPath.row]
|
||||
cell.modelHandler.isUserInteractionEnabled = false
|
||||
|
||||
//cancel if sub exists
|
||||
if let sub = cellsubs[indexPath.row] {
|
||||
sub.cancel()
|
||||
cellsubs[indexPath.row] = nil
|
||||
}
|
||||
|
||||
let sub = cell.modelHandler
|
||||
.handlerPublisher()
|
||||
.sink { [weak self] changed in
|
||||
if cell.modelHandler.shouldUpdateView(viewModel: model) {
|
||||
print("Model Change: \(changed)")
|
||||
self?.replace(viewModel: changed)
|
||||
}
|
||||
}
|
||||
cellsubs[indexPath.row] = sub
|
||||
|
||||
cell.set(with: model)
|
||||
return cell
|
||||
|
||||
}
|
||||
@ -217,18 +191,4 @@ public class RadioSwatchGroupBase<GroupModelType: RadioSwatchGroupModel, ModelHa
|
||||
open func didSelect(selector: ModelHandlerType) {
|
||||
fatalError("Must override didSelect")
|
||||
}
|
||||
|
||||
public func valueChanged() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.ModelStateDebounce) { [weak self] in
|
||||
self?.sendActions(for: .valueChanged)
|
||||
}
|
||||
}
|
||||
|
||||
public var selectedModelHandler: ModelHandlerType? {
|
||||
guard let index = model.selectors.firstIndex(where: {$0.selected == true }),
|
||||
let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0)) as? CollectionViewCell<ModelHandlerType> else {
|
||||
return nil
|
||||
}
|
||||
return cell.modelHandler
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ import Combine
|
||||
Knob: The circular indicator that slides on the container.
|
||||
*/
|
||||
|
||||
public class Toggle: ToggleBase<DefaultToggleModel>{
|
||||
public class Toggle: ToggleBase{
|
||||
public override func initialSetup() {
|
||||
super.initialSetup()
|
||||
publisher(for: .touchUpInside)
|
||||
@ -28,7 +28,25 @@ public class Toggle: ToggleBase<DefaultToggleModel>{
|
||||
}
|
||||
}
|
||||
|
||||
open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
open class ToggleBase: Control, Accessable, DataTrackable, BinaryColorable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// 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: - Private Properties
|
||||
@ -86,62 +104,60 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
$0.forFalse.disabled.darkColor = VDSColor.paletteGray44
|
||||
}
|
||||
|
||||
private var typograpicalStyle: TypographicalStyle {
|
||||
if textSize == .small {
|
||||
if textWeight == .bold {
|
||||
return .BoldBodySmall
|
||||
} else {
|
||||
return .BodySmall
|
||||
}
|
||||
} else {
|
||||
if textWeight == .bold {
|
||||
return .BoldBodyLarge
|
||||
} else {
|
||||
return .BodyLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
@Proxy(\.model.on)
|
||||
open var isOn: Bool
|
||||
open var isOn: Bool = false { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.showText)
|
||||
public var showText: Bool
|
||||
public var showText: Bool = false { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.onText)
|
||||
public var onText: String
|
||||
public var onText: String = "On" { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.offText)
|
||||
public var offText: String
|
||||
public var offText: String = "Off" { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.textSize)
|
||||
public var textSize: ToggleTextSize
|
||||
public var textSize: ToggleTextSize = .small { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.textWeight)
|
||||
public var textWeight: ToggleTextWeight
|
||||
public var textWeight: ToggleTextWeight = .regular { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.textPosition)
|
||||
public var textPosition: ToggleTextPosition
|
||||
public var textPosition: ToggleTextPosition = .left { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.inputId)
|
||||
open var inputId: String?
|
||||
open var inputId: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.value)
|
||||
open var value: AnyHashable?
|
||||
open var value: AnyHashable? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataAnalyticsTrack)
|
||||
open var dataAnalyticsTrack: String?
|
||||
open var dataAnalyticsTrack: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataClickStream)
|
||||
open var dataClickStream: String?
|
||||
open var dataClickStream: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.dataTrack)
|
||||
open var dataTrack: String?
|
||||
open var dataTrack: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintEnabled)
|
||||
open var accessibilityHintEnabled: String?
|
||||
open var accessibilityHintEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityHintDisabled)
|
||||
open var accessibilityHintDisabled: String?
|
||||
open var accessibilityHintDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueEnabled)
|
||||
open var accessibilityValueEnabled: String?
|
||||
open var accessibilityValueEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityValueDisabled)
|
||||
open var accessibilityValueDisabled: String?
|
||||
open var accessibilityValueDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelEnabled)
|
||||
open var accessibilityLabelEnabled: String?
|
||||
open var accessibilityLabelEnabled: String? { didSet { subject.send() }}
|
||||
|
||||
@Proxy(\.model.accessibilityLabelDisabled)
|
||||
open var accessibilityLabelDisabled: String?
|
||||
open var accessibilityLabelDisabled: String? { didSet { subject.send() }}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
@ -157,12 +173,12 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Toggle
|
||||
//--------------------------------------------------
|
||||
private func updateToggle(_ viewModel: ModelType) {
|
||||
private func updateToggle() {
|
||||
//private func
|
||||
func constrainKnob(){
|
||||
self.knobLeadingConstraint?.isActive = false
|
||||
self.knobTrailingConstraint?.isActive = false
|
||||
if viewModel.on {
|
||||
if isOn {
|
||||
self.knobTrailingConstraint = self.toggleView.trailingAnchor.constraint(equalTo: self.knobView.trailingAnchor, constant: 2)
|
||||
self.knobLeadingConstraint = self.knobView.leadingAnchor.constraint(greaterThanOrEqualTo: self.toggleView.leadingAnchor)
|
||||
} else {
|
||||
@ -175,10 +191,10 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
self.layoutIfNeeded()
|
||||
}
|
||||
|
||||
let toggleColor = toggleColorConfiguration.getColor(viewModel)
|
||||
let knobColor = knobColorConfiguration.getColor(viewModel)
|
||||
let toggleColor = toggleColorConfiguration.getColor(self)
|
||||
let knobColor = knobColorConfiguration.getColor(self)
|
||||
|
||||
if viewModel.disabled {
|
||||
if disabled {
|
||||
toggleView.backgroundColor = toggleColor
|
||||
knobView.backgroundColor = knobColor
|
||||
constrainKnob()
|
||||
@ -197,16 +213,21 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Labels
|
||||
//--------------------------------------------------
|
||||
private func updateLabel(_ viewModel: ModelType) {
|
||||
let showText = viewModel.showText
|
||||
stackView.spacing = showText ? 12 : 0
|
||||
label.set(with: viewModel.labelModel)
|
||||
private func updateLabel() {
|
||||
|
||||
stackView.spacing = showText ? 12 : 0
|
||||
|
||||
if stackView.subviews.contains(label) {
|
||||
label.removeFromSuperview()
|
||||
}
|
||||
|
||||
if showText {
|
||||
label.textPosition = textPosition == .left ? .left : .right
|
||||
label.typograpicalStyle = typograpicalStyle
|
||||
label.text = isOn ? onText : offText
|
||||
label.surface = surface
|
||||
label.disabled = disabled
|
||||
|
||||
if textPosition == .left {
|
||||
stackView.insertArrangedSubview(label, at: 0)
|
||||
} else {
|
||||
@ -246,7 +267,7 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
toggleView.layer.cornerRadius = toggleSize.height / 2.0
|
||||
knobView.layer.cornerRadius = knobSize.height / 2.0
|
||||
|
||||
toggleView.backgroundColor = toggleColorConfiguration.getColor(model)
|
||||
toggleView.backgroundColor = toggleColorConfiguration.getColor(self)
|
||||
|
||||
toggleContainerView.addSubview(toggleView)
|
||||
toggleView.addSubview(knobView)
|
||||
@ -261,7 +282,7 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
|
||||
toggleView.bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true
|
||||
|
||||
updateLabel(model)
|
||||
updateLabel()
|
||||
stackView.addArrangedSubview(toggleContainerView)
|
||||
stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||
stackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||
@ -275,8 +296,8 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
|
||||
public override func reset() {
|
||||
super.reset()
|
||||
toggleView.backgroundColor = toggleColorConfiguration.getColor(model)
|
||||
knobView.backgroundColor = knobColorConfiguration.getColor(model)
|
||||
toggleView.backgroundColor = toggleColorConfiguration.getColor(self)
|
||||
knobView.backgroundColor = knobColorConfiguration.getColor(self)
|
||||
setAccessibilityLabel()
|
||||
}
|
||||
|
||||
@ -289,13 +310,13 @@ open class ToggleBase<ModelType: ToggleModel>: Control<ModelType> {
|
||||
//--------------------------------------------------
|
||||
// MARK: - State
|
||||
//--------------------------------------------------
|
||||
open override func updateView(viewModel: ModelType) {
|
||||
updateLabel(viewModel)
|
||||
updateToggle(viewModel)
|
||||
open override func updateView() {
|
||||
updateLabel()
|
||||
updateToggle()
|
||||
setAccessibilityHint()
|
||||
setAccessibilityValue(viewModel.on)
|
||||
setAccessibilityLabel(viewModel.on)
|
||||
backgroundColor = viewModel.surface.color
|
||||
setAccessibilityValue(isOn)
|
||||
setAccessibilityLabel(isOn)
|
||||
backgroundColor = surface.color
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
}
|
||||
|
||||
@ -18,37 +18,31 @@ public protocol Accessable {
|
||||
}
|
||||
|
||||
//Configurations to set within the UIControl
|
||||
extension ModelHandlerable where Self: UIView {
|
||||
private var accessableModel: Accessable? {
|
||||
guard let model = self.model as? Accessable else {
|
||||
return nil
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
extension ModelHandlerable where Self: UIView, Self: Accessable {
|
||||
|
||||
public func setAccessibilityHint(_ override: Bool? = nil) {
|
||||
let check = override ?? !model.disabled
|
||||
if let value = accessableModel?.accessibilityHintEnabled, check {
|
||||
let check = override ?? !disabled
|
||||
if let value = accessibilityHintEnabled, check {
|
||||
accessibilityHint = value
|
||||
} else if let value = accessableModel?.accessibilityHintDisabled, !check {
|
||||
} else if let value = accessibilityHintDisabled, !check {
|
||||
accessibilityHint = value
|
||||
}
|
||||
}
|
||||
|
||||
public func setAccessibilityValue(_ override: Bool? = nil) {
|
||||
let check = override ?? !model.disabled
|
||||
if let value = accessableModel?.accessibilityValueEnabled, check {
|
||||
let check = override ?? !disabled
|
||||
if let value = accessibilityValueEnabled, check {
|
||||
accessibilityValue = value
|
||||
} else if let value = accessableModel?.accessibilityValueDisabled, !check {
|
||||
} else if let value = accessibilityValueDisabled, !check {
|
||||
accessibilityValue = value
|
||||
}
|
||||
}
|
||||
|
||||
public func setAccessibilityLabel(_ override: Bool? = nil) {
|
||||
let check = override ?? !model.disabled
|
||||
if let value = accessableModel?.accessibilityLabelEnabled, check {
|
||||
let check = override ?? !disabled
|
||||
if let value = accessibilityLabelEnabled, check {
|
||||
accessibilityLabel = value
|
||||
} else if let value = accessableModel?.accessibilityLabelDisabled, !check {
|
||||
} else if let value = accessibilityLabelDisabled, !check {
|
||||
accessibilityLabel = value
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,44 +9,26 @@ import Foundation
|
||||
import Combine
|
||||
import UIKit
|
||||
|
||||
public protocol ModelHandlerable: AnyObject, Initable {
|
||||
associatedtype ModelType: Modelable
|
||||
var model: ModelType { get set }
|
||||
var modelPublisher: Published<ModelType>.Publisher { get }
|
||||
public protocol ModelHandlerable: AnyObject, Initable, Disabling, Surfaceable {
|
||||
var subject: PassthroughSubject<Void, Never> { get set }
|
||||
var subscribers: Set<AnyCancellable> { get set }
|
||||
init(with model: ModelType)
|
||||
func set(with model: ModelType)
|
||||
func shouldUpdateView(viewModel: ModelType) -> Bool
|
||||
func updateView(viewModel: ModelType)
|
||||
func updateView()
|
||||
}
|
||||
|
||||
extension ModelHandlerable {
|
||||
public init() {
|
||||
self.init(with: ModelType())
|
||||
}
|
||||
|
||||
public func set(with model: ModelType) {
|
||||
if shouldUpdateView(viewModel: model){
|
||||
updateView(viewModel: model)
|
||||
self.model = model
|
||||
}
|
||||
}
|
||||
|
||||
public func shouldUpdateView(viewModel: ModelType) -> Bool {
|
||||
model != viewModel
|
||||
}
|
||||
|
||||
public func setupUpdateView() {
|
||||
handlerPublisher()
|
||||
.subscribe(on: RunLoop.main)
|
||||
.sink { [weak self] viewModel in
|
||||
self?.updateView(viewModel: viewModel)
|
||||
.sink { [weak self] _ in
|
||||
self?.updateView()
|
||||
}
|
||||
.store(in: &subscribers)
|
||||
}
|
||||
|
||||
public func handlerPublisher() -> AnyPublisher<ModelType, Never> {
|
||||
modelPublisher
|
||||
public func handlerPublisher() -> AnyPublisher<Void, Never> {
|
||||
subject
|
||||
.eraseToAnyPublisher()
|
||||
.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user