diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 3cc6f842..3fce1455 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -269,6 +269,8 @@ BB2FB3BB247E7EBC00DF73CD /* TagCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2FB3BA247E7EBC00DF73CD /* TagCollectionViewCell.swift */; }; BB2FB3BD247E7EF200DF73CD /* Tags.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2FB3BC247E7EF200DF73CD /* Tags.swift */; }; BB2FB3BF247E7F0900DF73CD /* TagsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2FB3BE247E7F0900DF73CD /* TagsModel.swift */; }; + BB3BC12F2550094500297977 /* ListLeftVariableIconWithRightCaretAllTextLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3BC12D2550094500297977 /* ListLeftVariableIconWithRightCaretAllTextLinks.swift */; }; + BB3BC1302550094500297977 /* ListLeftVariableIconWithRightCaretAllTextLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3BC12E2550094500297977 /* ListLeftVariableIconWithRightCaretAllTextLinksModel.swift */; }; BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */; }; BB47A588241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47A587241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift */; }; BB54C5202434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB54C51E2434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift */; }; @@ -810,6 +812,8 @@ BB2FB3BA247E7EBC00DF73CD /* TagCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagCollectionViewCell.swift; sourceTree = ""; }; BB2FB3BC247E7EF200DF73CD /* Tags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tags.swift; sourceTree = ""; }; BB2FB3BE247E7F0900DF73CD /* TagsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsModel.swift; sourceTree = ""; }; + BB3BC12D2550094500297977 /* ListLeftVariableIconWithRightCaretAllTextLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconWithRightCaretAllTextLinks.swift; sourceTree = ""; }; + BB3BC12E2550094500297977 /* ListLeftVariableIconWithRightCaretAllTextLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableIconWithRightCaretAllTextLinksModel.swift; sourceTree = ""; }; BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextDividerSubsectionModel.swift; sourceTree = ""; }; BB47A587241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextDividerSubsection.swift; sourceTree = ""; }; BB54C51E2434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRightVariableButtonAllTextAndLinks.swift; sourceTree = ""; }; @@ -1562,6 +1566,8 @@ AA69AAF52445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift */, 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */, 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */, + BB3BC12E2550094500297977 /* ListLeftVariableIconWithRightCaretAllTextLinksModel.swift */, + BB3BC12D2550094500297977 /* ListLeftVariableIconWithRightCaretAllTextLinks.swift */, 8D24041423E7FC0B009E23BE /* ListLeftVariableIconWithRightCaretModel.swift */, 8D24041023E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift */, AA0A257724766C8A00862F64 /* ListLeftVariableIconWithRightCaretBodyTextModel.swift */, @@ -2574,6 +2580,7 @@ 944589232385DA9600DE9FD4 /* ImageViewModel.swift in Sources */, D213347723843825008E41B3 /* Line.swift in Sources */, D2E2A99C23D8D975000B42E6 /* ImageHeadlineBodyModel.swift in Sources */, + BB3BC1302550094500297977 /* ListLeftVariableIconWithRightCaretAllTextLinksModel.swift in Sources */, 525019DE2406430800EED91C /* ListProgressBarData.swift in Sources */, D28A837F23CCA96400DFE4FC /* TabsModel.swift in Sources */, AAB8549A24DC01DB00477C40 /* ListThreeColumnBillHistoryDivider.swift in Sources */, @@ -2754,6 +2761,7 @@ D2D90B442404789000DD6EC9 /* MoleculeContainerProtocol.swift in Sources */, 0A7ECC5F243CEB1200C828E8 /* ColorViewWithLabel.swift in Sources */, 94C0150A24215643005811A9 /* ActionTopAlertModel.swift in Sources */, + BB3BC12F2550094500297977 /* ListLeftVariableIconWithRightCaretAllTextLinks.swift in Sources */, 012A88DB238ED45900FE3DA1 /* CarouselModel.swift in Sources */, D2092355244FA0FD0044AD09 /* ThreeLayerTemplateModelProtocol.swift in Sources */, 0AE14F64238315D2005417F8 /* TextField.swift in Sources */, diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index 8bf22edf..ff262876 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -171,6 +171,7 @@ import Foundation // MARK:- Designed List Items MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableIconWithRightCaret.self, viewModelClass: ListLeftVariableIconWithRightCaretModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableIconWithRightCaretBodyText.self, viewModelClass: ListLeftVariableIconWithRightCaretBodyTextModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableIconWithRightCaretAllTextLinks.self, viewModelClass: ListLeftVariableIconWithRightCaretAllTextLinksModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableCheckboxAllTextAndLinks.self, viewModelClass: ListLeftVariableCheckboxAllTextAndLinksModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableRadioButtonAndPaymentMethod.self, viewModelClass: ListLeftVariableRadioButtonAndPaymentMethodModel.self) MoleculeObjectMapping.shared()?.register(viewClass: ListLeftVariableRadioButtonBodyText.self, viewModelClass: ListLeftVariableRadioButtonBodyTextModel.self) diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinks.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinks.swift new file mode 100644 index 00000000..d6175136 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinks.swift @@ -0,0 +1,126 @@ +// +// ListLeftVariableIconWithRightCaretAllTextLinks.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 26/10/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +@objcMembers open class ListLeftVariableIconWithRightCaretAllTextLinks: TableViewCell { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + + public let leftImage = LoadImageView() + public let eyebrowHeadlineBodyLink = EyebrowHeadlineBodyLink() + public let rightLabel = Label(fontStyle: .RegularBodySmall) + + public lazy var rightLabelStackItem: StackItem = { + return StackItem(andContain: rightLabel) + }() + + public lazy var stack: Stack = { + return Stack(with: StackModel(molecules: [StackItemModel(horizontalAlignment: .fill), + + StackItemModel(horizontalAlignment: .fill), + + StackItemModel(horizontalAlignment: .fill)], + axis: .horizontal), stackItems: [StackItem(andContain: leftImage), StackItem(andContain: eyebrowHeadlineBodyLink), rightLabelStackItem]) + + }() + + open override func alignAccessoryToHero() -> CGPoint? { + // Ensures that the right label is centered vertically with headline. + let heroCenter = super.alignAccessoryToHero() + if let heroCenter = heroCenter { + let convertedPoint = stack.convert(heroCenter, from: self) + rightLabelStackItem.containerHelper.alignCenterVerticalConstraint?.constant = convertedPoint.y - stack.bounds.midY + } + return heroCenter + } + + //----------------------------------------------------- + // MARK: - View Lifecycle + //-------------------------------------------------- + + override open func setupView() { + super.setupView() + + leftImage.addSizeConstraintsForAspectRatio = true + leftImage.contentMode = .scaleAspectFit + rightLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 900), for: .horizontal) + rightLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) + rightLabel.numberOfLines = 1 + addMolecule(stack) + stack.restack() + } + + //-------------------------------------------------- + // MARK: - Molecule + //--------------------------------------------------- + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + + guard let model = model as? ListLeftVariableIconWithRightCaretAllTextLinksModel else { return } + + leftImage.set(with: model.image, delegateObject, additionalData) + eyebrowHeadlineBodyLink.set(with: model.eyebrowHeadlineBodyLink, delegateObject, additionalData) + rightLabel.set(with: model.rightLabel, delegateObject, additionalData) + updateAccessibilityLabel() + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 90 + } + + open override func reset() { + super.reset() + rightLabel.setFontStyle(.RegularBodySmall) + } + + //-------------------------------------------------- + // MARK: - Accessibility + //-------------------------------------------------- + + func getAccessibilityMessage() -> String? { + var message = "" + if let leftImageLabel = leftImage.imageView.accessibilityLabel { + message += leftImageLabel + ", " + } + if let label = eyebrowHeadlineBodyLink.getAccessibilityMessage() { + message += label + ", " + } + if let rightLabel = rightLabel.text { + message += rightLabel + } + return message + } + + func updateAccessibilityLabel() { + let linkShowing = eyebrowHeadlineBodyLink.link.titleLabel?.text?.count ?? 0 > 0 + isAccessibilityElement = !linkShowing + accessibilityTraits = (isAccessibilityElement && accessoryView != nil) ? .button : .none + + if !linkShowing { + // Make whole cell focusable if no link. + accessibilityLabel = getAccessibilityMessage() + } else if let accessoryView = accessoryView { + // Both caret and link. Read all content on caret. + accessoryView.accessibilityLabel = getAccessibilityMessage() + accessibilityElements = [accessoryView, eyebrowHeadlineBodyLink.link] + } else { + // Only link. Manually add accessibility elements to ensure they are read in the right order. + var elements: [Any] = [] + elements.append(leftImage.imageView) + if let otherElements = eyebrowHeadlineBodyLink.getAccessibilityElements() { + elements.append(otherElements) + } + if let rightLabelText = rightLabel.text, !rightLabelText.isEmpty { + elements.append(rightLabel) + } + accessibilityElements = elements + } + } +} + diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinksModel.swift new file mode 100644 index 00000000..9d9291e8 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/LeftVariable/ListLeftVariableIconWithRightCaretAllTextLinksModel.swift @@ -0,0 +1,73 @@ +// +// ListLeftVariableIconWithRightCaretAllTextLinksModel.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 26/10/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +public class ListLeftVariableIconWithRightCaretAllTextLinksModel: ListItemModel, MoleculeModelProtocol { + //----------------------------------------------------- + // MARK: - Properties + //----------------------------------------------------- + + public static var identifier: String = "listLVImgRCAll" + public var image: ImageViewModel + public var eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel + public var rightLabel: LabelModel + + //----------------------------------------------------- + // MARK: - Methods + //----------------------------------------------------- + + override public func setDefaults() { + super.setDefaults() + if image.width == nil, image.height == nil { + image.width = 30 + image.height = 30 + } + } + + //----------------------------------------------------- + // MARK: - Initializers + //----------------------------------------------------- + + public init(image: ImageViewModel, eyebrowHeadlineBodyLink: EyebrowHeadlineBodyLinkModel, rightLabel: LabelModel) { + self.image = image + self.eyebrowHeadlineBodyLink = eyebrowHeadlineBodyLink + self.rightLabel = rightLabel + super.init() + } + + //----------------------------------------------------- + // MARK: - Keys + //----------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case moleculeName + case image + case eyebrowHeadlineBodyLink + case rightLabel + } + + //----------------------------------------------------- + // MARK: - Codec + //----------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + image = try typeContainer.decode(ImageViewModel.self, forKey: .image) + eyebrowHeadlineBodyLink = try typeContainer.decode(EyebrowHeadlineBodyLinkModel.self, forKey: .eyebrowHeadlineBodyLink) + rightLabel = try typeContainer.decode(LabelModel.self, forKey: .rightLabel) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(image, forKey: .image) + try container.encode(eyebrowHeadlineBodyLink, forKey: .eyebrowHeadlineBodyLink) + try container.encode(rightLabel, forKey: .rightLabel) + } +}