diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index fbb120aa..21144926 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -602,6 +602,8 @@ EA7AE5512C74EB4500107C74 /* CalendarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7AE5502C74EB4500107C74 /* CalendarViewModel.swift */; }; EA7AE5532C74F1F600107C74 /* DatePickerEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7AE5522C74F1F600107C74 /* DatePickerEntryField.swift */; }; EA7AE5552C74F20600107C74 /* DatePickerEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7AE5542C74F20600107C74 /* DatePickerEntryFieldModel.swift */; }; + EA7AE55C2C7D18A100107C74 /* BreadcrumbsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7AE55B2C7D18A100107C74 /* BreadcrumbsModel.swift */; }; + EA7AE55E2C7D234500107C74 /* Breadcrumbs.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7AE55D2C7D234500107C74 /* Breadcrumbs.swift */; }; EA7D81602B2B6E6800D29F9E /* Icon.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7D815F2B2B6E6800D29F9E /* Icon.swift */; }; EA7D81622B2B6E7F00D29F9E /* IconModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7D81612B2B6E7F00D29F9E /* IconModel.swift */; }; EA7D81642B2BABCB00D29F9E /* TooltipModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7D81632B2BABCB00D29F9E /* TooltipModel.swift */; }; @@ -1239,6 +1241,8 @@ EA7AE5502C74EB4500107C74 /* CalendarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarViewModel.swift; sourceTree = ""; }; EA7AE5522C74F1F600107C74 /* DatePickerEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerEntryField.swift; sourceTree = ""; }; EA7AE5542C74F20600107C74 /* DatePickerEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerEntryFieldModel.swift; sourceTree = ""; }; + EA7AE55B2C7D18A100107C74 /* BreadcrumbsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbsModel.swift; sourceTree = ""; }; + EA7AE55D2C7D234500107C74 /* Breadcrumbs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Breadcrumbs.swift; sourceTree = ""; }; EA7D815F2B2B6E6800D29F9E /* Icon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Icon.swift; sourceTree = ""; }; EA7D81612B2B6E7F00D29F9E /* IconModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconModel.swift; sourceTree = ""; }; EA7D81632B2BABCB00D29F9E /* TooltipModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipModel.swift; sourceTree = ""; }; @@ -2203,6 +2207,7 @@ D29DF10E21E67A77003B2FB9 /* Molecules */ = { isa = PBXGroup; children = ( + EA7AE55A2C7D188900107C74 /* Breadcrumbs */, D2EC7BD22527A1E400F540AF /* HeadersAndFooters */, D2CAC7C9251104CB00C75681 /* TopNotification */, D2509ED42472EE0B001BFB9D /* NavigationBar */, @@ -2641,6 +2646,15 @@ path = Alerts; sourceTree = ""; }; + EA7AE55A2C7D188900107C74 /* Breadcrumbs */ = { + isa = PBXGroup; + children = ( + EA7AE55B2C7D18A100107C74 /* BreadcrumbsModel.swift */, + EA7AE55D2C7D234500107C74 /* Breadcrumbs.swift */, + ); + path = Breadcrumbs; + sourceTree = ""; + }; EAA0CFAD275E7D5A00D65EB0 /* FormFieldEffect */ = { isa = PBXGroup; children = ( @@ -2968,6 +2982,7 @@ 525239C02407BCFF00454969 /* ListTwoColumnPriceDetailsModel.swift in Sources */, D2E2A99A23D8D6B4000B42E6 /* HeadlineBodyButtonModel.swift in Sources */, D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */, + EA7AE55C2C7D18A100107C74 /* BreadcrumbsModel.swift in Sources */, D20F3B44252E00E4004B3F56 /* PageProtocol.swift in Sources */, AA37CBD3251907200027344C /* StarsModel.swift in Sources */, 8D084AD22410BF7600951227 /* ListOneColumnFullWidthTextBodyText.swift in Sources */, @@ -2996,6 +3011,7 @@ AAE96FA525341F7D0037A989 /* ListStoreLocator.swift in Sources */, D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */, 944589232385DA9600DE9FD4 /* ImageViewModel.swift in Sources */, + EA7AE55E2C7D234500107C74 /* Breadcrumbs.swift in Sources */, D213347723843825008E41B3 /* Line.swift in Sources */, D2E2A99C23D8D975000B42E6 /* ImageHeadlineBodyModel.swift in Sources */, BB3BC1302550094500297977 /* ListLeftVariableIconWithRightCaretAllTextLinksModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Molecules/Breadcrumbs/Breadcrumbs.swift b/MVMCoreUI/Atomic/Molecules/Breadcrumbs/Breadcrumbs.swift new file mode 100644 index 00000000..6498ecb5 --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/Breadcrumbs/Breadcrumbs.swift @@ -0,0 +1,71 @@ +// +// Breadcrumbs.swift +// MVMCoreUI +// +// Created by Matt Bruce on 8/26/24. +// Copyright © 2024 Verizon Wireless. All rights reserved. +// + +import Foundation +import VDS + +@objcMembers open class Breadcrumbs: VDS.Breadcrumbs, VDSMoleculeViewProtocol { + //------------------------------------------------------ + // MARK: - Properties + //------------------------------------------------------ + open var viewModel: BreadcrumbsModel! + open var delegateObject: MVMCoreUIDelegateObject? + open var additionalData: [AnyHashable : Any]? + + // Form Validation + open var fieldKey: String? + open var fieldValue: JSONValue? + open var groupName: String? + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + override public init(frame: CGRect) { + super.init(frame: frame) + } + + /// There is currently no intention on using xib files. + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + fatalError("xib file is not implemented for Checkbox.") + } + + public convenience required init() { + self.init(frame:.zero) + } + + open func viewModelDidUpdate() { + isEnabled = viewModel.enabled + surface = viewModel.surface + breadcrumbModels = viewModel.breadcrumbs.compactMap { [unowned self] breadcrumb in + var onClick: ((BreadcrumbItem) -> Void)? + if let action = breadcrumb.action { + onClick = { _ in + MVMCoreUIActionHandler.performActionUnstructured(with: action, + sourceModel: breadcrumb, + additionalData: self.additionalData, + delegateObject: self.delegateObject) + } + } + return .init(text: breadcrumb.text, + selected: breadcrumb.selected, + onClick: onClick) + } + } + + //-------------------------------------------------- + // MARK: - Actions + //-------------------------------------------------- + + //-------------------------------------------------- + // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + public func updateView(_ size: CGFloat) {} + +} diff --git a/MVMCoreUI/Atomic/Molecules/Breadcrumbs/BreadcrumbsModel.swift b/MVMCoreUI/Atomic/Molecules/Breadcrumbs/BreadcrumbsModel.swift new file mode 100644 index 00000000..6c8ba47c --- /dev/null +++ b/MVMCoreUI/Atomic/Molecules/Breadcrumbs/BreadcrumbsModel.swift @@ -0,0 +1,111 @@ +// +// BreadCrumbs.swift +// MVMCoreUI +// +// Created by Matt Bruce on 8/26/24. +// Copyright © 2024 Verizon Wireless. All rights reserved. +// + +import Foundation +import VDS +import MVMCore + +open class BreadcrumbsModel: MoleculeModelProtocol { + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + open class var identifier: String { "breadcrumbs" } + open var moleculeName: String { Self.identifier } + open var backgroundColor: Color? + open var id: String = UUID().uuidString + + open var breadcrumbs: [BreadcrumbModel] = [] + open var enabled: Bool = true + open var inverted: Bool = false + open var surface: Surface { inverted ? .dark : .light } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case breadcrumbs + case enabled + case inverted + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + breadcrumbs = try container.decode([BreadcrumbModel].self, forKey: .breadcrumbs) + enabled = try container.decodeIfPresent(Bool.self, forKey: .enabled) ?? false + inverted = try container.decodeIfPresent(Bool.self, forKey: .inverted) ?? false + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + try container.encode(inverted, forKey: .inverted) + } + + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return inverted == model.inverted + && enabled == model.enabled + && breadcrumbs == model.breadcrumbs + } +} + +open class BreadcrumbModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + open class var identifier: String { "breadcrumb" } + open var moleculeName: String { Self.identifier } + open var backgroundColor: Color? + open var id: String = UUID().uuidString + + open var text: String = "" + open var selected: Bool = false + open var action: ActionModelProtocol? + + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case text + case selected + case action + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + text = try container.decode(String.self, forKey: .text) + selected = try container.decodeIfPresent(Bool.self, forKey: .selected) ?? false + action = try container.decodeModelIfPresent(codingKey: .action) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(text, forKey: .text) + try container.encode(selected, forKey: .selected) + try container.encodeModelIfPresent(action, forKey: .action) + } + + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return text == model.text + && selected == model.selected + && action.isEqual(to: model.action) + } +}