Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios into feature/Table

This commit is contained in:
Sumanth Nadigadda 2024-04-30 16:52:09 +05:30
commit a82550ed57
31 changed files with 179 additions and 114 deletions

View File

@ -1393,7 +1393,7 @@
BUILD_LIBRARY_FOR_DISTRIBUTION = YES; BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CODE_SIGN_IDENTITY = ""; CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 59; CURRENT_PROJECT_VERSION = 60;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
@ -1430,7 +1430,7 @@
BUILD_LIBRARY_FOR_DISTRIBUTION = YES; BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CODE_SIGN_IDENTITY = ""; CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 59; CURRENT_PROJECT_VERSION = 60;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;

View File

@ -35,13 +35,7 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
//-------------------------------------------------- //--------------------------------------------------
open var subscribers = Set<AnyCancellable>() open var subscribers = Set<AnyCancellable>()
open var onClickSubscriber: AnyCancellable? { open var onClickSubscriber: AnyCancellable?
willSet {
if let onClickSubscriber {
onClickSubscriber.cancel()
}
}
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
@ -118,6 +112,8 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
backgroundColor = .clear backgroundColor = .clear
surface = .light surface = .light
isEnabled = true isEnabled = true
onClick = nil
userInfo.removeAll()
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -47,13 +47,7 @@ open class SelectorBase: Control, SelectorControlable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
open var onChangeSubscriber: AnyCancellable? { open var onChangeSubscriber: AnyCancellable?
willSet {
if let onChangeSubscriber {
onChangeSubscriber.cancel()
}
}
}
open var size = CGSize(width: 20, height: 20) { didSet { setNeedsUpdate() } } open var size = CGSize(width: 20, height: 20) { didSet { setNeedsUpdate() } }
@ -120,19 +114,23 @@ open class SelectorBase: Control, SelectorControlable {
accessibilityTraits = .button accessibilityTraits = .button
} }
/// Used to make changes to the View based off a change events or from local properties.
open override func updateView() { open override func updateView() {
super.updateView() super.updateView()
setNeedsLayout() setNeedsLayout()
layoutIfNeeded() layoutIfNeeded()
} }
/// Used to update any Accessibility properties.ß /// Used to update any Accessibility properties.ß
open override func updateAccessibility() { open override func updateAccessibility() {
super.updateAccessibility() super.updateAccessibility()
accessibilityLabel = "\(Self.self)\(showError ? ", error" : "")"
} }
/// This will change the state of the Selector and execute the actionBlock if provided. /// This will change the state of the Selector and execute the actionBlock if provided.
open func toggle() { } open func toggle() { }
open override func reset() {
super.reset()
onChange = nil
}
} }

View File

@ -75,13 +75,7 @@ open class SelectorGroupBase<SelectorItemType: Control>: Control, SelectorGroup,
} }
} }
open var onChangeSubscriber: AnyCancellable? { open var onChangeSubscriber: AnyCancellable?
willSet {
if let onChangeSubscriber {
onChangeSubscriber.cancel()
}
}
}
/// Whether the Control is enabled or not. /// Whether the Control is enabled or not.
override open var isEnabled: Bool { override open var isEnabled: Bool {
@ -130,6 +124,7 @@ open class SelectorGroupBase<SelectorItemType: Control>: Control, SelectorGroup,
/// Resets to default settings. /// Resets to default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
onChange = nil
items.forEach{ $0.reset() } items.forEach{ $0.reset() }
} }
} }

View File

@ -61,13 +61,7 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
open var onChangeSubscriber: AnyCancellable? { open var onChangeSubscriber: AnyCancellable?
willSet {
if let onChangeSubscriber {
onChangeSubscriber.cancel()
}
}
}
/// Label used to render labelText. /// Label used to render labelText.
open var label = Label().with { open var label = Label().with {
@ -196,7 +190,7 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
/// Used to update any Accessibility properties. /// Used to update any Accessibility properties.
open override func updateAccessibility() { open override func updateAccessibility() {
super.updateAccessibility() super.updateAccessibility()
setAccessibilityLabel(for: [label, childLabel, errorLabel]) setAccessibilityLabel(for: [selectorView, label, childLabel, errorLabel])
} }
/// Resets to default settings. /// Resets to default settings.
@ -223,6 +217,7 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
value = nil value = nil
isSelected = false isSelected = false
onChange = nil
shouldUpdateView = true shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -344,7 +344,6 @@ open class BadgeIndicator: View {
label.isEnabled = isEnabled label.isEnabled = isEnabled
label.sizeToFit() label.sizeToFit()
setNeedsLayout() setNeedsLayout()
layoutIfNeeded()
} }
open override func updateAccessibility() { open override func updateAccessibility() {

View File

@ -84,7 +84,7 @@ final class BreadcrumbCellItem: UICollectionViewCell {
separator.textColor = textColorConfiguration.getColor(surface) separator.textColor = textColorConfiguration.getColor(surface)
separator.isHidden = hideSlash separator.isHidden = hideSlash
self.breadCrumbItem = breadCrumbItem self.breadCrumbItem = breadCrumbItem
layoutIfNeeded() setNeedsLayout()
} }
} }

View File

@ -38,13 +38,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {
/// Set of Subscribers for any Publishers for this Control. /// Set of Subscribers for any Publishers for this Control.
open var subscribers = Set<AnyCancellable>() open var subscribers = Set<AnyCancellable>()
open var onClickSubscriber: AnyCancellable? { open var onClickSubscriber: AnyCancellable?
willSet {
if let onClickSubscriber {
onClickSubscriber.cancel()
}
}
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
@ -140,6 +134,8 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {
isEnabled = true isEnabled = true
text = nil text = nil
accessibilityCustomActions = [] accessibilityCustomActions = []
onClick = nil
userInfo.removeAll()
shouldUpdateView = true shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -42,7 +42,6 @@ open class Checkbox: SelectorBase {
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
open override func setup() { open override func setup() {
super.setup() super.setup()
accessibilityLabel = "Checkbox"
backgroundColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected) backgroundColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .selected)
backgroundColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: [.selected, .highlighted]) backgroundColorConfiguration.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: [.selected, .highlighted])
@ -71,7 +70,7 @@ open class Checkbox: SelectorBase {
isSelected.toggle() isSelected.toggle()
sendActions(for: .valueChanged) sendActions(for: .valueChanged)
} }
open override func layoutSubviews() { open override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()

View File

@ -109,7 +109,8 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
public var onChangeSubscriber: AnyCancellable?
///Badge Indicator object used to render for the ButtonIcon. ///Badge Indicator object used to render for the ButtonIcon.
open var badgeIndicator = BadgeIndicator().with { open var badgeIndicator = BadgeIndicator().with {
$0.translatesAutoresizingMaskIntoConstraints = false $0.translatesAutoresizingMaskIntoConstraints = false
@ -172,8 +173,6 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
/// Used to move the icon inside the button in both x and y axis. /// Used to move the icon inside the button in both x and y axis.
open var iconOffset: CGPoint = .init(x: 0, y: 0) { didSet { setNeedsUpdate() } } open var iconOffset: CGPoint = .init(x: 0, y: 0) { didSet { setNeedsUpdate() } }
open var onChangeSubscriber: AnyCancellable?
open var inputId: String? { didSet { setNeedsUpdate() } } open var inputId: String? { didSet { setNeedsUpdate() } }
open var value: AnyHashable? { didSet { setNeedsUpdate() } } open var value: AnyHashable? { didSet { setNeedsUpdate() } }
@ -365,7 +364,6 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
open override func setup() { open override func setup() {
super.setup() super.setup()
isAccessibilityElement = false isAccessibilityElement = false
accessibilityElements = [icon, badgeIndicator]
//create a layoutGuide for the icon to key off of //create a layoutGuide for the icon to key off of
let iconLayoutGuide = UILayoutGuide() let iconLayoutGuide = UILayoutGuide()
@ -436,6 +434,7 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
showBadgeIndicator = false showBadgeIndicator = false
selectable = false selectable = false
badgeIndicatorModel = nil badgeIndicatorModel = nil
onChange = nil
shouldUpdateView = true shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }
@ -459,6 +458,18 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
setNeedsLayout() setNeedsLayout()
} }
open override func updateAccessibility() {
super.updateAccessibility()
var elements = [Any]()
if iconName != nil {
elements.append(icon)
}
if badgeIndicatorModel != nil && showBadgeIndicator {
elements.append(badgeIndicator)
}
accessibilityElements = elements.count > 0 ? elements : nil
}
open override func layoutSubviews() { open override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()

View File

@ -31,12 +31,13 @@ public struct ColorLabelAttribute: LabelAttributeModel {
} }
public func setAttribute(on attributedString: NSMutableAttributedString) { public func setAttribute(on attributedString: NSMutableAttributedString) {
guard isValidRange(on: attributedString) else { return }
var colorRange = range var colorRange = range
if length == 0 && location == 0 { if length == 0 && location == 0 {
colorRange = .init(location: location, length: attributedString.length) colorRange = .init(location: location, length: attributedString.length)
} }
if !attributedString.isValid(range: colorRange) { return }
let attributeKey = isForegroundColor ? NSAttributedString.Key.foregroundColor : NSAttributedString.Key.backgroundColor let attributeKey = isForegroundColor ? NSAttributedString.Key.foregroundColor : NSAttributedString.Key.backgroundColor
attributedString.removeAttribute(attributeKey, range: colorRange) attributedString.removeAttribute(attributeKey, range: colorRange)
attributedString.addAttribute(attributeKey, value: color, range: colorRange) attributedString.addAttribute(attributeKey, value: color, range: colorRange)

View File

@ -230,7 +230,6 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
setNeedsDisplay() setNeedsDisplay()
setNeedsLayout() setNeedsLayout()
layoutIfNeeded()
} }
open func updateAccessibility() { open func updateAccessibility() {

View File

@ -264,8 +264,6 @@ open class Notification: View {
isAccessibilityElement = false isAccessibilityElement = false
accessibilityElements = [closeButton, typeIcon, titleLabel, subTitleLabel, buttonGroup] accessibilityElements = [closeButton, typeIcon, titleLabel, subTitleLabel, buttonGroup]
closeButton.accessibilityTraits = [.button] closeButton.accessibilityTraits = [.button]
closeButton.accessibilityLabel = "Close Notification"
} }
/// Resets to default settings. /// Resets to default settings.
@ -325,7 +323,6 @@ open class Notification: View {
let iconColor = surface == .dark ? VDSColor.paletteWhite : VDSColor.paletteBlack let iconColor = surface == .dark ? VDSColor.paletteWhite : VDSColor.paletteBlack
typeIcon.name = style.iconName typeIcon.name = style.iconName
typeIcon.color = iconColor typeIcon.color = iconColor
typeIcon.accessibilityLabel = style.accessibilityText
closeButton.color = iconColor closeButton.color = iconColor
closeButton.isHidden = hideCloseButton closeButton.isHidden = hideCloseButton
} }
@ -374,6 +371,12 @@ open class Notification: View {
} }
} }
open override func updateAccessibility() {
super.updateAccessibility()
closeButton.accessibilityLabel = "Close Notification"
typeIcon.accessibilityLabel = style.accessibilityText
}
private func setConstraints() { private func setConstraints() {
labelViewAndButtonViewConstraint?.deactivate() labelViewAndButtonViewConstraint?.deactivate()
labelViewBottomConstraint?.deactivate() labelViewBottomConstraint?.deactivate()

View File

@ -174,7 +174,7 @@ open class Pagination: View {
let isNextAction = sender == nextButton let isNextAction = sender == nextButton
_selectedPageIndex = if isNextAction { _selectedPageIndex + 1 } else { _selectedPageIndex - 1 } _selectedPageIndex = if isNextAction { _selectedPageIndex + 1 } else { _selectedPageIndex - 1 }
updateSelection() updateSelection()
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
guard let self else { return } guard let self else { return }
UIAccessibility.post(notification: .announcement, argument: "Page \(self.selectedPage) of \(self.total) selected") UIAccessibility.post(notification: .announcement, argument: "Page \(self.selectedPage) of \(self.total) selected")
} }

View File

@ -78,6 +78,11 @@ open class PaginationButton: ButtonBase {
tintColor = color tintColor = color
super.updateView() super.updateView()
} }
open override func accessibilityActivate() -> Bool {
sendActions(for: .touchUpInside)
return true
}
} }
extension PaginationButton { extension PaginationButton {

View File

@ -49,6 +49,8 @@ open class RadioBoxGroup: SelectorGroupBase<RadioBoxItem>, SelectorGroupSingleSe
$0.isEnabled = !model.disabled $0.isEnabled = !model.disabled
$0.inputId = model.inputId $0.inputId = model.inputId
$0.isSelected = model.selected $0.isSelected = model.selected
$0.strikethrough = model.strikethrough
$0.strikethroughAccessibilityText = model.strikethroughAccessibileText
} }
} }
} }
@ -115,12 +117,14 @@ extension RadioBoxGroup {
/// Array of LabelAttributeModel objects used in rendering the subTextRight. /// Array of LabelAttributeModel objects used in rendering the subTextRight.
public var subTextRightAttributes: [any LabelAttributeModel]? public var subTextRightAttributes: [any LabelAttributeModel]?
public var selected: Bool public var selected: Bool
public var strikethrough: Bool = false
public var strikethroughAccessibileText: String
public init(disabled: Bool, surface: Surface = .light, inputId: String? = nil, value: AnyHashable? = nil, public init(disabled: Bool, surface: Surface = .light, inputId: String? = nil, value: AnyHashable? = nil,
text: String = "", textAttributes: [any LabelAttributeModel]? = nil, text: String = "", textAttributes: [any LabelAttributeModel]? = nil,
subText: String? = nil, subTextAttributes: [any LabelAttributeModel]? = nil, subText: String? = nil, subTextAttributes: [any LabelAttributeModel]? = nil,
subTextRight: String? = nil, subTextRightAttributes: [any LabelAttributeModel]? = nil, subTextRight: String? = nil, subTextRightAttributes: [any LabelAttributeModel]? = nil,
selected: Bool = false, errorText: String? = nil, accessibileText: String? = nil) { selected: Bool = false, errorText: String? = nil, accessibileText: String? = nil, strikethrough: Bool = false, strikethroughAccessibileText: String = "not available") {
self.disabled = disabled self.disabled = disabled
self.surface = surface self.surface = surface
self.inputId = inputId self.inputId = inputId
@ -133,6 +137,8 @@ extension RadioBoxGroup {
self.subTextRightAttributes = subTextRightAttributes self.subTextRightAttributes = subTextRightAttributes
self.selected = selected self.selected = selected
self.accessibileText = accessibileText self.accessibileText = accessibileText
self.strikethrough = strikethrough
self.strikethroughAccessibileText = strikethroughAccessibileText
} }
public init() { public init() {

View File

@ -38,7 +38,7 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
$0.alignment = .top $0.alignment = .top
$0.distribution = .fill $0.distribution = .fill
$0.axis = .horizontal $0.axis = .horizontal
$0.spacing = 12 $0.spacing = VDSLayout.space3X
} }
private var selectorLeftLabelStackView = UIStackView().with { private var selectorLeftLabelStackView = UIStackView().with {
@ -52,13 +52,7 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
open var onChangeSubscriber: AnyCancellable? { open var onChangeSubscriber: AnyCancellable?
willSet {
if let onChangeSubscriber {
onChangeSubscriber.cancel()
}
}
}
/// Label used to render the text. /// Label used to render the text.
open var textLabel = Label().with { open var textLabel = Label().with {
@ -129,6 +123,8 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
/// If provided, the radio box will be rendered to show the option with a strikethrough. /// If provided, the radio box will be rendered to show the option with a strikethrough.
open var strikethrough: Bool = false { didSet { setNeedsUpdate() } } open var strikethrough: Bool = false { didSet { setNeedsUpdate() } }
open var strikethroughAccessibilityText: String = "not available" { didSet { setNeedsUpdate() } }
open var inputId: String? { didSet { setNeedsUpdate() } } open var inputId: String? { didSet { setNeedsUpdate() } }
open var value: AnyHashable? { didSet { setNeedsUpdate() } } open var value: AnyHashable? { didSet { setNeedsUpdate() } }
@ -189,7 +185,7 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
.pinTrailing(0, .defaultHigh) .pinTrailing(0, .defaultHigh)
.pinBottom(0, .defaultHigh) .pinBottom(0, .defaultHigh)
selectorStackView.pinToSuperView(.uniform(16)) selectorStackView.pinToSuperView(.uniform(VDSLayout.space3X))
} }
/// Resets to default settings. /// Resets to default settings.
@ -218,6 +214,7 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
value = nil value = nil
isSelected = false isSelected = false
onChange = nil
shouldUpdateView = true shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
@ -236,15 +233,13 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
updateLabels() updateLabels()
setNeedsLayout() setNeedsLayout()
layoutIfNeeded()
} }
/// Used to update any Accessibility properties. /// Used to update any Accessibility properties.
open override func updateAccessibility() { open override func updateAccessibility() {
super.updateAccessibility() super.updateAccessibility()
if accessibilityLabel == nil { setAccessibilityLabel(for: [textLabel, subTextLabel, subTextRightLabel])
setAccessibilityLabel(for: [textLabel, subTextLabel, subTextRightLabel]) accessibilityValue = strikethrough ? strikethroughAccessibilityText : nil
}
} }
//-------------------------------------------------- //--------------------------------------------------

View File

@ -172,7 +172,6 @@ extension Tabs {
label.textAlignment = textAlignment.value label.textAlignment = textAlignment.value
label.textColorConfiguration = textColorConfiguration.eraseToAnyColorable() label.textColorConfiguration = textColorConfiguration.eraseToAnyColorable()
setNeedsLayout() setNeedsLayout()
layoutIfNeeded()
} }
/// Used to update any Accessibility properties. /// Used to update any Accessibility properties.

View File

@ -119,13 +119,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
open var onChangeSubscriber: AnyCancellable? { open var onChangeSubscriber: AnyCancellable?
willSet {
if let onChangeSubscriber {
onChangeSubscriber.cancel()
}
}
}
open var titleLabel = Label().with { open var titleLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
@ -135,6 +129,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
open var errorLabel = Label().with { open var errorLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.textStyle = .bodySmall $0.textStyle = .bodySmall
$0.accessibilityValue = "error"
} }
open var helperLabel = Label().with { open var helperLabel = Label().with {
@ -304,6 +299,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
defaultValue = nil defaultValue = nil
required = false required = false
readOnly = false readOnly = false
onChange = nil
} }
/// Used to make changes to the View based off a change events or from local properties. /// Used to make changes to the View based off a change events or from local properties.

View File

@ -151,7 +151,8 @@ open class InputField: EntryFieldBase, UITextFieldDelegate {
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
open override func setup() { open override func setup() {
super.setup() super.setup()
isAccessibilityElement = false
minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 0) minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 0)
minWidthConstraint?.isActive = true minWidthConstraint?.isActive = true
@ -272,6 +273,25 @@ open class InputField: EntryFieldBase, UITextFieldDelegate {
} }
} }
/// Used to update any Accessibility properties.
open override func updateAccessibility() {
super.updateAccessibility()
textField.accessibilityLabel = showError ? "error" : nil
if showError {
accessibilityElements = [titleLabel, textField, icon, errorLabel, helperLabel]
} else {
accessibilityElements = [titleLabel, textField, helperLabel]
}
}
open override var canBecomeFirstResponder: Bool { true }
open override func resignFirstResponder() -> Bool {
if textField.isFirstResponder {
textField.resignFirstResponder()
}
return super.resignFirstResponder()
}
} }
extension InputField.FieldType { extension InputField.FieldType {

View File

@ -239,6 +239,7 @@ open class TextArea: EntryFieldBase {
/// Used to update any Accessibility properties. /// Used to update any Accessibility properties.
open override func updateAccessibility() { open override func updateAccessibility() {
super.updateAccessibility() super.updateAccessibility()
textView.accessibilityLabel = showError ? "error" : nil
if showError { if showError {
accessibilityElements = [titleLabel, textView, icon, errorLabel, helperLabel] accessibilityElements = [titleLabel, textView, icon, errorLabel, helperLabel]
} else { } else {
@ -246,6 +247,15 @@ open class TextArea: EntryFieldBase {
} }
} }
open override var canBecomeFirstResponder: Bool { true }
open override func resignFirstResponder() -> Bool {
if textView.isFirstResponder {
textView.resignFirstResponder()
}
return super.resignFirstResponder()
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Methods // MARK: - Private Methods
//-------------------------------------------------- //--------------------------------------------------

View File

@ -147,5 +147,6 @@ open class TextView: UITextView, ViewProtocol {
attributedText = nil attributedText = nil
} }
} }
} }

View File

@ -8,6 +8,7 @@
import Foundation import Foundation
import VDSTokens import VDSTokens
import UIKit import UIKit
import Combine
@objc(VDSTileContainer) @objc(VDSTileContainer)
open class TileContainer: TileContainerBase<TileContainer.Padding> { open class TileContainer: TileContainerBase<TileContainer.Padding> {
@ -43,7 +44,6 @@ open class TileContainer: TileContainerBase<TileContainer.Padding> {
} }
open class TileContainerBase<PaddingType: DefaultValuing>: Control where PaddingType.ValueType == CGFloat { open class TileContainerBase<PaddingType: DefaultValuing>: Control where PaddingType.ValueType == CGFloat {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
@ -111,6 +111,19 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
public override var onClickSubscriber: AnyCancellable? {
didSet {
if onClickSubscriber != nil {
isAccessibilityElement = true
accessibilityTraits = .link
accessibilityHint = "Double tap to open."
} else {
isAccessibilityElement = false
accessibilityTraits.remove(.link)
}
}
}
/// This takes an image source url and applies it as a background image. /// This takes an image source url and applies it as a background image.
open var backgroundImage: UIImage? { didSet { setNeedsUpdate() } } open var backgroundImage: UIImage? { didSet { setNeedsUpdate() } }
@ -171,7 +184,7 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
open var showBorder: Bool = false { didSet { setNeedsUpdate() } } open var showBorder: Bool = false { didSet { setNeedsUpdate() } }
/// Determines if there is a drop shadow or not. /// Determines if there is a drop shadow or not.
open var showDropShadows: Bool = false { didSet { setNeedsUpdate() } } open var showDropShadow: Bool = false { didSet { setNeedsUpdate() } }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Constraints // MARK: - Constraints
@ -274,7 +287,7 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
width = nil width = nil
height = nil height = nil
showBorder = false showBorder = false
showDropShadows = false showDropShadow = false
shouldUpdateView = true shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }
@ -316,12 +329,14 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
widthConstraint?.isActive = false widthConstraint?.isActive = false
heightConstraint?.isActive = false heightConstraint?.isActive = false
} }
if showDropShadows, surface == .light {
applyBackgroundEffects()
if showDropShadow, surface == .light {
addDropShadow(dropShadowConfiguration) addDropShadow(dropShadowConfiguration)
} else { } else {
removeDropShadows() removeDropShadows()
} }
applyBackgroundEffects()
} }
/// Used to update frames for the added CAlayers to our view /// Used to update frames for the added CAlayers to our view
@ -337,6 +352,7 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
/// This will place a view within the contentView of this component. /// This will place a view within the contentView of this component.
public func addContentView(_ view: UIView, shouldPin: Bool = true) { public func addContentView(_ view: UIView, shouldPin: Bool = true) {
view.removeFromSuperview()
containerView.addSubview(view) containerView.addSubview(view)
if shouldPin { if shouldPin {
view.pinToSuperView() view.pinToSuperView()

View File

@ -22,7 +22,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
public enum Padding: String, DefaultValuing, CaseIterable { public enum Padding: String, DefaultValuing, CaseIterable {
case small case small
case large case large
public static var defaultValue: Self { .large } public static var defaultValue: Self { .large }
public var value: CGFloat { public var value: CGFloat {
@ -63,7 +63,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
public required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Enums // MARK: - Enums
//-------------------------------------------------- //--------------------------------------------------
@ -105,12 +105,6 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
public override var onClickSubscriber: AnyCancellable? {
didSet {
isAccessibilityElement = onClickSubscriber != nil
}
}
/// Title lockup positioned in the contentView. /// Title lockup positioned in the contentView.
open var titleLockup = TitleLockup().with { open var titleLockup = TitleLockup().with {
$0.standardStyleConfiguration = .init(styleConfigurations: [ $0.standardStyleConfiguration = .init(styleConfigurations: [
@ -213,7 +207,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
open var directionalIcon = Icon().with { open var directionalIcon = Icon().with {
$0.name = .rightArrow $0.name = .rightArrow
} }
private var _textWidth: TextWidth? private var _textWidth: TextWidth?
/// If provided, width of Button components will be rendered based on this value. If omitted, default button widths are rendered. /// If provided, width of Button components will be rendered based on this value. If omitted, default button widths are rendered.
@ -276,7 +270,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
_descriptiveIconModel = nil _descriptiveIconModel = nil
setNeedsUpdate() setNeedsUpdate()
} }
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Constraints // MARK: - Constraints
@ -294,7 +288,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
internal var titleLockupEyebrowLabelHeightGreaterThanConstraint: NSLayoutConstraint? internal var titleLockupEyebrowLabelHeightGreaterThanConstraint: NSLayoutConstraint?
internal var titleLockupTitleLabelHeightGreaterThanConstraint: NSLayoutConstraint? internal var titleLockupTitleLabelHeightGreaterThanConstraint: NSLayoutConstraint?
internal var titleLockupSubTitleLabelHeightGreaterThanConstraint: NSLayoutConstraint? internal var titleLockupSubTitleLabelHeightGreaterThanConstraint: NSLayoutConstraint?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
@ -305,9 +299,6 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
color = .black color = .black
addContentView(stackView) addContentView(stackView)
accessibilityTraits = .link
accessibilityElements = [badge, titleLockup, descriptiveIcon, directionalIcon]
//badge //badge
badgeContainerView.addSubview(badge) badgeContainerView.addSubview(badge)
badge badge
@ -328,7 +319,7 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
titleLockupBottomGreaterThanConstraint = titleLockupContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: titleLockup.bottomAnchor) titleLockupBottomGreaterThanConstraint = titleLockupContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: titleLockup.bottomAnchor)
titleLockupTopGreaterThanConstraint = titleLockup.topAnchor.constraint(greaterThanOrEqualTo: titleLockupContainerView.topAnchor) titleLockupTopGreaterThanConstraint = titleLockup.topAnchor.constraint(greaterThanOrEqualTo: titleLockupContainerView.topAnchor)
titleLockupCenterYConstraint = titleLockup.centerYAnchor.constraint(equalTo: titleLockupContainerView.centerYAnchor) titleLockupCenterYConstraint = titleLockup.centerYAnchor.constraint(equalTo: titleLockupContainerView.centerYAnchor)
iconContainerView.addSubview(descriptiveIcon) iconContainerView.addSubview(descriptiveIcon)
iconContainerView.addSubview(directionalIcon) iconContainerView.addSubview(directionalIcon)
@ -357,12 +348,12 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
badge.label.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-1), for: .vertical) badge.label.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-1), for: .vertical)
titleLockup.subTitleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-2), for: .vertical) titleLockup.subTitleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-2), for: .vertical)
titleLockup.eyebrowLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-3), for: .vertical) titleLockup.eyebrowLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-3), for: .vertical)
titleLockup.titleLabel.setContentHuggingPriority(UILayoutPriority(labelPriority), for: .vertical) titleLockup.titleLabel.setContentHuggingPriority(UILayoutPriority(labelPriority), for: .vertical)
badge.label.setContentHuggingPriority(UILayoutPriority(labelPriority-1), for: .vertical) badge.label.setContentHuggingPriority(UILayoutPriority(labelPriority-1), for: .vertical)
titleLockup.subTitleLabel.setContentHuggingPriority(UILayoutPriority(labelPriority-2), for: .vertical) titleLockup.subTitleLabel.setContentHuggingPriority(UILayoutPriority(labelPriority-2), for: .vertical)
titleLockup.eyebrowLabel.setContentHuggingPriority(UILayoutPriority(labelPriority-3), for: .vertical) titleLockup.eyebrowLabel.setContentHuggingPriority(UILayoutPriority(labelPriority-3), for: .vertical)
/** /**
Added these constraints for: Added these constraints for:
At fixed width & height if all the labels(Badge, Eyebrow, Title, Subtitle) are having more number of lines then we should display atleast one line of content per label instead of pushing labels out of bounds. At fixed width & height if all the labels(Badge, Eyebrow, Title, Subtitle) are having more number of lines then we should display atleast one line of content per label instead of pushing labels out of bounds.
@ -412,13 +403,27 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
if width != nil && (aspectRatio != .none || height != nil) { if width != nil && (aspectRatio != .none || height != nil) {
updateTextPositionAlignment() updateTextPositionAlignment()
} }
layoutIfNeeded() setNeedsLayout()
} }
/// Used to update any Accessibility properties. /// Used to update any Accessibility properties.
open override func updateAccessibility() { open override func updateAccessibility() {
super.updateAccessibility() super.updateAccessibility()
var elements = [Any]()
if badgeModel != nil {
elements.append(badge)
}
if titleModel != nil || subTitleModel != nil || eyebrowModel != nil {
elements.append(titleLockup)
}
if descriptiveIconModel != nil {
elements.append(descriptiveIcon)
}
if directionalIconModel != nil {
elements.append(directionalIcon)
}
accessibilityElements = elements.count > 0 ? elements : nil
setAccessibilityLabel(for: [badge.label, titleLockup.eyebrowLabel, titleLockup.titleLabel, titleLockup.subTitleLabel]) setAccessibilityLabel(for: [badge.label, titleLockup.eyebrowLabel, titleLockup.titleLabel, titleLockup.subTitleLabel])
} }

View File

@ -272,7 +272,21 @@ open class TitleLockup: View {
titleLabel.textColorConfiguration = textColorPrimaryConfiguration titleLabel.textColorConfiguration = textColorPrimaryConfiguration
accessibilityElements = [eyebrowLabel, titleLabel, subTitleLabel] }
open override func updateAccessibility() {
super.updateAccessibility()
var elements = [Any]()
if eyebrowModel != nil {
elements.append(eyebrowLabel)
}
if titleModel != nil {
elements.append(titleLabel)
}
if subTitleModel != nil {
elements.append(subTitleLabel)
}
accessibilityElements = elements.count > 0 ? elements : nil
} }
/// Resets to default settings. /// Resets to default settings.

View File

@ -88,13 +88,7 @@ open class Toggle: Control, Changeable, FormFieldable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
open var onChangeSubscriber: AnyCancellable? { open var onChangeSubscriber: AnyCancellable?
willSet {
if let onChangeSubscriber {
onChangeSubscriber.cancel()
}
}
}
/// Actual toggle used in this component. /// Actual toggle used in this component.
open var toggleView = ToggleView().with { open var toggleView = ToggleView().with {
@ -231,6 +225,7 @@ open class Toggle: Control, Changeable, FormFieldable {
textPosition = .left textPosition = .left
inputId = nil inputId = nil
value = nil value = nil
onChange = nil
shouldUpdateView = true shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -166,6 +166,7 @@ open class ToggleView: Control, Changeable, FormFieldable {
value = nil value = nil
toggleView.backgroundColor = toggleColorConfiguration.getColor(self) toggleView.backgroundColor = toggleColorConfiguration.getColor(self)
knobView.backgroundColor = knobColorConfiguration.getColor(self) knobView.backgroundColor = knobColorConfiguration.getColor(self)
onChange = nil
shouldUpdateView = true shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }
@ -227,7 +228,6 @@ open class ToggleView: Control, Changeable, FormFieldable {
knobTrailingConstraint?.isActive = true knobTrailingConstraint?.isActive = true
knobLeadingConstraint?.isActive = true knobLeadingConstraint?.isActive = true
setNeedsLayout() setNeedsLayout()
layoutIfNeeded()
} }
private func updateToggle() { private func updateToggle() {

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
extension DispatchQueue { extension DispatchQueue {
private static var _onceTracker = [String]() private static var _onceTracker = Set<String>()
public class func once( public class func once(
file: String = #file, file: String = #file,
@ -31,12 +31,17 @@ extension DispatchQueue {
token: String, token: String,
block: () -> Void block: () -> Void
) { ) {
// Peek ahead to avoid the intersection.
guard !_onceTracker.contains(token) else { return }
objc_sync_enter(self) objc_sync_enter(self)
defer { objc_sync_exit(self) } defer { objc_sync_exit(self) }
// Double check we are the first in the critical section.
guard !_onceTracker.contains(token) else { return } guard !_onceTracker.contains(token) else { return }
_onceTracker.append(token) // Execute.
_onceTracker.insert(token)
block() block()
} }
} }

View File

@ -26,6 +26,8 @@ extension Changeable {
.sink { c in .sink { c in
newValue(c) newValue(c)
} }
} else {
onChangeSubscriber = nil
} }
} }
} }

View File

@ -20,6 +20,7 @@ extension Clickable {
public var onClick: ((Self) -> ())? { public var onClick: ((Self) -> ())? {
get { return nil } get { return nil }
set { set {
onClickSubscriber?.cancel()
if let newValue { if let newValue {
onClickSubscriber = publisher(for: .touchUpInside) onClickSubscriber = publisher(for: .touchUpInside)
.sink { [weak self] c in .sink { [weak self] c in
@ -27,7 +28,6 @@ extension Clickable {
newValue(c) newValue(c)
} }
} else { } else {
onClickSubscriber?.cancel()
onClickSubscriber = nil onClickSubscriber = nil
} }
} }

View File

@ -3,6 +3,10 @@
- CXTDT-544442 - Button Icon - Selected state needs to allow custom color - CXTDT-544442 - Button Icon - Selected state needs to allow custom color
- CXTDT-546821 - TextArea - Accessibility - input field is not receiving swipe focus - CXTDT-546821 - TextArea - Accessibility - input field is not receiving swipe focus
- CXTDT-547200 - Carousel scrollbar Accessibility - The scrollbar is receiving right/ left swipe focus - CXTDT-547200 - Carousel scrollbar Accessibility - The scrollbar is receiving right/ left swipe focus
- CXTDT-549888 - Pagination - Accessibility - Next/Previous
- CXTDT-542333 - RadioBox Padding
- CXTDT-549901 - RadioBox strike through state
- CXTDT-546824 - Notification - Accessibility - Improper label for close notification button & icon
1.0.59 1.0.59
---------------- ----------------