Merge branch 'feature/atomic_vds_button' into 'develop'
VDS Button in Atomic. ### Summary Integration of the VDS button. Co-authored-by: Scott Pfeil <Scott.Pfeil3@verizonwireless.com> Co-authored-by: Matt Bruce <matt.bruce@verizon.com> See merge request https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui/-/merge_requests/995
This commit is contained in:
commit
0dc647a849
@ -289,6 +289,7 @@
|
|||||||
AF766D262A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */; };
|
AF766D262A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */; };
|
||||||
AF7E509829E477C1009DC2AD /* AlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7E509629E477C0009DC2AD /* AlertHandler.swift */; };
|
AF7E509829E477C1009DC2AD /* AlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7E509629E477C0009DC2AD /* AlertHandler.swift */; };
|
||||||
AF7E509929E477C1009DC2AD /* AlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7E509729E477C0009DC2AD /* AlertController.swift */; };
|
AF7E509929E477C1009DC2AD /* AlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7E509729E477C0009DC2AD /* AlertController.swift */; };
|
||||||
|
AF8118302AB39B0900FAD1BA /* RawRepresentableCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF81182F2AB39B0900FAD1BA /* RawRepresentableCodable.swift */; };
|
||||||
AFA4932029E5CA73001A9663 /* AlertOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4931F29E5CA73001A9663 /* AlertOperation.swift */; };
|
AFA4932029E5CA73001A9663 /* AlertOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4931F29E5CA73001A9663 /* AlertOperation.swift */; };
|
||||||
AFA4932229E5EF2E001A9663 /* NotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4932129E5EF2E001A9663 /* NotificationHandler.swift */; };
|
AFA4932229E5EF2E001A9663 /* NotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4932129E5EF2E001A9663 /* NotificationHandler.swift */; };
|
||||||
AFA4933F29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4933E29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift */; };
|
AFA4933F29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4933E29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift */; };
|
||||||
@ -875,6 +876,7 @@
|
|||||||
AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAccessibilityTraits+Codable.swift"; sourceTree = "<group>"; };
|
AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAccessibilityTraits+Codable.swift"; sourceTree = "<group>"; };
|
||||||
AF7E509629E477C0009DC2AD /* AlertHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertHandler.swift; sourceTree = "<group>"; };
|
AF7E509629E477C0009DC2AD /* AlertHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertHandler.swift; sourceTree = "<group>"; };
|
||||||
AF7E509729E477C0009DC2AD /* AlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertController.swift; sourceTree = "<group>"; };
|
AF7E509729E477C0009DC2AD /* AlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertController.swift; sourceTree = "<group>"; };
|
||||||
|
AF81182F2AB39B0900FAD1BA /* RawRepresentableCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawRepresentableCodable.swift; sourceTree = "<group>"; };
|
||||||
AFA4931F29E5CA73001A9663 /* AlertOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertOperation.swift; sourceTree = "<group>"; };
|
AFA4931F29E5CA73001A9663 /* AlertOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertOperation.swift; sourceTree = "<group>"; };
|
||||||
AFA4932129E5EF2E001A9663 /* NotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationHandler.swift; sourceTree = "<group>"; };
|
AFA4932129E5EF2E001A9663 /* NotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationHandler.swift; sourceTree = "<group>"; };
|
||||||
AFA4933E29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUILoggingDelegateProtocol.swift; sourceTree = "<group>"; };
|
AFA4933E29E874F0001A9663 /* MVMCoreUILoggingDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUILoggingDelegateProtocol.swift; sourceTree = "<group>"; };
|
||||||
@ -2152,6 +2154,7 @@
|
|||||||
D29DF2A821E7B2F9003B2FB9 /* MVMCoreUIConstants.m */,
|
D29DF2A821E7B2F9003B2FB9 /* MVMCoreUIConstants.m */,
|
||||||
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */,
|
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */,
|
||||||
0AF60F0826B3316E00AC3DB4 /* MVMCoreUIUtility+Extension.swift */,
|
0AF60F0826B3316E00AC3DB4 /* MVMCoreUIUtility+Extension.swift */,
|
||||||
|
AF81182F2AB39B0900FAD1BA /* RawRepresentableCodable.swift */,
|
||||||
);
|
);
|
||||||
path = Utility;
|
path = Utility;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -2746,6 +2749,7 @@
|
|||||||
0AE98BAF23FEF956004C5109 /* ExternalLink.swift in Sources */,
|
0AE98BAF23FEF956004C5109 /* ExternalLink.swift in Sources */,
|
||||||
012A88C4238D86E600FE3DA1 /* CarouselItemModelProtocol.swift in Sources */,
|
012A88C4238D86E600FE3DA1 /* CarouselItemModelProtocol.swift in Sources */,
|
||||||
D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */,
|
D2E2A9A123E095AB000B42E6 /* ButtonModelProtocol.swift in Sources */,
|
||||||
|
AF8118302AB39B0900FAD1BA /* RawRepresentableCodable.swift in Sources */,
|
||||||
94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */,
|
94C2D9AB23872EB50006CF46 /* LabelAttributeActionModel.swift in Sources */,
|
||||||
D22D8395241FB41200D3DF69 /* UIStackView+Extension.swift in Sources */,
|
D22D8395241FB41200D3DF69 /* UIStackView+Extension.swift in Sources */,
|
||||||
52B201D324081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift in Sources */,
|
52B201D324081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethodModel.swift in Sources */,
|
||||||
|
|||||||
@ -8,10 +8,11 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
|
import VDS
|
||||||
|
import MVMCore
|
||||||
|
|
||||||
public typealias FacadeElements = (fill: UIColor?, text: UIColor?, border: UIColor?)
|
public typealias FacadeElements = (fill: UIColor?, text: UIColor?, border: UIColor?)
|
||||||
|
|
||||||
|
|
||||||
open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol {
|
open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
@ -26,13 +27,13 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
public var action: ActionModelProtocol
|
public var action: ActionModelProtocol
|
||||||
public var enabled: Bool = true
|
public var enabled: Bool = true
|
||||||
public var width: CGFloat?
|
public var width: CGFloat?
|
||||||
public var style: Styler.Button.Style? {
|
public var style: Use? {
|
||||||
didSet {
|
didSet {
|
||||||
guard let style = style else { return }
|
guard let style = style else { return }
|
||||||
setFacade(by: style)
|
setFacade(by: style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public var size: Styler.Button.Size? = .standard
|
public var size: VDS.Button.Size = .large
|
||||||
public var groupName: String = ""
|
public var groupName: String = ""
|
||||||
public var inverted: Bool = false
|
public var inverted: Bool = false
|
||||||
|
|
||||||
@ -160,14 +161,14 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
disabledBorderColor_inverted = Color(uiColor: VDSColor.interactiveDisabledOndark)
|
disabledBorderColor_inverted = Color(uiColor: VDSColor.interactiveDisabledOndark)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setFacade(by style: Styler.Button.Style) {
|
public func setFacade(by style: VDS.Use) {
|
||||||
|
|
||||||
switch style {
|
switch style {
|
||||||
case .primary:
|
case .primary:
|
||||||
setPrimaryFacade()
|
setPrimaryFacade()
|
||||||
|
|
||||||
case .secondary:
|
case .secondary:
|
||||||
setSecondaryFacade()
|
setSecondaryFacade()
|
||||||
|
@unknown default:
|
||||||
|
setPrimaryFacade()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,17 +212,17 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
action = try typeContainer.decodeModel(codingKey: .action)
|
action = try typeContainer.decodeModel(codingKey: .action)
|
||||||
|
|
||||||
///Style captured from the JSON
|
///Style captured from the JSON
|
||||||
if let style = try typeContainer.decodeIfPresent(Styler.Button.Style.self, forKey: .style){
|
if let style = try typeContainer.decodeIfPresent(Use.self, forKey: .style) {
|
||||||
self.style = style
|
self.style = style
|
||||||
setFacade(by: style)
|
setFacade(by: style)
|
||||||
} else if let style = decoder.context?.value(forKey: CodingKeys.style.stringValue) as? Styler.Button.Style { ///Reading the style param from context which is set is molecules, ex: TwoButtonView
|
} else if let style = decoder.context?.value(forKey: CodingKeys.style.stringValue) as? Use { ///Reading the style param from context which is set is molecules, ex: TwoButtonView
|
||||||
self.style = style
|
self.style = style
|
||||||
setFacade(by: style)
|
setFacade(by: style)
|
||||||
} else { ///Default style
|
} else { ///Default style
|
||||||
setFacade(by: .primary)
|
setFacade(by: .primary)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let size = try typeContainer.decodeIfPresent(Styler.Button.Size.self, forKey: .size) {
|
if let size = try typeContainer.decodeIfPresent(VDS.Button.Size.self, forKey: .size) {
|
||||||
self.size = size
|
self.size = size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,223 +8,68 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
|
import VDS
|
||||||
|
import MVMCore
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
open class PillButton: VDS.Button, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol, VDSMoleculeViewProtocol {
|
||||||
|
|
||||||
open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
/// Used to size the button.
|
open var viewModel: ButtonModel!
|
||||||
var size = MVMCoreUIUtility.getWidth()
|
public var delegateObject: MVMCoreUIDelegateObject?
|
||||||
|
public var additionalData: [AnyHashable: Any]?
|
||||||
|
|
||||||
var buttonModel: ButtonModel? {
|
internal var onClickCancellable: Cancellable?
|
||||||
get { model as? ButtonModel }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Need to re-style on set.
|
|
||||||
open override var isEnabled: Bool {
|
|
||||||
didSet { style(with: buttonModel) }
|
|
||||||
}
|
|
||||||
|
|
||||||
open var buttonSize: Styler.Button.Size = .standard {
|
|
||||||
didSet { buttonModel?.size = buttonSize }
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Constraints
|
// MARK: - VDSMoleculeViewProtocol
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public var widthConstraint: NSLayoutConstraint?
|
open func viewModelDidUpdate() {
|
||||||
public var minimumWidthConstraint: NSLayoutConstraint?
|
if let accessibilityIdentifier = viewModel.accessibilityIdentifier {
|
||||||
|
self.accessibilityIdentifier = accessibilityIdentifier
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Initializers
|
|
||||||
//--------------------------------------------------
|
|
||||||
|
|
||||||
@objc public convenience init(asPrimaryButton isPrimary: Bool, makeTiny istiny: Bool) {
|
|
||||||
let model = ButtonModel(with: "", action: ActionNoopModel())
|
|
||||||
model.style = isPrimary ? .primary : .secondary
|
|
||||||
model.size = istiny ? .tiny : .standard
|
|
||||||
self.init(model: model, nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Computed Properties
|
|
||||||
//--------------------------------------------------
|
|
||||||
|
|
||||||
public var enabledTitleColor: UIColor? {
|
|
||||||
get { titleColor(for: .normal) }
|
|
||||||
set { setTitleColor(newValue, for: .normal) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public var disabledTitleColor: UIColor? {
|
|
||||||
get { titleColor(for: .disabled) }
|
|
||||||
set { setTitleColor(newValue, for: .disabled) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public var borderColor: UIColor? {
|
|
||||||
get {
|
|
||||||
guard let currentColor = layer.borderColor else { return nil }
|
|
||||||
return UIColor(cgColor: currentColor)
|
|
||||||
}
|
|
||||||
set { layer.borderColor = newValue?.cgColor }
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Methods
|
|
||||||
//--------------------------------------------------
|
|
||||||
|
|
||||||
/// The primary styling for a button. Should be used for main buttons
|
|
||||||
public func stylePrimary() {
|
|
||||||
let buttonModel = ButtonModel(primaryButtonWith: "", action: ActionNoopModel())
|
|
||||||
style(with: buttonModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The secondary styling for a button. Should be used for secondary buttons
|
|
||||||
public func styleSecondary() {
|
|
||||||
let buttonModel = ButtonModel(secondaryButtonWith: "", action: ActionNoopModel())
|
|
||||||
style(with: buttonModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Styles the button based on the model style
|
|
||||||
private func style(with model: ButtonModel?) {
|
|
||||||
|
|
||||||
layer.borderWidth = model?.style == .secondary ? 1 : 0
|
|
||||||
|
|
||||||
if let titleColor = model?.enabledColors.text {
|
|
||||||
enabledTitleColor = titleColor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let disabledTitleColor = model?.disabledColors.text {
|
text = viewModel.title
|
||||||
self.disabledTitleColor = disabledTitleColor
|
isEnabled = viewModel.enabled
|
||||||
|
size = viewModel.size
|
||||||
|
use = viewModel.style ?? .primary
|
||||||
|
surface = viewModel.inverted ? .dark : .light
|
||||||
|
if let accessibilityText = viewModel.accessibilityText {
|
||||||
|
accessibilityLabel = accessibilityText
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
set(with: viewModel.action, delegateObject: delegateObject, additionalData: additionalData)
|
||||||
// Useful to detect with isHittable when performing UI testing.
|
|
||||||
isAccessibilityElement = isEnabled
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if isEnabled {
|
viewModel.updateUI = { [weak self] in
|
||||||
if let fillColor = model?.enabledColors.fill {
|
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
||||||
backgroundColor = fillColor
|
guard let self = self else { return }
|
||||||
}
|
self.isEnabled = self.viewModel.enabled
|
||||||
|
})
|
||||||
if let borderColor = model?.enabledColors.border {
|
|
||||||
self.borderColor = borderColor
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let fillColor = model?.disabledColors.fill {
|
|
||||||
backgroundColor = fillColor
|
|
||||||
}
|
|
||||||
|
|
||||||
if let borderColor = model?.disabledColors.border {
|
|
||||||
self.borderColor = borderColor
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private func getInnerPadding() -> CGFloat {
|
FormValidator.setupValidation(for: viewModel, delegate: delegateObject?.formHolderDelegate)
|
||||||
buttonSize.getHeight() / 2.0
|
|
||||||
}
|
|
||||||
|
|
||||||
private func getContentEdgeInsets() -> UIEdgeInsets {
|
|
||||||
var verticalPadding = 0.0
|
|
||||||
var horizontalPadding = 0.0
|
|
||||||
switch buttonSize {
|
|
||||||
case .standard:
|
|
||||||
verticalPadding = Padding.Three
|
|
||||||
horizontalPadding = Padding.Five
|
|
||||||
break
|
|
||||||
case .small:
|
|
||||||
verticalPadding = Padding.Two
|
|
||||||
horizontalPadding = Padding.Four
|
|
||||||
break
|
|
||||||
case .tiny:
|
|
||||||
verticalPadding = Padding.One
|
|
||||||
horizontalPadding = Padding.Two
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - MVMCoreViewProtocol
|
// MARK: - MVMCoreViewProtocol
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||||
// The button will get styled in the enable check in super.
|
return (model as? ButtonModel)?.size.height
|
||||||
super.set(with: model, delegateObject, additionalData)
|
|
||||||
|
|
||||||
guard let model = model as? ButtonModel else { return }
|
|
||||||
|
|
||||||
setTitle(model.title, for: .normal)
|
|
||||||
if let accessibilityText = model.accessibilityText {
|
|
||||||
accessibilityLabel = accessibilityText
|
|
||||||
}
|
|
||||||
|
|
||||||
if let size = model.size {
|
|
||||||
buttonSize = size
|
|
||||||
}
|
|
||||||
|
|
||||||
model.updateUI = { [weak self] in
|
|
||||||
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
|
||||||
self?.enableField(model.enabled)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
open func updateView(_ size: CGFloat) {}
|
||||||
return (model as? ButtonModel)?.size?.getHeight()
|
|
||||||
|
open override func setup() {
|
||||||
|
super.setup()
|
||||||
|
setupView()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateView(_ size: CGFloat) {
|
open func setupView() {}
|
||||||
super.updateView(size)
|
|
||||||
self.size = size
|
|
||||||
|
|
||||||
switch buttonSize {
|
|
||||||
case .tiny:
|
|
||||||
titleLabel?.font = Styler.Font.BoldMicro.getFont(false)
|
|
||||||
case .small:
|
|
||||||
titleLabel?.font = Styler.Font.BoldBodySmall.getFont(false)
|
|
||||||
case .standard:
|
|
||||||
titleLabel?.font = Styler.Font.BoldBodyLarge.getFont(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
layer.cornerRadius = getInnerPadding()
|
|
||||||
contentEdgeInsets = getContentEdgeInsets()
|
|
||||||
|
|
||||||
if let contraint = buttonModel?.width {
|
|
||||||
|
|
||||||
if widthConstraint == nil {
|
|
||||||
widthConstraint = widthAnchor.constraint(equalToConstant: contraint)
|
|
||||||
} else if widthConstraint?.constant != contraint {
|
|
||||||
widthConstraint?.constant = contraint
|
|
||||||
}
|
|
||||||
widthConstraint?.isActive = true
|
|
||||||
minimumWidthConstraint?.isActive = false
|
|
||||||
} else {
|
|
||||||
|
|
||||||
if minimumWidthConstraint == nil {
|
|
||||||
minimumWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: buttonSize.minimumWidth())
|
|
||||||
} else {
|
|
||||||
minimumWidthConstraint?.constant = buttonSize.minimumWidth()
|
|
||||||
}
|
|
||||||
minimumWidthConstraint?.isActive = true
|
|
||||||
widthConstraint?.isActive = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func setupView() {
|
|
||||||
super.setupView()
|
|
||||||
|
|
||||||
titleLabel?.numberOfLines = 1
|
|
||||||
titleLabel?.lineBreakMode = .byTruncatingTail
|
|
||||||
titleLabel?.textAlignment = .center
|
|
||||||
contentHorizontalAlignment = .center
|
|
||||||
stylePrimary()
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - MVMCoreUIViewConstrainingProtocol
|
// MARK: - MVMCoreUIViewConstrainingProtocol
|
||||||
@ -232,7 +77,21 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
|
|||||||
|
|
||||||
open func horizontalAlignment() -> UIStackView.Alignment { .center }
|
open func horizontalAlignment() -> UIStackView.Alignment { .center }
|
||||||
|
|
||||||
public func enableField(_ enable: Bool) {
|
//--------------------------------------------------
|
||||||
isEnabled = enable
|
// MARK: - Action
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
open func set(with actionModel: ActionModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||||
|
onClick = { [weak self] control in
|
||||||
|
guard let self = self, let viewModel = self.viewModel else { return }
|
||||||
|
Task(priority: .userInitiated) {
|
||||||
|
try await Self.performButtonAction(with: viewModel.action, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: viewModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class func performButtonAction(with model: ActionModelProtocol, button: MFButtonProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, sourceModel: MoleculeModelProtocol? = nil) async throws {
|
||||||
|
guard delegateObject?.buttonDelegate?.button?(button, shouldPerformActionWithMap: model.toJSON(), additionalData: additionalData) ?? true else { return }
|
||||||
|
try await (delegateObject?.actionDelegate as? ActionDelegateProtocol)?.performAction(with: model, additionalData: MVMCoreUIActionHandler.add(sourceModel: sourceModel, to: additionalData), delegateObject: delegateObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,14 +50,13 @@ open class Tilelet: VDS.Tilelet, VDSMoleculeViewProtocol{
|
|||||||
//setup action
|
//setup action
|
||||||
if let action = viewModel.action {
|
if let action = viewModel.action {
|
||||||
//add the subscriber
|
//add the subscriber
|
||||||
onClickSubscriber = publisher(for: .touchUpInside)
|
onClick = { [weak self] control in
|
||||||
.sink {[weak self] control in
|
guard let self, let viewModel = self.viewModel else { return }
|
||||||
guard let self else { return }
|
MVMCoreUIActionHandler.performActionUnstructured(with: action,
|
||||||
MVMCoreUIActionHandler.performActionUnstructured(with: action,
|
sourceModel: viewModel,
|
||||||
sourceModel: self.viewModel,
|
additionalData: self.additionalData,
|
||||||
additionalData: self.additionalData,
|
delegateObject: self.delegateObject)
|
||||||
delegateObject: self.delegateObject)
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,3 +16,8 @@ extension Icon.Size: Codable {}
|
|||||||
extension TileContainer.BackgroundColor: Codable {}
|
extension TileContainer.BackgroundColor: Codable {}
|
||||||
extension TileContainer.Padding: Codable {}
|
extension TileContainer.Padding: Codable {}
|
||||||
extension TileContainer.AspectRatio: Codable {}
|
extension TileContainer.AspectRatio: Codable {}
|
||||||
|
extension Use: Codable {}
|
||||||
|
extension VDS.Button.Size: RawRepresentableCodable {
|
||||||
|
public static var mapping: [String : VDS.Button.Size] { ["standard": .large, "tiny": .small] }
|
||||||
|
public static var defaultValue: VDS.Button.Size? { nil }
|
||||||
|
}
|
||||||
|
|||||||
@ -42,7 +42,7 @@ public class ListDeviceComplexButtonMediumModel: ListItemModel, MoleculeModelPro
|
|||||||
|
|
||||||
override public func setDefaults() {
|
override public func setDefaults() {
|
||||||
super.setDefaults()
|
super.setDefaults()
|
||||||
button.size = .tiny
|
button.size = .small
|
||||||
button.style = .secondary
|
button.style = .secondary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -42,7 +42,7 @@ public class ListDeviceComplexButtonSmallModel: ListItemModel, MoleculeModelProt
|
|||||||
|
|
||||||
override public func setDefaults() {
|
override public func setDefaults() {
|
||||||
super.setDefaults()
|
super.setDefaults()
|
||||||
button.size = .tiny
|
button.size = .small
|
||||||
button.style = .secondary
|
button.style = .secondary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,7 +32,7 @@ public class ListRightVariableButtonAllTextAndLinksModel: ListItemModel, Molecul
|
|||||||
|
|
||||||
override public func setDefaults() {
|
override public func setDefaults() {
|
||||||
super.setDefaults()
|
super.setDefaults()
|
||||||
self.button.size = .tiny
|
self.button.size = .small
|
||||||
self.button.style = .secondary
|
self.button.style = .secondary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,5 +24,14 @@
|
|||||||
if bottomPadding == nil {
|
if bottomPadding == nil {
|
||||||
bottomPadding = PaddingDefaultVerticalSpacing
|
bottomPadding = PaddingDefaultVerticalSpacing
|
||||||
}
|
}
|
||||||
|
guard !MVMCoreGetterUtility.isOnIPad(),
|
||||||
|
horizontalAlignment == nil else { return }
|
||||||
|
|
||||||
|
if let _ = molecule as? ButtonModel {
|
||||||
|
horizontalAlignment = .fill
|
||||||
|
} else if let model = molecule as? TwoButtonViewModel,
|
||||||
|
model.primaryButton == nil || model.secondaryButton == nil {
|
||||||
|
horizontalAlignment = .fill
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import VDS
|
||||||
|
|
||||||
@objcMembers open class TwoButtonView: View, MVMCoreUIViewConstrainingProtocol {
|
@objcMembers open class TwoButtonView: View, MVMCoreUIViewConstrainingProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -29,9 +29,8 @@ import UIKit
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public func setDefaultAppearance() {
|
public func setDefaultAppearance() {
|
||||||
|
primaryButton.use = .primary
|
||||||
primaryButton.stylePrimary()
|
secondaryButton.use = .secondary
|
||||||
secondaryButton.styleSecondary()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateView(_ size: CGFloat) {
|
open override func updateView(_ size: CGFloat) {
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import VDS
|
||||||
|
|
||||||
public class TwoButtonViewModel: ParentMoleculeModelProtocol {
|
public class TwoButtonViewModel: ParentMoleculeModelProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -56,13 +56,13 @@ public class TwoButtonViewModel: ParentMoleculeModelProtocol {
|
|||||||
|
|
||||||
//set context value for 'primary' style to be set for the primaryButton in case the
|
//set context value for 'primary' style to be set for the primaryButton in case the
|
||||||
//property is not returned in the JSON and once decoded, this value is removed from the context
|
//property is not returned in the JSON and once decoded, this value is removed from the context
|
||||||
try decoder.setContext(value: Styler.Button.Style.primary, for: "style") {
|
try decoder.setContext(value: Use.primary, for: "style") {
|
||||||
self.primaryButton = try typeContainer.decodeMoleculeIfPresent(codingKey: .primaryButton)
|
self.primaryButton = try typeContainer.decodeMoleculeIfPresent(codingKey: .primaryButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
//set context value for 'secondary' style to be set for the primaryButton in case the
|
//set context value for 'secondary' style to be set for the primaryButton in case the
|
||||||
//property is not returned in the JSON and once decoded, this value is removed from the context
|
//property is not returned in the JSON and once decoded, this value is removed from the context
|
||||||
try decoder.setContext(value: Styler.Button.Style.secondary, for: "style") {
|
try decoder.setContext(value: Use.secondary, for: "style") {
|
||||||
self.secondaryButton = try typeContainer.decodeMoleculeIfPresent(codingKey: .secondaryButton)
|
self.secondaryButton = try typeContainer.decodeMoleculeIfPresent(codingKey: .secondaryButton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,7 +89,7 @@ open class NotificationMoleculeModel: ContainerModel, MoleculeModelProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button?.size = .tiny
|
button?.size = .small
|
||||||
button?.style = .secondary
|
button?.style = .secondary
|
||||||
switch style {
|
switch style {
|
||||||
case .error, .warning:
|
case .error, .warning:
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import Foundation
|
|||||||
|
|
||||||
public let headline = Label(fontStyle: .BoldBodySmall)
|
public let headline = Label(fontStyle: .BoldBodySmall)
|
||||||
public let body = Label(fontStyle: .RegularBodySmall)
|
public let body = Label(fontStyle: .RegularBodySmall)
|
||||||
public let button = PillButton(asPrimaryButton: false, makeTiny: true)
|
public let button = PillButton()
|
||||||
public let closeButton = NotificationXButton()
|
public let closeButton = NotificationXButton()
|
||||||
public var labelStack: Stack<StackModel>!
|
public var labelStack: Stack<StackModel>!
|
||||||
public var horizontalStack: Stack<StackModel>!
|
public var horizontalStack: Stack<StackModel>!
|
||||||
|
|||||||
@ -54,7 +54,7 @@ public class BGImageHeadlineBodyButtonModel: ContainerModel, MoleculeModelProtoc
|
|||||||
image.height = BGImageHeadlineBodyButton.heightConstant
|
image.height = BGImageHeadlineBodyButton.heightConstant
|
||||||
}
|
}
|
||||||
|
|
||||||
button?.size = .tiny
|
button?.size = .small
|
||||||
button?.style = .secondary
|
button?.style = .secondary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -64,7 +64,7 @@
|
|||||||
|
|
||||||
headlineBody.headlineLabel.font = Styler.Font.BoldTitleMedium.getFont()
|
headlineBody.headlineLabel.font = Styler.Font.BoldTitleMedium.getFont()
|
||||||
headlineBody.messageLabel.font = Styler.Font.RegularMicro.getFont()
|
headlineBody.messageLabel.font = Styler.Font.RegularMicro.getFont()
|
||||||
button.styleSecondary()
|
button.use = .secondary
|
||||||
button.isHidden = false
|
button.isHidden = false
|
||||||
buttonHeadlinePadding = PaddingTwo
|
buttonHeadlinePadding = PaddingTwo
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,7 +38,7 @@ public class HeadlineBodyButtonModel: MoleculeModelProtocol {
|
|||||||
|
|
||||||
/// Defaults to set
|
/// Defaults to set
|
||||||
public func setDefaults() {
|
public func setDefaults() {
|
||||||
button.size = .tiny
|
button.size = .small
|
||||||
button.style = .secondary
|
button.style = .secondary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -159,42 +159,6 @@ open class Styler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Button {
|
|
||||||
|
|
||||||
public enum Style: String, Codable {
|
|
||||||
case primary
|
|
||||||
case secondary
|
|
||||||
}
|
|
||||||
///MVA 3.0 - Button sizes are standard(default size), small, Tiny. Tiny button has been depricated as of Rebranding 3.0.
|
|
||||||
public enum Size: String, Codable {
|
|
||||||
case standard
|
|
||||||
case small
|
|
||||||
case tiny
|
|
||||||
|
|
||||||
func getHeight() -> CGFloat {
|
|
||||||
switch self {
|
|
||||||
case .standard:
|
|
||||||
return 44
|
|
||||||
case .small:
|
|
||||||
return 32
|
|
||||||
case .tiny:
|
|
||||||
return 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func minimumWidth() -> CGFloat {
|
|
||||||
switch self {
|
|
||||||
case .standard:
|
|
||||||
return 76
|
|
||||||
case .small:
|
|
||||||
return 0
|
|
||||||
case .tiny:
|
|
||||||
return 49
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Functions
|
// MARK: - Functions
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
40
MVMCoreUI/Utility/RawRepresentableCodable.swift
Normal file
40
MVMCoreUI/Utility/RawRepresentableCodable.swift
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// RawRepresentableCodable.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Scott Pfeil on 9/14/23.
|
||||||
|
// Copyright © 2023 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol RawRepresentableCodable: RawRepresentable, Codable where RawValue: Hashable & Decodable {
|
||||||
|
static var mapping: [RawValue: Self] { get }
|
||||||
|
static var defaultValue: Self? { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum RawRepresentableCodableError: Swift.Error {
|
||||||
|
case invalid(value: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RawRepresentableCodable {
|
||||||
|
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.singleValueContainer()
|
||||||
|
let rawValue = try container.decode(RawValue.self)
|
||||||
|
if let found = Self(rawValue: rawValue) {
|
||||||
|
self = found
|
||||||
|
} else if let found = Self.mapping[rawValue] {
|
||||||
|
self = found
|
||||||
|
} else if let defaultValue = Self.defaultValue {
|
||||||
|
self = defaultValue
|
||||||
|
} else {
|
||||||
|
throw RawRepresentableCodableError.invalid(value: "\(rawValue)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.singleValueContainer()
|
||||||
|
try container.encode(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user