Merge branch 'feature/vds-form-controls' of https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui.git into feature/vds-toggle

# Conflicts:
#	MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2024-07-11 12:51:01 -05:00
commit c7d5cac1af
10 changed files with 179 additions and 103 deletions

View File

@ -581,6 +581,7 @@
EA41F4AC2787927100F5B377 /* DynamicRuleFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA41F4AB2787927100F5B377 /* DynamicRuleFormFieldEffectModel.swift */; };
EA5124FD243601600051A3A4 /* BGImageHeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5124FC243601600051A3A4 /* BGImageHeadlineBodyButton.swift */; };
EA5124FF2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */; };
EA5DBDAB2C35B6C500290DF8 /* FormFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5DBDAA2C35B6C500290DF8 /* FormFieldModel.swift */; };
EA6642912BCDA97300D81DC4 /* TileContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6642902BCDA97300D81DC4 /* TileContainer.swift */; };
EA6642932BCDA97D00D81DC4 /* TileContainerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6642922BCDA97D00D81DC4 /* TileContainerModel.swift */; };
EA6E8B952B504A43000139B4 /* ButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6E8B942B504A43000139B4 /* ButtonGroup.swift */; };
@ -1201,6 +1202,7 @@
EA41F4AB2787927100F5B377 /* DynamicRuleFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicRuleFormFieldEffectModel.swift; sourceTree = "<group>"; };
EA5124FC243601600051A3A4 /* BGImageHeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButton.swift; sourceTree = "<group>"; };
EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButtonModel.swift; sourceTree = "<group>"; };
EA5DBDAA2C35B6C500290DF8 /* FormFieldModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormFieldModel.swift; sourceTree = "<group>"; };
EA6642902BCDA97300D81DC4 /* TileContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TileContainer.swift; sourceTree = "<group>"; };
EA6642922BCDA97D00D81DC4 /* TileContainerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TileContainerModel.swift; sourceTree = "<group>"; };
EA6E8B942B504A43000139B4 /* ButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroup.swift; sourceTree = "<group>"; };
@ -2487,6 +2489,7 @@
D2BEFEF5248A954C00FAB3A9 /* FormFields */ = {
isa = PBXGroup;
children = (
EA5DBDAA2C35B6C500290DF8 /* FormFieldModel.swift */,
D2BEFEF6248A957A00FAB3A9 /* Tags */,
D29DF22B21E6A0FA003B2FB9 /* TextFields */,
);
@ -3292,6 +3295,7 @@
D28BA7432480284E00B75CB8 /* TabBar.swift in Sources */,
AA3561AE24C96B9000452EB1 /* ListRightVariableRightCaretAllTextAndLinks.swift in Sources */,
AA26850C244840AE00CE34CC /* HeadersH2TinyButton.swift in Sources */,
EA5DBDAB2C35B6C500290DF8 /* FormFieldModel.swift in Sources */,
011D95AB2405C553000E3791 /* FormItemProtocol.swift in Sources */,
D21EE53C23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift in Sources */,
0A25209824645B76000FA9F6 /* TextViewEntryFieldModel.swift in Sources */,

View File

@ -0,0 +1,130 @@
//
// FormFieldModel.swift
// MVMCoreUI
//
// Created by Matt Bruce on 7/3/24.
// Copyright © 2024 Verizon Wireless. All rights reserved.
//
import Foundation
import VDS
@objcMembers open class FormFieldModel: MoleculeModelProtocol, FormFieldProtocol, FormRuleWatcherFieldProtocol, UIUpdatableModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public class var identifier: String { "" }
public var id: String = UUID().uuidString
public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var enabled: Bool = true
public var required: Bool = true
public var readOnly: Bool = false
public var showError: Bool?
public var errorMessage: String?
public var fieldKey: String?
public var groupName: String = FormValidator.defaultGroupName
public var baseValue: AnyHashable?
public var inverted: Bool = false
public var surface: Surface { inverted ? .dark : .light }
public var dynamicErrorMessage: String? {
didSet {
isValid = dynamicErrorMessage?.isEmpty ?? true
updateUIDynamicError?()
}
}
public var isValid: Bool? = true {
didSet { updateUI?() }
}
/// Temporary binding mechanism for the view to update on enable changes.
public var updateUI: ActionBlock?
// TODO: Remove once updateUI is fixed with isSelected
public var updateUIDynamicError: ActionBlock?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init() {}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case id
case moleculeName
case accessibilityIdentifier
case errorMessage
case enabled
case readOnly
case required
case fieldKey
case groupName
case inverted
}
//--------------------------------------------------
// MARK: - Validation Methods
//--------------------------------------------------
open func formFieldValue() -> AnyHashable? {
fatalError("developer must implement")
}
open func formFieldServerValue() -> AnyHashable? {
return formFieldValue()
}
public func setValidity(_ valid: Bool, errorMessage: String?) {
if let ruleErrorMessage = errorMessage, fieldKey != nil {
self.errorMessage = ruleErrorMessage
}
self.isValid = valid
updateUI?()
}
//--------------------------------------------------
// MARK: - Codable
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
errorMessage = try typeContainer.decodeIfPresent(String.self, forKey: .errorMessage)
enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true
required = try typeContainer.decodeIfPresent(Bool.self, forKey: .required) ?? true
readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) ?? FormValidator.defaultGroupName
if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) {
self.inverted = inverted
}
}
open func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encodeIfPresent(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeIfPresent(errorMessage, forKey: .errorMessage)
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encodeIfPresent(groupName, forKey: .groupName)
try container.encode(readOnly, forKey: .readOnly)
try container.encode(enabled, forKey: .enabled)
try container.encode(required, forKey: .required)
try container.encode(inverted, forKey: .inverted)
}
}

View File

@ -9,79 +9,39 @@
import Foundation
@objcMembers open class EntryFieldModel: MoleculeModelProtocol, FormFieldProtocol, FormRuleWatcherFieldProtocol, UIUpdatableModelProtocol, ClearableModelProtocol {
@objcMembers open class EntryFieldModel: FormFieldModel, ClearableModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public class var identifier: String { "" }
public var id: String = UUID().uuidString
public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var shouldClearText: Bool = false
public var dynamicErrorMessage: String? {
didSet {
isValid = dynamicErrorMessage?.isEmpty ?? true
updateUIDynamicError?()
}
}
public var errorMessage: String?
public var errorTextColor: Color?
public var enabled: Bool = true
public var required: Bool = true
public var readOnly: Bool = false
public var showError: Bool?
public var hideBorders = false
public var locked: Bool?
public var selected: Bool?
public var text: String?
public var fieldKey: String?
public var groupName: String = FormValidator.defaultGroupName
public var baseValue: AnyHashable?
public var wasInitiallySelected: Bool = false
public var text: String?
public var title: String?
public var feedback: String?
public var shouldMaskRecordedView: Bool? = true
//used to drive the EntryFieldView UI
public var titleStateLabel: FormLabelModel
public var feedbackStateLabel: FormLabelModel
public var isValid: Bool? = true {
didSet { updateUI?() }
}
/// Temporary binding mechanism for the view to update on enable changes.
public var updateUI: ActionBlock?
// TODO: Remove once updateUI is fixed with isSelected
public var updateUIDynamicError: ActionBlock?
public var titleStateLabel = FormLabelModel(text: "")
public var feedbackStateLabel = FormLabelModel(text: "")
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case id
case moleculeName
case backgroundColor
case accessibilityIdentifier
case title
case enabled
case readOnly
case feedback
case errorMessage
case errorTextColor
case locked
case selected
case showError
case hideBorders
case text
case fieldKey
case groupName
case required
case shouldMaskRecordedView
}
@ -92,7 +52,7 @@ import Foundation
// MARK: - Validation Methods
//--------------------------------------------------
public func formFieldValue() -> AnyHashable? {
open override func formFieldValue() -> AnyHashable? {
guard enabled else { return nil }
if dynamicErrorMessage != nil {
@ -100,30 +60,15 @@ import Foundation
}
return text
}
open func formFieldServerValue() -> AnyHashable? {
return formFieldValue()
}
public func setValidity(_ valid: Bool, errorMessage: String?) {
if let ruleErrorMessage = errorMessage, fieldKey != nil {
self.errorMessage = ruleErrorMessage
}
self.isValid = valid
updateUI?()
}
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
public init(with text: String) {
super.init()
self.text = text
baseValue = text
self.titleStateLabel = FormLabelModel(text: "")
self.feedbackStateLabel = FormLabelModel(text: "")
setDefaults()
}
@ -131,7 +76,7 @@ import Foundation
// MARK: - Initializers
//--------------------------------------------------
public func clear() {
self.text = ""
text = ""
}
//--------------------------------------------------
@ -139,54 +84,35 @@ import Foundation
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
try super.init(from: decoder)
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
title = try typeContainer.decodeIfPresent(String.self, forKey: .title)
feedback = try typeContainer.decodeIfPresent(String.self, forKey: .feedback)
errorMessage = try typeContainer.decodeIfPresent(String.self, forKey: .errorMessage)
errorTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .errorTextColor)
enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true
required = try typeContainer.decodeIfPresent(Bool.self, forKey: .required) ?? true
readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false
locked = try typeContainer.decodeIfPresent(Bool.self, forKey: .locked)
selected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected)
text = try typeContainer.decodeIfPresent(String.self, forKey: .text)
hideBorders = try typeContainer.decodeIfPresent(Bool.self, forKey: .hideBorders) ?? false
baseValue = text
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
shouldMaskRecordedView = try typeContainer.decodeIfPresent(Bool.self, forKey: .shouldMaskRecordedView) ?? shouldMaskRecordedView
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName
}
self.titleStateLabel = FormLabelModel(text: title ?? "")
self.feedbackStateLabel = FormLabelModel(model: LabelModel(text: feedback ?? "",
titleStateLabel = FormLabelModel(text: title ?? "")
feedbackStateLabel = FormLabelModel(model: LabelModel(text: feedback ?? "",
fontStyle: FormLabelModel.defaultFontStyle,
textColor: Color(uiColor: .mvmCoolGray6)))
setDefaults()
}
public func encode(to encoder: Encoder) throws {
open override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encodeIfPresent(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeIfPresent(title, forKey: .title)
try container.encodeIfPresent(feedback, forKey: .feedback)
try container.encodeIfPresent(text, forKey: .text)
try container.encodeIfPresent(locked, forKey: .locked)
try container.encodeIfPresent(showError, forKey: .showError)
try container.encodeIfPresent(selected, forKey: .selected)
try container.encodeIfPresent(errorTextColor, forKey: .errorTextColor)
try container.encodeIfPresent(errorMessage, forKey: .errorMessage)
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encodeIfPresent(groupName, forKey: .groupName)
try container.encode(readOnly, forKey: .readOnly)
try container.encode(enabled, forKey: .enabled)
try container.encode(required, forKey: .required)
try container.encode(hideBorders, forKey: .hideBorders)
try container.encode(shouldMaskRecordedView, forKey: .shouldMaskRecordedView)
}

View File

@ -135,8 +135,6 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{
}
extension TileContainer: MVMCoreUIViewConstrainingProtocol {
public func horizontalAlignment() -> UIStackView.Alignment { .leading }
public func isClippable() -> Bool {
return false
}

View File

@ -67,7 +67,7 @@ open class TileContainerBaseModel<PaddingType: DefaultValuing & Codable, TileCon
public var showDropShadow: Bool = false
public var padding = PaddingType.defaultValue
public var color: TileContainerType.BackgroundColor = .black
public var aspectRatio: TileContainerType.AspectRatio = .ratio1x1
public var aspectRatio: TileContainerType.AspectRatio = .none
public var backgroundEffect: TileContainerType.BackgroundEffect = .none
public var surface: Surface { inverted ? .dark : .light }
@ -98,7 +98,7 @@ open class TileContainerBaseModel<PaddingType: DefaultValuing & Codable, TileCon
showDropShadow = try container.decodeIfPresent(Bool.self, forKey: .showDropShadow) ?? false
padding = try container.decodeIfPresent(PaddingType.self, forKey: .padding) ?? PaddingType.defaultValue
color = try container.decodeIfPresent(TileContainerType.BackgroundColor.self, forKey: .color) ?? .black
aspectRatio = try container.decodeIfPresent(TileContainerType.AspectRatio.self, forKey: .aspectRatio) ?? .ratio1x1
aspectRatio = try container.decodeIfPresent(TileContainerType.AspectRatio.self, forKey: .aspectRatio) ?? .none
backgroundEffect = try container.decodeIfPresent(TileContainerType.BackgroundEffect.self, forKey: .backgroundEffect) ?? .none
}

View File

@ -136,10 +136,6 @@ open class Tilelet: VDS.Tilelet, VDSMoleculeViewProtocol{
}
extension Tilelet: MVMCoreUIViewConstrainingProtocol {
// Investigate later.
//public func horizontalAlignment() -> UIStackView.Alignment { .leading }
public func isClippable() -> Bool {
return false
}

View File

@ -45,6 +45,7 @@ open class TileletModel: TileContainerBaseModel<Tilelet.Padding, Tilelet>, Molec
case textWidth
case textPercentage
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
@ -86,7 +87,7 @@ open class TileletModel: TileContainerBaseModel<Tilelet.Padding, Tilelet>, Molec
} else {
subTitleColor = .primary
}
try super.init(from: decoder)
}

View File

@ -96,3 +96,17 @@ open class TooltipModel: MoleculeModelProtocol {
}
}
extension TooltipModel {
public func toVDSTooltipModel() -> Tooltip.TooltipModel {
var moleculeView: MoleculeViewProtocol?
if let molecule, let view = ModelRegistry.createMolecule(molecule) {
moleculeView = view
}
return .init(closeButtonText: closeButtonText,
title: title,
content: content,
contentView: moleculeView
)
}
}

View File

@ -25,24 +25,31 @@ extension VDS.ButtonIcon.Size: Codable {}
extension VDS.ButtonIcon.BadgeIndicatorModel.ExpandDirection: Codable {}
extension VDS.ButtonIcon.SurfaceType: Codable {}
extension VDS.ButtonGroup.Alignment: Codable {}
extension VDS.CarouselScrollbar.Layout: Codable {}
extension VDS.DatePicker.DateFormat: Codable {}
extension VDS.EntryFieldBase.HelperTextPlacement: Codable {}
extension VDS.Icon.Name: Codable {}
extension VDS.Icon.Size: Codable {}
extension VDS.InputField.CreditCardType: Codable {}
extension VDS.InputField.DateFormat: Codable {}
extension VDS.InputField.FieldType: Codable {}
extension VDS.Line.Style: Codable {}
extension VDS.Line.Orientation: Codable {}
extension VDS.Tabs.Orientation: Codable {}
extension VDS.Tabs.IndicatorPosition: Codable {}
extension VDS.Tabs.Overflow: Codable {}
extension VDS.Tabs.Size: Codable {}
extension VDS.TextArea.Height: Codable {}
extension VDS.TextLink.Size: Codable {}
extension VDS.TextLinkCaret.IconPosition: Codable {}
extension VDS.TileContainerBase.AspectRatio: Codable {}
extension VDS.Tilelet.Padding: Codable {}
extension VDS.TitleLockup.TextAlignment: Codable {}
extension VDS.Toggle.TextSize: Codable {}
extension VDS.Toggle.TextWeight: Codable {}
extension VDS.Toggle.TextPosition: Codable {}
extension VDS.Toggle.TextWeight: Codable {}
extension VDS.Tooltip.FillColor: Codable {}
extension VDS.Tooltip.Size: Codable {}
extension VDS.Line.Style: Codable {}
extension VDS.Line.Orientation: Codable {}
extension VDS.Use: Codable {}
extension VDS.Button.Size: RawRepresentableCodable {

View File

@ -137,7 +137,7 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol,
open override func pageShown() {
// Currently not calling super until we can decouple page shown logics for managers.
hideNavigationBarLine(true)
hideNavigationBarLine(!tabs.isHidden)
}
open override func viewWillDisappear(_ animated: Bool) {
@ -148,7 +148,7 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol,
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
hideNavigationBarLine(true)
hideNavigationBarLine(!tabs.isHidden)
}
/// ensures margin for tabs are correct
private func updateTabsMargin() {
@ -263,7 +263,7 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol,
}
tabs.selectIndex(index, animated: true)
self.index = nil
hideNavigationBarLine(true)
hideNavigationBarLine(!tabs.isHidden)
}
public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
@ -354,12 +354,12 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol,
open func newDataReceived(in viewController: UIViewController) {
manager?.newDataReceived?(in: viewController)
hideNavigationBarLine(true)
hideNavigationBarLine(!tabs.isHidden)
}
public func willDisplay(_ viewController: UIViewController) {
manager?.willDisplay?(viewController)
hideNavigationBarLine(true)
hideNavigationBarLine(!tabs.isHidden)
}
public func displayedViewController(_ viewController: UIViewController) {