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,7 +114,6 @@ 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()
@ -130,9 +123,14 @@ open class SelectorBase: Control, SelectorControlable {
/// 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])

View File

@ -109,6 +109,7 @@ 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 {
@ -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,6 +151,7 @@ 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

@ -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: [
@ -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
@ -412,12 +403,26 @@ 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
---------------- ----------------