Merge branch 'mbruce/bugfixes' into 'develop'
fixed bug for trailing tooltip See merge request BPHV_MIPS/vds_ios!100
This commit is contained in:
commit
9c358c39ce
@ -128,12 +128,23 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
|
||||
|
||||
/// Update this view based off of property changes
|
||||
open func updateView() {
|
||||
updateAccessibilityLabel()
|
||||
updateAccessibility()
|
||||
}
|
||||
|
||||
/// Used to update any Accessibility properties
|
||||
open func updateAccessibilityLabel() {
|
||||
open func updateAccessibility() {
|
||||
if isSelected {
|
||||
accessibilityTraits.insert(.selected)
|
||||
} else {
|
||||
accessibilityTraits.remove(.selected)
|
||||
}
|
||||
|
||||
if isEnabled {
|
||||
accessibilityTraits.remove(.notEnabled)
|
||||
} else {
|
||||
accessibilityTraits.insert(.notEnabled)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Resets to the Controls default values
|
||||
|
||||
@ -88,18 +88,7 @@ open class SelectorBase: Control, SelectorControlable {
|
||||
layoutIfNeeded()
|
||||
}
|
||||
|
||||
open override func updateAccessibilityLabel() {
|
||||
accessibilityValue = isSelected ? "1" : "0"
|
||||
if !accessibilityTraits.contains(.selected) && isSelected {
|
||||
accessibilityTraits.insert(.selected)
|
||||
} else if accessibilityTraits.contains(.selected) && !isSelected{
|
||||
accessibilityTraits.remove(.selected)
|
||||
}
|
||||
|
||||
if !accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.insert(.notEnabled)
|
||||
} else if accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.remove(.notEnabled)
|
||||
}
|
||||
open override func updateAccessibility() {
|
||||
super.updateAccessibility()
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,18 +69,8 @@ open class SelectorGroupHandlerBase<HandlerType: Control>: Control, Changeable {
|
||||
selectorViews.forEach{ $0.reset() }
|
||||
}
|
||||
|
||||
open override func updateAccessibilityLabel() {
|
||||
if !accessibilityTraits.contains(.selected) && isSelected {
|
||||
accessibilityTraits.insert(.selected)
|
||||
} else if accessibilityTraits.contains(.selected) && !isSelected{
|
||||
accessibilityTraits.remove(.selected)
|
||||
}
|
||||
|
||||
if !accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.insert(.notEnabled)
|
||||
} else if accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.remove(.notEnabled)
|
||||
}
|
||||
open override func updateAccessibility() {
|
||||
super.updateAccessibility()
|
||||
setAccessibilityLabel(for: selectorViews)
|
||||
}
|
||||
}
|
||||
@ -92,8 +82,8 @@ open class SelectorGroupSelectedHandlerBase<HandlerType: Control>: SelectorGroup
|
||||
return selectorViews.filter { $0.isSelected == true }.first
|
||||
}
|
||||
|
||||
open override func updateAccessibilityLabel() {
|
||||
super.updateAccessibilityLabel()
|
||||
open override func updateAccessibility() {
|
||||
super.updateAccessibility()
|
||||
if let selectedHandler, let value = selectedHandler.accessibilityValue, let label = selectedHandler.accessibilityLabel {
|
||||
accessibilityValue = "\(label) \(value)"
|
||||
} else {
|
||||
|
||||
@ -267,22 +267,11 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
|
||||
selectorView.isHighlighted = isHighlighted
|
||||
selectorView.disabled = disabled
|
||||
selectorView.surface = surface
|
||||
updateAccessibilityLabel()
|
||||
updateAccessibility()
|
||||
}
|
||||
|
||||
open override func updateAccessibilityLabel() {
|
||||
accessibilityValue = isSelected ? "1" : "0"
|
||||
if !accessibilityTraits.contains(.selected) && isSelected {
|
||||
accessibilityTraits.insert(.selected)
|
||||
} else if accessibilityTraits.contains(.selected) && !isSelected{
|
||||
accessibilityTraits.remove(.selected)
|
||||
}
|
||||
|
||||
if !accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.insert(.notEnabled)
|
||||
} else if accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.remove(.notEnabled)
|
||||
}
|
||||
open override func updateAccessibility() {
|
||||
super.updateAccessibility()
|
||||
setAccessibilityLabel(for: [label, childLabel, errorLabel])
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,12 +82,16 @@ open class View: UIView, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
||||
|
||||
/// Update this view based off of property changes
|
||||
open func updateView() {
|
||||
updateAccessibilityLabel()
|
||||
updateAccessibility()
|
||||
}
|
||||
|
||||
/// Used to update any Accessibility properties
|
||||
open func updateAccessibilityLabel() {
|
||||
|
||||
open func updateAccessibility() {
|
||||
if isEnabled {
|
||||
accessibilityTraits.remove(.notEnabled)
|
||||
} else {
|
||||
accessibilityTraits.insert(.notEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets to the Views default values
|
||||
|
||||
@ -158,14 +158,14 @@ open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettab
|
||||
|
||||
open func updateView() {
|
||||
updateLabel()
|
||||
updateAccessibilityLabel()
|
||||
updateAccessibility()
|
||||
}
|
||||
|
||||
open func updateAccessibilityLabel() {
|
||||
if !accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.insert(.notEnabled)
|
||||
} else if accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
open func updateAccessibility() {
|
||||
if isEnabled {
|
||||
accessibilityTraits.remove(.notEnabled)
|
||||
} else {
|
||||
accessibilityTraits.insert(.notEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -38,13 +38,14 @@ open class CheckboxGroup: SelectorGroupHandlerBase<CheckboxItem> {
|
||||
public var selectorModels: [CheckboxModel]? {
|
||||
didSet {
|
||||
if let selectorModels {
|
||||
selectorViews = selectorModels.map { model in
|
||||
selectorViews = selectorModels.enumerated().map { index, model in
|
||||
return CheckboxItem().with {
|
||||
$0.disabled = model.disabled
|
||||
$0.surface = model.surface
|
||||
$0.inputId = model.inputId
|
||||
$0.value = model.value
|
||||
$0.accessibilityLabel = model.accessibileText
|
||||
$0.accessibilityValue = "item \(index+1) of \(selectorModels.count)"
|
||||
$0.labelText = model.labelText
|
||||
$0.labelTextAttributes = model.labelTextAttributes
|
||||
$0.childText = model.childText
|
||||
|
||||
@ -102,11 +102,7 @@ open class Icon: View {
|
||||
imageView.image = nil
|
||||
}
|
||||
}
|
||||
|
||||
public override func updateAccessibilityLabel() {
|
||||
|
||||
}
|
||||
|
||||
private func getImage(for imageName: String) -> UIImage? {
|
||||
|
||||
return BundleManager.shared.image(for: imageName)
|
||||
|
||||
@ -23,6 +23,7 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable
|
||||
public var title: String?
|
||||
public var content: String?
|
||||
public var contentView: UIView?
|
||||
public var presenter: UIView?
|
||||
|
||||
public func setAttribute(on attributedString: NSMutableAttributedString) {
|
||||
//update the location
|
||||
@ -66,7 +67,7 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable
|
||||
addHandler(on: attributedString)
|
||||
}
|
||||
|
||||
public init(id: UUID = UUID(), action: PassthroughSubject<Void, Never> = PassthroughSubject<Void, Never>(), subscriber: AnyCancellable? = nil, surface: Surface, accessibleText: String? = nil, closeButtonText: String = "Close", title: String? = nil, content: String? = nil, contentView: UIView? = nil) {
|
||||
public init(id: UUID = UUID(), action: PassthroughSubject<Void, Never> = PassthroughSubject<Void, Never>(), subscriber: AnyCancellable? = nil, surface: Surface, accessibleText: String? = nil, closeButtonText: String = "Close", title: String? = nil, content: String? = nil, contentView: UIView? = nil, presenter: UIView? = nil) {
|
||||
self.id = id
|
||||
self.action = action
|
||||
self.subscriber = subscriber
|
||||
@ -76,6 +77,8 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable
|
||||
self.title = title
|
||||
self.content = content
|
||||
self.contentView = contentView
|
||||
self.presenter = presenter
|
||||
|
||||
//create the tooltip click event
|
||||
self.subscriber = action.sink { [weak self] in
|
||||
guard let self else { return }
|
||||
@ -83,7 +86,8 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable
|
||||
title: self.title,
|
||||
content: self.content,
|
||||
contentView: contentView,
|
||||
closeButtonText: self.closeButtonText)
|
||||
closeButtonText: self.closeButtonText,
|
||||
presenter: self.presenter)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -66,7 +66,7 @@ open class Label: UILabel, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
||||
public var textColorConfiguration: AnyColorable = ViewColorConfiguration().with {
|
||||
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true)
|
||||
$0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false)
|
||||
}.eraseToAnyColorable()
|
||||
}.eraseToAnyColorable(){ didSet { setNeedsUpdate() }}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
@ -156,7 +156,7 @@ open class Label: UILabel, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
||||
attributedText = mutableText
|
||||
|
||||
//get accessibility
|
||||
updateAccessibilityLabel()
|
||||
updateAccessibility()
|
||||
|
||||
//force a drawText
|
||||
setNeedsDisplay()
|
||||
@ -164,7 +164,7 @@ open class Label: UILabel, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
||||
}
|
||||
}
|
||||
|
||||
open func updateAccessibilityLabel() {
|
||||
open func updateAccessibility() {
|
||||
accessibilityLabel = text
|
||||
}
|
||||
|
||||
@ -222,6 +222,8 @@ open class Label: UILabel, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
||||
|
||||
private var actions: [LabelAction] = [] {
|
||||
didSet {
|
||||
isUserInteractionEnabled = !actions.isEmpty
|
||||
accessibilityTraits = !actions.isEmpty ? .link : .staticText
|
||||
if actions.isEmpty {
|
||||
tapGesture = nil
|
||||
} else {
|
||||
@ -264,7 +266,6 @@ open class Label: UILabel, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
||||
let actionText = accessibleText ?? NSString(string:text).substring(with: range)
|
||||
let accessibleAction = UIAccessibilityCustomAction(name: actionText, target: self, selector: #selector(accessibilityCustomAction(_:)))
|
||||
accessibilityCustomActions?.append(accessibleAction)
|
||||
|
||||
return accessibleAction
|
||||
}
|
||||
|
||||
@ -294,4 +295,3 @@ open class Label: UILabel, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -33,9 +33,10 @@ open class RadioBoxGroup: SelectorGroupSelectedHandlerBase<RadioBoxItem> {
|
||||
public var selectorModels: [RadioBoxModel]? {
|
||||
didSet {
|
||||
if let selectorModels {
|
||||
selectorViews = selectorModels.map { model in
|
||||
selectorViews = selectorModels.enumerated().map { index, model in
|
||||
return RadioBoxItem().with {
|
||||
$0.accessibilityLabel = model.accessibileText
|
||||
$0.accessibilityValue = "item \(index+1) of \(selectorModels.count)"
|
||||
$0.text = model.text
|
||||
$0.textAttributes = model.textAttributes
|
||||
$0.subText = model.subText
|
||||
|
||||
@ -243,24 +243,13 @@ open class RadioBoxItem: Control, Changeable {
|
||||
//--------------------------------------------------
|
||||
open override func updateView() {
|
||||
updateLabels()
|
||||
updateAccessibilityLabel()
|
||||
updateAccessibility()
|
||||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
}
|
||||
|
||||
open override func updateAccessibilityLabel() {
|
||||
accessibilityValue = isSelected ? "1" : "0"
|
||||
if !accessibilityTraits.contains(.selected) && isSelected {
|
||||
accessibilityTraits.insert(.selected)
|
||||
} else if accessibilityTraits.contains(.selected) && !isSelected{
|
||||
accessibilityTraits.remove(.selected)
|
||||
}
|
||||
|
||||
if !accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.insert(.notEnabled)
|
||||
} else if accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.remove(.notEnabled)
|
||||
}
|
||||
open override func updateAccessibility() {
|
||||
super.updateAccessibility()
|
||||
if accessibilityLabel == nil {
|
||||
setAccessibilityLabel(for: [textLabel, subTextLabel, subTextRightLabel])
|
||||
}
|
||||
|
||||
@ -33,13 +33,14 @@ open class RadioButtonGroup: SelectorGroupSelectedHandlerBase<RadioButtonItem> {
|
||||
public var selectorModels: [RadioButtonModel]? {
|
||||
didSet {
|
||||
if let selectorModels {
|
||||
selectorViews = selectorModels.map { model in
|
||||
selectorViews = selectorModels.enumerated().map { index, model in
|
||||
return RadioButtonItem().with {
|
||||
$0.disabled = model.disabled
|
||||
$0.surface = model.surface
|
||||
$0.inputId = model.inputId
|
||||
$0.value = model.value
|
||||
$0.accessibilityLabel = model.accessibileText
|
||||
$0.accessibilityValue = "item \(index+1) of \(selectorModels.count)"
|
||||
$0.labelText = model.labelText
|
||||
$0.labelTextAttributes = model.labelTextAttributes
|
||||
$0.childText = model.childText
|
||||
|
||||
@ -117,7 +117,7 @@ open class RadioSwatch: Control {
|
||||
layer.setNeedsDisplay()
|
||||
}
|
||||
|
||||
public override func updateAccessibilityLabel() {
|
||||
public override func updateAccessibility() {
|
||||
accessibilityLabel = text
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ import Combine
|
||||
extension Tabs {
|
||||
|
||||
@objc(VDSTab)
|
||||
open class Tab: View {
|
||||
open class Tab: Control {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
@ -20,7 +20,7 @@ extension Tabs {
|
||||
open var index: Int = 0
|
||||
|
||||
///label to write out the text
|
||||
open var label: Label = Label()
|
||||
open var label: Label = Label().with { $0.isUserInteractionEnabled = false }
|
||||
|
||||
///orientation of the tabs
|
||||
open var orientation: Tabs.Orientation = .horizontal { didSet { setNeedsUpdate() } }
|
||||
@ -33,16 +33,10 @@ extension Tabs {
|
||||
|
||||
///Sets the Position of the Selected/Hover Border Accent for All Tabs.
|
||||
open var indicatorPosition: Tabs.IndicatorPosition = .bottom { didSet { setNeedsUpdate() } }
|
||||
|
||||
///An optional callback that is called when this Tab is clicked. Passes parameters (tabIndex).
|
||||
open var onClick: ((Int) -> Void)? { didSet { setNeedsUpdate() } }
|
||||
|
||||
|
||||
///If provided, it will set fixed width for this Tab.
|
||||
open var width: CGFloat? { didSet { setNeedsUpdate() } }
|
||||
|
||||
///If provided, it will set this Tab to the Active Tab on render.
|
||||
open var selected: Bool = false { didSet { setNeedsUpdate() } }
|
||||
|
||||
|
||||
///The text label of the tab.
|
||||
open var text: String = "" { didSet { setNeedsUpdate() } }
|
||||
|
||||
@ -71,7 +65,7 @@ extension Tabs {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Configuration
|
||||
//--------------------------------------------------
|
||||
private var textColorConfiguration: SurfaceColorConfiguration { selected ? textColorSelectedConfiguration : textColorNonSelectedConfiguration }
|
||||
private var textColorConfiguration: SurfaceColorConfiguration { isSelected ? textColorSelectedConfiguration : textColorNonSelectedConfiguration }
|
||||
private var textColorNonSelectedConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight , VDSColor.elementsSecondaryOnlight)
|
||||
private var textColorSelectedConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark)
|
||||
private var indicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteRed, VDSColor.elementsPrimaryOndark)
|
||||
@ -125,7 +119,8 @@ extension Tabs {
|
||||
super.setup()
|
||||
addSubview(label)
|
||||
accessibilityTraits = .button
|
||||
|
||||
isAccessibilityElement = true
|
||||
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.pinTrailing()
|
||||
|
||||
@ -149,23 +144,18 @@ extension Tabs {
|
||||
layoutGuide.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
layoutGuide.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
layoutGuide.trailingAnchor.constraint(equalTo: trailingAnchor)])
|
||||
|
||||
publisher(for: UITapGestureRecognizer())
|
||||
.sink { [weak self] _ in
|
||||
guard let self else { return }
|
||||
self.onClick?(self.index)
|
||||
}.store(in: &subscribers)
|
||||
|
||||
}
|
||||
|
||||
open override func updateView() {
|
||||
super.updateView()
|
||||
|
||||
guard !text.isEmpty else { return }
|
||||
|
||||
//label properties
|
||||
label.text = text
|
||||
label.textStyle = textStyle
|
||||
label.textPosition = textPosition
|
||||
label.textColor = textColorConfiguration.getColor(self)
|
||||
label.textColorConfiguration = textColorConfiguration.eraseToAnyColorable()
|
||||
|
||||
//constaints
|
||||
labelWidthConstraint?.isActive = false
|
||||
@ -178,12 +168,17 @@ extension Tabs {
|
||||
setNeedsLayout()
|
||||
}
|
||||
|
||||
open override func updateAccessibility() {
|
||||
super.updateAccessibility()
|
||||
accessibilityLabel = text
|
||||
}
|
||||
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
removeBorders()
|
||||
|
||||
if selected {
|
||||
if isSelected {
|
||||
addBorder(side: indicatorSide, width: indicatorWidth, color: indicatorColorConfiguration.getColor(self))
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,20 +211,15 @@ open class Tabs: View {
|
||||
let tabItem = Tab()
|
||||
tabItem.size = size
|
||||
tabItem.text = model.text
|
||||
tabItem.onClick = model.onClick
|
||||
tabItem.width = model.width
|
||||
tabViews.append(tabItem)
|
||||
tabStackView.addArrangedSubview(tabItem)
|
||||
|
||||
tabItem
|
||||
.publisher(for: UITapGestureRecognizer())
|
||||
.sink { [weak self] gesture in
|
||||
guard let self, let tabItem = gesture.view as? Tab else { return }
|
||||
if let selectedIndex = self.tabViews.firstIndex(of: tabItem) {
|
||||
self.selectedIndex = selectedIndex
|
||||
self.onTabChange?(selectedIndex)
|
||||
}
|
||||
}.store(in: &tabItem.subscribers)
|
||||
tabItem.onClick = { [weak self] tab in
|
||||
guard let self else { return }
|
||||
model.onClick?(tab.index)
|
||||
self.selectedIndex = tab.index
|
||||
self.onTabChange?(tab.index)
|
||||
}
|
||||
}
|
||||
setNeedsUpdate()
|
||||
scrollToSelectedIndex(animated: false)
|
||||
@ -249,14 +244,14 @@ open class Tabs: View {
|
||||
// Update tab appearance based on properties
|
||||
for (index, tabItem) in tabViews.enumerated() {
|
||||
tabItem.size = size
|
||||
tabItem.selected = selectedIndex == index
|
||||
tabItem.isSelected = selectedIndex == index
|
||||
tabItem.index = index
|
||||
tabItem.minWidth = minWidth
|
||||
tabItem.textPosition = textPosition
|
||||
tabItem.orientation = orientation
|
||||
tabItem.surface = surface
|
||||
tabItem.indicatorPosition = indicatorPosition
|
||||
tabItem.accessibilityLabel = "\(tabItem.text) \(tabItem.selected ? "selected" : "unselected") \(index+1) of \(tabViews.count)"
|
||||
tabItem.accessibilityValue = "\(index+1) of \(tabViews.count) Tabs"
|
||||
}
|
||||
|
||||
//update the width based on rules
|
||||
|
||||
@ -295,7 +295,7 @@ open class EntryField: Control, Changeable {
|
||||
}
|
||||
|
||||
if let tooltipTitle, let tooltipContent {
|
||||
attributes.append(TooltipLabelAttribute(surface: surface, title: tooltipTitle, content: tooltipContent, contentView: tooltipContentView))
|
||||
attributes.append(TooltipLabelAttribute(surface: surface, title: tooltipTitle, content: tooltipContent, contentView: tooltipContentView, presenter: self))
|
||||
}
|
||||
|
||||
//set the titleLabel
|
||||
|
||||
@ -198,10 +198,6 @@ open class InputField: EntryField, UITextFieldDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
public override func updateAccessibilityLabel() {
|
||||
|
||||
}
|
||||
|
||||
open override func updateHelperLabel(){
|
||||
//remove first
|
||||
helperLabel.removeFromSuperview()
|
||||
|
||||
@ -376,10 +376,10 @@ open class Tilelet: TileContainer {
|
||||
updateIcons()
|
||||
|
||||
layoutIfNeeded()
|
||||
updateAccessibilityLabel()
|
||||
updateAccessibility()
|
||||
}
|
||||
|
||||
open override func updateAccessibilityLabel() {
|
||||
open override func updateAccessibility() {
|
||||
setAccessibilityLabel(for: [badge.label, titleLockup.eyebrowLabel, titleLockup.titleLabel, titleLockup.subTitleLabel])
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,20 +46,57 @@ open class TitleLockup: View {
|
||||
$0.distribution = .fill
|
||||
}
|
||||
|
||||
///This logic applies when the type style and size used for the title and subtitle/eyebrow is exactly the same (not including the type weight). This should be automatically detected.
|
||||
private var isUniformSize: Bool {
|
||||
otherStandardStyle.value == titleModel?.standardStyle.value
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Configuration Properties
|
||||
//--------------------------------------------------
|
||||
// Sizes are from InVision design specs.
|
||||
open var standardStyleConfiguration: StandardStyleConfigurationProvider = StandardStyleConfigurationProvider(styleConfigurations: [
|
||||
|
||||
.init(deviceType: .iPad,
|
||||
titleStandardStyles: [.titleSmall, .titleMedium],
|
||||
.init(deviceType: .iPad,
|
||||
titleStandardStyles: [.bodySmall],
|
||||
spacingConfigurations: [
|
||||
.init(otherStandardStyles: [.bodySmall, .bodyMedium, .bodyLarge],
|
||||
topSpacing: VDSLayout.Spacing.space2X.value,
|
||||
bottomSpacing: VDSLayout.Spacing.space2X.value)
|
||||
.init(otherStandardStyles: [.bodySmall],
|
||||
topSpacing: VDSLayout.Spacing.space1X.value,
|
||||
bottomSpacing: VDSLayout.Spacing.space1X.value)
|
||||
]),
|
||||
|
||||
.init(deviceType: .iPad,
|
||||
titleStandardStyles: [.bodyMedium],
|
||||
spacingConfigurations: [
|
||||
.init(otherStandardStyles: [.bodyMedium],
|
||||
topSpacing: VDSLayout.Spacing.space1X.value,
|
||||
bottomSpacing: VDSLayout.Spacing.space1X.value)
|
||||
]),
|
||||
|
||||
.init(deviceType: .iPad,
|
||||
titleStandardStyles: [.bodyLarge],
|
||||
spacingConfigurations: [
|
||||
.init(otherStandardStyles: [.bodyLarge],
|
||||
topSpacing: VDSLayout.Spacing.space1X.value,
|
||||
bottomSpacing: VDSLayout.Spacing.space1X.value)
|
||||
]),
|
||||
|
||||
.init(deviceType: .iPad,
|
||||
titleStandardStyles: [.titleSmall],
|
||||
spacingConfigurations: [
|
||||
.init(otherStandardStyles: [.bodySmall, .bodyMedium, .bodyLarge, .titleSmall],
|
||||
topSpacing: VDSLayout.Spacing.space2X.value,
|
||||
bottomSpacing: VDSLayout.Spacing.space2X.value)
|
||||
]),
|
||||
|
||||
.init(deviceType: .iPad,
|
||||
titleStandardStyles: [.titleMedium],
|
||||
spacingConfigurations: [
|
||||
.init(otherStandardStyles: [.bodySmall, .bodyMedium, .bodyLarge],
|
||||
topSpacing: VDSLayout.Spacing.space2X.value,
|
||||
bottomSpacing: VDSLayout.Spacing.space2X.value)
|
||||
]),
|
||||
|
||||
.init(deviceType: .iPad,
|
||||
titleStandardStyles: [.titleLarge],
|
||||
spacingConfigurations: [
|
||||
@ -100,6 +137,30 @@ open class TitleLockup: View {
|
||||
bottomSpacing: VDSLayout.Spacing.space6X.value),
|
||||
]),
|
||||
|
||||
.init(deviceType: .iPhone,
|
||||
titleStandardStyles: [.bodySmall],
|
||||
spacingConfigurations: [
|
||||
.init(otherStandardStyles: [.bodySmall],
|
||||
topSpacing: VDSLayout.Spacing.space1X.value,
|
||||
bottomSpacing: VDSLayout.Spacing.space1X.value)
|
||||
]),
|
||||
|
||||
.init(deviceType: .iPhone,
|
||||
titleStandardStyles: [.bodyMedium],
|
||||
spacingConfigurations: [
|
||||
.init(otherStandardStyles: [.bodyMedium],
|
||||
topSpacing: VDSLayout.Spacing.space1X.value,
|
||||
bottomSpacing: VDSLayout.Spacing.space1X.value)
|
||||
]),
|
||||
|
||||
.init(deviceType: .iPhone,
|
||||
titleStandardStyles: [.bodyLarge],
|
||||
spacingConfigurations: [
|
||||
.init(otherStandardStyles: [.bodyLarge],
|
||||
topSpacing: VDSLayout.Spacing.space1X.value,
|
||||
bottomSpacing: VDSLayout.Spacing.space1X.value)
|
||||
]),
|
||||
|
||||
.init(deviceType: .iPhone,
|
||||
titleStandardStyles: [.titleSmall],
|
||||
spacingConfigurations: [
|
||||
@ -152,6 +213,10 @@ open class TitleLockup: View {
|
||||
bottomSpacing: VDSLayout.Spacing.space6X.value)
|
||||
]),
|
||||
])
|
||||
|
||||
private var textColorSecondaryConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight , VDSColor.elementsSecondaryOnlight).eraseToAnyColorable()
|
||||
|
||||
private var textColorPrimaryConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark).eraseToAnyColorable()
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
@ -182,6 +247,8 @@ open class TitleLockup: View {
|
||||
open override func setup() {
|
||||
super.setup()
|
||||
|
||||
titleLabel.textColorConfiguration = textColorPrimaryConfiguration
|
||||
|
||||
accessibilityElements = [eyebrowLabel, titleLabel, subTitleLabel]
|
||||
addSubview(stackView)
|
||||
|
||||
@ -223,7 +290,7 @@ open class TitleLockup: View {
|
||||
//--------------------------------------------------
|
||||
open override func updateView() {
|
||||
super.updateView()
|
||||
|
||||
|
||||
let allLabelsTextPosition = textPosition.value
|
||||
var eyebrowTextIsEmpty = true
|
||||
var titleTextIsEmpty = true
|
||||
@ -231,7 +298,7 @@ open class TitleLockup: View {
|
||||
|
||||
var topSpacing: CGFloat = 0.0
|
||||
var bottomSpacing: CGFloat = 0.0
|
||||
|
||||
|
||||
//get the spacing based on the title style and other style used for eyebrow and subtitle
|
||||
if let titleModel,
|
||||
let config = standardStyleConfiguration.spacing(for: titleModel.standardStyle, otherStandardStyle: otherStandardStyle) {
|
||||
@ -242,15 +309,32 @@ open class TitleLockup: View {
|
||||
if let eyebrowModel, !eyebrowModel.text.isEmpty {
|
||||
eyebrowTextIsEmpty = false
|
||||
eyebrowLabel.textPosition = allLabelsTextPosition
|
||||
eyebrowLabel.textStyle = eyebrowModel.isBold ? otherStandardStyle.value.bold : otherStandardStyle.value.regular
|
||||
eyebrowLabel.text = eyebrowModel.text
|
||||
eyebrowLabel.attributes = eyebrowModel.textAttributes
|
||||
eyebrowLabel.numberOfLines = eyebrowModel.numberOfLines
|
||||
eyebrowLabel.surface = surface
|
||||
|
||||
//When uniform size is true
|
||||
if let titleModel, isUniformSize {
|
||||
if titleModel.isBold {
|
||||
//When uniform size is true and the title is bold,
|
||||
//the eyebrow is always regular weight and the secondary color.
|
||||
eyebrowLabel.textStyle = otherStandardStyle.value.regular
|
||||
eyebrowLabel.textColorConfiguration = textColorSecondaryConfiguration
|
||||
} else {
|
||||
//When uniform size is true and the title is regular weight
|
||||
//the eyebrow is always bold and uses the primary color.
|
||||
eyebrowLabel.textStyle = otherStandardStyle.value.bold
|
||||
eyebrowLabel.textColorConfiguration = textColorPrimaryConfiguration
|
||||
}
|
||||
} else {
|
||||
eyebrowLabel.textColorConfiguration = textColorPrimaryConfiguration
|
||||
eyebrowLabel.textStyle = eyebrowModel.isBold ? otherStandardStyle.value.bold : otherStandardStyle.value.regular
|
||||
}
|
||||
} else {
|
||||
eyebrowLabel.reset()
|
||||
}
|
||||
|
||||
|
||||
if let titleModel, !titleModel.text.isEmpty {
|
||||
titleTextIsEmpty = false
|
||||
titleLabel.textPosition = allLabelsTextPosition
|
||||
@ -267,11 +351,11 @@ open class TitleLockup: View {
|
||||
subTitleTextIsEmpty = false
|
||||
subTitleLabel.textPosition = allLabelsTextPosition
|
||||
subTitleLabel.textStyle = otherStandardStyle.value.regular
|
||||
subTitleLabel.textColorConfiguration = subTitleModel.textColor == .secondary ? textColorSecondaryConfiguration : textColorPrimaryConfiguration
|
||||
subTitleLabel.text = subTitleModel.text
|
||||
subTitleLabel.attributes = subTitleModel.textAttributes
|
||||
subTitleLabel.numberOfLines = subTitleModel.numberOfLines
|
||||
subTitleLabel.surface = surface
|
||||
subTitleLabel.disabled = subTitleModel.textColor == .secondary
|
||||
} else {
|
||||
subTitleLabel.reset()
|
||||
}
|
||||
|
||||
@ -23,6 +23,10 @@ extension TitleLockup {
|
||||
case titleMedium
|
||||
case titleSmall
|
||||
|
||||
case bodyLarge
|
||||
case bodyMedium
|
||||
case bodySmall
|
||||
|
||||
public var defaultValue: TextStyle.StandardStyle {.featureXSmall }
|
||||
|
||||
public var value: TextStyle.StandardStyle {
|
||||
|
||||
@ -237,23 +237,16 @@ open class Toggle: Control, Changeable {
|
||||
toggleView.surface = surface
|
||||
toggleView.disabled = disabled
|
||||
toggleView.isOn = isOn
|
||||
updateAccessibilityLabel()
|
||||
updateAccessibility()
|
||||
}
|
||||
|
||||
open override func updateAccessibilityLabel() {
|
||||
accessibilityValue = isSelected ? "1" : "0"
|
||||
if !accessibilityTraits.contains(.selected) && isSelected {
|
||||
accessibilityTraits.insert(.selected)
|
||||
} else if accessibilityTraits.contains(.selected) && !isSelected{
|
||||
accessibilityTraits.remove(.selected)
|
||||
open override func updateAccessibility() {
|
||||
super.updateAccessibility()
|
||||
if showText {
|
||||
setAccessibilityLabel(for: [label])
|
||||
} else {
|
||||
accessibilityLabel = "Toggle"
|
||||
}
|
||||
|
||||
if !accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.insert(.notEnabled)
|
||||
} else if accessibilityTraits.contains(.notEnabled) && !isEnabled{
|
||||
accessibilityTraits.remove(.notEnabled)
|
||||
}
|
||||
setAccessibilityLabel(for: [label])
|
||||
}
|
||||
|
||||
open override var intrinsicContentSize: CGSize {
|
||||
|
||||
@ -199,23 +199,12 @@ open class ToggleView: Control, Changeable {
|
||||
//--------------------------------------------------
|
||||
open override func updateView() {
|
||||
updateToggle()
|
||||
updateAccessibilityLabel()
|
||||
updateAccessibility()
|
||||
}
|
||||
|
||||
open override func updateAccessibilityLabel() {
|
||||
open override func updateAccessibility() {
|
||||
super.updateAccessibility()
|
||||
accessibilityLabel = "Toggle"
|
||||
accessibilityValue = isSelected ? "1" : "0"
|
||||
if !accessibilityTraits.contains(.selected) && isSelected {
|
||||
accessibilityTraits.insert(.selected)
|
||||
} else if accessibilityTraits.contains(.selected) && !isSelected{
|
||||
accessibilityTraits.remove(.selected)
|
||||
}
|
||||
|
||||
if !accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.insert(.notEnabled)
|
||||
} else if accessibilityTraits.contains(.notEnabled) && !isEnabled{
|
||||
accessibilityTraits.remove(.notEnabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -135,7 +135,8 @@ open class Tooltip: Control, TooltipLaunchable {
|
||||
title: tooltip.title,
|
||||
content: tooltip.content,
|
||||
contentView: tooltip.contentView,
|
||||
closeButtonText: tooltip.closeButtonText)
|
||||
closeButtonText: tooltip.closeButtonText,
|
||||
presenter: self)
|
||||
})
|
||||
}
|
||||
|
||||
@ -166,20 +167,33 @@ open class Tooltip: Control, TooltipLaunchable {
|
||||
imageView.image = infoImage.withTintColor(imageColor)
|
||||
}
|
||||
|
||||
open override func updateAccessibilityLabel() {
|
||||
if !accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.insert(.notEnabled)
|
||||
} else if accessibilityTraits.contains(.notEnabled) && !isEnabled {
|
||||
accessibilityTraits.remove(.notEnabled)
|
||||
}
|
||||
open override func updateAccessibility() {
|
||||
super.updateAccessibility()
|
||||
|
||||
var label = title
|
||||
if label == nil {
|
||||
label = content
|
||||
}
|
||||
accessibilityHint = isEnabled ? "Click to open Tooltip." : ""
|
||||
accessibilityValue = "collapsed"
|
||||
if let label {
|
||||
accessibilityLabel = "Tooltip: \(label)"
|
||||
accessibilityLabel = label
|
||||
}
|
||||
}
|
||||
|
||||
public static func accessibleText(for title: String?, content: String?, closeButtonText: String) -> String {
|
||||
var label = ""
|
||||
if let title {
|
||||
label = title
|
||||
}
|
||||
if let content {
|
||||
if !label.isEmpty {
|
||||
label += ","
|
||||
}
|
||||
label += content
|
||||
}
|
||||
return label
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ open class TooltipAlertViewController: UIViewController, Surfaceable {
|
||||
open var contentText: String? { didSet { updateView() }}
|
||||
open var contentView: UIView? { didSet { updateView() }}
|
||||
open var closeButtonText: String = "Close" { didSet { updateView() }}
|
||||
|
||||
open var presenter: UIView? { didSet { updateView() }}
|
||||
//--------------------------------------------------
|
||||
// MARK: - Configuration
|
||||
//--------------------------------------------------
|
||||
@ -50,28 +50,41 @@ open class TooltipAlertViewController: UIViewController, Surfaceable {
|
||||
isModalInPresentation = true
|
||||
setup()
|
||||
}
|
||||
open override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
UIAccessibility.post(notification: .screenChanged, argument: tooltipDialog)
|
||||
}
|
||||
|
||||
private func dismiss() {
|
||||
dismiss(animated: true) { [weak self] in
|
||||
guard let self, let presenter else { return }
|
||||
UIAccessibility.post(notification: .layoutChanged, argument: presenter)
|
||||
}
|
||||
}
|
||||
|
||||
open func setup() {
|
||||
view.accessibilityElements = [tooltipDialog]
|
||||
|
||||
//left-right swipe
|
||||
view.publisher(for: UISwipeGestureRecognizer().with{ $0.direction = .right })
|
||||
.sink { [weak self] swipe in
|
||||
guard let self else { return }
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
}.store(in: &subscribers)
|
||||
|
||||
guard let self else { return }
|
||||
self.dismiss()
|
||||
}.store(in: &subscribers)
|
||||
|
||||
//tapping in background
|
||||
view.publisher(for: UITapGestureRecognizer().with{ $0.numberOfTapsRequired = 1 })
|
||||
.sink { [weak self] swipe in
|
||||
guard let self else { return }
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
}.store(in: &subscribers)
|
||||
|
||||
guard let self else { return }
|
||||
self.dismiss()
|
||||
}.store(in: &subscribers)
|
||||
|
||||
//clicking button
|
||||
onClickSubscriber = tooltipDialog.closeButton.publisher(for: .touchUpInside)
|
||||
.sink {[weak self] button in
|
||||
guard let self else { return }
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
guard let self else { return }
|
||||
self.dismiss()
|
||||
}
|
||||
|
||||
view.addSubview(tooltipDialog)
|
||||
|
||||
@ -160,9 +173,8 @@ open class TooltipDialog: View, UIScrollViewDelegate {
|
||||
//--------------------------------------------------
|
||||
open override func setup() {
|
||||
super.setup()
|
||||
|
||||
layer.cornerRadius = 8
|
||||
|
||||
contentStackView.isAccessibilityElement = true
|
||||
contentStackView.addArrangedSubview(titleLabel)
|
||||
contentStackView.addArrangedSubview(contentLabel)
|
||||
scrollView.addSubview(contentStackView)
|
||||
@ -204,7 +216,7 @@ open class TooltipDialog: View, UIScrollViewDelegate {
|
||||
|
||||
open override func updateView() {
|
||||
super.updateView()
|
||||
|
||||
|
||||
backgroundColor = backgroundColorConfiguration.getColor(self)
|
||||
scrollView.indicatorStyle = surface == .light ? .black : .white
|
||||
|
||||
@ -257,6 +269,7 @@ open class TooltipDialog: View, UIScrollViewDelegate {
|
||||
closeButton.setTitleColor(closeButtonTextColor, for: .normal)
|
||||
closeButton.setTitleColor(closeButtonTextColor, for: .highlighted)
|
||||
closeButton.setTitle(closeButtonText, for: .normal)
|
||||
closeButton.accessibilityLabel = closeButtonText
|
||||
|
||||
contentStackView.setNeedsLayout()
|
||||
contentStackView.layoutIfNeeded()
|
||||
@ -282,7 +295,19 @@ open class TooltipDialog: View, UIScrollViewDelegate {
|
||||
//stackView between the bottom of the scrollView
|
||||
contentStackViewBottomConstraint?.constant = -containerViewInset
|
||||
}
|
||||
|
||||
|
||||
heightConstraint?.constant = contentHeight
|
||||
}
|
||||
|
||||
open override func updateAccessibility() {
|
||||
var label = Tooltip.accessibleText(for: titleText, content: contentText, closeButtonText: closeButtonText)
|
||||
if !label.isEmpty {
|
||||
label += ","
|
||||
}
|
||||
|
||||
contentStackView.accessibilityLabel = label
|
||||
contentStackView.accessibilityHint = "Click on the \(closeButtonText) button to close."
|
||||
contentStackView.accessibilityValue = "expanded"
|
||||
accessibilityElements = [contentStackView, closeButton]
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,11 +9,11 @@ import Foundation
|
||||
import UIKit
|
||||
|
||||
public protocol TooltipLaunchable {
|
||||
func presentTooltip(surface: Surface, title: String?, content: String?, contentView: UIView?, closeButtonText: String)
|
||||
func presentTooltip(surface: Surface, title: String?, content: String?, contentView: UIView?, closeButtonText: String, presenter: UIView?)
|
||||
}
|
||||
|
||||
extension TooltipLaunchable {
|
||||
public func presentTooltip(surface: Surface, title: String?, content: String?, contentView: UIView? = nil, closeButtonText: String = "Close") {
|
||||
public func presentTooltip(surface: Surface, title: String?, content: String?, contentView: UIView? = nil, closeButtonText: String = "Close", presenter: UIView? = nil) {
|
||||
if let presenting = UIApplication.topViewController() {
|
||||
let tooltipViewController = TooltipAlertViewController(nibName: nil, bundle: nil).with {
|
||||
$0.surface = surface
|
||||
@ -21,6 +21,7 @@ extension TooltipLaunchable {
|
||||
$0.contentText = content
|
||||
$0.contentView = contentView
|
||||
$0.closeButtonText = closeButtonText
|
||||
$0.presenter = presenter
|
||||
$0.modalPresentationStyle = .overCurrentContext
|
||||
$0.modalTransitionStyle = .crossDissolve
|
||||
}
|
||||
|
||||
@ -57,7 +57,8 @@ open class TrailingTooltipLabel: View, TooltipLaunchable {
|
||||
self.presentTooltip(surface: self.surface,
|
||||
title: self.tooltipTitle,
|
||||
content: self.tooltipContent,
|
||||
closeButtonText: self.tooltipCloseButtonText)
|
||||
closeButtonText: self.tooltipCloseButtonText,
|
||||
presenter: self)
|
||||
}.store(in: &subscribers)
|
||||
}
|
||||
|
||||
@ -126,7 +127,8 @@ extension Label {
|
||||
closeButtonText: model.closeButtonText,
|
||||
title: model.title,
|
||||
content: model.content,
|
||||
contentView: model.contentView)
|
||||
contentView: model.contentView,
|
||||
presenter: self)
|
||||
newAttributes.append(tooltip)
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ public protocol ViewProtocol {
|
||||
|
||||
// Can setup ui here. Should be called in the initialization functions.
|
||||
func setup()
|
||||
func updateAccessibilityLabel()
|
||||
func updateAccessibility()
|
||||
}
|
||||
|
||||
extension ViewProtocol where Self: UIView {
|
||||
|
||||
@ -1,3 +1,12 @@
|
||||
1.0.35
|
||||
=======
|
||||
- ONEAPP-4684 - (Acessibility) Tooltip
|
||||
- ONEAPP-4681 - (Acessibility) Checkbox
|
||||
- ONEAPP-4825 - (Acessibility) Radiobutton
|
||||
- ONEAPP-5104 - (Acessibility) Button
|
||||
- ONEAPP-4115 - (Acessibility) Tabs
|
||||
- TitleLockup update for Janet release
|
||||
|
||||
1.0.34
|
||||
=======
|
||||
- Added new spec for Bottom Inset for TextStyle
|
||||
|
||||
Loading…
Reference in New Issue
Block a user