diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 1c042443..407051bd 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -146,6 +146,8 @@ 32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F8804524765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift */; }; 32F8804824765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F8804724765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift */; }; 4457904E27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4457904D27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift */; }; + 4461F77A2800AB2D00BA0222 /* EyebrowHeadlineBodyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4461F7792800AB2D00BA0222 /* EyebrowHeadlineBodyModel.swift */; }; + 4461F77C2800AB6C00BA0222 /* EyebrowHeadlineBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4461F77B2800AB6C00BA0222 /* EyebrowHeadlineBody.swift */; }; 522679C123FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */; }; 522679C223FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */; }; 52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */; }; @@ -734,6 +736,8 @@ 32F8804524765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableNumberedListAllTextAndLinksModel.swift; sourceTree = ""; }; 32F8804724765C8400C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListLeftVariableNumberedListAllTextAndLinks.swift; sourceTree = ""; }; 4457904D27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageRenderingMode+Extension.swift"; sourceTree = ""; }; + 4461F7792800AB2D00BA0222 /* EyebrowHeadlineBodyModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EyebrowHeadlineBodyModel.swift; sourceTree = ""; }; + 4461F77B2800AB6C00BA0222 /* EyebrowHeadlineBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EyebrowHeadlineBody.swift; sourceTree = ""; }; 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinks.swift; sourceTree = ""; }; 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinksModel.swift; sourceTree = ""; }; 52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextAllTextAndLinks.swift; sourceTree = ""; }; @@ -1637,6 +1641,8 @@ EA5124FC243601600051A3A4 /* BGImageHeadlineBodyButton.swift */, 0A775F2524893916009EFB58 /* ThreeHeadlineBodyLink.swift */, 0A775F2724893937009EFB58 /* ThreeHeadlineBodyLinkModel.swift */, + 4461F7792800AB2D00BA0222 /* EyebrowHeadlineBodyModel.swift */, + 4461F77B2800AB6C00BA0222 /* EyebrowHeadlineBody.swift */, ); path = VerticalCombinationViews; sourceTree = ""; @@ -2731,6 +2737,7 @@ D260106323D0C05000764D80 /* StackItemModel.swift in Sources */, D2E2A99823D8D63C000B42E6 /* ActionDetailWithImageModel.swift in Sources */, D28764AC245898A400CB882D /* ThreeLayerFillMiddleTemplateModel.swift in Sources */, + 4461F77A2800AB2D00BA0222 /* EyebrowHeadlineBodyModel.swift in Sources */, BBBBC87D24374A4900B0F079 /* ListThreeColumnBillChangesDividerModel.swift in Sources */, D2ED2800254B0E0300A1C293 /* MVMCoreAlertHandler+Extension.swift in Sources */, D2ED27EE254B0CE700A1C293 /* ActionAlertModel.swift in Sources */, @@ -2923,6 +2930,7 @@ 32F8804624765C6E00C2ACB3 /* ListLeftVariableNumberedListAllTextAndLinksModel.swift in Sources */, 011D958524042432000E3791 /* RulesProtocol.swift in Sources */, 4457904E27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift in Sources */, + 4461F77C2800AB6C00BA0222 /* EyebrowHeadlineBody.swift in Sources */, D23118B325124E18001C8440 /* Notification.swift in Sources */, AA9972502475309F00FC7472 /* ListLeftVariableIconAllTextLinksModel.swift in Sources */, AA69AAF62445BF5700AF3D3B /* ListLeftVariableCheckboxBodyText.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBody.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBody.swift new file mode 100644 index 00000000..7e9c5dc0 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBody.swift @@ -0,0 +1,159 @@ +// +// EyebrowHeadlineBody.swift +// MVMCoreUI +// +// Created by Nadigadda, Sumanth on 08/04/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +@objcMembers open class EyebrowHeadlineBody: View { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + + public let stack = Stack(frame: .zero) + public let eyebrow = Label(fontStyle: .RegularBodySmall) + public let headline = Label(fontStyle: .RegularBodySmall) + public let body = Label(fontStyle: .RegularBodySmall) + + var castModel: EyebrowHeadlineBodyModel? { + get { return model as? EyebrowHeadlineBodyModel } + } + + //-------------------------------------------------- + // MARK: - Initialization + //-------------------------------------------------- + + public convenience init(spacing: CGFloat) { + self.init(frame: .zero) + stack.stackModel?.spacing = spacing + stack.restack() + } + + //-------------------------------------------------- + // MARK: - MFViewProtocol + //-------------------------------------------------- + + open override func setupView() { + super.setupView() + stack.setAndCreateModel(with: [eyebrow, headline, body]) + stack.stackModel?.spacing = 0 + addSubview(stack) + NSLayoutConstraint.constraintPinSubview(toSuperview: stack) + stack.restack() + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + stack.stackModel?.spacing = castModel?.style.defaultSpacing() ?? EyebrowHeadlineBodyModel.Style.large.defaultSpacing() + stack.updateView(size) + } + + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + + open override func reset() { + super.reset() + stack.reset() + eyebrow.setFontStyle(.RegularBodySmall) + headline.setFontStyle(.RegularBodySmall) + body.setFontStyle(.RegularBodySmall) + } + + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + setStyle(style: castModel?.style) + + ///Updating the text color + castModel?.eyebrow?.textColor = castModel?.headlineColor + castModel?.headline.textColor = castModel?.headlineColor + castModel?.body?.textColor = castModel?.bodyColor + + if let alignment = castModel?.alignment, let textAlignment = NSTextAlignment(rawValue: alignment.rawValue) { + castModel?.eyebrow?.textAlignment = textAlignment + castModel?.headline.textAlignment = textAlignment + castModel?.body?.textAlignment = textAlignment + } + + stack.updateContainedMolecules(with: [castModel?.eyebrow, + castModel?.headline, + castModel?.body], + delegateObject, additionalData) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 65 + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + public func setStyle(style: EyebrowHeadlineBodyModel.Style? = .large) { + + ///If eyebrow style is not available, will set font style based on the component + ///Eyebrow and body share the same font size + if castModel?.eyebrow?.fontStyle == nil { + castModel?.eyebrow?.fontStyle = style?.defaultBodyFontStyle() + } + + ///If headline style is not available, will set font style based on the component + if castModel?.headline.fontStyle == nil { + castModel?.headline.fontStyle = style?.defaultHeadlineFontStyle() + } + + ///If body style is not available, will set font style based on the component + if castModel?.body?.fontStyle == nil { + castModel?.body?.fontStyle = style?.defaultBodyFontStyle() + } + } + + //-------------------------------------------------- + // MARK: - Accessibility Helpers + //-------------------------------------------------- + + /// Returns the labels text in one message. + func getAccessibilityMessage() -> String? { + + var message = "" + + if let eyebrowLabel = eyebrow.text { + message += eyebrowLabel + ", " + } + + if let headlineLabel = headline.text { + message += headlineLabel + ", " + } + + if let bodyLabel = body.text { + message += bodyLabel + } + + return message.count > 0 ? message : nil + } + + /// Returns an array of the appropriate accessibility elements. + func getAccessibilityElements() -> [Any]? { + + var elements: [UIView] = [] + + if eyebrow.hasText { + elements.append(eyebrow) + } + + if headline.hasText { + elements.append(headline) + } + + if body.hasText { + elements.append(body) + } + + return elements.count > 0 ? elements : nil + } +} diff --git a/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyModel.swift b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyModel.swift new file mode 100644 index 00000000..97b37637 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyModel.swift @@ -0,0 +1,167 @@ +// +// EyebrowHeadlineBodyModel.swift +// MVMCoreUI +// +// Created by Nadigadda, Sumanth on 08/04/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import VDSColorTokens + +public class EyebrowHeadlineBodyModel: MoleculeModelProtocol, ParentMoleculeModelProtocol { + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "eyebrowHeadlineBody" + public var moleculeName: String = EyebrowHeadlineBodyModel.identifier + + public var eyebrow: LabelModel? + public var headline: LabelModel + public var body: LabelModel? + + public var style: Style = .large + public var alignment: Alignment = .left + public var inverted: Bool = false + + private var _backgroundColor: Color? + public var backgroundColor: Color? { + get { + return inverted ? Color(uiColor: VDSColor.backgroundPrimaryDark) : Color(uiColor: VDSColor.backgroundPrimaryLight) + } + set { + _backgroundColor = newValue + } + } + + public var headlineColor: Color? { + return inverted ? Color(uiColor: VDSColor.elementsPrimaryOndark) : Color(uiColor: VDSColor.elementsPrimaryOnlight) + } + + public var bodyColor: Color? { + return inverted ? Color(uiColor: VDSColor.elementsSecondaryOndark) : Color(uiColor: VDSColor.elementsSecondaryOnlight) + } + + public var children: [MoleculeModelProtocol] { + [eyebrow, headline, body].compactMap { (molecule: MoleculeModelProtocol?) in molecule } + } + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + + public init(eyebrow: LabelModel? = nil, headline: LabelModel, body: LabelModel? = nil) throws { + + self.eyebrow = eyebrow + self.headline = headline + self.body = body + } + + //-------------------------------------------------- + // MARK: - Enum + //-------------------------------------------------- + + /// Convenience styles for common situations. + public enum Style: String, Codable { + case small + case medium + case large + case xlarge + case xxlarge + + func defaultHeadlineFontStyle() -> Styler.Font { + + switch self { + case .small: + return .BoldBodyLarge + case .medium: + return .BoldTitleMedium + case .large: + return .BoldTitleLarge + case .xlarge: + return .TitleXLarge + case .xxlarge: + return .Title2XLarge + } + } + + func defaultBodyFontStyle() -> Styler.Font { + + switch self { + case .small,.medium,.large: + return .RegularBodySmall + case .xlarge,.xxlarge: + return .RegularBodyLarge + } + } + + func defaultSpacing() -> CGFloat { + + switch self { + case .small,.medium,.large: + return Padding.One + case .xlarge: + return Padding.Three + case .xxlarge: + return Padding.Four + } + } + } + + public enum Alignment: String, Codable { + case left + case center + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case eyebrow + case headline + case body + case inverted + case alignment + case style + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + eyebrow = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .eyebrow) + headline = try typeContainer.decodeMolecule(codingKey: .headline) + body = try typeContainer.decodeMoleculeIfPresent(codingKey: .body) + + if let style = try typeContainer.decodeIfPresent(Style.self, forKey: .style) { + self.style = style + } + + if let alignment = try typeContainer.decodeIfPresent(Alignment.self, forKey: .alignment) { + self.alignment = alignment + } + + if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { + self.inverted = inverted + } + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(eyebrow, forKey: .eyebrow) + try container.encodeModelIfPresent(headline, forKey: .headline) + try container.encodeIfPresent(body, forKey: .body) + try container.encodeIfPresent(style, forKey: .style) + try container.encode(alignment, forKey: .alignment) + try container.encode(inverted, forKey: .inverted) + try container.encodeIfPresent(_backgroundColor, forKey: .backgroundColor) + } +} diff --git a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift index 2a7c9d90..5527813b 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift @@ -87,6 +87,7 @@ open class CoreUIModelMapping: ModelMapping { ModelRegistry.register(handler: BGImageHeadlineBodyButton.self, for: BGImageHeadlineBodyButtonModel.self) ModelRegistry.register(handler: ThreeHeadlineBodyLink.self, for: ThreeHeadlineBodyLinkModel.self) ModelRegistry.register(handler: ImageButton.self, for: ImageButtonModel.self) + ModelRegistry.register(handler: EyebrowHeadlineBody.self, for: EyebrowHeadlineBodyModel.self) // MARK:- Left Right Molecules ModelRegistry.register(handler: CornerLabels.self, for: CornerLabelsModel.self)