This commit is contained in:
Kevin G Christiano 2020-12-17 13:33:46 -05:00
commit ddd8cc2f07
10 changed files with 91 additions and 51 deletions

View File

@ -80,8 +80,6 @@ import UIKit
heightConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: 1) heightConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: 1)
heightConstraint?.isActive = true heightConstraint?.isActive = true
isAccessibilityElement = true isAccessibilityElement = true
accessibilityTraits = .button
updateAccessibilityLabel()
} }
public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
@ -91,6 +89,8 @@ import UIKit
guard let model = model as? HeartModel else { return } guard let model = model as? HeartModel else { return }
accessibilityIdentifier = model.accessibilityIdentifier accessibilityIdentifier = model.accessibilityIdentifier
isSelected = model.isActive isSelected = model.isActive
isEnabled = model.enabled
updateAccessibilityLabel()
} }
//-------------------------------------------------- //--------------------------------------------------
@ -98,11 +98,13 @@ import UIKit
//-------------------------------------------------- //--------------------------------------------------
/// Adjust accessibility label based on selection of Heart. /// Adjust accessibility label based on selection of Heart.
func updateAccessibilityLabel() { func updateAccessibilityLabel() {
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "heart_unfavorite_action_hint" : "heart_favorite_action_hint") accessibilityHint = isEnabled ? MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "heart_unfavorite_action_hint" : "heart_favorite_action_hint") : nil
accessibilityTraits = isEnabled ? .button : .none
accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "heart_selected_state" : "heart_not_selected_state") accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "heart_selected_state" : "heart_not_selected_state")
} }
func tapAction() { func tapAction() {
guard isEnabled else { return }
isSelected = !isSelected isSelected = !isSelected
if let heartModel = heartModel { if let heartModel = heartModel {
Button.performButtonAction(with: heartModel.action, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: heartModel) Button.performButtonAction(with: heartModel.action, button: self, delegateObject: delegateObject, additionalData: additionalData, sourceModel: heartModel)

View File

@ -7,7 +7,8 @@
// //
open class HeartModel: MoleculeModelProtocol { open class HeartModel: MoleculeModelProtocol, EnableableModelProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -19,6 +20,7 @@ open class HeartModel: MoleculeModelProtocol {
public var activeColor: Color = Color(uiColor: .mvmRed) public var activeColor: Color = Color(uiColor: .mvmRed)
public var inActiveColor: Color = Color(uiColor: .clear) public var inActiveColor: Color = Color(uiColor: .clear)
public var action: ActionModelProtocol = ActionNoopModel() public var action: ActionModelProtocol = ActionNoopModel()
public var enabled: Bool = true
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
@ -32,6 +34,7 @@ open class HeartModel: MoleculeModelProtocol {
case activeColor case activeColor
case inActiveColor case inActiveColor
case action case action
case enabled
} }
//-------------------------------------------------- //--------------------------------------------------
@ -58,6 +61,9 @@ open class HeartModel: MoleculeModelProtocol {
if let action: ActionModelProtocol = try typeContainer.decodeModelIfPresent(codingKey: .action) { if let action: ActionModelProtocol = try typeContainer.decodeModelIfPresent(codingKey: .action) {
self.action = action self.action = action
} }
if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) {
self.enabled = enabled
}
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -69,5 +75,6 @@ open class HeartModel: MoleculeModelProtocol {
try container.encode(activeColor, forKey: .activeColor) try container.encode(activeColor, forKey: .activeColor)
try container.encode(inActiveColor, forKey: .inActiveColor) try container.encode(inActiveColor, forKey: .inActiveColor)
try container.encodeModel(action, forKey: .action) try container.encodeModel(action, forKey: .action)
try container.encode(enabled, forKey: .enabled)
} }
} }

View File

@ -42,8 +42,6 @@
addMolecule(stack) addMolecule(stack)
stack.restack() stack.restack()
horizontalStack.restack() horizontalStack.restack()
accessibilityHint = heart.accessibilityHint
accessibilityTraits = heart.accessibilityTraits
} }
public override func updateView(_ size: CGFloat) { public override func updateView(_ size: CGFloat) {
@ -123,20 +121,40 @@
} }
func updateAccessibilityLabel() { func updateAccessibilityLabel() {
if let accessoryView = accessoryView { let hasHeart = !(horizontalStack.stackModel?.molecules[1].gone ?? true)
// Both caret and heart. if let accessoryView = accessoryView,
hasHeart {
// Both accessory and heart actions.
isAccessibilityElement = false isAccessibilityElement = false
accessoryView.accessibilityLabel = getAccessibilityMessage() accessoryView.accessibilityLabel = getAccessibilityMessage()
accessibilityElements = [accessoryView, heart] accessibilityElements = [accessoryView, heart]
} else { } else {
// Make whole cell focusable if no action. // Make whole cell focusable if no action.
isAccessibilityElement = true isAccessibilityElement = true
if let message = getAccessibilityMessage(), var message = getAccessibilityMessage()
let heartLabel = heart.accessibilityLabel { if hasHeart {
accessibilityLabel = message + ", " + heartLabel accessibilityHint = heart.accessibilityHint
if let heartLabel = heart.accessibilityLabel {
message = (message ?? "") + ", " + heartLabel
}
} else { } else {
accessibilityLabel = getAccessibilityMessage() accessibilityHint = nil
} }
accessibilityLabel = message
} }
} }
// Ensures voice over does not read "selected" after user triggers action on cell.
override public var accessibilityTraits: UIAccessibilityTraits {
get {
if (accessoryView != nil) {
return .button
} else if (!(horizontalStack.stackModel?.molecules[1].gone ?? true)) {
return heart.accessibilityTraits
} else {
return .none
}
}
set {}
}
} }

View File

@ -12,7 +12,7 @@ public class ListStoreLocatorModel: ListItemModel, MoleculeModelProtocol {
//-------------------------------------------------- //--------------------------------------------------
public static var identifier = "listStoreLocator" public static var identifier = "listStoreLocator"
public var heart: HeartModel public var heart: HeartModel?
public var leftHeadline: LabelModel public var leftHeadline: LabelModel
public var leftBody: LabelModel public var leftBody: LabelModel
public var leftSubBody: LabelModel public var leftSubBody: LabelModel
@ -22,7 +22,7 @@ public class ListStoreLocatorModel: ListItemModel, MoleculeModelProtocol {
// MARK: - Initializer // MARK: - Initializer
//-------------------------------------------------- //--------------------------------------------------
public init(heart: HeartModel, leftHeadline: LabelModel, leftBody: LabelModel, leftSubBody: LabelModel, rightLabel: LabelModel) { public init(heart: HeartModel?, leftHeadline: LabelModel, leftBody: LabelModel, leftSubBody: LabelModel, rightLabel: LabelModel) {
self.heart = heart self.heart = heart
self.leftHeadline = leftHeadline self.leftHeadline = leftHeadline
self.leftBody = leftBody self.leftBody = leftBody
@ -59,7 +59,7 @@ public class ListStoreLocatorModel: ListItemModel, MoleculeModelProtocol {
public required init(from decoder: Decoder) throws { public required init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
heart = try typeContainer.decode(HeartModel.self, forKey:.heart) heart = try typeContainer.decodeIfPresent(HeartModel.self, forKey:.heart)
leftHeadline = try typeContainer.decode(LabelModel.self, forKey: .leftHeadline) leftHeadline = try typeContainer.decode(LabelModel.self, forKey: .leftHeadline)
leftBody = try typeContainer.decode(LabelModel.self, forKey: .leftBody) leftBody = try typeContainer.decode(LabelModel.self, forKey: .leftBody)
leftSubBody = try typeContainer.decode(LabelModel.self, forKey: .leftSubBody) leftSubBody = try typeContainer.decode(LabelModel.self, forKey: .leftSubBody)
@ -71,7 +71,7 @@ public class ListStoreLocatorModel: ListItemModel, MoleculeModelProtocol {
try super.encode(to: encoder) try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(heart, forKey: .heart) try container.encodeIfPresent(heart, forKey: .heart)
try container.encode(leftHeadline, forKey: .leftHeadline) try container.encode(leftHeadline, forKey: .leftHeadline)
try container.encode(leftBody, forKey: .leftBody) try container.encode(leftBody, forKey: .leftBody)
try container.encode(leftSubBody, forKey: .leftSubBody) try container.encode(leftSubBody, forKey: .leftSubBody)

View File

@ -16,6 +16,7 @@ public class NavigationImageButtonModel: NavigationButtonModelProtocol, Molecule
public static var identifier: String = "navigationImageButton" public static var identifier: String = "navigationImageButton"
public var image: String public var image: String
public var action: ActionModelProtocol public var action: ActionModelProtocol
public var accessibilityText: String?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializer // MARK: - Initializer
@ -35,6 +36,7 @@ public class NavigationImageButtonModel: NavigationButtonModelProtocol, Molecule
case action case action
case accessibilityIdentifier case accessibilityIdentifier
case moleculeName case moleculeName
case accessibilityText
} }
//-------------------------------------------------- //--------------------------------------------------
@ -46,6 +48,7 @@ public class NavigationImageButtonModel: NavigationButtonModelProtocol, Molecule
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
image = try typeContainer.decode(String.self, forKey: .image) image = try typeContainer.decode(String.self, forKey: .image)
action = try typeContainer.decodeModel(codingKey: .action) action = try typeContainer.decodeModel(codingKey: .action)
accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
} }
open func encode(to encoder: Encoder) throws { open func encode(to encoder: Encoder) throws {
@ -54,6 +57,7 @@ public class NavigationImageButtonModel: NavigationButtonModelProtocol, Molecule
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeModel(action, forKey: .action) try container.encodeModel(action, forKey: .action)
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
} }
//-------------------------------------------------- //--------------------------------------------------
@ -65,6 +69,11 @@ public class NavigationImageButtonModel: NavigationButtonModelProtocol, Molecule
let uiImage = MVMCoreCache.shared()?.getImageFromRegisteredBundles(image) let uiImage = MVMCoreCache.shared()?.getImageFromRegisteredBundles(image)
let buttonItem = ImageBarButtonItem.create(with: uiImage, actionModel: action, delegateObject: delegateObject, additionalData: additionalData) let buttonItem = ImageBarButtonItem.create(with: uiImage, actionModel: action, delegateObject: delegateObject, additionalData: additionalData)
buttonItem.accessibilityIdentifier = accessibilityIdentifier ?? image buttonItem.accessibilityIdentifier = accessibilityIdentifier ?? image
if let accessibilityString = accessibilityText {
buttonItem.accessibilityLabel = accessibilityString
buttonItem.isAccessibilityElement = true
}
return buttonItem return buttonItem
} }
} }

View File

@ -12,21 +12,24 @@ import Foundation
public override class var identifier: String { public override class var identifier: String {
return "numberedList" return "numberedList"
} }
public var numberColor: Color
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case list case list
case numberColor
} }
// Numbered list model comes in the from of list = [MoleculeModelProtocol] // Numbered list model comes in the from of list = [MoleculeModelProtocol]
public required init(from decoder: Decoder) throws { public required init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
numberColor = try typeContainer.decodeIfPresent(Color.self, forKey: .numberColor) ?? Color(uiColor: .mvmBlack)
let list: [MoleculeModelProtocol] = try typeContainer.decodeModels(codingKey: .list) let list: [MoleculeModelProtocol] = try typeContainer.decodeModels(codingKey: .list)
var models: [MoleculeStackItemModel] = [] var models: [MoleculeStackItemModel] = []
for (index, molecule) in list.enumerated() { for (index, molecule) in list.enumerated() {
models.append(MoleculeStackItemModel(with: StringAndMoleculeModel(string: "\(index+1).", molecule: molecule))) models.append(MoleculeStackItemModel(with: StringAndMoleculeModel(string: "\(index+1).", molecule: molecule, stringColor: numberColor)))
} }
super.init(molecules: models, spacing: 0) super.init(molecules: models, spacing: 0)
} }
@ -41,6 +44,7 @@ import Foundation
models.append(((molecule as! MoleculeStackItemModel).molecule as! StringAndMoleculeModel).molecule) models.append(((molecule as! MoleculeStackItemModel).molecule as! StringAndMoleculeModel).molecule)
} }
try container.encodeModels(models, forKey: .list) try container.encodeModels(models, forKey: .list)
try container.encode(numberColor, forKey: .numberColor)
} }
} }

View File

@ -13,10 +13,12 @@ public class StringAndMoleculeModel: MoleculeModelProtocol {
public var backgroundColor: Color? public var backgroundColor: Color?
public var string: String public var string: String
public var molecule: MoleculeModelProtocol public var molecule: MoleculeModelProtocol
public var stringColor: Color
public init(string: String, molecule: MoleculeModelProtocol) {
public init(string: String, molecule: MoleculeModelProtocol, stringColor: Color = Color(uiColor: .mvmBlack)) {
self.string = string self.string = string
self.molecule = molecule self.molecule = molecule
self.stringColor = stringColor
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
@ -24,6 +26,7 @@ public class StringAndMoleculeModel: MoleculeModelProtocol {
case backgroundColor case backgroundColor
case string case string
case molecule case molecule
case stringColor
} }
public required init(from decoder: Decoder) throws { public required init(from decoder: Decoder) throws {
@ -31,6 +34,7 @@ public class StringAndMoleculeModel: MoleculeModelProtocol {
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
string = try typeContainer.decode(String.self, forKey: .string) string = try typeContainer.decode(String.self, forKey: .string)
molecule = try typeContainer.decodeModel(codingKey: .molecule) molecule = try typeContainer.decodeModel(codingKey: .molecule)
stringColor = try typeContainer.decodeIfPresent(Color.self, forKey: .stringColor) ?? Color(uiColor: .mvmBlack)
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -39,5 +43,6 @@ public class StringAndMoleculeModel: MoleculeModelProtocol {
try container.encode(string, forKey: .string) try container.encode(string, forKey: .string)
try container.encodeModel(molecule, forKey: .molecule) try container.encodeModel(molecule, forKey: .molecule)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(stringColor, forKey: .stringColor)
} }
} }

View File

@ -84,6 +84,7 @@ open class StringAndMoleculeView: View {
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
guard let model = model as? StringAndMoleculeModel else { return } guard let model = model as? StringAndMoleculeModel else { return }
label.text = model.string label.text = model.string
label.textColor = model.stringColor.uiColor
molecule.set(with: model.molecule, delegateObject, additionalData) molecule.set(with: model.molecule, delegateObject, additionalData)
} }

View File

@ -13,12 +13,14 @@ import Foundation
return "unOrderedList" return "unOrderedList"
} }
public var bulletChar = "" public var bulletChar = ""
public var bulletColor: Color
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case list case list
case bulletChar case bulletChar
case bulletColor
} }
// Numbered list model comes in the from of list = [MoleculeModelProtocol] // Numbered list model comes in the from of list = [MoleculeModelProtocol]
@ -28,10 +30,11 @@ import Foundation
self.bulletChar = bulletChar self.bulletChar = bulletChar
} }
bulletColor = try typeContainer.decodeIfPresent(Color.self, forKey: .bulletColor) ?? Color(uiColor: .mvmBlack)
let list: [MoleculeModelProtocol] = try typeContainer.decodeModels(codingKey: .list) let list: [MoleculeModelProtocol] = try typeContainer.decodeModels(codingKey: .list)
var models: [MoleculeStackItemModel] = [] var models: [MoleculeStackItemModel] = []
for molecule in list { for molecule in list {
models.append(MoleculeStackItemModel(with: StringAndMoleculeModel(string: bulletChar, molecule: molecule))) models.append(MoleculeStackItemModel(with: StringAndMoleculeModel(string: bulletChar, molecule: molecule, stringColor: bulletColor)))
} }
super.init(molecules: models, spacing: 0) super.init(molecules: models, spacing: 0)
} }
@ -47,5 +50,6 @@ import Foundation
models.append(((molecule as! MoleculeStackItemModel).molecule as! StringAndMoleculeModel).molecule) models.append(((molecule as! MoleculeStackItemModel).molecule as! StringAndMoleculeModel).molecule)
} }
try container.encodeModels(models, forKey: .list) try container.encodeModels(models, forKey: .list)
try container.encode(bulletColor, forKey: .bulletColor)
} }
} }

View File

@ -28,13 +28,13 @@ open class Styler {
// Legacy Fonts // Legacy Fonts
case H1 case H1
case H2
case H3
case H32 case H32
case H2
case B20
case H3
case B1 case B1
case B2 case B2
case B3 case B3
case B20
/// Returns the font size of the current enum case. /// Returns the font size of the current enum case.
public func pointSize() -> CGFloat { public func pointSize() -> CGFloat {
@ -45,8 +45,7 @@ open class Styler {
case .Title2XLarge: case .Title2XLarge:
return 36 return 36
case .TitleXLarge, case .TitleXLarge, .H32:
.H32:
return 32 return 32
case .H2: case .H2:
@ -57,8 +56,7 @@ open class Styler {
return 24 return 24
case .BoldTitleMedium, case .BoldTitleMedium,
.RegularTitleMedium, .RegularTitleMedium, .B20:
.B20:
return 20 return 20
case .H3: case .H3:
@ -68,15 +66,12 @@ open class Styler {
.RegularBodyLarge: .RegularBodyLarge:
return 16 return 16
case .BoldBodySmall, case .BoldBodySmall, .B1,
.RegularBodySmall, .RegularBodySmall, .B2:
.B1,
.B2:
return 13 return 13
case .BoldMicro, case .BoldMicro,
.RegularMicro, .RegularMicro, .B3:
.B3:
return 11 return 11
} }
} }
@ -96,27 +91,22 @@ open class Styler {
switch self { switch self {
case .RegularTitleLarge, case .RegularTitleLarge,
.RegularTitleMedium, .RegularTitleMedium, .B20,
.RegularBodyLarge, .RegularBodyLarge,
.RegularBodySmall, .RegularBodySmall, .B2,
.RegularMicro, .RegularMicro, .B3:
.B2,
.B3,
.B20:
return false return false
case .Title2XLarge, case .H1,
.TitleXLarge, .Title2XLarge,
.TitleXLarge, .H32,
.H2,
.BoldTitleLarge, .BoldTitleLarge,
.BoldTitleMedium, .BoldTitleMedium,
.BoldBodyLarge,
.BoldBodySmall,
.BoldMicro,
.H1,
.H2,
.H3, .H3,
.H32, .BoldBodyLarge,
.B1: .BoldBodySmall, .B1,
.BoldMicro:
return true return true
} }
} }
@ -140,9 +130,9 @@ open class Styler {
return false return false
case .H1, case .H1,
.H32,
.H2, .H2,
.H3, .H3,
.H32,
.B1, .B1,
.B2, .B2,
.B3, .B3,