From 59a5c6a3e79fb4aaf98dce9ac2352f270d0d516d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 12 Apr 2024 10:22:12 -0500 Subject: [PATCH 01/24] added BadgeIndicator Atom Signed-off-by: Matt Bruce --- MVMCoreUI.xcodeproj/project.pbxproj | 12 +- .../Atomic/Atoms/Views/BadgeIndicator.swift | 70 +++++++++++ .../Atoms/Views/BadgeIndicatorModel.swift | 111 ++++++++++++++++++ .../Atomic/Extensions/VDS-Enums+Codable.swift | 4 + .../OtherHandlers/CoreUIModelMapping.swift | 1 + 5 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 MVMCoreUI/Atomic/Atoms/Views/BadgeIndicator.swift create mode 100644 MVMCoreUI/Atomic/Atoms/Views/BadgeIndicatorModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 038ecb6c..6a08a82b 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -174,8 +174,6 @@ 5870636F2ACF238E00CA18D5 /* ReadableDecodingErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5870636E2ACF238E00CA18D5 /* ReadableDecodingErrors.swift */; }; 58A9DD7D2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A9DD7C2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift */; }; 608211282AC6B57E00C3FC39 /* MVMCoreUILoggingHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608211262AC6AF8200C3FC39 /* MVMCoreUILoggingHandler.swift */; }; - 7199C8162A4F3A64001568B7 /* AccessibilityHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7199C8152A4F3A64001568B7 /* AccessibilityHandler.swift */; }; - 71BE969E2AD96BE6000B5DB7 /* RotorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BE969D2AD96BE6000B5DB7 /* RotorHandler.swift */; }; 8D070BB0241B56530099AC56 /* ListRightVariableTotalDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D070BAF241B56530099AC56 /* ListRightVariableTotalDataModel.swift */; }; 8D070BB2241B56AD0099AC56 /* ListRightVariableTotalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D070BB1241B56AD0099AC56 /* ListRightVariableTotalData.swift */; }; 8D084AD02410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D084ACF2410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift */; }; @@ -575,6 +573,8 @@ 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 */; }; 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 */; }; @@ -777,8 +777,6 @@ 5870636E2ACF238E00CA18D5 /* ReadableDecodingErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadableDecodingErrors.swift; sourceTree = ""; }; 58A9DD7C2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaceableMoleculeBehaviorModel.swift; sourceTree = ""; }; 608211262AC6AF8200C3FC39 /* MVMCoreUILoggingHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUILoggingHandler.swift; sourceTree = ""; }; - 7199C8152A4F3A64001568B7 /* AccessibilityHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityHandler.swift; sourceTree = ""; }; - 71BE969D2AD96BE6000B5DB7 /* RotorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RotorHandler.swift; sourceTree = ""; }; 8D070BAF241B56530099AC56 /* ListRightVariableTotalDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableTotalDataModel.swift; sourceTree = ""; }; 8D070BB1241B56AD0099AC56 /* ListRightVariableTotalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariableTotalData.swift; sourceTree = ""; }; 8D084ACF2410BF4800951227 /* ListOneColumnFullWidthTextBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextBodyTextModel.swift; sourceTree = ""; }; @@ -1179,6 +1177,8 @@ DBC4391A224421A0001AB423 /* CaretLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretLink.swift; sourceTree = ""; }; EA05EFA8278DDE2C00828819 /* ClearFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearFormFieldEffectModel.swift; sourceTree = ""; }; EA05EFAA278DE53600828819 /* ClearableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearableModelProtocol.swift; sourceTree = ""; }; + EA1758472BC97ED800A5C0D9 /* BadgeIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeIndicator.swift; sourceTree = ""; }; + EA1758492BC97EF100A5C0D9 /* BadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeIndicatorModel.swift; sourceTree = ""; }; EA41F4AB2787927100F5B377 /* DynamicRuleFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicRuleFormFieldEffectModel.swift; sourceTree = ""; }; EA5124FC243601600051A3A4 /* BGImageHeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButton.swift; sourceTree = ""; }; EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButtonModel.swift; sourceTree = ""; }; @@ -2268,6 +2268,8 @@ AA07EA922510A451009A2AE3 /* Star.swift */, B4CC8FBE29DF34730005D28B /* BadgeModel.swift */, B4CC8FBC29DF34680005D28B /* Badge.swift */, + EA1758492BC97EF100A5C0D9 /* BadgeIndicatorModel.swift */, + EA1758472BC97ED800A5C0D9 /* BadgeIndicator.swift */, EA985C3F2970939A00F2FF2E /* TileletModel.swift */, EA985C3D2970938F00F2FF2E /* Tilelet.swift */, EA7D81612B2B6E7F00D29F9E /* IconModel.swift */, @@ -2684,6 +2686,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 */, @@ -2989,6 +2992,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 */, diff --git a/MVMCoreUI/Atomic/Atoms/Views/BadgeIndicator.swift b/MVMCoreUI/Atomic/Atoms/Views/BadgeIndicator.swift new file mode 100644 index 00000000..3eb78919 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/BadgeIndicator.swift @@ -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 } +} diff --git a/MVMCoreUI/Atomic/Atoms/Views/BadgeIndicatorModel.swift b/MVMCoreUI/Atomic/Atoms/Views/BadgeIndicatorModel.swift new file mode 100644 index 00000000..059f4c7f --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/BadgeIndicatorModel.swift @@ -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 { + self.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + 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) + } +} diff --git a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift index 9e524462..caed0f13 100644 --- a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift +++ b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift @@ -16,6 +16,10 @@ import VDSColorTokens 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.ButtonGroup.Alignment: Codable {} extension VDS.Icon.Name: Codable {} extension VDS.Icon.Size: Codable {} diff --git a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift index 6a14767e..c864acdb 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift @@ -74,6 +74,7 @@ open class CoreUIModelMapping: ModelMapping { ModelRegistry.register(handler: Video.self, for: VideoModel.self) ModelRegistry.register(handler: Tilelet.self, for: TileletModel.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: Tooltip.self, for: TooltipModel.self) From c6fccb014a177af24beec2c8fe1718d27668eb74 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 12 Apr 2024 13:10:51 -0500 Subject: [PATCH 02/24] initial button icon Signed-off-by: Matt Bruce --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++ MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift | 9 +++ .../Atomic/Atoms/Views/ButtonIconModel.swift | 80 +++++++++++++++++++ .../Atomic/Extensions/VDS-Enums+Codable.swift | 4 + 4 files changed, 101 insertions(+) create mode 100644 MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift create mode 100644 MVMCoreUI/Atomic/Atoms/Views/ButtonIconModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 6a08a82b..5f715a0c 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -575,6 +575,8 @@ 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 */; }; @@ -1179,6 +1181,8 @@ EA05EFAA278DE53600828819 /* ClearableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearableModelProtocol.swift; sourceTree = ""; }; EA1758472BC97ED800A5C0D9 /* BadgeIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeIndicator.swift; sourceTree = ""; }; EA1758492BC97EF100A5C0D9 /* BadgeIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeIndicatorModel.swift; sourceTree = ""; }; + EA17584B2BC9894800A5C0D9 /* ButtonIconModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIconModel.swift; sourceTree = ""; }; + EA17584D2BC9895A00A5C0D9 /* ButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIcon.swift; sourceTree = ""; }; EA41F4AB2787927100F5B377 /* DynamicRuleFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicRuleFormFieldEffectModel.swift; sourceTree = ""; }; EA5124FC243601600051A3A4 /* BGImageHeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButton.swift; sourceTree = ""; }; EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButtonModel.swift; sourceTree = ""; }; @@ -2270,6 +2274,8 @@ B4CC8FBC29DF34680005D28B /* Badge.swift */, EA1758492BC97EF100A5C0D9 /* BadgeIndicatorModel.swift */, EA1758472BC97ED800A5C0D9 /* BadgeIndicator.swift */, + EA17584B2BC9894800A5C0D9 /* ButtonIconModel.swift */, + EA17584D2BC9895A00A5C0D9 /* ButtonIcon.swift */, EA985C3F2970939A00F2FF2E /* TileletModel.swift */, EA985C3D2970938F00F2FF2E /* Tilelet.swift */, EA7D81612B2B6E7F00D29F9E /* IconModel.swift */, @@ -2911,6 +2917,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 */, @@ -2938,6 +2945,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 */, diff --git a/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift b/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift new file mode 100644 index 00000000..71aef24a --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift @@ -0,0 +1,9 @@ +// +// ButtonIcon.swift +// MVMCoreUI +// +// Created by Matt Bruce on 4/12/24. +// Copyright © 2024 Verizon Wireless. All rights reserved. +// + +import Foundation diff --git a/MVMCoreUI/Atomic/Atoms/Views/ButtonIconModel.swift b/MVMCoreUI/Atomic/Atoms/Views/ButtonIconModel.swift new file mode 100644 index 00000000..ea6f7a8e --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/ButtonIconModel.swift @@ -0,0 +1,80 @@ +// +// 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: 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 badgeIndicator: BadgeIndicatorModel? + public var expandDirection = ButtonIcon.BadgeIndicatorModel.ExpandDirection.right + 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 selectedable: Bool = false + public var iconOffSet: CGPoint = .zero + + private enum CodingKeys: String, CodingKey { + case id + case inverted + 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 selectedable + case iconOffSet + } + + required public convenience init(from decoder: Decoder) throws { + self.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + id = try container.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString + inverted = try container.decodeIfPresent(Bool.self, forKey: .inverted) ?? false + 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 + + } + + 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) + } +} diff --git a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift index caed0f13..59bf6966 100644 --- a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift +++ b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift @@ -20,6 +20,10 @@ 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 {} From 02973badbde55a1860ae2377314a1b814fe2ddd5 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 15 Apr 2024 13:12:04 -0500 Subject: [PATCH 03/24] refactor Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/BadgeIndicatorModel.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/BadgeIndicatorModel.swift b/MVMCoreUI/Atomic/Atoms/Views/BadgeIndicatorModel.swift index 059f4c7f..bdc724b6 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/BadgeIndicatorModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/BadgeIndicatorModel.swift @@ -14,7 +14,7 @@ open class BadgeIndicatorModel: MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - public static var identifier: String = "badgeIndicator" + public static var identifier: String { "badgeIndicator" } public var id: String = UUID().uuidString public var backgroundColor: Color? @@ -64,8 +64,8 @@ open class BadgeIndicatorModel: MoleculeModelProtocol { } required public convenience init(from decoder: Decoder) throws { - self.init() 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) From 122f5b6c977d72be322ec35d6b0c76787c7a85c3 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 15 Apr 2024 13:12:15 -0500 Subject: [PATCH 04/24] added ButtonIcon Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift | 63 ++++++++++++++ .../Atomic/Atoms/Views/ButtonIconModel.swift | 86 ++++++++++++++++--- .../OtherHandlers/CoreUIModelMapping.swift | 1 + 3 files changed, 138 insertions(+), 12 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift b/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift index 71aef24a..389033cb 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift @@ -7,3 +7,66 @@ // 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 } +} + diff --git a/MVMCoreUI/Atomic/Atoms/Views/ButtonIconModel.swift b/MVMCoreUI/Atomic/Atoms/Views/ButtonIconModel.swift index ea6f7a8e..ed93e969 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/ButtonIconModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/ButtonIconModel.swift @@ -9,8 +9,7 @@ import Foundation import VDS - -open class ButtonIconModel: MoleculeModelProtocol { +open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -23,8 +22,10 @@ open class ButtonIconModel: MoleculeModelProtocol { //-------------------------------------------------- public var surface: Surface { inverted ? .dark : .light } public var inverted: Bool = false - public var badgeIndicator: BadgeIndicatorModel? - public var expandDirection = ButtonIcon.BadgeIndicatorModel.ExpandDirection.right + 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 @@ -35,12 +36,48 @@ open class ButtonIconModel: MoleculeModelProtocol { public var fitToIcon: Bool = false public var hideBorder: Bool = true public var showBadgeIndicator: Bool = false - public var selectedable: Bool = false - public var iconOffSet: CGPoint = .zero + 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 @@ -53,15 +90,19 @@ open class ButtonIconModel: MoleculeModelProtocol { case fitToIcon case hideBorder case showBadgeIndicator - case selectedable - case iconOffSet + case selectable + case iconOffset } - - required public convenience init(from decoder: Decoder) throws { - self.init() + + //-------------------------------------------------- + // 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 @@ -69,12 +110,33 @@ open class ButtonIconModel: MoleculeModelProtocol { 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) } } diff --git a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift index c864acdb..97f87e28 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift @@ -76,6 +76,7 @@ open class CoreUIModelMapping: ModelMapping { 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 From 6e5abcc91205b79182fc7d1733d7556c0e131264 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 15 Apr 2024 15:14:56 -0500 Subject: [PATCH 05/24] added tileContainer view/model Signed-off-by: Matt Bruce --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++ .../Atomic/Atoms/Views/TileContainer.swift | 89 +++++++++++++++++ .../Atoms/Views/TileContainerModel.swift | 99 +++++++++++++++++++ .../Atomic/Extensions/VDS-Enums+Codable.swift | 48 +++++++++ 4 files changed, 244 insertions(+) create mode 100644 MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift create mode 100644 MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 4e9fdca8..b7c94d3f 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -578,6 +578,8 @@ 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 */; }; @@ -1184,6 +1186,8 @@ EA41F4AB2787927100F5B377 /* DynamicRuleFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicRuleFormFieldEffectModel.swift; sourceTree = ""; }; EA5124FC243601600051A3A4 /* BGImageHeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButton.swift; sourceTree = ""; }; EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButtonModel.swift; sourceTree = ""; }; + EA6642902BCDA97300D81DC4 /* TileContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TileContainer.swift; sourceTree = ""; }; + EA6642922BCDA97D00D81DC4 /* TileContainerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TileContainerModel.swift; sourceTree = ""; }; EA6E8B942B504A43000139B4 /* ButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroup.swift; sourceTree = ""; }; EA6E8B962B504A4D000139B4 /* ButtonGroupModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupModel.swift; sourceTree = ""; }; EA7D815F2B2B6E6800D29F9E /* Icon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Icon.swift; sourceTree = ""; }; @@ -2274,6 +2278,8 @@ EA1758472BC97ED800A5C0D9 /* BadgeIndicator.swift */, EA17584B2BC9894800A5C0D9 /* ButtonIconModel.swift */, EA17584D2BC9895A00A5C0D9 /* ButtonIcon.swift */, + EA6642922BCDA97D00D81DC4 /* TileContainerModel.swift */, + EA6642902BCDA97300D81DC4 /* TileContainer.swift */, EA985C3F2970939A00F2FF2E /* TileletModel.swift */, EA985C3D2970938F00F2FF2E /* Tilelet.swift */, EA7D81612B2B6E7F00D29F9E /* IconModel.swift */, @@ -3063,6 +3069,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 */, @@ -3189,6 +3196,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 */, diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift new file mode 100644 index 00000000..3c588795 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift @@ -0,0 +1,89 @@ +// +// 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? + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public convenience required init() { + self.init(frame: .zero) + } + + //-------------------------------------------------- + // MARK: - Public + //-------------------------------------------------- + public func viewModelDidUpdate() { + + if molecule != nil { + molecule?.set(with: viewModel.molecule, delegateObject, additionalData) + } else if let moleculeView = ModelRegistry.createMolecule(viewModel.molecule, delegateObject: delegateObject, additionalData: additionalData) { + addContentView(moleculeView) + } + + 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) + } + } + } + + //-------------------------------------------------- + // 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 + } + + //-------------------------------------------------- + // 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) + } + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift new file mode 100644 index 00000000..bb20d38d --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift @@ -0,0 +1,99 @@ +// +// 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, MoleculeModelProtocol { + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "tileContainer" + public var id: String = UUID().uuidString + public var backgroundColor: Color? + public var padding: TileContainer.Padding = .padding4X + public var aspectRatio: TileContainer.AspectRatio = .ratio1x1 + public var color: TileContainer.BackgroundColor = .secondary + public var backgroundEffect: TileContainer.BackgroundEffect = .none + public var molecule: MoleculeModelProtocol + + private enum CodingKeys: String, CodingKey { + case id + case moleculeName + case molecule + case padding + case aspectRatio + case color + case backgroundEffect + } + 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.decodeModel(codingKey: .molecule) + padding = try container.decodeIfPresent(TileContainer.Padding.self, forKey: .padding) ?? .padding4X + color = try container.decodeIfPresent(TileContainer.BackgroundColor.self, forKey: .color) ?? .secondary + backgroundEffect = try container.decodeIfPresent(TileContainer.BackgroundEffect.self, forKey: .backgroundEffect) ?? .none + aspectRatio = try container.decodeIfPresent(TileContainer.AspectRatio.self, forKey: .aspectRatio) ?? .none + 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.encodeIfPresent(padding, forKey: .padding) + try container.encodeIfPresent(color, forKey: .color) + try container.encodeIfPresent(backgroundEffect, forKey: .backgroundEffect) + try container.encodeIfPresent(aspectRatio, forKey: .aspectRatio) + try super.encode(to: encoder) + } +} + + +open class TileContainerBaseModel: 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 + + private enum CodingKeys: String, CodingKey { + case backgroundImage + case action + case padding + case imageFallbackColor + case width + case height + case showBorder + case showDropShadows + } + 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) + } + + 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.encodeModelIfPresent(action, forKey: .action) + } + + +} diff --git a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift index 19efa0a9..0cd9ba2e 100644 --- a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift +++ b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift @@ -100,6 +100,54 @@ extension VDS.TileContainerBase.BackgroundColor: Codable { } } +extension VDS.TileContainerBase.BackgroundEffect: Codable { + enum Error: Swift.Error { + case valueNotFound(type: String) + } + 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) From 61a5f4d7d1e7854f1d72b69dd8fdf56eec0f0375 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 15 Apr 2024 15:16:12 -0500 Subject: [PATCH 06/24] registered tilecontainer Signed-off-by: Matt Bruce --- MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift index 97f87e28..bbde1bf9 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift @@ -73,6 +73,7 @@ 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) From 44cbeeb69510ba16a8669bebb356a57742c33a83 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Mon, 15 Apr 2024 15:16:28 -0500 Subject: [PATCH 07/24] refactored the Tilelet with missing tilecontainer properties Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/Tilelet.swift | 8 +++- .../Atomic/Atoms/Views/TileletModel.swift | 47 +++++++++---------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/Tilelet.swift b/MVMCoreUI/Atomic/Atoms/Views/Tilelet.swift index 09f905e7..28ece685 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/Tilelet.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/Tilelet.swift @@ -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 { diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift index 62fac7e9..2abd1eec 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift @@ -9,7 +9,7 @@ import Foundation import VDS -open class TileletModel: MoleculeModelProtocol { +open class TileletModel: TileContainerBaseModel, MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties @@ -17,23 +17,23 @@ 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 color: Tilelet.BackgroundColor = .black + public var padding: Tilelet.Padding = .large + public var aspectRatio: Tilelet.AspectRatio = .ratio1x1 + public var backgroundEffect: Tilelet.BackgroundEffect = .none 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 backgroundEffect case color case padding case aspectRatio @@ -42,27 +42,25 @@ open class TileletModel: MoleculeModelProtocol { 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 + backgroundColor = try container.decodeIfPresent(Color.self, forKey: .backgroundColor) + color = try container.decodeIfPresent(Tilelet.BackgroundColor.self, forKey: .color) ?? Tilelet.BackgroundColor.black + backgroundEffect = try container.decodeIfPresent(Tilelet.BackgroundEffect.self, forKey: .backgroundEffect) ?? .none + padding = try container.decodeIfPresent(Tilelet.Padding.self, forKey: .padding) ?? Tilelet.Padding.small + aspectRatio = try container.decodeIfPresent(Tilelet.AspectRatio.self, forKey: .aspectRatio) ?? Tilelet.AspectRatio.none + 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,7 +97,7 @@ 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) @@ -112,9 +110,8 @@ open class TileletModel: MoleculeModelProtocol { 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) } } From 300047fdbe7893f0b575bf71b4dbd25e781ef4ef Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 16 Apr 2024 10:45:47 -0500 Subject: [PATCH 08/24] updated the loadImage made ViewModelMolecule optional Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Views/TileContainer.swift | 57 +++++++++++++++++-- .../Atoms/Views/TileContainerModel.swift | 5 +- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift index 3c588795..8356089c 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift @@ -35,10 +35,18 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ //-------------------------------------------------- public func viewModelDidUpdate() { - if molecule != nil { - molecule?.set(with: viewModel.molecule, delegateObject, additionalData) - } else if let moleculeView = ModelRegistry.createMolecule(viewModel.molecule, delegateObject: delegateObject, additionalData: additionalData) { - addContentView(moleculeView) + if let moleculeModel = viewModel.molecule{ + if molecule != nil { + molecule?.set(with: moleculeModel, delegateObject, additionalData) + } else if let moleculeView = ModelRegistry.createMolecule(moleculeModel, delegateObject: delegateObject, additionalData: additionalData) { + addContentView(moleculeView) + } + } else { + molecule = nil + } + + if let imageName = viewModel.backgroundImage { + loadImage(imageName) } padding = viewModel.padding @@ -61,6 +69,21 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ } } } + + 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 @@ -86,4 +109,30 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) } } + + public func needsToBeConstrained() -> Bool { true } + + public func horizontalAlignment() -> UIStackView.Alignment { .leading } +} + +extension UIImageView { + public func loadImage(withName imageName: String?, format: String? = nil, width: CGFloat? = nil, height: CGFloat? = nil, customFallbackImage: String? = nil, allowServerParameters: Bool = false, localBundle: Bundle? = nil, completionHandler: MVMCoreGetImageBlock? = nil) { + + MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in + + let finishedLoadingBlock: MVMCoreGetImageBlock = {[weak self] (image, data, isFallbackImage) in MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in + guard let self = self else { return } + self.image = image + completionHandler?(image,data,isFallbackImage) + })} + + let fallbackImageName = customFallbackImage ?? MVMCoreUIUtility.localizedImageName("fallback") + if let format = format, format.lowercased().contains("gif") { + // Gifs aren't supported by default and need special handling + MVMCoreCache.shared()?.getGif(imageName, useWidth: width != nil, widthForS7: Int(width ?? 0), useHeight: height != nil, heightForS7: Int(height ?? 0), format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock) + } else { + MVMCoreCache.shared()?.getImage(imageName, useWidth: width != nil, widthForS7: Int(width ?? 0), useHeight: height != nil, heightForS7: Int(height ?? 0), format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock) + } + }) + } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift index bb20d38d..72ffb974 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift @@ -21,7 +21,7 @@ open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { public var aspectRatio: TileContainer.AspectRatio = .ratio1x1 public var color: TileContainer.BackgroundColor = .secondary public var backgroundEffect: TileContainer.BackgroundEffect = .none - public var molecule: MoleculeModelProtocol + public var molecule: MoleculeModelProtocol? private enum CodingKeys: String, CodingKey { case id @@ -35,7 +35,7 @@ open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { 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.decodeModel(codingKey: .molecule) + molecule = try container.decodeModelIfPresent(codingKey: .molecule) padding = try container.decodeIfPresent(TileContainer.Padding.self, forKey: .padding) ?? .padding4X color = try container.decodeIfPresent(TileContainer.BackgroundColor.self, forKey: .color) ?? .secondary backgroundEffect = try container.decodeIfPresent(TileContainer.BackgroundEffect.self, forKey: .backgroundEffect) ?? .none @@ -47,6 +47,7 @@ open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { 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 container.encodeIfPresent(padding, forKey: .padding) try container.encodeIfPresent(color, forKey: .color) try container.encodeIfPresent(backgroundEffect, forKey: .backgroundEffect) From f5d9423b1bb2f01e10604debfae22945fca84698 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 16 Apr 2024 11:47:08 -0500 Subject: [PATCH 09/24] removed code not nedded Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Views/TileContainer.swift | 26 ++----------------- 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift index 8356089c..4737c2db 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift @@ -109,30 +109,8 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) } } - - public func needsToBeConstrained() -> Bool { true } - - public func horizontalAlignment() -> UIStackView.Alignment { .leading } } -extension UIImageView { - public func loadImage(withName imageName: String?, format: String? = nil, width: CGFloat? = nil, height: CGFloat? = nil, customFallbackImage: String? = nil, allowServerParameters: Bool = false, localBundle: Bundle? = nil, completionHandler: MVMCoreGetImageBlock? = nil) { - - MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in - - let finishedLoadingBlock: MVMCoreGetImageBlock = {[weak self] (image, data, isFallbackImage) in MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in - guard let self = self else { return } - self.image = image - completionHandler?(image,data,isFallbackImage) - })} - - let fallbackImageName = customFallbackImage ?? MVMCoreUIUtility.localizedImageName("fallback") - if let format = format, format.lowercased().contains("gif") { - // Gifs aren't supported by default and need special handling - MVMCoreCache.shared()?.getGif(imageName, useWidth: width != nil, widthForS7: Int(width ?? 0), useHeight: height != nil, heightForS7: Int(height ?? 0), format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock) - } else { - MVMCoreCache.shared()?.getImage(imageName, useWidth: width != nil, widthForS7: Int(width ?? 0), useHeight: height != nil, heightForS7: Int(height ?? 0), format: format, localFallbackImageName: fallbackImageName, allowServerQueryParameters: allowServerParameters, localBundle: localBundle, completionHandler: finishedLoadingBlock) - } - }) - } +extension TileContainer: MVMCoreUIViewConstrainingProtocol { + public func horizontalAlignment() -> UIStackView.Alignment { .leading } } From 03318e863fff9b560a12facd269ff579620ed595 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 16 Apr 2024 14:27:17 -0500 Subject: [PATCH 10/24] refactored backgroundEffect Signed-off-by: Matt Bruce --- .../Atoms/Views/TileContainerModel.swift | 72 +++++++++++++++++-- .../Atomic/Atoms/Views/TileletModel.swift | 7 +- .../Atomic/Extensions/VDS-Enums+Codable.swift | 48 ------------- 3 files changed, 70 insertions(+), 57 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift index 72ffb974..9933cdf1 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift @@ -20,8 +20,11 @@ open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { public var padding: TileContainer.Padding = .padding4X public var aspectRatio: TileContainer.AspectRatio = .ratio1x1 public var color: TileContainer.BackgroundColor = .secondary - public var backgroundEffect: TileContainer.BackgroundEffect = .none public var molecule: MoleculeModelProtocol? + public var backgroundEffect: TileContainer.BackgroundEffect { + guard let effect = backgroundEffectContainer else { return .none } + return .convert(from: effect) + } private enum CodingKeys: String, CodingKey { case id @@ -30,7 +33,6 @@ open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { case padding case aspectRatio case color - case backgroundEffect } required public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) @@ -38,7 +40,6 @@ open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { molecule = try container.decodeModelIfPresent(codingKey: .molecule) padding = try container.decodeIfPresent(TileContainer.Padding.self, forKey: .padding) ?? .padding4X color = try container.decodeIfPresent(TileContainer.BackgroundColor.self, forKey: .color) ?? .secondary - backgroundEffect = try container.decodeIfPresent(TileContainer.BackgroundEffect.self, forKey: .backgroundEffect) ?? .none aspectRatio = try container.decodeIfPresent(TileContainer.AspectRatio.self, forKey: .aspectRatio) ?? .none try super.init(from: decoder) } @@ -50,7 +51,6 @@ open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { try container.encodeModelIfPresent(molecule, forKey: .molecule) try container.encodeIfPresent(padding, forKey: .padding) try container.encodeIfPresent(color, forKey: .color) - try container.encodeIfPresent(backgroundEffect, forKey: .backgroundEffect) try container.encodeIfPresent(aspectRatio, forKey: .aspectRatio) try super.encode(to: encoder) } @@ -69,6 +69,7 @@ open class TileContainerBaseModel: Codable { public var height: CGFloat? public var showBorder: Bool = false public var showDropShadwows: Bool = false + internal var backgroundEffectContainer: BackgroundEffectContainer? private enum CodingKeys: String, CodingKey { case backgroundImage @@ -79,6 +80,7 @@ open class TileContainerBaseModel: Codable { case height case showBorder case showDropShadows + case backgroundEffect } required public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) @@ -86,6 +88,7 @@ open class TileContainerBaseModel: Codable { width = try container.decodeIfPresent(CGFloat.self, forKey: .width) height = try container.decodeIfPresent(CGFloat.self, forKey: .height) action = try container.decodeModelIfPresent(codingKey: .action) + backgroundEffectContainer = try container.decodeIfPresent(BackgroundEffectContainer.self, forKey: .backgroundEffect) } public func encode(to encoder: Encoder) throws { @@ -93,8 +96,65 @@ open class TileContainerBaseModel: Codable { try container.encodeIfPresent(backgroundImage, forKey: .backgroundImage) try container.encodeIfPresent(width, forKey: .width) try container.encodeIfPresent(height, forKey: .height) + try container.encodeIfPresent(backgroundEffectContainer, forKey: .backgroundEffect) try container.encodeModelIfPresent(action, forKey: .action) } - - +} + +internal struct BackgroundEffectContainer: Codable { + public enum BackgroundEffectType: String, Codable { + case transparency + case none + case gradient + } + + public struct GradientValue: Codable { + public let firstColor: String + public let secondColor: String + } + + private enum CodingKeys: String, CodingKey { + case type + case value + } + + public let type: BackgroundEffectType + public let value: GradientValue? + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + type = try container.decode(BackgroundEffectType.self, forKey: .type) + if type == .gradient { + value = try container.decode(GradientValue.self, forKey: .value) + } else { + value = nil + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(type, forKey: .type) + switch type { + case .transparency, .none: + break // No value to encode + case .gradient: + try container.encode(value, forKey: .value) + } + } +} + +extension TileContainerBase.BackgroundEffect { + internal static func convert(from effect: BackgroundEffectContainer) -> Self { + switch effect.type { + case .transparency: + return .transparency + case .none: + return .none + case .gradient: + guard let value = effect.value else { + fatalError("Gradient must have a value") + } + return .gradient(value.firstColor, value.secondColor) + } + } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift index 2abd1eec..6afc9c12 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift @@ -20,7 +20,10 @@ open class TileletModel: TileContainerBaseModel, MoleculeModelProtocol { public var color: Tilelet.BackgroundColor = .black public var padding: Tilelet.Padding = .large public var aspectRatio: Tilelet.AspectRatio = .ratio1x1 - public var backgroundEffect: Tilelet.BackgroundEffect = .none + public var backgroundEffect: Tilelet.BackgroundEffect { + guard let effect = backgroundEffectContainer else { return .none } + return .convert(from: effect) + } public var badge: Tilelet.BadgeModel? public var title: LabelModel? public var subTitle: LabelModel? @@ -33,7 +36,6 @@ open class TileletModel: TileContainerBaseModel, MoleculeModelProtocol { case id case moleculeName case backgroundColor - case backgroundEffect case color case padding case aspectRatio @@ -50,7 +52,6 @@ open class TileletModel: TileContainerBaseModel, MoleculeModelProtocol { id = try container.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString backgroundColor = try container.decodeIfPresent(Color.self, forKey: .backgroundColor) color = try container.decodeIfPresent(Tilelet.BackgroundColor.self, forKey: .color) ?? Tilelet.BackgroundColor.black - backgroundEffect = try container.decodeIfPresent(Tilelet.BackgroundEffect.self, forKey: .backgroundEffect) ?? .none padding = try container.decodeIfPresent(Tilelet.Padding.self, forKey: .padding) ?? Tilelet.Padding.small aspectRatio = try container.decodeIfPresent(Tilelet.AspectRatio.self, forKey: .aspectRatio) ?? Tilelet.AspectRatio.none badge = try container.decodeIfPresent(Tilelet.BadgeModel.self, forKey: .badge) diff --git a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift index 0cd9ba2e..19efa0a9 100644 --- a/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift +++ b/MVMCoreUI/Atomic/Extensions/VDS-Enums+Codable.swift @@ -100,54 +100,6 @@ extension VDS.TileContainerBase.BackgroundColor: Codable { } } -extension VDS.TileContainerBase.BackgroundEffect: Codable { - enum Error: Swift.Error { - case valueNotFound(type: String) - } - 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) From 08a2079654ce59897b9313866723459cc92cacd4 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 16 Apr 2024 14:35:56 -0500 Subject: [PATCH 11/24] rearranged Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift index 9933cdf1..079f2823 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift @@ -17,10 +17,10 @@ open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { public static var identifier: String = "tileContainer" public var id: String = UUID().uuidString public var backgroundColor: Color? + public var molecule: MoleculeModelProtocol? public var padding: TileContainer.Padding = .padding4X public var aspectRatio: TileContainer.AspectRatio = .ratio1x1 public var color: TileContainer.BackgroundColor = .secondary - public var molecule: MoleculeModelProtocol? public var backgroundEffect: TileContainer.BackgroundEffect { guard let effect = backgroundEffectContainer else { return .none } return .convert(from: effect) From 0ddf76b576a729260d2c95f7dafe052720d7e99b Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 16 Apr 2024 15:15:02 -0500 Subject: [PATCH 12/24] refactored out base enums into base model Signed-off-by: Matt Bruce --- .../Atoms/Views/TileContainerModel.swift | 72 +++++++++---------- .../Atomic/Atoms/Views/TileletModel.swift | 22 +----- 2 files changed, 36 insertions(+), 58 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift index 079f2823..56693c53 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift @@ -9,7 +9,7 @@ import Foundation import VDS -open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { +open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties @@ -17,30 +17,18 @@ open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { public static var identifier: String = "tileContainer" public var id: String = UUID().uuidString public var backgroundColor: Color? + public var molecule: MoleculeModelProtocol? - public var padding: TileContainer.Padding = .padding4X - public var aspectRatio: TileContainer.AspectRatio = .ratio1x1 - public var color: TileContainer.BackgroundColor = .secondary - public var backgroundEffect: TileContainer.BackgroundEffect { - guard let effect = backgroundEffectContainer else { return .none } - return .convert(from: effect) - } private enum CodingKeys: String, CodingKey { case id case moleculeName case molecule - case padding - case aspectRatio - case color } 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) - padding = try container.decodeIfPresent(TileContainer.Padding.self, forKey: .padding) ?? .padding4X - color = try container.decodeIfPresent(TileContainer.BackgroundColor.self, forKey: .color) ?? .secondary - aspectRatio = try container.decodeIfPresent(TileContainer.AspectRatio.self, forKey: .aspectRatio) ?? .none try super.init(from: decoder) } @@ -49,15 +37,12 @@ open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { try container.encode(id, forKey: .id) try container.encode(moleculeName, forKey: .moleculeName) try container.encodeModelIfPresent(molecule, forKey: .molecule) - try container.encodeIfPresent(padding, forKey: .padding) - try container.encodeIfPresent(color, forKey: .color) - try container.encodeIfPresent(aspectRatio, forKey: .aspectRatio) try super.encode(to: encoder) } } -open class TileContainerBaseModel: Codable { +open class TileContainerBaseModel> : Codable{ //-------------------------------------------------- // MARK: - Properties @@ -69,12 +54,32 @@ open class TileContainerBaseModel: Codable { public var height: CGFloat? public var showBorder: Bool = false public var showDropShadwows: Bool = false - internal var backgroundEffectContainer: BackgroundEffectContainer? + public var padding = PaddingType.defaultValue + public var color: TileContainerType.BackgroundColor = .black + public var aspectRatio: TileContainerType.AspectRatio = .ratio1x1 + internal var backgroundEffectCodable: BackgroundEffectCodable? + public var backgroundEffect: TileContainerType.BackgroundEffect { + guard let effect = backgroundEffectCodable else { return .none } + switch effect.type { + case .transparency: + return .transparency + case .none: + return .none + case .gradient: + guard let value = effect.value else { + fatalError("Gradient must have a value") + } + return .gradient(value.firstColor, value.secondColor) + } + } + private enum CodingKeys: String, CodingKey { case backgroundImage case action case padding + case color + case aspectRatio case imageFallbackColor case width case height @@ -88,7 +93,11 @@ open class TileContainerBaseModel: Codable { width = try container.decodeIfPresent(CGFloat.self, forKey: .width) height = try container.decodeIfPresent(CGFloat.self, forKey: .height) action = try container.decodeModelIfPresent(codingKey: .action) - backgroundEffectContainer = try container.decodeIfPresent(BackgroundEffectContainer.self, forKey: .backgroundEffect) + 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 + backgroundEffectCodable = try container.decodeIfPresent(BackgroundEffectCodable.self, forKey: .backgroundEffect) + } public func encode(to encoder: Encoder) throws { @@ -96,12 +105,15 @@ open class TileContainerBaseModel: Codable { try container.encodeIfPresent(backgroundImage, forKey: .backgroundImage) try container.encodeIfPresent(width, forKey: .width) try container.encodeIfPresent(height, forKey: .height) - try container.encodeIfPresent(backgroundEffectContainer, forKey: .backgroundEffect) + try container.encodeIfPresent(backgroundEffectCodable, 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) } } -internal struct BackgroundEffectContainer: Codable { +internal struct BackgroundEffectCodable: Codable { public enum BackgroundEffectType: String, Codable { case transparency case none @@ -142,19 +154,3 @@ internal struct BackgroundEffectContainer: Codable { } } } - -extension TileContainerBase.BackgroundEffect { - internal static func convert(from effect: BackgroundEffectContainer) -> Self { - switch effect.type { - case .transparency: - return .transparency - case .none: - return .none - case .gradient: - guard let value = effect.value else { - fatalError("Gradient must have a value") - } - return .gradient(value.firstColor, value.secondColor) - } - } -} diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift index 6afc9c12..b287a9ff 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileletModel.swift @@ -9,7 +9,7 @@ import Foundation import VDS -open class TileletModel: TileContainerBaseModel, MoleculeModelProtocol { +open class TileletModel: TileContainerBaseModel, MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties @@ -17,13 +17,7 @@ open class TileletModel: TileContainerBaseModel, MoleculeModelProtocol { public static var identifier: String = "tilelet" public var id: String = UUID().uuidString public var backgroundColor: Color? - public var color: Tilelet.BackgroundColor = .black - public var padding: Tilelet.Padding = .large - public var aspectRatio: Tilelet.AspectRatio = .ratio1x1 - public var backgroundEffect: Tilelet.BackgroundEffect { - guard let effect = backgroundEffectContainer else { return .none } - return .convert(from: effect) - } + public var badge: Tilelet.BadgeModel? public var title: LabelModel? public var subTitle: LabelModel? @@ -35,10 +29,6 @@ open class TileletModel: TileContainerBaseModel, MoleculeModelProtocol { private enum CodingKeys: String, CodingKey { case id case moleculeName - case backgroundColor - case color - case padding - case aspectRatio case badge case title case subTitle @@ -50,10 +40,6 @@ open class TileletModel: TileContainerBaseModel, MoleculeModelProtocol { 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 - backgroundColor = try container.decodeIfPresent(Color.self, forKey: .backgroundColor) - color = try container.decodeIfPresent(Tilelet.BackgroundColor.self, forKey: .color) ?? Tilelet.BackgroundColor.black - padding = try container.decodeIfPresent(Tilelet.Padding.self, forKey: .padding) ?? Tilelet.Padding.small - aspectRatio = try container.decodeIfPresent(Tilelet.AspectRatio.self, forKey: .aspectRatio) ?? Tilelet.AspectRatio.none 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) @@ -102,10 +88,6 @@ open class TileletModel: TileContainerBaseModel, MoleculeModelProtocol { 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) From c87bbb3a7861d4b6471845202b074fcbe46623e2 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 17 Apr 2024 09:43:50 -0500 Subject: [PATCH 13/24] refdactored to use better codable for the BackgroundEffect Enum Signed-off-by: Matt Bruce --- .../Atoms/Views/TileContainerModel.swift | 63 +------------------ .../Atomic/Extensions/VDS-Enums+Codable.swift | 47 ++++++++++++++ 2 files changed, 50 insertions(+), 60 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift index 56693c53..0c03df02 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift @@ -57,22 +57,7 @@ open class TileContainerBaseModel Date: Wed, 24 Apr 2024 17:15:11 -0500 Subject: [PATCH 14/24] added nameforReuse Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift index 4737c2db..941e9b3e 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift @@ -97,6 +97,16 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ 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 model = model as? ButtonGroupModel else { + return "tileContainer<>" + } + return "tileContainer<\(model.moleculeName)>" + } + //-------------------------------------------------- // MARK: - Overrides From 980ffaee67641088cd2f31731472edfe2c41ea64 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 24 Apr 2024 17:15:36 -0500 Subject: [PATCH 15/24] removed carriage return Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift index 941e9b3e..d93c8799 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift @@ -107,7 +107,6 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ return "tileContainer<\(model.moleculeName)>" } - //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- From 725d80b5ce560616e256ac31aa6343cd7db736b3 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 24 Apr 2024 17:24:20 -0500 Subject: [PATCH 16/24] added praentmoleculemodel protocol Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Views/TileContainerModel.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift index 0c03df02..b6932ef9 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift @@ -9,7 +9,7 @@ import Foundation import VDS -open class TileContainerModel: TileContainerBaseModel, MoleculeModelProtocol { +open class TileContainerModel: TileContainerBaseModel, ParentMoleculeModelProtocol, MoleculeModelProtocol { //-------------------------------------------------- // MARK: - Properties @@ -19,7 +19,15 @@ open class TileContainerModel: TileContainerBaseModel Bool { + return try replaceChildMolecule(at: &self.molecule, with: molecule) + } + private enum CodingKeys: String, CodingKey { case id case moleculeName From dac871125855796ccc75266e6bd316aee7324bb6 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 25 Apr 2024 09:07:40 -0500 Subject: [PATCH 17/24] ensure molecule is removed from parent Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift index d93c8799..7c2c13ac 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift @@ -21,7 +21,13 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ public var delegateObject: MVMCoreUIDelegateObject? public var additionalData: [AnyHashable: Any]? - public var molecule: MoleculeViewProtocol? + public var molecule: MoleculeViewProtocol? { + willSet { + if newValue == nil { + molecule?.removeFromSuperview() + } + } + } //-------------------------------------------------- // MARK: - Initializers From 0f3d7c13208ec8bdf273add2e00f8e77b05d54c0 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 25 Apr 2024 09:13:25 -0500 Subject: [PATCH 18/24] removed setting the molecule to nil Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift index 7c2c13ac..f74c7885 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift @@ -47,8 +47,6 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ } else if let moleculeView = ModelRegistry.createMolecule(moleculeModel, delegateObject: delegateObject, additionalData: additionalData) { addContentView(moleculeView) } - } else { - molecule = nil } if let imageName = viewModel.backgroundImage { From 9292e6a8d0ac3bbdbbc26c91469a23bb9f34b607 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 25 Apr 2024 09:18:49 -0500 Subject: [PATCH 19/24] set the molecule to the moleculeView found Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift index f74c7885..77f32707 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift @@ -41,10 +41,11 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ //-------------------------------------------------- public func viewModelDidUpdate() { - if let moleculeModel = viewModel.molecule{ - if molecule != nil { - molecule?.set(with: moleculeModel, delegateObject, additionalData) + if let moleculeModel = viewModel.molecule { + if let molecule { + molecule.set(with: moleculeModel, delegateObject, additionalData) } else if let moleculeView = ModelRegistry.createMolecule(moleculeModel, delegateObject: delegateObject, additionalData: additionalData) { + molecule = moleculeView addContentView(moleculeView) } } From e25a0cadc1724e6c780ac3dea0c39feebe93bdd1 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 25 Apr 2024 09:55:50 -0500 Subject: [PATCH 20/24] updated nameforreuse Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift index 77f32707..57b8b8f2 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift @@ -42,7 +42,8 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ public func viewModelDidUpdate() { if let moleculeModel = viewModel.molecule { - if let 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 @@ -106,10 +107,13 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ /// 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 model = model as? ButtonGroupModel else { - return "tileContainer<>" - } - return "tileContainer<\(model.moleculeName)>" + 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)>" } //-------------------------------------------------- From e06788ca66cbcbb4c76bd5f3acc01d45fb749a19 Mon Sep 17 00:00:00 2001 From: "Hedden, Kyle Matthew" Date: Thu, 25 Apr 2024 15:51:35 -0400 Subject: [PATCH 21/24] Digital PCT265 defect CXTDT-531317 - Throw error during decode when pageType is empty. --- MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift b/MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift index b5c4763c..c42797bc 100644 --- a/MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/BaseTemplateModel.swift @@ -79,6 +79,9 @@ import Foundation } else { pageType = try typeContainer.decode(String.self, forKey: .pageType) } + if pageType.isEmpty { + throw ModelRegistry.Error.decoderOther(message: "pageType cannot be an empty string!") + } screenHeading = try typeContainer.decodeIfPresent(String.self, forKey: .screenHeading) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) formRules = try typeContainer.decodeIfPresent([FormGroupRule].self, forKey: .formRules) From ce8b5b85cb841d0e20a86084d4d89bded015cf28 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 25 Apr 2024 14:56:00 -0500 Subject: [PATCH 22/24] sync properties also ensured same properties were in Signed-off-by: Matt Bruce --- .../Atomic/Atoms/Views/TileContainer.swift | 23 +++++--- .../Atoms/Views/TileContainerModel.swift | 28 +++++++--- MVMCoreUI/Atomic/Atoms/Views/Tilelet.swift | 56 +++++++++++++++---- 3 files changed, 79 insertions(+), 28 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift index 57b8b8f2..e00c2784 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainer.swift @@ -51,19 +51,12 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ } } + // set backgroundImage 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 + //set action if let action = viewModel.action { //add the subscriber onClick = { [weak self] control in @@ -74,6 +67,18 @@ open class TileContainer: VDS.TileContainer, VDSMoleculeViewProtocol{ delegateObject: self.delegateObject) } } + + //set the rest of the properties + surface = viewModel.surface + imageFallbackColor = viewModel.imageFallbackColor + width = viewModel.width + height = viewModel.height + showBorder = viewModel.showBorder + showDropShadow = viewModel.showDropShadow + padding = viewModel.padding + color = viewModel.color + aspectRatio = viewModel.aspectRatio + backgroundEffect = viewModel.backgroundEffect } private func loadImage(_ imageName: String? = nil) { diff --git a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift index b6932ef9..895bb027 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/TileContainerModel.swift @@ -55,37 +55,44 @@ open class TileContainerBaseModel Date: Thu, 25 Apr 2024 15:51:44 -0400 Subject: [PATCH 23/24] Digital PCT265 defect CXTDT-531317 - Adjust logging to capture webview errors. --- MVMCoreUI.xcodeproj/project.pbxproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 5d09ab93..3a12aa10 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -171,7 +171,6 @@ 5822720B2B1FC55F00F75BAE /* AccessibilityHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582272092B1FC55F00F75BAE /* AccessibilityHandler.swift */; }; 5822720C2B1FC55F00F75BAE /* RotorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5822720A2B1FC55F00F75BAE /* RotorHandler.swift */; }; 5846ABF62B4762A600FA6C76 /* PollingBehaviorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5846ABF52B4762A600FA6C76 /* PollingBehaviorModel.swift */; }; - 5870636F2ACF238E00CA18D5 /* ReadableDecodingErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5870636E2ACF238E00CA18D5 /* ReadableDecodingErrors.swift */; }; 58A9DD7D2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A9DD7C2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift */; }; 608211282AC6B57E00C3FC39 /* MVMCoreUILoggingHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608211262AC6AF8200C3FC39 /* MVMCoreUILoggingHandler.swift */; }; 8D070BB0241B56530099AC56 /* ListRightVariableTotalDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D070BAF241B56530099AC56 /* ListRightVariableTotalDataModel.swift */; }; @@ -772,7 +771,6 @@ 582272092B1FC55F00F75BAE /* AccessibilityHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessibilityHandler.swift; sourceTree = ""; }; 5822720A2B1FC55F00F75BAE /* RotorHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RotorHandler.swift; sourceTree = ""; }; 5846ABF52B4762A600FA6C76 /* PollingBehaviorModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PollingBehaviorModel.swift; sourceTree = ""; }; - 5870636E2ACF238E00CA18D5 /* ReadableDecodingErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadableDecodingErrors.swift; sourceTree = ""; }; 5878F0A42BD7E68800ADE23D /* mvmcoreui.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = mvmcoreui.xcconfig; sourceTree = ""; }; 5878F0A52BD7E6BE00ADE23D /* mvmcoreui_dev.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = mvmcoreui_dev.xcconfig; sourceTree = ""; }; 58A9DD7C2AC2103300F5E0B0 /* ReplaceableMoleculeBehaviorModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaceableMoleculeBehaviorModel.swift; sourceTree = ""; }; @@ -1606,7 +1604,6 @@ D202AFE2242A5F1400E5BEDF /* Extensions */ = { isa = PBXGroup; children = ( - 5870636E2ACF238E00CA18D5 /* ReadableDecodingErrors.swift */, D202AFE3242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift */, 0A209CD223A7E2810068F8B0 /* UIStackViewAlignment+Extension.swift */, D21EE53B23AD3AD4003D1A30 /* NSLayoutConstraintAxis+Extension.swift */, @@ -2824,7 +2821,6 @@ 0A0FEC7425D42A5E00AF2548 /* BaseItemPickerEntryField.swift in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, D23A8FF82612308D007E14CE /* PageBehaviorProtocolRequirer.swift in Sources */, - 5870636F2ACF238E00CA18D5 /* ReadableDecodingErrors.swift in Sources */, 94C2D9A723872DA90006CF46 /* LabelAttributeColorModel.swift in Sources */, 943820842432382400B43AF3 /* WebView.swift in Sources */, 0103B84E23D7E33A009C315C /* HeadlineBodyToggleModel.swift in Sources */, From 6e0b135f3013448ef6b2db4291ee42e353a9a28c Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 25 Apr 2024 15:14:55 -0500 Subject: [PATCH 24/24] forgot to add the action Signed-off-by: Matt Bruce --- MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift b/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift index 389033cb..89237662 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/ButtonIcon.swift @@ -27,6 +27,14 @@ open class ButtonIcon: VDS.ButtonIcon, VDSMoleculeViewProtocol { public func viewModelDidUpdate() { surface = viewModel.surface + onClick = { [weak self] control in + guard let self, let viewModel = self.viewModel else { return } + MVMCoreUIActionHandler.performActionUnstructured(with: viewModel.action, + sourceModel: viewModel, + additionalData: self.additionalData, + delegateObject: self.delegateObject) + } + badgeIndicatorModel = viewModel.badgeIndicatorModel kind = viewModel.kind surfaceType = viewModel.surfaceType