Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui into feature/monarch

This commit is contained in:
Scott Pfeil 2024-04-25 12:48:41 -04:00
commit 0033009bb9
11 changed files with 738 additions and 34 deletions

View File

@ -572,9 +572,15 @@
DBEFFA04225A829700230692 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB891E822253FA8500022516 /* Label.swift */; };
EA05EFA9278DDE2C00828819 /* ClearFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA05EFA8278DDE2C00828819 /* ClearFormFieldEffectModel.swift */; };
EA05EFAB278DE53600828819 /* ClearableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA05EFAA278DE53600828819 /* ClearableModelProtocol.swift */; };
EA1758482BC97ED800A5C0D9 /* BadgeIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1758472BC97ED800A5C0D9 /* BadgeIndicator.swift */; };
EA17584A2BC97EF100A5C0D9 /* BadgeIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1758492BC97EF100A5C0D9 /* BadgeIndicatorModel.swift */; };
EA17584C2BC9894800A5C0D9 /* ButtonIconModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA17584B2BC9894800A5C0D9 /* ButtonIconModel.swift */; };
EA17584E2BC9895A00A5C0D9 /* ButtonIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA17584D2BC9895A00A5C0D9 /* ButtonIcon.swift */; };
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 */; };
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 */; };
EA6E8B972B504A4D000139B4 /* ButtonGroupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6E8B962B504A4D000139B4 /* ButtonGroupModel.swift */; };
EA7D81602B2B6E6800D29F9E /* Icon.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7D815F2B2B6E6800D29F9E /* Icon.swift */; };
@ -1177,9 +1183,15 @@
DBC4391A224421A0001AB423 /* CaretLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretLink.swift; sourceTree = "<group>"; };
EA05EFA8278DDE2C00828819 /* ClearFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearFormFieldEffectModel.swift; sourceTree = "<group>"; };
EA05EFAA278DE53600828819 /* ClearableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearableModelProtocol.swift; sourceTree = "<group>"; };
EA1758472BC97ED800A5C0D9 /* BadgeIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeIndicator.swift; sourceTree = "<group>"; };
EA1758492BC97EF100A5C0D9 /* BadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeIndicatorModel.swift; sourceTree = "<group>"; };
EA17584B2BC9894800A5C0D9 /* ButtonIconModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconModel.swift; sourceTree = "<group>"; };
EA17584D2BC9895A00A5C0D9 /* ButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIcon.swift; sourceTree = "<group>"; };
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>"; };
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>"; };
EA6E8B962B504A4D000139B4 /* ButtonGroupModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupModel.swift; sourceTree = "<group>"; };
EA7D815F2B2B6E6800D29F9E /* Icon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Icon.swift; sourceTree = "<group>"; };
@ -2267,6 +2279,12 @@
AA07EA922510A451009A2AE3 /* Star.swift */,
B4CC8FBE29DF34730005D28B /* BadgeModel.swift */,
B4CC8FBC29DF34680005D28B /* Badge.swift */,
EA1758492BC97EF100A5C0D9 /* BadgeIndicatorModel.swift */,
EA1758472BC97ED800A5C0D9 /* BadgeIndicator.swift */,
EA17584B2BC9894800A5C0D9 /* ButtonIconModel.swift */,
EA17584D2BC9895A00A5C0D9 /* ButtonIcon.swift */,
EA6642922BCDA97D00D81DC4 /* TileContainerModel.swift */,
EA6642902BCDA97300D81DC4 /* TileContainer.swift */,
EA985C3F2970939A00F2FF2E /* TileletModel.swift */,
EA985C3D2970938F00F2FF2E /* Tilelet.swift */,
EA7D81612B2B6E7F00D29F9E /* IconModel.swift */,
@ -2685,6 +2703,7 @@
D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */,
AAE96FA225341F6A0037A989 /* ListStoreLocatorModel.swift in Sources */,
D28A838923CCCFCB00DFE4FC /* LinkModel.swift in Sources */,
EA17584A2BC97EF100A5C0D9 /* BadgeIndicatorModel.swift in Sources */,
AA56A20F243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift in Sources */,
AAB9C10824346F4B00151545 /* RadioSwatches.swift in Sources */,
94C2D9A923872E5E0006CF46 /* LabelAttributeImageModel.swift in Sources */,
@ -2909,6 +2928,7 @@
D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */,
D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */,
0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */,
EA17584E2BC9895A00A5C0D9 /* ButtonIcon.swift in Sources */,
D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */,
D260105D23D0BCD400764D80 /* Stack.swift in Sources */,
0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */,
@ -2936,6 +2956,7 @@
D22479962316AF6E003FCCF9 /* HeadlineBodyLink.swift in Sources */,
8DE5BECD2456F7A200772E76 /* ListTwoColumnDropdownSelectorsModel.swift in Sources */,
AA7F47732541AD560015A2C1 /* ListStarRatingModel.swift in Sources */,
EA17584C2BC9894800A5C0D9 /* ButtonIconModel.swift in Sources */,
AA7F47762541AD6A0015A2C1 /* ListStarRating.swift in Sources */,
0A41BA7F23453A6400D4C0BC /* TextEntryField.swift in Sources */,
AF7E509829E477C1009DC2AD /* AlertHandler.swift in Sources */,
@ -2990,6 +3011,7 @@
D264FAAC2441009400D98315 /* RadioBoxCollectionViewCell.swift in Sources */,
BB2C969224330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift in Sources */,
D2D90B42240463E100DD6EC9 /* MoleculeHeaderModel.swift in Sources */,
EA1758482BC97ED800A5C0D9 /* BadgeIndicator.swift in Sources */,
012A88B1238C880100FE3DA1 /* CarouselPagingModelProtocol.swift in Sources */,
0A9D091E2433796500D2E6C0 /* NumericCarouselIndicatorModel.swift in Sources */,
D29DF2C921E7BFC6003B2FB9 /* MFSizeObject.m in Sources */,
@ -3054,6 +3076,7 @@
0AE14F64238315D2005417F8 /* TextField.swift in Sources */,
0A51F3E22475CB73002E08B6 /* LoadingSpinnerModel.swift in Sources */,
D2169303251E53D9002A6324 /* SectionListTemplateModel.swift in Sources */,
EA6642932BCDA97D00D81DC4 /* TileContainerModel.swift in Sources */,
AF7E509929E477C1009DC2AD /* AlertController.swift in Sources */,
0AB764D124460F6300E7FE72 /* UIDatePicker+Extension.swift in Sources */,
BB105859248DEFF70069D008 /* UICollectionViewLeftAlignedLayout.swift in Sources */,
@ -3181,6 +3204,7 @@
D29C559625C099630082E7D6 /* VideoDataManager.swift in Sources */,
8D4687E2242E2DE400802879 /* ListFourColumnDataUsageListItemModel.swift in Sources */,
D29E28DD23D7404C00ACEA85 /* ContainerHelper.swift in Sources */,
EA6642912BCDA97300D81DC4 /* TileContainer.swift in Sources */,
012A88C2238D7BCA00FE3DA1 /* CarouselItemModel.swift in Sources */,
27F6B08C26052AFF008529AA /* ParentMoleculeModelProtocol.swift in Sources */,
EA985C402970939A00F2FF2E /* TileletModel.swift in Sources */,

View File

@ -0,0 +1,70 @@
//
// BadgeIndicator.swift
// MVMCoreUI
//
// Created by Matt Bruce on 4/12/24.
// Copyright © 2024 Verizon Wireless. All rights reserved.
//
import Foundation
import VDS
open class BadgeIndicator: VDS.BadgeIndicator, VDSMoleculeViewProtocol {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
public var viewModel: BadgeIndicatorModel!
public var delegateObject: MVMCoreUIDelegateObject?
public var additionalData: [AnyHashable : Any]?
//--------------------------------------------------
// MARK: - Public Methods
//--------------------------------------------------
public func viewModelDidUpdate() {
surface = viewModel.surface
number = viewModel.number
fillColor = viewModel.fillColor
borderColorLight = viewModel.borderColorLight?.uiColor
borderColorDark = viewModel.borderColorDark?.uiColor
kind = viewModel.kind
maximumDigits = viewModel.maximumDigits
size = viewModel.size
leadingCharacter = viewModel.leadingCharacter
trailingText = viewModel.trailingText
dotSize = viewModel.dotSize
verticalPadding = viewModel.verticalPadding
horizontalPadding = viewModel.horizontalPadding
hideDot = viewModel.hideDot
hideBorder = viewModel.hideBorder
width = viewModel.width
height = viewModel.height
}
public func updateView(_ size: CGFloat) {}
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
open override func updateAccessibility() {
super.updateAccessibility()
if let viewModel {
if let accessibilityText = viewModel.accessibilityText {
self.accessibilityLabel = accessibilityText
}
}
}
}
//to deal with how it's parent constrains this control
extension BadgeIndicator: MVMCoreUIViewConstrainingProtocol {
public func needsToBeConstrained() -> Bool { true }
public func horizontalAlignment() -> UIStackView.Alignment { .leading }
}

View File

@ -0,0 +1,111 @@
//
// BadgeIndicatorModel.swift
// MVMCoreUI
//
// Created by Matt Bruce on 4/12/24.
// Copyright © 2024 Verizon Wireless. All rights reserved.
//
import Foundation
import VDS
open class BadgeIndicatorModel: MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String { "badgeIndicator" }
public var id: String = UUID().uuidString
public var backgroundColor: Color?
//--------------------------------------------------
// MARK: - VDS Properties
//--------------------------------------------------
public var surface: Surface { inverted ? .dark : .light }
public var inverted: Bool = false
public var number: Int?
public var accessibilityText: String?
public var fillColor = BadgeIndicator.FillColor.red
public var borderColorLight: Color?
public var borderColorDark: Color?
public var kind = BadgeIndicator.Kind.simple
public var maximumDigits = BadgeIndicator.MaximumDigits.two
public var size = BadgeIndicator.Size.xxlarge
public var leadingCharacter: String?
public var trailingText: String?
public var dotSize: CGFloat?
public var verticalPadding: CGFloat?
public var horizontalPadding: CGFloat?
public var hideDot: Bool = false
public var hideBorder: Bool = false
public var width: CGFloat?
public var height: CGFloat?
private enum CodingKeys: String, CodingKey {
case id
case inverted
case accessibilityText
case number
case fillColor
case borderColorLight
case borderColorDark
case kind
case maximumDigits
case size
case leadingCharacter
case trailingText
case dotSize
case verticalPadding
case horizontalPadding
case hideDot
case hideBorder
case width
case height
}
required public convenience init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.init()
id = try container.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
inverted = try container.decodeIfPresent(Bool.self, forKey: .inverted) ?? false
accessibilityText = try container.decodeIfPresent(String.self, forKey: .accessibilityText)
number = try container.decodeIfPresent(Int.self, forKey: .number)
fillColor = try container.decodeIfPresent(BadgeIndicator.FillColor.self, forKey: .fillColor) ?? .red
borderColorLight = try container.decodeIfPresent(Color.self, forKey: .borderColorLight)
borderColorDark = try container.decodeIfPresent(Color.self, forKey: .borderColorDark)
kind = try container.decodeIfPresent(BadgeIndicator.Kind.self, forKey: .kind) ?? .simple
maximumDigits = try container.decodeIfPresent(BadgeIndicator.MaximumDigits.self, forKey: .maximumDigits) ?? .two
size = try container.decodeIfPresent(BadgeIndicator.Size.self, forKey: .size) ?? .xxlarge
leadingCharacter = try container.decodeIfPresent(String.self, forKey: .leadingCharacter)
trailingText = try container.decodeIfPresent(String.self, forKey: .trailingText)
dotSize = try container.decodeIfPresent(CGFloat.self, forKey: .dotSize)
verticalPadding = try container.decodeIfPresent(CGFloat.self, forKey: .verticalPadding)
horizontalPadding = try container.decodeIfPresent(CGFloat.self, forKey: .horizontalPadding)
hideDot = try container.decodeIfPresent(Bool.self, forKey: .hideDot) ?? false
hideBorder = try container.decodeIfPresent(Bool.self, forKey: .hideBorder) ?? false
width = try container.decodeIfPresent(CGFloat.self, forKey: .width)
height = try container.decodeIfPresent(CGFloat.self, forKey: .height)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(inverted, forKey: .inverted)
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
try container.encodeIfPresent(number, forKey: .number)
try container.encodeIfPresent(fillColor, forKey: .fillColor)
try container.encodeIfPresent(borderColorLight, forKey: .borderColorLight)
try container.encodeIfPresent(borderColorDark, forKey: .borderColorDark)
try container.encodeIfPresent(kind, forKey: .kind)
try container.encodeIfPresent(maximumDigits, forKey: .maximumDigits)
try container.encodeIfPresent(size, forKey: .size)
try container.encodeIfPresent(leadingCharacter, forKey: .leadingCharacter)
try container.encodeIfPresent(trailingText, forKey: .trailingText)
try container.encodeIfPresent(dotSize, forKey: .dotSize)
try container.encodeIfPresent(verticalPadding, forKey: .verticalPadding)
try container.encodeIfPresent(hideDot, forKey: .hideDot)
try container.encodeIfPresent(hideBorder, forKey: .hideBorder)
try container.encodeIfPresent(width, forKey: .width)
try container.encodeIfPresent(height, forKey: .height)
}
}

View File

@ -0,0 +1,72 @@
//
// ButtonIcon.swift
// MVMCoreUI
//
// Created by Matt Bruce on 4/12/24.
// Copyright © 2024 Verizon Wireless. All rights reserved.
//
import Foundation
import VDS
open class ButtonIcon: VDS.ButtonIcon, VDSMoleculeViewProtocol {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
public var viewModel: ButtonIconModel!
public var delegateObject: MVMCoreUIDelegateObject?
public var additionalData: [AnyHashable : Any]?
//--------------------------------------------------
// MARK: - Public Methods
//--------------------------------------------------
public func viewModelDidUpdate() {
surface = viewModel.surface
badgeIndicatorModel = viewModel.badgeIndicatorModel
kind = viewModel.kind
surfaceType = viewModel.surfaceType
iconName = viewModel.iconName
selectedIconName = viewModel.selectedIconName
size = viewModel.size
customSize = viewModel.customSize
floating = viewModel.floating
fitToIcon = viewModel.fitToIcon
hideBorder = viewModel.hideBorder
showBadgeIndicator = viewModel.showBadgeIndicator
selectable = viewModel.selectable
iconOffset = viewModel.iconOffset
}
public func updateView(_ size: CGFloat) {}
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
open override func updateAccessibility() {
super.updateAccessibility()
if let viewModel {
if let accessibilityText = viewModel.accessibilityText {
//since this is a container control and the
//icon & badgeIndicator (gets from it's own model) are traversed separatly
icon.accessibilityLabel = accessibilityText
}
}
}
}
//to deal with how it's parent constrains this control
extension ButtonIcon: MVMCoreUIViewConstrainingProtocol {
public func needsToBeConstrained() -> Bool { true }
public func horizontalAlignment() -> UIStackView.Alignment { .leading }
}

View File

@ -0,0 +1,142 @@
//
// ButtonIconModel.swift
// MVMCoreUI
//
// Created by Matt Bruce on 4/12/24.
// Copyright © 2024 Verizon Wireless. All rights reserved.
//
import Foundation
import VDS
open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "buttonIcon"
public var id: String = UUID().uuidString
public var backgroundColor: Color?
//--------------------------------------------------
// MARK: - VDS Properties
//--------------------------------------------------
public var surface: Surface { inverted ? .dark : .light }
public var inverted: Bool = false
public var accessibilityText: String?
public var action: ActionModelProtocol
public var kind = ButtonIcon.Kind.ghost
public var surfaceType = ButtonIcon.SurfaceType.colorFill
public var iconName: Icon.Name = .info
public var selectedIconName: Icon.Name?
public var size = ButtonIcon.Size.large
public var customSize : Int?
public var floating: Bool = false
public var fitToIcon: Bool = false
public var hideBorder: Bool = true
public var showBadgeIndicator: Bool = false
public var selectable: Bool = false
public var iconOffset: CGPoint = .zero
public var badgeIndicatorModel: VDS.ButtonIcon.BadgeIndicatorModel? {
guard let model = badgeIndicator else { return nil }
return .init(kind: model.kind,
fillColor: model.fillColor,
expandDirection: expandDirection,
size: model.size,
maximumDigits: model.maximumDigits,
width: model.width,
height: model.height,
number: model.number,
leadingCharacter: model.leadingCharacter,
trailingText: model.trailingText,
dotSize: model.dotSize,
verticalPadding: model.verticalPadding,
horizontalPadding: model.horizontalPadding,
hideDot: model.hideDot,
hideBorder: model.hideBorder)
}
private var badgeIndicator: BadgeIndicatorModel?
private var expandDirection = ButtonIcon.BadgeIndicatorModel.ExpandDirection.right
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
public init(with iconName: VDS.Icon.Name, action: ActionModelProtocol) {
self.iconName = iconName
self.action = action
}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case id
case inverted
case accessibilityText
case action
case badgeIndicator
case expandDirection
case kind
case surfaceType
case iconName
case selectedIconName
case size
case customSize
case floating
case fitToIcon
case hideBorder
case showBadgeIndicator
case selectable
case iconOffset
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
action = try container.decodeModel(codingKey: .action)
id = try container.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
inverted = try container.decodeIfPresent(Bool.self, forKey: .inverted) ?? false
accessibilityText = try container.decodeIfPresent(String.self, forKey: .accessibilityText)
badgeIndicator = try container.decodeIfPresent(BadgeIndicatorModel.self, forKey: .badgeIndicator)
expandDirection = try container.decodeIfPresent(ButtonIcon.BadgeIndicatorModel.ExpandDirection.self, forKey: .expandDirection) ?? .right
kind = try container.decodeIfPresent(ButtonIcon.Kind.self, forKey: .kind) ?? .ghost
surfaceType = try container.decodeIfPresent(ButtonIcon.SurfaceType.self, forKey: .kind) ?? .colorFill
iconName = try container.decode(Icon.Name.self, forKey: .iconName)
selectedIconName = try container.decodeIfPresent(Icon.Name.self, forKey: .selectedIconName)
size = try container.decodeIfPresent(ButtonIcon.Size.self, forKey: .size) ?? .large
customSize = try container.decodeIfPresent(Int.self, forKey: .customSize)
floating = try container.decodeIfPresent(Bool.self, forKey: .floating) ?? false
fitToIcon = try container.decodeIfPresent(Bool.self, forKey: .fitToIcon) ?? false
hideBorder = try container.decodeIfPresent(Bool.self, forKey: .hideBorder) ?? false
showBadgeIndicator = try container.decodeIfPresent(Bool.self, forKey: .showBadgeIndicator) ?? false
selectable = try container.decodeIfPresent(Bool.self, forKey: .selectable) ?? false
iconOffset = try container.decodeIfPresent(CGPoint.self, forKey: .iconOffset) ?? .zero
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(inverted, forKey: .inverted)
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
try container.encodeIfPresent(badgeIndicator, forKey: .badgeIndicator)
try container.encodeIfPresent(expandDirection, forKey: .expandDirection)
try container.encodeIfPresent(kind, forKey: .kind)
try container.encodeIfPresent(surfaceType, forKey: .kind)
try container.encode(iconName, forKey: .iconName)
try container.encodeIfPresent(selectedIconName, forKey: .selectedIconName)
try container.encodeIfPresent(size, forKey: .size)
try container.encodeIfPresent(customSize, forKey: .customSize)
try container.encodeIfPresent(floating, forKey: .floating)
try container.encodeIfPresent(fitToIcon, forKey: .fitToIcon)
try container.encodeIfPresent(hideBorder, forKey: .hideBorder)
try container.encodeIfPresent(showBadgeIndicator, forKey: .showBadgeIndicator)
try container.encodeIfPresent(selectable, forKey: .selectable)
try container.encodeIfPresent(iconOffset, forKey: .iconOffset)
}
}

View File

@ -0,0 +1,134 @@
//
// TileContainer.swift
// MVMCoreUI
//
// Created by Matt Bruce on 4/15/24.
// Copyright © 2024 Verizon Wireless. All rights reserved.
//
import Foundation
import VDS
import Combine
open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public var model: MoleculeModelProtocol?
public var viewModel: TileContainerModel!
public var delegateObject: MVMCoreUIDelegateObject?
public var additionalData: [AnyHashable: Any]?
public var molecule: MoleculeViewProtocol? {
willSet {
if newValue == nil {
molecule?.removeFromSuperview()
}
}
}
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
public convenience required init() {
self.init(frame: .zero)
}
//--------------------------------------------------
// MARK: - Public
//--------------------------------------------------
public func viewModelDidUpdate() {
if let moleculeModel = viewModel.molecule {
if let molecule,
moleculeModel.moleculeName == molecule.model?.moleculeName {
molecule.set(with: moleculeModel, delegateObject, additionalData)
} else if let moleculeView = ModelRegistry.createMolecule(moleculeModel, delegateObject: delegateObject, additionalData: additionalData) {
molecule = moleculeView
addContentView(moleculeView)
}
}
if let imageName = viewModel.backgroundImage {
loadImage(imageName)
}
padding = viewModel.padding
color = viewModel.color
backgroundEffect = viewModel.backgroundEffect
aspectRatio = viewModel.aspectRatio
width = viewModel.width
height = viewModel.height
showBorder = viewModel.showBorder
showDropShadows = viewModel.showDropShadwows
//setup action
if let action = viewModel.action {
//add the subscriber
onClick = { [weak self] control in
guard let self, let viewModel = self.viewModel else { return }
MVMCoreUIActionHandler.performActionUnstructured(with: action,
sourceModel: viewModel,
additionalData: self.additionalData,
delegateObject: self.delegateObject)
}
}
}
private func loadImage(_ imageName: String? = nil) {
guard let imageName else {
if backgroundImage != nil {
backgroundImage = nil
}
return
}
let finishedLoadingBlock: MVMCoreGetImageBlock = {[weak self] (image, data, isFallbackImage) in MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in
guard let self = self else { return }
self.backgroundImage = image
})}
MVMCoreCache.shared()?.getImage(imageName, useWidth: false, widthForS7: 0, useHeight: false, heightForS7: 0, format: nil, localFallbackImageName: nil, allowServerQueryParameters: false, localBundle: nil, completionHandler: finishedLoadingBlock)
}
//--------------------------------------------------
// MARK: - MVMCoreViewProtocol
//--------------------------------------------------
open func updateView(_ size: CGFloat) {}
//--------------------------------------------------
// MARK: - MoleculeViewProtocol
//--------------------------------------------------
//since this is a class func, we can't reference it directly
public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
100
}
/// Allows the molecule to set its name for reuse. Default could be moleculeName. Mainly used for list or collections.
public static func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
// This will aggregate names of molecules to make an id.
guard let containerModel = model as? TileContainerModel,
let molecule = containerModel.molecule,
let moleculeClass = ModelRegistry.getMoleculeClass(molecule),
let moleculeName = moleculeClass.nameForReuse(with: molecule, delegateObject)
else { return "\(model.moleculeName)<>" }
return "\(model.moleculeName)<\(moleculeName)>"
}
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
open override func layoutSubviews() {
super.layoutSubviews()
// Accounts for any collection size changes
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self)
}
}
}
extension TileContainer: MVMCoreUIViewConstrainingProtocol {
public func horizontalAlignment() -> UIStackView.Alignment { .leading }
}

View File

@ -0,0 +1,107 @@
//
// TileContainerModel.swift
// MVMCoreUI
//
// Created by Matt Bruce on 4/15/24.
// Copyright © 2024 Verizon Wireless. All rights reserved.
//
import Foundation
import VDS
open class TileContainerModel: TileContainerBaseModel<TileContainer.Padding, TileContainer>, ParentMoleculeModelProtocol, MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "tileContainer"
public var id: String = UUID().uuidString
public var backgroundColor: Color?
public var molecule: MoleculeModelProtocol?
public var children: [any MoleculeModelProtocol] {
guard let molecule else { return [] }
return [molecule]
}
public func replaceChildMolecule(with molecule: MoleculeModelProtocol) throws -> Bool {
return try replaceChildMolecule(at: &self.molecule, with: molecule)
}
private enum CodingKeys: String, CodingKey {
case id
case moleculeName
case molecule
}
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
molecule = try container.decodeModelIfPresent(codingKey: .molecule)
try super.init(from: decoder)
}
public override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeModelIfPresent(molecule, forKey: .molecule)
try super.encode(to: encoder)
}
}
open class TileContainerBaseModel<PaddingType: DefaultValuing & Codable, TileContainerType:TileContainerBase<PaddingType>> : Codable{
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public var backgroundImage: String?
public var action: ActionModelProtocol?
public var imageFallbackColor: Surface = .light
public var width: CGFloat?
public var height: CGFloat?
public var showBorder: Bool = false
public var showDropShadwows: Bool = false
public var padding = PaddingType.defaultValue
public var color: TileContainerType.BackgroundColor = .black
public var aspectRatio: TileContainerType.AspectRatio = .ratio1x1
public var backgroundEffect: TileContainerType.BackgroundEffect = .none
private enum CodingKeys: String, CodingKey {
case backgroundImage
case action
case padding
case color
case aspectRatio
case imageFallbackColor
case width
case height
case showBorder
case showDropShadows
case backgroundEffect
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
backgroundImage = try container.decodeIfPresent(String.self, forKey: .backgroundImage)
width = try container.decodeIfPresent(CGFloat.self, forKey: .width)
height = try container.decodeIfPresent(CGFloat.self, forKey: .height)
action = try container.decodeModelIfPresent(codingKey: .action)
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
backgroundEffect = try container.decodeIfPresent(TileContainerType.BackgroundEffect.self, forKey: .backgroundEffect) ?? .none
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(backgroundImage, forKey: .backgroundImage)
try container.encodeIfPresent(width, forKey: .width)
try container.encodeIfPresent(height, forKey: .height)
try container.encodeIfPresent(backgroundEffect, forKey: .backgroundEffect)
try container.encodeIfPresent(padding, forKey: .padding)
try container.encodeIfPresent(color, forKey: .color)
try container.encodeIfPresent(aspectRatio, forKey: .aspectRatio)
try container.encodeModelIfPresent(action, forKey: .action)
}
}

View File

@ -39,10 +39,16 @@ open class Tilelet: VDS.Tilelet, VDSMoleculeViewProtocol{
// MARK: - Public
//--------------------------------------------------
public func viewModelDidUpdate() {
color = viewModel.color
padding = viewModel.padding
color = viewModel.color
backgroundEffect = viewModel.backgroundEffect
aspectRatio = viewModel.aspectRatio
width = viewModel.width
height = viewModel.height
showBorder = viewModel.showBorder
showDropShadows = viewModel.showDropShadwows
if let value = viewModel.textWidth {
textWidth = .value(value)
} else if let percentage = viewModel.textPercentage {

View File

@ -9,7 +9,7 @@
import Foundation
import VDS
open class TileletModel: MoleculeModelProtocol {
open class TileletModel: TileContainerBaseModel<Tilelet.Padding, Tilelet>, MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
@ -17,52 +17,37 @@ open class TileletModel: MoleculeModelProtocol {
public static var identifier: String = "tilelet"
public var id: String = UUID().uuidString
public var backgroundColor: Color?
public var color: Tilelet.BackgroundColor
public var padding: Tilelet.Padding
public var aspectRatio: Tilelet.AspectRatio
public var badge: Tilelet.BadgeModel?
public var title: LabelModel?
public var subTitle: LabelModel?
public var descriptiveIcon: Tilelet.DescriptiveIcon?
public var directionalIcon: Tilelet.DirectionalIcon?
public var width: CGFloat?
public var textWidth: CGFloat?
public var textPercentage: CGFloat?
public var action: ActionModelProtocol?
private enum CodingKeys: String, CodingKey {
case id
case moleculeName
case backgroundColor
case color
case padding
case aspectRatio
case badge
case title
case subTitle
case descriptiveIcon
case directionalIcon
case width
case textWidth
case textPercentage
case action
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
self.backgroundColor = try container.decodeIfPresent(Color.self, forKey: .backgroundColor)
self.color = try container.decodeIfPresent(Tilelet.BackgroundColor.self, forKey: .color) ?? Tilelet.BackgroundColor.black
self.padding = try container.decodeIfPresent(Tilelet.Padding.self, forKey: .padding) ?? Tilelet.Padding.small
self.aspectRatio = try container.decodeIfPresent(Tilelet.AspectRatio.self, forKey: .aspectRatio) ?? Tilelet.AspectRatio.none
self.badge = try container.decodeIfPresent(Tilelet.BadgeModel.self, forKey: .badge)
self.title = try container.decodeIfPresent(LabelModel.self, forKey: .title)
self.subTitle = try container.decodeIfPresent(LabelModel.self, forKey: .subTitle)
self.descriptiveIcon = try container.decodeIfPresent(Tilelet.DescriptiveIcon.self, forKey: .descriptiveIcon)
self.directionalIcon = try container.decodeIfPresent(Tilelet.DirectionalIcon.self, forKey: .directionalIcon)
self.width = try container.decodeIfPresent(CGFloat.self, forKey: .width)
self.textWidth = try container.decodeIfPresent(CGFloat.self, forKey: .textWidth)
self.textPercentage = try container.decodeIfPresent(CGFloat.self, forKey: .textPercentage)
action = try container.decodeModelIfPresent(codingKey: .action)
id = try container.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
badge = try container.decodeIfPresent(Tilelet.BadgeModel.self, forKey: .badge)
title = try container.decodeIfPresent(LabelModel.self, forKey: .title)
subTitle = try container.decodeIfPresent(LabelModel.self, forKey: .subTitle)
descriptiveIcon = try container.decodeIfPresent(Tilelet.DescriptiveIcon.self, forKey: .descriptiveIcon)
directionalIcon = try container.decodeIfPresent(Tilelet.DirectionalIcon.self, forKey: .directionalIcon)
textWidth = try container.decodeIfPresent(CGFloat.self, forKey: .textWidth)
textPercentage = try container.decodeIfPresent(CGFloat.self, forKey: .textPercentage)
try super.init(from: decoder)
}
public func titleModel(delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) -> Tilelet.TitleModel? {
@ -99,22 +84,17 @@ open class TileletModel: MoleculeModelProtocol {
return .init(text: subTitle.text, textAttributes: attrs)
}
public func encode(to encoder: Encoder) throws {
public override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(color, forKey: .color)
try container.encodeIfPresent(padding, forKey: .padding)
try container.encodeIfPresent(aspectRatio, forKey: .aspectRatio)
try container.encodeIfPresent(badge, forKey: .badge)
try container.encodeIfPresent(title, forKey: .title)
try container.encodeIfPresent(subTitle, forKey: .subTitle)
try container.encodeIfPresent(descriptiveIcon, forKey: .descriptiveIcon)
try container.encodeIfPresent(directionalIcon, forKey: .directionalIcon)
try container.encodeIfPresent(width, forKey: .width)
try container.encodeIfPresent(textWidth, forKey: .textWidth)
try container.encodeIfPresent(textPercentage, forKey: .textPercentage)
try container.encodeModelIfPresent(action, forKey: .action)
try super.encode(to: encoder)
}
}

View File

@ -16,6 +16,14 @@ import VDSTokens
extension VDS.Surface: Codable {}
extension VDS.Badge.FillColor: Codable {}
extension VDS.BadgeIndicator.FillColor: Codable {}
extension VDS.BadgeIndicator.Kind: Codable {}
extension VDS.BadgeIndicator.MaximumDigits: Codable {}
extension VDS.BadgeIndicator.Size: Codable {}
extension VDS.ButtonIcon.Kind: Codable {}
extension VDS.ButtonIcon.Size: Codable {}
extension VDS.ButtonIcon.BadgeIndicatorModel.ExpandDirection: Codable {}
extension VDS.ButtonIcon.SurfaceType: Codable {}
extension VDS.ButtonGroup.Alignment: Codable {}
extension VDS.Icon.Name: Codable {}
extension VDS.Icon.Size: Codable {}
@ -92,6 +100,53 @@ extension VDS.TileContainerBase.BackgroundColor: Codable {
}
}
extension VDS.TileContainerBase.BackgroundEffect: Codable {
enum CodingKeys: String, CodingKey {
case type
case firstColor
case secondColor
}
enum BackgroundEffectType: String, Codable {
case transparency
case none
case gradient
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(BackgroundEffectType.self, forKey: .type)
switch type {
case .transparency:
self = .transparency
case .none:
self = .none
case .gradient:
let firstColor = try container.decode(String.self, forKey: .firstColor)
let secondColor = try container.decode(String.self, forKey: .secondColor)
self = .gradient(firstColor, secondColor)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .transparency:
try container.encode(BackgroundEffectType.transparency.rawValue, forKey: .type)
case .none:
try container.encode(BackgroundEffectType.none.rawValue, forKey: .type)
case .gradient(let firstColor, let secondColor):
try container.encode(BackgroundEffectType.gradient.rawValue, forKey: .type)
try container.encode(firstColor, forKey: .firstColor)
try container.encode(secondColor, forKey: .secondColor)
@unknown default:
break
}
}
}
extension VDS.TileContainer.Padding: Codable {
enum PaddingError: Error {
case valueNotFound(type: String)

View File

@ -73,8 +73,11 @@ open class CoreUIModelMapping: ModelMapping {
ModelRegistry.register(handler: LoadingSpinner.self, for: LoadingSpinnerModel.self)
ModelRegistry.register(handler: Video.self, for: VideoModel.self)
ModelRegistry.register(handler: Tilelet.self, for: TileletModel.self)
ModelRegistry.register(handler: TileContainer.self, for: TileContainerModel.self)
ModelRegistry.register(handler: Badge.self, for: BadgeModel.self)
ModelRegistry.register(handler: BadgeIndicator.self, for: BadgeIndicatorModel.self)
ModelRegistry.register(handler: Icon.self, for: IconModel.self)
ModelRegistry.register(handler: ButtonIcon.self, for: ButtonIconModel.self)
ModelRegistry.register(handler: Tooltip.self, for: TooltipModel.self)
// MARK:- Horizontal Combination Molecules