Digital ACT192 story ONEAPP-8912- YIR | Year in review Prototype Updates
@ -293,6 +293,7 @@
|
||||
AF1C336F2885A16A006B1001 /* ActionCollapseNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */; };
|
||||
AF1C33712885AE76006B1001 /* MVMCoreUIActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */; };
|
||||
AF1C33732885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1C33722885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift */; };
|
||||
AF1CEFE82BEA73890001F9A5 /* VDSCoreTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF1CEFE72BEA73890001F9A5 /* VDSCoreTokens.xcframework */; };
|
||||
AF60A7F62892D2E300919EEB /* ActionDismissNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F52892D2E300919EEB /* ActionDismissNotificationModel.swift */; };
|
||||
AF60A7F82892D34D00919EEB /* ActionDismissNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F72892D34D00919EEB /* ActionDismissNotificationHandler.swift */; };
|
||||
AF766D262A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */; };
|
||||
@ -607,7 +608,6 @@
|
||||
EABFC1412763BB8D00E78B40 /* FormLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC1402763BB8D00E78B40 /* FormLabel.swift */; };
|
||||
EABFC152276913E800E78B40 /* FormLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC151276913E800E78B40 /* FormLabelModel.swift */; };
|
||||
EACCF38C2ABB346700E0F104 /* VDS-Interpreters.swift in Sources */ = {isa = PBXBuildFile; fileRef = EACCF38B2ABB346700E0F104 /* VDS-Interpreters.swift */; };
|
||||
EAD715AA2BBC8FAF00DEDA6A /* VDSTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAD715A92BBC8FAF00DEDA6A /* VDSTokens.xcframework */; };
|
||||
FD99130028E21E4900542CC3 /* RuleNotEqualsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9912FF28E21E4900542CC3 /* RuleNotEqualsModel.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@ -911,6 +911,7 @@
|
||||
AF1C336E2885A16A006B1001 /* ActionCollapseNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCollapseNotificationHandler.swift; sourceTree = "<group>"; };
|
||||
AF1C33702885AE76006B1001 /* MVMCoreUIActionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIActionHandler.swift; sourceTree = "<group>"; };
|
||||
AF1C33722885D481006B1001 /* MVMCoreUIActionOpenPageHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIActionOpenPageHandler.swift; sourceTree = "<group>"; };
|
||||
AF1CEFE72BEA73890001F9A5 /* VDSCoreTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSCoreTokens.xcframework; path = ../SharedFrameworks/VDSCoreTokens.xcframework; sourceTree = "<group>"; };
|
||||
AF60A7F52892D2E300919EEB /* ActionDismissNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionDismissNotificationModel.swift; sourceTree = "<group>"; };
|
||||
AF60A7F72892D34D00919EEB /* ActionDismissNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionDismissNotificationHandler.swift; sourceTree = "<group>"; };
|
||||
AF766D252A3CD4C600749099 /* UIAccessibilityTraits+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAccessibilityTraits+Codable.swift"; sourceTree = "<group>"; };
|
||||
@ -1244,9 +1245,9 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AF1CEFE82BEA73890001F9A5 /* VDSCoreTokens.xcframework in Frameworks */,
|
||||
D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */,
|
||||
EA985C602970A3F000F2FF2E /* VDS.framework in Frameworks */,
|
||||
EAD715AA2BBC8FAF00DEDA6A /* VDSTokens.xcframework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -2138,6 +2139,7 @@
|
||||
D29DF0E421E4F3C7003B2FB9 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AF1CEFE72BEA73890001F9A5 /* VDSCoreTokens.xcframework */,
|
||||
EAD715A92BBC8FAF00DEDA6A /* VDSTokens.xcframework */,
|
||||
EA985C5F2970A3F000F2FF2E /* VDS.framework */,
|
||||
D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */,
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
import VDS
|
||||
import MVMCore
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
import VDS
|
||||
|
||||
open class Link: VDS.TextLink, VDSMoleculeViewProtocol {
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
import VDS
|
||||
import MVMCore
|
||||
import Combine
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
|
||||
@objcMembers open class RadioButton: Control, MFButtonProtocol {
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -41,7 +41,9 @@ open class ButtonIcon: VDS.ButtonIcon, VDSMoleculeViewProtocol {
|
||||
iconName = viewModel.iconName
|
||||
selectedIconName = viewModel.selectedIconName
|
||||
size = viewModel.size
|
||||
customSize = viewModel.customSize
|
||||
customContainerSize = viewModel.customContainerSize
|
||||
customIconSize = viewModel.customIconSize
|
||||
customBadgeIndicatorOffset = viewModel.customBadgeIndicatorOffSet
|
||||
floating = viewModel.floating
|
||||
fitToIcon = viewModel.fitToIcon
|
||||
hideBorder = viewModel.hideBorder
|
||||
|
||||
@ -31,7 +31,9 @@ open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol {
|
||||
public var iconName: Icon.Name = .info
|
||||
public var selectedIconName: Icon.Name?
|
||||
public var size = ButtonIcon.Size.large
|
||||
public var customSize : Int?
|
||||
public var customContainerSize : Int?
|
||||
public var customIconSize : Int?
|
||||
public var customBadgeIndicatorOffSet : CGPoint?
|
||||
public var floating: Bool = false
|
||||
public var fitToIcon: Bool = false
|
||||
public var hideBorder: Bool = true
|
||||
@ -85,7 +87,9 @@ open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol {
|
||||
case iconName
|
||||
case selectedIconName
|
||||
case size
|
||||
case customSize
|
||||
case customContainerSize
|
||||
case customIconSize
|
||||
case customBadgeIndicatorOffSet
|
||||
case floating
|
||||
case fitToIcon
|
||||
case hideBorder
|
||||
@ -110,7 +114,9 @@ open class ButtonIconModel: ButtonModelProtocol, 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)
|
||||
customContainerSize = try container.decodeIfPresent(Int.self, forKey: .customContainerSize)
|
||||
customIconSize = try container.decodeIfPresent(Int.self, forKey: .customIconSize)
|
||||
customBadgeIndicatorOffSet = try container.decodeIfPresent(CGPoint.self, forKey: .customBadgeIndicatorOffSet)
|
||||
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
|
||||
@ -131,7 +137,9 @@ open class ButtonIconModel: ButtonModelProtocol, MoleculeModelProtocol {
|
||||
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(customContainerSize, forKey: .customContainerSize)
|
||||
try container.encodeIfPresent(customIconSize, forKey: .customIconSize)
|
||||
try container.encodeIfPresent(customBadgeIndicatorOffSet, forKey: .customBadgeIndicatorOffSet)
|
||||
try container.encodeIfPresent(floating, forKey: .floating)
|
||||
try container.encodeIfPresent(fitToIcon, forKey: .fitToIcon)
|
||||
try container.encodeIfPresent(hideBorder, forKey: .hideBorder)
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
// Created by Kevin Christiano on 1/30/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
|
||||
open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
|
||||
open class CarouselIndicatorModel: CarouselPagingModelProtocol, MoleculeModelProtocol, EnableableModelProtocol {
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import VDS
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
|
||||
open class IconModel: MoleculeModelProtocol {
|
||||
|
||||
|
||||
@ -375,8 +375,8 @@ extension Label {
|
||||
public static func boundingRect(forCharacterRange range: NSRange, in label: Label) -> CGRect {
|
||||
|
||||
guard let abstractContainer = label.abstractTextContainer() else { return CGRect() }
|
||||
let textContainer = abstractContainer.0
|
||||
let layoutManager = abstractContainer.1
|
||||
let textContainer = abstractContainer.textContainer
|
||||
let layoutManager = abstractContainer.layoutManager
|
||||
|
||||
var glyphRange = NSRange()
|
||||
|
||||
@ -385,36 +385,6 @@ extension Label {
|
||||
|
||||
return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a text container and layout manager of how the text would appear on screen.
|
||||
They are used in tandem to derive low-level TextKit results of the label.
|
||||
*/
|
||||
public func abstractTextContainer() -> (NSTextContainer, NSLayoutManager, NSTextStorage)? {
|
||||
|
||||
// Must configure the attributed string to translate what would appear on screen to accurately analyze.
|
||||
guard let attributedText = attributedText else { return nil }
|
||||
|
||||
let paragraph = NSMutableParagraphStyle()
|
||||
paragraph.alignment = textAlignment
|
||||
|
||||
let stagedAttributedString = NSMutableAttributedString(attributedString: attributedText)
|
||||
stagedAttributedString.addAttributes([NSAttributedString.Key.paragraphStyle: paragraph], range: NSRange(location: 0, length: attributedText.string.count))
|
||||
|
||||
let textStorage = NSTextStorage(attributedString: stagedAttributedString)
|
||||
let layoutManager = NSLayoutManager()
|
||||
let textContainer = NSTextContainer(size: .zero)
|
||||
|
||||
layoutManager.addTextContainer(textContainer)
|
||||
textStorage.addLayoutManager(layoutManager)
|
||||
|
||||
textContainer.lineFragmentPadding = 0.0
|
||||
textContainer.lineBreakMode = lineBreakMode
|
||||
textContainer.maximumNumberOfLines = numberOfLines
|
||||
textContainer.size = bounds.size
|
||||
|
||||
return (textContainer, layoutManager, textStorage)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Atomization
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
import VDS
|
||||
|
||||
public class LineModel: MoleculeModelProtocol, Invertable {
|
||||
|
||||
@ -34,6 +34,7 @@ open class TileContainerModel: TileContainerBaseModel<TileContainer.Padding, Til
|
||||
case moleculeName
|
||||
case molecule
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
id = try container.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
|
||||
@ -84,6 +85,7 @@ open class TileContainerBaseModel<PaddingType: DefaultValuing & Codable, TileCon
|
||||
case aspectRatio
|
||||
case backgroundEffect
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
inverted = try container.decodeIfPresent(Bool.self, forKey: .inverted) ?? false
|
||||
@ -98,7 +100,6 @@ open class TileContainerBaseModel<PaddingType: DefaultValuing & Codable, TileCon
|
||||
color = try container.decodeIfPresent(TileContainerType.BackgroundColor.self, forKey: .color) ?? .black
|
||||
aspectRatio = try container.decodeIfPresent(TileContainerType.AspectRatio.self, forKey: .aspectRatio) ?? .ratio1x1
|
||||
backgroundEffect = try container.decodeIfPresent(TileContainerType.BackgroundEffect.self, forKey: .backgroundEffect) ?? .none
|
||||
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import VDS
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
import MVMCore
|
||||
|
||||
open class TooltipModel: MoleculeModelProtocol {
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import VDS
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codable Extensions
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
// Copyright © 2022 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
import VDS
|
||||
|
||||
public class TitleLockupModel: ParentMoleculeModelProtocol {
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
// Created by Scott Pfeil on 5/28/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
|
||||
@objcMembers open class TabBar: UITabBar, MoleculeViewProtocol, TabBarProtocol, UITabBarDelegate {
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
|
||||
open class TabBarModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "tabBar"
|
||||
@ -34,7 +34,7 @@ open class TabBarModel: MoleculeModelProtocol {
|
||||
if let selectedColor = _selectedColor { return selectedColor }
|
||||
if let style = style,
|
||||
style == .dark { return Color(uiColor: VDSColor.elementsPrimaryOndark) }
|
||||
return Color(uiColor: VDSColor.elementsPrimaryOnlight)
|
||||
return Color(uiColor: UIColor.mvmRed)
|
||||
}
|
||||
set {
|
||||
_selectedColor = newValue
|
||||
@ -54,7 +54,7 @@ open class TabBarModel: MoleculeModelProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
open var style: NavigationItemStyle? = .dark
|
||||
open var style: NavigationItemStyle? = .light
|
||||
|
||||
// Must be capped to 0...(tabs.count - 1)
|
||||
open var selectedTab: Int = 0
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
import VDS
|
||||
|
||||
@objc public protocol TabsDelegate {
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
import VDS
|
||||
open class TabsModel: MoleculeModelProtocol {
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
public var fieldKey: String?
|
||||
public var groupName: String = FormValidator.defaultGroupName
|
||||
public var baseValue: AnyHashable?
|
||||
public var gone: Bool = false
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Validation
|
||||
@ -53,6 +54,7 @@
|
||||
case groupName
|
||||
case enabled
|
||||
case readOnly
|
||||
case gone
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -76,6 +78,7 @@
|
||||
if let readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) {
|
||||
self.readOnly = readOnly
|
||||
}
|
||||
gone = try typeContainer.decodeIfPresent(Bool.self, forKey: .gone) ?? false
|
||||
try super.init(from: decoder)
|
||||
}
|
||||
|
||||
@ -99,6 +102,7 @@
|
||||
&& fieldValue == model.fieldValue
|
||||
&& enabled == model.enabled
|
||||
&& readOnly == model.readOnly
|
||||
&& gone == model.gone
|
||||
}
|
||||
|
||||
public override func isVisuallyEquivalent(to model: any MoleculeModelComparisonProtocol) -> Bool {
|
||||
@ -107,5 +111,6 @@
|
||||
&& peakingArrowColor == model.peakingArrowColor
|
||||
&& enabled == model.enabled
|
||||
&& readOnly == model.readOnly
|
||||
&& gone == model.gone
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
|
||||
public enum NavigationItemStyle: String, Codable {
|
||||
case light
|
||||
|
||||
@ -10,7 +10,7 @@ import Foundation
|
||||
import Combine
|
||||
import Dispatch
|
||||
import MVMCore
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
|
||||
@objcMembers open class CollapsableNotification: View {
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -90,7 +90,7 @@ open class Carousel: View {
|
||||
showPeaking(false)
|
||||
|
||||
// Go to current cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. The dispatch is a sad way to ensure the collection view is ready to be scrolled.
|
||||
guard let model = model as? CarouselModel, !model.molecules.isEmpty else { return }
|
||||
guard let model = model as? CarouselModel, !model.visibleMolecules.isEmpty else { return }
|
||||
guard (model.paging == true || loop == true) else {
|
||||
DispatchQueue.main.async { [self] in
|
||||
updatePagerVisibility()
|
||||
@ -174,9 +174,12 @@ open class Carousel: View {
|
||||
MVMCoreLoggingHandler.shared()?.handleDebugMessage("[\(Self.self)] [\(ObjectIdentifier(self).hashValue)]\noriginal model: \(originalModel?.debugDescription ?? "none")\nnew model: \(model)")
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
if let originalModel, carouselModel.isDeeplyVisuallyEquivalent(to: originalModel) {
|
||||
if let originalModel, carouselModel.isDeeplyVisuallyEquivalent(to: originalModel),
|
||||
originalModel.visibleMolecules.isVisuallyEquivalent(to: molecules ?? []) // Since the carousel model's children are in place replaced and we do not have a deep copy of this model tree, add in this hack to check if the prior captured carousel items match the newly visible ones.
|
||||
{
|
||||
// Prevents a carousel reset while still updating the cell backing data through reconfigureItems.
|
||||
MVMCoreLoggingHandler.shared()?.handleDebugMessage("[\(Self.self)] Model is visually equivalent. Skipping rebuild...")
|
||||
prepareMolecules(with: carouselModel)
|
||||
FormValidator.setupValidation(for: carouselModel, delegate: delegateObject?.formHolderDelegate)
|
||||
updateModelIndex() // Ensure the new model indexing matches the old.
|
||||
pagingView?.currentIndex = pageIndex // Trigger a paging view render.
|
||||
@ -228,7 +231,7 @@ open class Carousel: View {
|
||||
//--------------------------------------------------
|
||||
|
||||
func prepareMolecules(with carouselModel: CarouselModel?) {
|
||||
guard let newMolecules = carouselModel?.molecules else {
|
||||
guard let newMolecules = carouselModel?.visibleMolecules else {
|
||||
numberOfPages = 0
|
||||
molecules = nil
|
||||
return
|
||||
@ -304,6 +307,7 @@ open class Carousel: View {
|
||||
|
||||
pagingView?.removeFromSuperview()
|
||||
bottomPin?.isActive = false
|
||||
pagingView = nil
|
||||
|
||||
guard var pagingView = view else {
|
||||
bottomPin = bottomAnchor.constraint(equalTo: collectionView.bottomAnchor)
|
||||
|
||||
@ -44,6 +44,10 @@ import UIKit
|
||||
public var selectable = false
|
||||
public var selectedIndex: Int?
|
||||
|
||||
public var visibleMolecules: [MoleculeModelProtocol & CarouselItemModelProtocol] {
|
||||
molecules.filter { !$0.gone }
|
||||
}
|
||||
|
||||
public init(molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]) {
|
||||
self.molecules = molecules
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
public protocol CarouselItemModelProtocol: FormFieldProtocol, ContainerModelProtocol {
|
||||
var analyticsData: JSONValueDictionary? { get set }
|
||||
var gone: Bool { get set }
|
||||
}
|
||||
|
||||
public extension CarouselItemModelProtocol {
|
||||
@ -16,4 +17,9 @@ public extension CarouselItemModelProtocol {
|
||||
get { nil }
|
||||
set { analyticsData = newValue }
|
||||
}
|
||||
|
||||
var gone: Bool{
|
||||
get { false }
|
||||
set { }
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ public extension ParentModelProtocol where Self: AnyObject {
|
||||
|
||||
func replaceChildMolecule<T>(in molecules: inout [T], with replacementMolecule: MoleculeModelProtocol) throws -> MoleculeModelProtocol? {
|
||||
var replacedMolecule: MoleculeModelProtocol?
|
||||
return try replaceChildMolecule(at: &molecules, with: replacementMolecule, replaced: &replacedMolecule) ? replacedMolecule : nil
|
||||
return try replaceChildMolecule(in: &molecules, with: replacementMolecule, replaced: &replacedMolecule) ? replacedMolecule : nil
|
||||
}
|
||||
|
||||
/// Helper for replacing a molecule in place within an array. Note the "in".
|
||||
@ -67,6 +67,26 @@ public protocol ParentMoleculeModelProtocol: ParentModelProtocol, MoleculeModelP
|
||||
|
||||
public extension ParentMoleculeModelProtocol {
|
||||
|
||||
/// Recursively finds and replaces the first child matching the replacement molecule id property.
|
||||
mutating func deepReplaceMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> MoleculeModelProtocol? {
|
||||
var replacedMolecule: MoleculeModelProtocol?
|
||||
var possibleError: Error?
|
||||
// Dive into each root.
|
||||
depthFirstTraverse(options: .parentFirst, depth: 0) { depth, molecule, stop in
|
||||
guard var parentMolecule = molecule as? ParentMoleculeModelProtocol else { return }
|
||||
do {
|
||||
replacedMolecule = try parentMolecule.replaceChildMolecule(with: replacementMolecule)
|
||||
} catch {
|
||||
possibleError = error
|
||||
}
|
||||
stop = replacedMolecule != nil || possibleError != nil
|
||||
}
|
||||
if let error = possibleError {
|
||||
throw error
|
||||
}
|
||||
return replacedMolecule
|
||||
}
|
||||
|
||||
func reduceDepthFirstTraverse<Result>(options: TreeTraversalOptions, depth: Int, initialResult: Result, nextPartialResult: (Result, MoleculeModelProtocol, Int) -> Result) -> Result {
|
||||
var result = initialResult
|
||||
if (options == .parentFirst) {
|
||||
|
||||
@ -40,31 +40,3 @@ public extension TemplateModelProtocol {
|
||||
return rootMolecules.depthFirstTraverse(options: options, depth: depth, onVisit: onVisit)
|
||||
}
|
||||
}
|
||||
|
||||
extension TemplateModelProtocol {
|
||||
|
||||
/// Recursively finds and replaces the first child matching the replacement molecule id property.
|
||||
mutating func replaceMolecule(with replacementMolecule: MoleculeModelProtocol) throws -> MoleculeModelProtocol? {
|
||||
// Attempt root level replacement on the template model first.
|
||||
if let replacedMolecule = try replaceChildMolecule(with: replacementMolecule) {
|
||||
return replacedMolecule
|
||||
}
|
||||
|
||||
var replacedMolecule: MoleculeModelProtocol?
|
||||
var possibleError: Error?
|
||||
// Dive into each root thereafter.
|
||||
depthFirstTraverse(options: .parentFirst, depth: 0) { depth, molecule, stop in
|
||||
guard var parentMolecule = molecule as? ParentMoleculeModelProtocol else { return }
|
||||
do {
|
||||
replacedMolecule = try parentMolecule.replaceChildMolecule(with: replacementMolecule)
|
||||
} catch {
|
||||
possibleError = error
|
||||
}
|
||||
stop = replacedMolecule != nil || possibleError != nil
|
||||
}
|
||||
if let error = possibleError {
|
||||
throw error
|
||||
}
|
||||
return replacedMolecule
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,6 +83,9 @@ import Foundation
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
if let defaultPageType = Self.defaultPageType() {
|
||||
pageType = try typeContainer.decodeIfPresent(String.self, forKey: .pageType) ?? defaultPageType
|
||||
if pageType.isEmpty {
|
||||
pageType = defaultPageType
|
||||
}
|
||||
} else {
|
||||
pageType = try typeContainer.decode(String.self, forKey: .pageType)
|
||||
}
|
||||
|
||||
@ -79,10 +79,10 @@
|
||||
}
|
||||
|
||||
|
||||
open override func handleNewData(_ pageModel: PageModelProtocol? = nil) {
|
||||
open override func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
|
||||
setup()
|
||||
registerCells()
|
||||
super.handleNewData(pageModel)
|
||||
super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender)
|
||||
}
|
||||
|
||||
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
|
||||
|
||||
@ -25,8 +25,8 @@ open class ModalMoleculeListTemplate: MoleculeListTemplate {
|
||||
try decoder.decode(ModalListPageTemplateModel.self, from: data)
|
||||
}
|
||||
|
||||
override open func handleNewData(_ pageModel: PageModelProtocol? = nil) {
|
||||
super.handleNewData(pageModel)
|
||||
override open func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
|
||||
super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender)
|
||||
|
||||
closeButton = MVMCoreUICommonViewsUtility.addCloseButton(to: view, tintColor: self.pageModel?.navigationBar?.tintColor.uiColor, action: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
|
||||
@ -23,8 +23,8 @@ open class ModalMoleculeStackTemplate: MoleculeStackTemplate {
|
||||
// MARK: - Lifecycle
|
||||
//--------------------------------------------------
|
||||
|
||||
override open func handleNewData(_ pageModel: PageModelProtocol? = nil) {
|
||||
super.handleNewData(pageModel)
|
||||
override open func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
|
||||
super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender)
|
||||
_ = MVMCoreUICommonViewsUtility.addCloseButton(to: view, tintColor: self.pageModel?.navigationBar?.tintColor.uiColor, action: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
let closeAction = (self.templateModel as? ModalStackPageTemplateModel)?.closeAction ??
|
||||
|
||||
@ -85,8 +85,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
|
||||
return view
|
||||
}
|
||||
|
||||
open override func handleNewData(_ pageModel: PageModelProtocol? = nil) {
|
||||
super.handleNewData(pageModel)
|
||||
open override func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
|
||||
super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender)
|
||||
|
||||
if pageModel != nil {
|
||||
setup()
|
||||
|
||||
@ -20,10 +20,10 @@ open class MoleculeStackTemplate: ThreeLayerViewController, TemplateProtocol {
|
||||
// MARK: - Lifecycle
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func handleNewData(_ pageModel: PageModelProtocol? = nil) {
|
||||
open override func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
|
||||
topViewOutsideOfScroll = templateModel?.anchorHeader ?? false
|
||||
bottomViewOutsideOfScroll = templateModel?.anchorFooter ?? false
|
||||
super.handleNewData(pageModel)
|
||||
super.handleNewData(pageModel, shouldTriggerRender: shouldTriggerRender)
|
||||
}
|
||||
|
||||
// For subclassing the model.
|
||||
|
||||
@ -52,17 +52,20 @@ open class ThreeLayerTableViewController: ProgrammaticTableViewController, Rotor
|
||||
bottomView.updateView(width)
|
||||
showFooter(width)
|
||||
}
|
||||
tableView.reloadData()
|
||||
tableView.visibleCells.forEach { cell in
|
||||
(cell as? MVMCoreViewProtocol)?.updateView(width)
|
||||
}
|
||||
}
|
||||
|
||||
open override func updateUI(for molecules: [MoleculeModelProtocol]? = nil) {
|
||||
super.updateUI(for: molecules)
|
||||
|
||||
guard molecules == nil else { return }
|
||||
|
||||
|
||||
createViewForTableHeader()
|
||||
createViewForTableFooter()
|
||||
tableView?.reloadData()
|
||||
// Reloading the table is handled in updateViews, however, update views is on a separate rendering task than the current thread. The table render needs to be bound and settled to the new model before others put in additional update requests.
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
override open func viewDidLoad() {
|
||||
|
||||
@ -146,6 +146,11 @@ import MVMCore
|
||||
do {
|
||||
let template = try parsePageJSON(loadObject: loadObject)
|
||||
pageModel = template // TODO: Eventually this page parsing should be done outside of this class and then set by the caller. For now, double duty.
|
||||
// Needed for PageMoleculeTransformationBehavior + PageLocalDataShareBehavior behaviors.
|
||||
if let behaviorContainer = template as? (PageBehaviorContainerModelProtocol & TemplateModelProtocol) {
|
||||
var behaviorHandler = self
|
||||
behaviorHandler.applyBehaviors(pageBehaviorModel: behaviorContainer)
|
||||
}
|
||||
isFirstRender = true // Assuming this is only on the first page load from the handler. Might need to revist later.
|
||||
if let backgroundRequest = loadObject.requestParameters?.backgroundRequest, !backgroundRequest, let pageType, let identifier = loadObject.identifier {
|
||||
MVMCoreLoggingHandler.shared()?.logCoreEvent(.pageProcessingComplete(pageType: pageType, requestUUID: identifier, webUrl: nil))
|
||||
@ -225,16 +230,16 @@ import MVMCore
|
||||
return navigationModel
|
||||
}
|
||||
|
||||
/// Processes any new data. Called after the page is loaded the first time and on response updates for this page. Triggers a render refresh.
|
||||
/// Processes any new data. Called after the page is loaded the first time and on response updates for this page. Triggers a render refresh unless specified otherwise.
|
||||
@MainActor
|
||||
open func handleNewData(_ pageModel: PageModelProtocol? = nil) {
|
||||
open func handleNewData(_ pageModel: PageModelProtocol? = nil, shouldTriggerRender: Bool = true) {
|
||||
|
||||
guard var newPageModel = pageModel ?? self.pageModel else { return }
|
||||
let originalModel = isFirstRender ? nil : self.pageModel as? MVMControllerModelProtocol
|
||||
let originalModel = self.pageModel as? MVMControllerModelProtocol
|
||||
|
||||
// Refresh our behaviors if there is a page change.
|
||||
// Refresh our behaviors if there is a page change. Originally set up in shouldFinishProcessingLoad.
|
||||
if let behaviorContainer = newPageModel as? (PageBehaviorContainerModelProtocol & TemplateModelProtocol),
|
||||
(originalModel == nil || originalModel!.id != behaviorContainer.id) {
|
||||
(originalModel == nil || originalModel!.id != behaviorContainer.id) {
|
||||
var behaviorHandler = self
|
||||
behaviorHandler.applyBehaviors(pageBehaviorModel: behaviorContainer)
|
||||
}
|
||||
@ -277,15 +282,20 @@ import MVMCore
|
||||
let allUpdatedMolecules = behaviorUpdatedModels //+ pageUpdatedModels
|
||||
|
||||
// Notify the manager of new data.
|
||||
// Warning: Some flows cause table reloads. Until the UI update is decoupled, should be after the updateUI.
|
||||
manager?.newDataReceived?(in: self)
|
||||
|
||||
if allUpdatedMolecules.isEmpty || isFirstRender {
|
||||
debugLog("Performing full page render...")
|
||||
updateUI()
|
||||
} else {
|
||||
debugLog("Performing partial render of \(allUpdatedMolecules) molecules...")
|
||||
updateUI(for: allUpdatedMolecules)
|
||||
|
||||
guard shouldTriggerRender else { return }
|
||||
|
||||
// Dispatch to decouple execution. First massage data through template classes, then render.
|
||||
Task { @MainActor in
|
||||
|
||||
if allUpdatedMolecules.isEmpty || isFirstRender {
|
||||
debugLog("Performing full page render...")
|
||||
updateUI()
|
||||
} else {
|
||||
debugLog("Performing partial render of \(allUpdatedMolecules) molecules...")
|
||||
updateUI(for: allUpdatedMolecules)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -399,7 +409,8 @@ import MVMCore
|
||||
initialLoad()
|
||||
}
|
||||
|
||||
handleNewData(pageModel) // Set outside shouldFinishProcessingLoad.
|
||||
handleNewData(pageModel, shouldTriggerRender: false) // Set outside shouldFinishProcessingLoad.
|
||||
updateUI() // Force the rendering on the same main UI thread.
|
||||
}
|
||||
|
||||
open override func viewDidLayoutSubviews() {
|
||||
|
||||
@ -41,7 +41,7 @@ public extension PageBehaviorHandlerProtocol {
|
||||
// Apply them to the page.
|
||||
self.behaviors = behaviors.count > 0 ? behaviors : nil
|
||||
|
||||
// Ask the session to apply any more. (Curently inverted contol due to Swift <--> Obj-C conflict.
|
||||
// Ask the session to apply any more. (Currently inverted contol due to Swift <--> Obj-C conflict.)
|
||||
if let viewController = self as? UIViewController {
|
||||
MVMCoreUISession.sharedGlobal()?.applyGlobalBehaviors(to: viewController)
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ public class ReplaceableMoleculeBehaviorModel: PageBehaviorModelProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, CoreLogging {
|
||||
public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, PageVisibilityBehavior, CoreLogging {
|
||||
|
||||
public var loggingPrefix: String {
|
||||
"\(self) \(ObjectIdentifier(self).hashValue) \(moleculeIds.prefix(3)) \(moleculeIds.count > 3 ? "+ \(moleculeIds.count - 3) more" : ""):\n"
|
||||
@ -30,6 +30,8 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
|
||||
String(describing: Self.self)
|
||||
}
|
||||
|
||||
var isPageShowing = false
|
||||
var previouslyReplacedIds = Set<String>()
|
||||
var moleculeIds: [String]
|
||||
public var modulesToListenFor: [String]
|
||||
private var observingForResponses: NSObjectProtocol?
|
||||
@ -41,7 +43,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
|
||||
self.delegateObject = delegateObject
|
||||
guard let pageType = delegateObject?.moleculeDelegate?.getTemplateModel()?.pageType else { return }
|
||||
MVMCoreViewControllerMappingObject.shared()?.addOptionalModules(toMapping: moleculeIds, forPageType: pageType)
|
||||
Self.debugLog("Initializing for \((model as! ReplaceableMoleculeBehaviorModel).moleculeIds)")
|
||||
Self.debugLog("Initializing for \(moleculeIds)")
|
||||
}
|
||||
|
||||
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?, changes: inout [MoleculeModelProtocol]) -> [MoleculeModelProtocol]? {
|
||||
@ -75,7 +77,7 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
|
||||
return rootMolecule
|
||||
}
|
||||
debugLog("top replacing \(rootMolecule) with \(updatedMolecule)")
|
||||
logUpdated(molecule: updatedMolecule)
|
||||
logUpdated(moleculeId: updatedMolecule.id)
|
||||
changeList.append(updatedMolecule)
|
||||
return updatedMolecule
|
||||
}
|
||||
@ -85,14 +87,14 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
|
||||
|
||||
moleculeModels.forEach { newMolecule in
|
||||
do {
|
||||
if let replacedMolecule = try parentMolecule.replaceChildMolecule(with: newMolecule) {
|
||||
if let replacedMolecule = try parentMolecule.deepReplaceMolecule(with: newMolecule) {
|
||||
guard !replacedMolecule.deepEquals(to: newMolecule) else {
|
||||
// Note: Slight risk here of replacing the something in the original tree and misreporting that is it not replaced based on equality.
|
||||
debugLog("deep molecule \(newMolecule) is the same as \(replacedMolecule). skipping...")
|
||||
return
|
||||
}
|
||||
debugLog("deep replacing \(replacedMolecule) with \(newMolecule)")
|
||||
logUpdated(molecule: newMolecule)
|
||||
logUpdated(moleculeId: newMolecule.id)
|
||||
changeList.append(newMolecule)
|
||||
}
|
||||
} catch {
|
||||
@ -111,15 +113,29 @@ public class ReplaceableMoleculeBehavior: PageMoleculeTransformationBehavior, Co
|
||||
return hasReplacement ? updatedRootMolecules : nil
|
||||
}
|
||||
|
||||
private func logUpdated(molecule: MoleculeModelProtocol) {
|
||||
guard let module: [AnyHashable: Any] = delegateObject?.moleculeDelegate?.getModuleWithName(molecule.id),
|
||||
private func logUpdated(moleculeId: String) {
|
||||
guard let module: [AnyHashable: Any] = delegateObject?.moleculeDelegate?.getModuleWithName(moleculeId),
|
||||
let viewController = delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol else {
|
||||
debugLog("Missing the originating module \(molecule.id) creating this molecule!")
|
||||
debugLog("Missing the originating module \(moleculeId) creating this molecule!")
|
||||
return
|
||||
}
|
||||
previouslyReplacedIds.insert(moleculeId)
|
||||
guard isPageShowing else { return } // Page has not been made visible yet. (Pulled and replaced from cache on load or background update.) Hold reporting until onPageShown.
|
||||
MVMCoreUILoggingHandler.shared()?.defaultLogPageUpdate(forController: viewController, from: module)
|
||||
}
|
||||
|
||||
public func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||
isPageShowing = true
|
||||
debugLog("Page shown. Send molecule analytics for: \(previouslyReplacedIds)")
|
||||
previouslyReplacedIds.forEach { id in
|
||||
logUpdated(moleculeId: id)
|
||||
}
|
||||
}
|
||||
|
||||
public func onPageHidden(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||
isPageShowing = false
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugLog("deinit")
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import VDSCoreTokens
|
||||
|
||||
public typealias ColorHexTuple = (uiColor: UIColor, hex: String)
|
||||
|
||||
@ -19,7 +20,8 @@ extension UIColor {
|
||||
/// Dictionary to access brand approved colors by name.
|
||||
public static let names: [String: ColorHexTuple] = ["black": (.mvmBlack, "#000000"),
|
||||
"white": (.mvmWhite, "#FFFFFF"),
|
||||
"red": (.mvmRed, "#D52B1E"),
|
||||
"red": (.mvmRed, "#EE0000"),
|
||||
"monarchRed": (VDSCoreTokens.VDSColor.paletteMonarchred, "#f50a23"),
|
||||
"pink": (.mvmPink, "#D90368"),
|
||||
"pink33": (.mvmPink33, "#F2ABCD"),
|
||||
"pink66": (.mvmPink66, "#E6589B"),
|
||||
@ -48,13 +50,20 @@ extension UIColor {
|
||||
"blueShade2": (.mvmBlueShade2, "#0B4467"),
|
||||
"blueInverted": (.mvmBlueInverted, "#0088CE"),
|
||||
"yellow": (.mvmYellow, "#FFBC3D"),
|
||||
"neonYellow": (VDSCoreTokens.VDSColor.paletteNeonyellow, "#f5ff1e"),
|
||||
"coolGray1": (.mvmCoolGray1, "#F6F6F6"),
|
||||
"coolGray3": (.mvmCoolGray3, "#D8DADA"),
|
||||
"coolGray6": (.mvmCoolGray6, "#747676"),
|
||||
"coolGray10": (.mvmCoolGray10, "#333333"),
|
||||
"upGold1": (.vzupGold1, "#F9D542"),
|
||||
"upGold2": (.vzupGold2, "#F4CA53"),
|
||||
"upGold3": (.vzupGold3, "#CC9B2D")]
|
||||
"upGold3": (.vzupGold3, "#CC9B2D"),
|
||||
"stone": (.stone, "#F3EDE0"),
|
||||
"coral": (.coral, "#FF3C2D"),
|
||||
"gray44": (.gray44, "#6F7171"),
|
||||
"gray85": (.gray85, "#D8DADA"),
|
||||
"gray95": (.gray95, "#F6F6F6")
|
||||
]
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Helper
|
||||
@ -82,9 +91,10 @@ extension UIColor {
|
||||
// MARK: - Red
|
||||
//--------------------------------------------------
|
||||
|
||||
/// HEX: #D52B1E
|
||||
/// HEX: #EE0000
|
||||
@objc
|
||||
public static let mvmRed = UIColor.assetColor(named: "red")
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Pink
|
||||
//--------------------------------------------------
|
||||
@ -190,7 +200,7 @@ extension UIColor {
|
||||
// MARK: - Yellow
|
||||
//--------------------------------------------------
|
||||
|
||||
/// HEX: #FFBC3D
|
||||
/// HEX: ##F5FF1E
|
||||
public static let mvmYellow = UIColor.assetColor(named: "yellow")
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -222,6 +232,25 @@ extension UIColor {
|
||||
/// HEX: #CC9B2D
|
||||
public static let vzupGold3 = UIColor.assetColor(named: "upGold3")
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Monarch
|
||||
//--------------------------------------------------
|
||||
|
||||
/// HEX: #F3EDE0
|
||||
@objc public static let stone = VDSCoreTokens.VDSColor.paletteStone
|
||||
|
||||
// HEX:#FF3C2D
|
||||
@objc public static let coral = VDSCoreTokens.VDSColor.paletteCoral
|
||||
|
||||
// HEX:#6F7171
|
||||
@objc public static let gray44 = VDSCoreTokens.VDSColor.paletteGray44
|
||||
|
||||
// HEX:#D8DADA
|
||||
@objc public static let gray85 = VDSCoreTokens.VDSColor.paletteGray85
|
||||
|
||||
// HEX:#F6F6F6
|
||||
@objc public static let gray95 = VDSCoreTokens.VDSColor.paletteGray95
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Functions
|
||||
//--------------------------------------------------
|
||||
|
||||
@ -77,7 +77,7 @@
|
||||
#pragma mark - legacy
|
||||
|
||||
+ (nonnull UIColor *)mfRedColor {
|
||||
return [UIColor colorWithRed:.804 green:.016 blue:.043 alpha:1.0];
|
||||
return [UIColor mvmRed];
|
||||
}
|
||||
|
||||
+ (nonnull UIColor *)mfDarkerRedColor {
|
||||
@ -307,8 +307,6 @@
|
||||
static dispatch_once_t once;
|
||||
dispatch_once(&once, ^{
|
||||
stringColorMapping = @{@"PrimaryRed":[UIColor mfRedColor],
|
||||
@"black":[UIColor blackColor],
|
||||
@"red":[UIColor mfRedColor],
|
||||
@"greyish":[UIColor mfLightGrayColor],
|
||||
@"robinsEggBlue" : [UIColor mfRobinsEggBlue],
|
||||
@"lightSalmon" : [UIColor mfLightSalmon],
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"red" : "0xD5",
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x1E",
|
||||
"green" : "0x2B"
|
||||
"blue" : "0x00",
|
||||
"green" : "0x00",
|
||||
"red" : "0xEE"
|
||||
}
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"red" : "0xFF",
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x3D",
|
||||
"green" : "0xBC"
|
||||
"green" : "0xBC",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import MVMCore
|
||||
import VDSTokens
|
||||
import VDSCoreTokens
|
||||
|
||||
open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, TabsDelegate, MVMCorePresentationDelegateProtocol, SubNavSwipeNavigationProtocol {
|
||||
/// The number of tabs count or less that will turn on the fillContainer
|
||||
|
||||
@ -1,23 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "exportBlack.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "exportBlack@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "exportBlack@3x.png",
|
||||
"scale" : "3x"
|
||||
"filename" : "Vector.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
MVMCoreUI/SupportingFiles/Media.xcassets/externalLink.imageset/Vector.svg
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 -3.05176e-05V7.11108H18.7555V2.1333L11.9555 8.9333L11.0666 8.04441L17.8666 1.25553H12.8889V-3.05176e-05H20ZM15 18.7555H1.25553V4.99997H12.2222L13.3333 3.75552H-2.28882e-05V20H16.2555V6.66664L15 7.77775V18.7555Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 343 B |
|
Before Width: | Height: | Size: 345 B |
|
Before Width: | Height: | Size: 589 B |
|
Before Width: | Height: | Size: 869 B |
@ -1,23 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "nav_back.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "nav_back@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "nav_back@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
"filename" : "Vector.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
||||
|
||||
3
MVMCoreUI/SupportingFiles/Media.xcassets/nav_back.imageset/Vector.svg
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 10.6361H2.46073L10.9975 19.1001L10.0897 20L0 10L10.0897 0L10.9974 0.899867L2.46077 9.36388H20V10.6361Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 235 B |
|
Before Width: | Height: | Size: 220 B |
|
Before Width: | Height: | Size: 302 B |
|
Before Width: | Height: | Size: 394 B |
|
Before Width: | Height: | Size: 435 B |
|
Before Width: | Height: | Size: 631 B |
|
Before Width: | Height: | Size: 257 B |
@ -1,23 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Close.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Close-1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Close-2.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
"filename" : "Vector.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
||||
|
||||
3
MVMCoreUI/SupportingFiles/Media.xcassets/nav_close.imageset/Vector.svg
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.87772 9.00001L17.7777 16.8889L16.8888 17.7778L8.99995 9.87778L1.11106 17.7778L0.222168 16.8889L8.11106 9.00001L0.222168 1.11112L1.11106 0.222229L8.99995 8.11112L16.8888 0.222229L17.7777 1.11112L9.87772 9.00001Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 342 B |
@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
// The bundle for this framework
|
||||
+ (nullable NSBundle *)bundleForMVMCoreUI;
|
||||
|
||||
/// The bundle for the VDS frameowrk. Handy for accessing VDS resources such as fonts.
|
||||
+ (nullable NSBundle *)bundleForFonts;
|
||||
|
||||
// Returns the hardcoded string from the string file.
|
||||
+ (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key;
|
||||
|
||||
|
||||
@ -22,6 +22,10 @@
|
||||
return [NSBundle bundleWithIdentifier:@"com.vzw.MVMCoreUI"];
|
||||
}
|
||||
|
||||
+ (nullable NSBundle *)bundleForFonts {
|
||||
return [NSBundle bundleWithIdentifier:@"com.vzw.vds"];
|
||||
}
|
||||
|
||||
+ (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key {
|
||||
// Redirect key with relevant module.
|
||||
return [MVMCoreGetterUtility hardcodedStringWithKey:key bundle:[MVMCoreUIUtility bundleForMVMCoreUI]];
|
||||
|
||||