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

This commit is contained in:
Scott Pfeil 2024-06-10 14:58:32 -04:00
commit af87f30328
21 changed files with 841 additions and 90 deletions

View File

@ -24,6 +24,9 @@
445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; };
44604AD429CE186A00E62B51 /* NotificationButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */; };
44604AD729CE196600E62B51 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44604AD629CE196600E62B51 /* Line.swift */; };
44A952D92BE384C40009F874 /* TableItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A952D82BE384C40009F874 /* TableItemModel.swift */; };
44A952DD2BE3DA820009F874 /* TableFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A952DC2BE3DA820009F874 /* TableFlowLayout.swift */; };
44BD43B62C04866600644F87 /* TableRowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44BD43B52C04866600644F87 /* TableRowModel.swift */; };
5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; };
5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; };
71ACE89C2BA0451200FB6ADC /* PaginationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71ACE89B2BA0451200FB6ADC /* PaginationContainer.swift */; };
@ -165,6 +168,8 @@
EAD068942A560C13002E3A2D /* LoaderLaunchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD068932A560C13002E3A2D /* LoaderLaunchable.swift */; };
EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; };
EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE785302BA0A438009428EA /* UIImage+Helper.swift */; };
EAF193422C134F3400C68D18 /* Table.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440B84C92BD8E0E9004A732A /* Table.swift */; };
EAF193432C134F3800C68D18 /* TableCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 443DBAF92BDA303F0021497E /* TableCellItem.swift */; };
EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9829D4850E00101452 /* Clickable.swift */; };
EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9A29DB1A6000101452 /* Changeable.swift */; };
EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0932899861000B287F5 /* CheckboxItem.swift */; };
@ -216,9 +221,15 @@
18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarIndicatorModel.swift; sourceTree = "<group>"; };
18FEA1B42BE0E63600A56439 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = "<group>"; };
18FEA1B82BE1301700A56439 /* CalendarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CalendarChangeLog.txt; sourceTree = "<group>"; };
440B84C92BD8E0E9004A732A /* Table.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Table.swift; sourceTree = "<group>"; };
443DBAF92BDA303F0021497E /* TableCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableCellItem.swift; sourceTree = "<group>"; };
445BA07729C07B3D0036A7C5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
44604AD329CE186A00E62B51 /* NotificationButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationButtonModel.swift; sourceTree = "<group>"; };
44604AD629CE196600E62B51 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = "<group>"; };
44A952D82BE384C40009F874 /* TableItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableItemModel.swift; sourceTree = "<group>"; };
44A952DC2BE3DA820009F874 /* TableFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableFlowLayout.swift; sourceTree = "<group>"; };
44BD43B52C04866600644F87 /* TableRowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableRowModel.swift; sourceTree = "<group>"; };
44CCF4942C0493A1005C9C5E /* TableChangeLog.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TableChangeLog.txt; sourceTree = "<group>"; };
5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = "<group>"; };
5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
710607942B91A99500F2863F /* TitleletChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TitleletChangeLog.txt; sourceTree = "<group>"; };
@ -470,6 +481,19 @@
path = Breadcrumbs;
sourceTree = "<group>";
};
440B84C82BD8E0CE004A732A /* Table */ = {
isa = PBXGroup;
children = (
440B84C92BD8E0E9004A732A /* Table.swift */,
443DBAF92BDA303F0021497E /* TableCellItem.swift */,
44A952DC2BE3DA820009F874 /* TableFlowLayout.swift */,
44BD43B52C04866600644F87 /* TableRowModel.swift */,
44A952D82BE384C40009F874 /* TableItemModel.swift */,
44CCF4942C0493A1005C9C5E /* TableChangeLog.txt */,
);
path = Table;
sourceTree = "<group>";
};
445BA07629C07ABA0036A7C5 /* Notification */ = {
isa = PBXGroup;
children = (
@ -639,6 +663,7 @@
71B23C2B2B91FA510027F7D9 /* Pagination */,
EA89200B28B530F0006B9984 /* RadioBox */,
EAF7F11428A1470D00B287F5 /* RadioButton */,
440B84C82BD8E0CE004A732A /* Table */,
EA596ABB2A16B4D500300C4B /* Tabs */,
EAC925852911C9DE00091998 /* TextFields */,
EA5E304A294CBDBB0082B959 /* TileContainer */,
@ -1222,7 +1247,9 @@
EAC58BFD2BE935C300BA39FA /* TitleLockupTextColor.swift in Sources */,
EAACB89A2B927108006A3869 /* Valuing.swift in Sources */,
EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */,
EAF193422C134F3400C68D18 /* Table.swift in Sources */,
EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */,
44A952D92BE384C40009F874 /* TableItemModel.swift in Sources */,
EA978EC5291D6AFE00ACC883 /* AnyLabelAttribute.swift in Sources */,
71ACE89C2BA0451200FB6ADC /* PaginationContainer.swift in Sources */,
EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */,
@ -1254,6 +1281,7 @@
EAC9258F2911C9DE00091998 /* EntryFieldBase.swift in Sources */,
EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */,
EAD068922A560B65002E3A2D /* LoaderViewController.swift in Sources */,
44BD43B62C04866600644F87 /* TableRowModel.swift in Sources */,
71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */,
EABFEB642A26473700C4C106 /* NSAttributedString.swift in Sources */,
EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */,
@ -1269,6 +1297,7 @@
EAC58C0A2BED004E00BA39FA /* FieldType.swift in Sources */,
EA471F3A2A95587500CE9E58 /* LayoutConstraintable.swift in Sources */,
EAC58C292BF4118C00BA39FA /* DatePickerViewController.swift in Sources */,
EAF193432C134F3800C68D18 /* TableCellItem.swift in Sources */,
EAB1D2CF28ABEF2B00DAE764 /* Typography+Base.swift in Sources */,
EA0D1C3B2A6AD51B00E5C127 /* Typogprahy+Styles.swift in Sources */,
EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */,
@ -1305,6 +1334,7 @@
EA985BF02968A93600F2FF2E /* TitleLockupEyebrowModel.swift in Sources */,
EA5E30532950DDA60082B959 /* TitleLockup.swift in Sources */,
EAD062B02A3B873E0015965D /* BadgeIndicator.swift in Sources */,
44A952DD2BE3DA820009F874 /* TableFlowLayout.swift in Sources */,
EAA5EEB528ECBFB4003B3210 /* ImageLabelAttribute.swift in Sources */,
18792A902B7431F2008C0D29 /* ButtonIconBadgeIndicatorModel.swift in Sources */,
EA0B18062A9E2D2D00F2D0CD /* SelectorItemBase.swift in Sources */,
@ -1493,7 +1523,7 @@
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 65;
CURRENT_PROJECT_VERSION = 66;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
@ -1531,7 +1561,7 @@
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 65;
CURRENT_PROJECT_VERSION = 66;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;

View File

@ -67,15 +67,20 @@ open class CalendarBase: Control, Changeable {
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
internal var containerSize: CGSize { CGSize(width: 328, height: 336) }
internal var containerSize: CGSize { CGSize(width: widthDefault, height: 336) }
internal var calendar = Calendar.current
private let cellItemSize = CGSize(width: 40, height: 40)
private let headerHeight = 88.0
private let footerHeight = 40.0
private let calendarWidth = 304.0
private let screenThreeSixty = 360.0
private let widthDefault = 328.0
private let widthTight = 320.0
private var heightConstraint: NSLayoutConstraint?
private var collectionViewLeadingConstraint: NSLayoutConstraint?
private var collectionViewHeightConstraint: NSLayoutConstraint?
private var containerWidthConstraint: NSLayoutConstraint?
private var containerHeightConstraint: NSLayoutConstraint?
private var selectedIndexPath : IndexPath?
private var dates: [Date] = []
@ -115,7 +120,7 @@ open class CalendarBase: Control, Changeable {
//--------------------------------------------------
internal var containerBorderColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight , VDSColor.elementsPrimaryOndark)
internal var backgroundColorConfiguration = SurfaceColorConfiguration(VDSFormControlsColor.backgroundOnlight, VDSFormControlsColor.backgroundOndark)
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
@ -133,21 +138,16 @@ open class CalendarBase: Control, Changeable {
.pinTop()
.pinBottom()
.pinLeadingGreaterThanOrEqualTo()
.pinTrailingLessThanOrEqualTo()
.width(containerSize.width)
.heightGreaterThanEqualTo(containerSize.height)
containerView.centerXAnchor.constraint(equalTo: centerXAnchor).activate()
// Calendar View
containerView.addSubview(collectionView)
let calendarHeight = containerSize.height - (2 * VDSLayout.space4X)
let spacing = (containerSize.width - calendarWidth) / 2
collectionView
.pinTop(VDSLayout.space4X)
.pinBottom(VDSLayout.space4X)
.pinLeading(spacing)
.pinTrailing(spacing)
.width(calendarWidth)
.heightGreaterThanEqualTo(calendarHeight)
@ -163,16 +163,14 @@ open class CalendarBase: Control, Changeable {
displayDate = fallsBetween ? displayDate : minDate
fetchDates(with: displayDate)
}
containerView.layer.backgroundColor = backgroundColorConfiguration.getColor(self).cgColor
containerView.backgroundColor = transparentBackground ? .clear : backgroundColorConfiguration.getColor(self)
containerView.layer.cornerRadius = VDSFormControls.borderRadius
if hideContainerBorder {
containerView.layer.borderColor = nil
containerView.layer.borderWidth = 0
containerView.layer.cornerRadius = 0
} else {
containerView.layer.borderColor = containerBorderColorConfiguration.getColor(self).cgColor
containerView.layer.borderWidth = VDSFormControls.borderWidth
containerView.layer.cornerRadius = VDSFormControls.borderRadius
}
}
@ -191,8 +189,6 @@ open class CalendarBase: Control, Changeable {
// MARK: - Private Methods
//--------------------------------------------------
func fetchDates(with aDate: Date) {
heightConstraint?.isActive = false
containerHeightConstraint?.isActive = false
days.removeAll()
dates = aDate.calendarDisplayDays
@ -204,17 +200,35 @@ open class CalendarBase: Control, Changeable {
days.append(date.getDay())
}
}
updateViewConstraints()
}
func updateViewConstraints() {
collectionView.reloadData()
// container width && collection view leading
collectionViewLeadingConstraint?.isActive = false
containerWidthConstraint?.isActive = false
var width = containerView.frame.size.width
width = ((width > 0) && (width < screenThreeSixty)) ? ((width > widthTight) && (width < screenThreeSixty)) ? widthTight : containerView.frame.size.width : widthDefault
let spacing = (width - calendarWidth) / 2
collectionViewLeadingConstraint = collectionView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: spacing)
containerWidthConstraint = containerView.widthAnchor.constraint(equalToConstant: calendarWidth + ( 2 * spacing))
collectionViewLeadingConstraint?.isActive = true
containerWidthConstraint?.isActive = true
// container height && collection view height
collectionViewHeightConstraint?.isActive = false
containerHeightConstraint?.isActive = false
var height = collectionView.collectionViewLayout.collectionViewContentSize.height
height = height > 0 ? height : containerSize.height
heightConstraint = collectionView.heightAnchor.constraint(equalToConstant: height)
containerHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: height + (2 * VDSLayout.space4X))
heightConstraint?.isActive = true
collectionViewHeightConstraint = collectionView.heightAnchor.constraint(equalToConstant: height)
containerHeightConstraint?.isActive = true
collectionViewHeightConstraint?.isActive = true
layoutIfNeeded()
}
}
}
extension CalendarBase: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

View File

@ -147,12 +147,7 @@ open class DatePicker: EntryFieldBase, DatePickerViewControllerDelegate, UIPopov
open override func updateAccessibility() {
super.updateAccessibility()
let label = "Date Picker, \(isReadOnly ? ", read only" : "")"
if let errorText, showError {
fieldStackView.accessibilityLabel = "\(label) ,error, \(errorText)"
} else {
fieldStackView.accessibilityLabel = label
}
fieldStackView.accessibilityLabel = "Date Picker, \(accessibilityLabelText)"
fieldStackView.accessibilityHint = isReadOnly || !isEnabled ? "" : "Double tap to open."
fieldStackView.accessibilityValue = value
}

View File

@ -278,12 +278,7 @@ open class DropdownSelect: EntryFieldBase {
open override func updateAccessibility() {
super.updateAccessibility()
let label = "Dropdown Select, \(isReadOnly ? ", read only" : "")"
if let errorText, showError {
fieldStackView.accessibilityLabel = "\(label) ,error, \(errorText)"
} else {
fieldStackView.accessibilityLabel = label
}
fieldStackView.accessibilityLabel = "Dropdown Select, \(accessibilityLabelText)"
fieldStackView.accessibilityHint = isReadOnly || !isEnabled ? "" : "Double tap to open."
fieldStackView.accessibilityValue = value
}

View File

@ -314,8 +314,11 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
super.text = newValue
return
}
//clear out accessibility
accessibilityElements?.removeAll()
accessibilityCustomActions = []
//create the primary string
let mutableText = NSMutableAttributedString.mutableText(for: newValue,
textStyle: textStyle,
@ -337,6 +340,10 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
return
}
//clear out accessibility
accessibilityElements?.removeAll()
accessibilityCustomActions = []
let mutableText = NSMutableAttributedString(attributedString: newValue)
applyAttributes(mutableText)
@ -348,7 +355,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
private func applyAttributes(_ mutableAttributedString: NSMutableAttributedString) {
actions = []
if let attributes = attributes {
if let attributes {
mutableAttributedString.apply(attributes: attributes)
}
}
@ -359,7 +366,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText)
if let attributes = attributes {
if let attributes {
//loop through the models attributes
for attribute in attributes {

View File

@ -0,0 +1,165 @@
//
// Table.swift
// VDS
//
// Created by Nadigadda, Sumanth on 24/04/24.
//
import Foundation
import UIKit
import VDSTokens
///Table is view composed of rows and columns, which takes any view into each cell and resizes based on the highest cell height.
@objc(VDSTable)
open class Table: View {
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
/// CollectionView to show the rows and columns
private lazy var matrixView = SelfSizingCollectionView(frame: .zero, collectionViewLayout: flowLayout).with {
$0.register(TableCellItem.self, forCellWithReuseIdentifier: TableCellItem.Identifier)
$0.dataSource = self
$0.delegate = self
$0.translatesAutoresizingMaskIntoConstraints = false
$0.allowsSelection = false
$0.showsVerticalScrollIndicator = false
$0.showsHorizontalScrollIndicator = false
$0.isAccessibilityElement = true
$0.backgroundColor = .clear
}
/// Custom flow layout to manage the height of the cells
private lazy var flowLayout = MatrixFlowLayout().with {
$0.delegate = self
$0.scrollDirection = .horizontal
}
/// Array of ``TableItemModel`` by combining Header & Row items
private var tableData: [TableRowModel] {
return tableHeader + tableRows
}
//--------------------------------------------------
// MARK: - Enums
//--------------------------------------------------
/// Enums used to define the padding for the cell edge spacing.
public enum Padding: String, CaseIterable {
case standard, compact
func horizontalValue() -> CGFloat {
switch self {
case .standard:
return UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space6X
case .compact:
return UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space6X
}
}
func verticalValue() -> CGFloat {
switch self {
case .standard:
return UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space6X
case .compact:
return UIDevice.isIPad ? VDSLayout.space4X : VDSLayout.space3X
}
}
}
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
/// Parameter to set striped status for the table
open var striped: Bool = false { didSet { setNeedsUpdate() } }
/// Parameter to set the padding for the cell
open var padding: Padding = .standard { didSet { setNeedsUpdate() } }
/// Parameter to show the table header row
open var tableHeader: [TableRowModel] = [] { didSet { setNeedsUpdate() } }
/// Parameter to show the all table rows
open var tableRows: [TableRowModel] = [] { didSet { setNeedsUpdate() } }
open var fillContainer: Bool = true { didSet { setNeedsUpdate() } }
open var columnWidths: [CGFloat]? { didSet { setNeedsUpdate() } }
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
///Called upon initializing the table view
open override func initialSetup() {
super.initialSetup()
addSubview(matrixView)
matrixView.pinToSuperView()
}
/// Will update the table view, when called becasue of any changes in component parameters
open override func updateView() {
super.updateView()
if fillContainer == true || (fillContainer == false && columnWidths == nil) {
columnWidths = calculateColumnWidths()
}
flowLayout.layoutPadding = padding
matrixView.reloadData()
matrixView.collectionViewLayout.invalidateLayout()
}
/// Resets to default settings.
open override func reset() {
super.reset()
striped = false
padding = .standard
tableHeader = []
tableRows = []
fillContainer = true
columnWidths = nil
setNeedsUpdate()
}
func calculateColumnWidths() -> [CGFloat] {
guard let noOfColumns = tableData.first?.columnsCount else { return [] }
let itemWidth = floor(matrixView.safeAreaLayoutGuide.layoutFrame.width / CGFloat(noOfColumns))
return Array(repeating: itemWidth, count: noOfColumns)
}
}
extension Table: UICollectionViewDelegate, UICollectionViewDataSource, TableCollectionViewLayoutDataDelegate {
//--------------------------------------------------
// MARK: - UICollectionViewDelegate & UICollectionViewDataSource
//--------------------------------------------------
public func numberOfSections(in collectionView: UICollectionView) -> Int {
return tableData.count
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return tableData[section].columnsCount
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TableCellItem.Identifier, for: indexPath) as? TableCellItem else { return UICollectionViewCell() }
let currentItem = tableData[indexPath.section].columns[indexPath.row]
let shouldStrip = striped ? (indexPath.section % 2 != 0) : false
cell.updateCell(content: currentItem, surface: surface, striped: shouldStrip, padding: padding)
return cell
}
//--------------------------------------------------
// MARK: - TableCollectionViewLayoutDataDelegate
//--------------------------------------------------
func collectionView(_ collectionView: UICollectionView, dataForItemAt indexPath: IndexPath) -> TableItemModel {
return tableData[indexPath.section].columns[indexPath.row]
}
func collectionView(_ collectionView: UICollectionView, widthForItemAt indexPath: IndexPath) -> CGFloat {
return columnWidths?[indexPath.row] ?? 0.0
}
}

View File

@ -0,0 +1,92 @@
//
// TableCellItem.swift
// VDS
//
// Created by Nadigadda, Sumanth on 25/04/24.
//
import Foundation
import UIKit
import VDSTokens
final class TableCellItem: UICollectionViewCell {
/// Identifier for TableCellItem
static let Identifier: String = String(describing: TableCellItem.self)
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
/// Main view which holds the content of the cell
private let containerView = View()
/// Line seperator for cell
private let separator: Line = Line()
/// Color configuration for default background color
private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark)
/// Color configuration for striped background color
private let stripedColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundSecondaryLight, VDSColor.backgroundSecondaryDark)
/// Padding parameter to maintain the edge spacing of the containerView
private var padding: Table.Padding = .standard
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
override init(frame: CGRect) {
super.init(frame: frame)
setupCell()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupCell()
}
private func setupCell() {
contentView.backgroundColor = .clear
addSubview(containerView)
containerView.pinToSuperView()
}
//--------------------------------------------------
// MARK: - Public Methods
//--------------------------------------------------
/// Updates the cell content with ``TableItemModel`` and styling/padding attributes from other parameters
public func updateCell(content: TableItemModel, surface: Surface, striped: Bool = false, padding: Table.Padding = .standard) {
containerView.subviews.forEach({ $0.removeFromSuperview() })
self.padding = padding
containerView.surface = surface
containerView.backgroundColor = striped ? stripedColorConfiguration.getColor(surface) : backgroundColorConfiguration.getColor(surface)
containerView.addSubview(separator)
separator.pinLeading().pinTrailing().pinBottom()
separator.style = content.bottomLine ?? .primary
separator.isHidden = content.bottomLine == nil
separator.surface = surface
guard let component = content.component else { return }
containerView.addSubview(component)
if var surfacedView = component as? Surfaceable {
surfacedView.surface = surface
}
NSLayoutConstraint.activate([
component.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: VDSLayout.space1X),
component.topAnchor.constraint(greaterThanOrEqualTo: containerView.topAnchor, constant: padding.verticalValue()),
containerView.bottomAnchor.constraint(greaterThanOrEqualTo: component.bottomAnchor, constant: padding.verticalValue()),
containerView.trailingAnchor.constraint(greaterThanOrEqualTo: component.trailingAnchor, constant: padding.horizontalValue()),
containerView.centerYAnchor.constraint(equalTo: component.centerYAnchor)
])
}
}

View File

@ -0,0 +1,33 @@
03/31/2022
----------------
- Initial Brand 3.0 handoff
08/02/2022
----------------
- Included a VDS Note about the Padding prop name rationale
08/10/2022
----------------
- Updated default and inverted prop to light and dark surface.
08/29/2022
----------------
- Noted that Striped style is set to false as default. Clarified Anatomy description of line elements to say that both are configurable at both group and item level via bottomLine prop.
09/02/2022
----------------
- Added dev note enhancment to fix vertical 1px height jumping
09/13/2022
----------------
- Updated Anatomy element names per decisions made for design/dev docs.
10/04/2022
----------------
- Added dev note to Viewport > Striped > Compact padding to specify that auto-indent also applies to striped tables with default padding.
12/16/2022
----------------
- Updated border color values to use element tokens.
- Removed Line section from first position of Configurations.
- Replaced spacing values with tokens.

View File

@ -0,0 +1,139 @@
//
// TableFlowLayout.swift
// VDS
//
// Created by Nadigadda, Sumanth on 02/05/24.
//
import UIKit
import VDSTokens
protocol TableCollectionViewLayoutDataDelegate: AnyObject {
func collectionView(_ collectionView: UICollectionView, dataForItemAt indexPath: IndexPath) -> TableItemModel
func collectionView(_ collectionView: UICollectionView, widthForItemAt indexPath: IndexPath) -> CGFloat
}
class MatrixFlowLayout : UICollectionViewFlowLayout {
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
///Spacing between the pagination cells
private let defaultLeadingPadding: CGFloat = VDSLayout.space1X
/// Parameter to store the layout attributes of cell, while calculate the size & position of the cell
private var itemCache: [UICollectionViewLayoutAttributes] = []
/// Parameter to store the total height of the collectionView
private var layoutHeight: CGFloat = 0.0
/// Parameter to store the total width of the collectionView
private var layoutWidth: CGFloat = 0.0
//--------------------------------------------------
// MARK: - Internal Properties
//--------------------------------------------------
weak var delegate: TableCollectionViewLayoutDataDelegate?
///padding type to be set from Table component, which is used to calculate the size & position of the cell.
var layoutPadding: Table.Padding = .standard
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
/// Calculates the layout attribute properties & total height of the collectionView
override func prepare() {
super.prepare()
itemCache.removeAll()
layoutHeight = 0.0
guard let collectionView, let delegate else { return }
let sections = collectionView.numberOfSections
var yPos: CGFloat = 0.0
///Looping through all the sections of the collectionView, visually these are rows
for currentSection in 0..<sections {
/// Reset the layout width after each row's calculation
layoutWidth = 0.0
let items = collectionView.numberOfItems(inSection: currentSection)
///Looping through all the items in section, visually these are each column in the row
for currentItem in 0..<items {
let indexPath = IndexPath(row: currentItem, section: currentSection)
/// Dividing the colletionView width by number of items(columns) in the row to determine width of each cell
let itemWidth = delegate.collectionView(collectionView, widthForItemAt: indexPath)
let selectedItem = delegate.collectionView(collectionView, dataForItemAt: indexPath)
///Calculate the estimated height of the cell
let itemHeight = estimateHeightFor(item: selectedItem, with: itemWidth)
layoutWidth += itemWidth
let attribute = UICollectionViewLayoutAttributes(forCellWith: indexPath)
let origin = CGPoint(x: itemWidth * CGFloat(indexPath.row), y: yPos)
let size = CGSize(width: itemWidth, height: itemHeight)
attribute.frame = CGRect(origin: origin, size: size)
itemCache.append(attribute)
}
///Determines the highest height from all the cells(columns) in the row
let highestHeightForSection = itemCache.filter({$0.indexPath.section == currentSection}).sorted(by: {$0.frame.size.height > $1.frame.size.height }).first?.frame.size.height ?? 0.0
///Set the highest height as height to all the cells in the row to make the row in uniform height.
itemCache.filter({$0.indexPath.section == currentSection}).forEach { attributes in
attributes.frame.size.height = highestHeightForSection
}
///Adds the height to y position for the next section
yPos += highestHeightForSection
}
layoutHeight = yPos
}
/// Fetches estimated height by calling the cell's component estimated height and adding padding
private func estimateHeightFor(item: TableItemModel, with width: CGFloat) -> CGFloat {
let itemWidth = width - layoutPadding.horizontalValue() - defaultLeadingPadding
let maxSize = CGSize(width: itemWidth, height: CGFloat.greatestFiniteMagnitude)
let estItemSize = item.component?.systemLayoutSizeFitting(maxSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) ?? CGSize(width: itemWidth, height: item.defaultHeight)
return estItemSize.height + (2 * layoutPadding.verticalValue())
}
///This will return the layout attributes for the elements in the defined rect
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
for attributes in itemCache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
///This will return the layout attributes at particular indexPath
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return itemCache.filter({ $0.indexPath == indexPath}).first
}
///Returns the collectionview content size
override var collectionViewContentSize: CGSize {
return CGSize(width: layoutWidth, height: layoutHeight)
}
}

View File

@ -0,0 +1,26 @@
//
// TableItemModel.swift
// VDS
//
// Created by Nadigadda, Sumanth on 02/05/24.
//
import Foundation
import UIKit
import VDSTokens
/// Model that represent the content of each cell of Table component
public struct TableItemModel {
public let defaultHeight: CGFloat = 50.0
public var bottomLine: Line.Style?
/// Component to be show in the Table cell
public var component: UIView?
public init(bottomLine: Line.Style? = nil, component: UIView? = nil) {
self.bottomLine = bottomLine
self.component = component
}
}

View File

@ -0,0 +1,21 @@
//
// TableRowModel.swift
// VDS
//
// Created by Sumanth Nadigadda on 27/05/24.
//
import Foundation
public struct TableRowModel {
public var columns: [TableItemModel]
public var columnsCount: Int {
return columns.count
}
public init(columns: [TableItemModel]) {
self.columns = columns
}
}

View File

@ -241,6 +241,23 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
open var rules = [AnyRule<String>]()
open var accessibilityLabelText: String {
var accessibilityLabels = [String]()
if let text = titleLabel.text {
accessibilityLabels.append(text)
}
if isReadOnly {
accessibilityLabels.append("read only")
}
if !isEnabled {
accessibilityLabels.append("dimmed")
}
if let errorText, showError {
accessibilityLabels.append("error, \(errorText)")
}
return accessibilityLabels.joined(separator: ", ")
}
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------

View File

@ -9,7 +9,20 @@ import Foundation
import UIKit
extension InputField {
public class CreditCardNumberRule: Rule, Withable {
public var cardType: CreditCardType?
public var errorMessage: String = "You have exceeded the character limit."
public func isValid(value: String?) -> Bool {
guard let count = value?.count, let min = cardType?.minLength, let max = cardType?.maxLength else { return true }
if min == max {
return count == max
} else {
return count >= min && count <= max
}
}
}
public enum CreditCardType: String, CaseIterable {
case generic
case visa
@ -38,15 +51,22 @@ extension InputField {
}
}
var separatorIndices: [Int] {
func separatorIndices(_ length: Int) -> [Int] {
var indices: [Int] = [4, 8, 12]
switch self {
case .dinersClub:
return [4, 10]
case .amex, .dinersClub:
indices = [4, 10]
case .unionPay:
if length == 19 {
indices = [5]
}
default:
return [4, 8, 12]
break
}
return indices
}
var securityCodeLength: Int {
if self == .amex {
return 4
@ -55,9 +75,21 @@ extension InputField {
}
}
var minLength: Int {
switch self {
case .visa: return 13
case .amex: return 15
case .dinersClub: return 14
default: return 16
}
}
var maxLength: Int {
switch self {
case .visa: return 19
case .amex: return 15
case .dinersClub: return 14
case .unionPay: return 19
default: return 16
}
}
@ -131,9 +163,8 @@ extension InputField {
override func appendRules(_ inputField: InputField) {
if let text = inputField.textField.text, text.count > 0 {
let rule = CharacterCountRule().copyWith {
$0.maxLength = inputField.cardType.maxLength
$0.compareType = .equals
let rule = CreditCardNumberRule().copyWith {
$0.cardType = inputField.cardType
$0.errorMessage = "Enter a valid credit card."
}
inputField.rules.append(.init(rule))
@ -205,8 +236,8 @@ extension InputField {
/// Private
internal func formatCreditCardNumber(_ cardType: CreditCardType, number: String) -> String {
let formattedInput = number.filter { $0.isNumber } // Remove any existing slashes
return String.format(formattedInput, indices: cardType.separatorIndices, with: " ")
let rawNumber = number.filter { $0.isNumber } // Remove any existing slashes
return String.format(rawNumber, indices: cardType.separatorIndices(rawNumber.count), with: " ")
}
internal func updateCardTypeIcon(_ inputField: InputField, rawNumber: String) {
@ -224,9 +255,8 @@ extension InputField {
guard rawNumber.count == cardType.maxLength else { return formatCreditCardNumber(cardType, number: number) }
let lastFourDigits = rawNumber.suffix(4)
let maskedSection = String(repeating: "", count: 12)
let formattedMaskSection = String.format(maskedSection, indices: cardType.separatorIndices, with: " ")
let formattedMaskSection = String.format(maskedSection, indices: cardType.separatorIndices(rawNumber.count), with: " ")
return formattedMaskSection + " " + lastFourDigits
}
}
}

View File

@ -10,6 +10,18 @@ import UIKit
extension InputField {
public class DateRule: Rule, Withable {
public var dateFormat: DateFormat?
public var errorMessage: String = "Enter a valid date"
private let dateFormatter = DateFormatter()
public func isValid(value: String?) -> Bool {
guard let value, let dateFormat, !value.isEmpty else { return true }
dateFormatter.dateFormat = dateFormat.formatString
return dateFormatter.date(from: value) != nil
}
}
public enum DateFormat: String, CaseIterable {
case mmyy
case mmddyy
@ -46,6 +58,102 @@ extension InputField {
case .mmddyyyy: [2,4]
}
}
public func isValid(_ date: String) -> Bool {
let allowedCharacters = CharacterSet(charactersIn: "0123456789/")
// Check if the input contains only allowed characters
if date.rangeOfCharacter(from: allowedCharacters.inverted) != nil || date.isEmpty {
return false
}
let components = date.split(separator: "/")
func isMonth(_ month: String) -> Bool {
switch month.count {
case 1:
guard let month = Int(month), (0...1).contains(month) else { return false }
return true
case 2:
guard let month = Int(month), (1...12).contains(month) else { return false }
return true
default:
return false
}
}
func isDay(_ day: String) -> Bool {
switch day.count {
case 1:
guard let day = Int(day),(1...3).contains(day) else { return false }
return true
case 2:
guard let day = Int(day), (1...31).contains(day) else { return false }
return true
default:
return false
}
}
func isYear(_ year: String, max: Int) -> Bool {
guard year.count <= max else {
return false
}
return true
}
switch self {
case .mmyy:
if components.count > 2 {
return false
}
// Validate month part
if components.count > 0, let monthPart = components.first {
if !isMonth(String(monthPart)) {
return false
}
}
// Validate year part
if components.count > 1, let yearPart = components.last {
if !isYear(String(yearPart), max: 2) {
return false
}
}
case .mmddyy, .mmddyyyy:
if components.count > 3 {
return false
}
// Validate month part
if components.count > 0, let monthPart = components.first {
if !isMonth(String(monthPart)) {
return false
}
}
// Validate day part
if components.count > 1 {
let dayPart = components[1]
if !isDay(String(dayPart)) {
return false
}
}
// Validate year part
if components.count > 2, let yearPart = components.last {
if !isYear(String(yearPart), max: self == .mmddyy ? 2 : 4) {
return false
}
}
}
return true
}
}
class DateHandler: FieldTypeHandler {
@ -58,16 +166,15 @@ extension InputField {
override func updateView(_ inputField: InputField) {
minWidth = 114.0
placeholderText = inputField.dateFormat.placeholderText
//placeholderText = inputField.dateFormat.placeholderText
inputField.textField.formatText = inputField.dateFormat.placeholderText
super.updateView(inputField)
}
override func appendRules(_ inputField: InputField) {
if let text = inputField.textField.text, text.count > 0 {
let rule = CharacterCountRule().copyWith {
$0.maxLength = inputField.dateFormat.maxLength
$0.compareType = .equals
let rule = DateRule().copyWith {
$0.dateFormat = inputField.dateFormat
$0.errorMessage = "Enter a valid date."
}
inputField.rules.append(.init(rule))
@ -86,9 +193,13 @@ extension InputField {
if newText.count > inputField.dateFormat.maxLength {
return false
}
if newText.count <= inputField.dateFormat.maxLength {
textField.text = String.format(newText, indices: inputField.dateFormat.separatorIndices, with: "/")
let rawNumber = newText.filter { $0.isNumber }
let formatted = String.format(rawNumber, indices: inputField.dateFormat.separatorIndices, with: "/")
if inputField.dateFormat.isValid(formatted) || formatted.isEmpty {
textField.text = formatted
}
return false
} else {
return true

View File

@ -190,9 +190,11 @@ open class InputField: EntryFieldBase {
successLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: .success)
borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success)
backgroundColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: [.success, .focused])
borderColorConfiguration.setSurfaceColors(VDSColor.feedbackSuccessOnlight, VDSColor.feedbackSuccessOndark, forState: .success)
textField.textColorConfiguration = textFieldTextColorConfiguration
}
open override func getFieldContainer() -> UIView {
@ -221,19 +223,14 @@ open class InputField: EntryFieldBase {
super.updateView()
textField.surface = surface
textField.isEnabled = isEnabled
textField.isUserInteractionEnabled = isEnabled && !isReadOnly
textField.textColor = textFieldTextColorConfiguration.getColor(self)
}
open override func updateAccessibility() {
super.updateAccessibility()
let label = "\(isReadOnly ? "read only" : "")"
if let errorText, showError {
textField.accessibilityLabel = "\(label) ,error, \(errorText)"
} else {
textField.accessibilityLabel = label
}
textField.accessibilityLabel = accessibilityLabelText
textField.accessibilityHint = isReadOnly || !isEnabled ? "" : "Double tap to open."
}
@ -253,7 +250,7 @@ open class InputField: EntryFieldBase {
statusIcon.name = .checkmarkAlt
statusIcon.color = iconColorConfiguration.getColor(self)
statusIcon.surface = surface
statusIcon.isHidden = !isEnabled
statusIcon.isHidden = !isEnabled || state.contains(.focused)
} else {
successLabel.isHidden = true
}
@ -308,6 +305,7 @@ extension InputField: UITextFieldDelegate {
public func textFieldDidBeginEditing(_ textField: UITextField) {
fieldType.handler().textFieldDidBeginEditing(self, textField: textField)
updateContainerView()
updateErrorLabel()
}
public func textFieldDidEndEditing(_ textField: UITextField) {

View File

@ -47,6 +47,17 @@ open class TextField: UITextField, ViewProtocol, Errorable {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
private var formatLabel = Label().with {
$0.tag = 999
$0.textColorConfiguration = ViewColorConfiguration().with {
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forDisabled: true)
$0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: false)
}.eraseToAnyColorable()
}
/// Format String similar to placeholder
open var formatText: String?
/// TextStyle used on the titleLabel.
open var textStyle: TextStyle = .defaultStyle { didSet { setNeedsUpdate() } }
@ -114,6 +125,37 @@ open class TextField: UITextField, ViewProtocol, Errorable {
open func updateView() {
updateLabel()
updateFormat()
}
open func updateFormat() {
guard let formatText else {
formatLabel.text = ""
return
}
if viewWithTag(999) == nil {
addSubview(formatLabel)
formatLabel.pinToSuperView()
}
var attributes: [any LabelAttributeModel]?
var finalFormatText = formatText
if let text, !text.isEmpty {
//make the color of the matching text clear
attributes = [ColorLabelAttribute(location: 0, length: text.count, color: .clear)]
let startIndex = formatText.index(formatText.startIndex, offsetBy: text.count)
if startIndex < formatText.endIndex {
finalFormatText = text + formatText[startIndex...]
}
}
//set the label
formatLabel.surface = surface
formatLabel.text = finalFormatText
formatLabel.attributes = attributes
}
open func updateAccessibility() {

View File

@ -195,12 +195,7 @@ open class TextArea: EntryFieldBase {
open override func updateAccessibility() {
super.updateAccessibility()
let label = "\(isReadOnly ? "read only" : "")"
if let errorText, showError {
textView.accessibilityLabel = "\(label) ,error, \(errorText)"
} else {
textView.accessibilityLabel = label
}
textView.accessibilityLabel = accessibilityLabelText
textView.accessibilityHint = isReadOnly || !isEnabled ? "" : "Double tap to open."
}

View File

@ -540,7 +540,9 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
var showIconContainerView = false
if let descriptiveIconModel {
descriptiveIcon.name = descriptiveIconModel.name
descriptiveIcon.colorConfiguration = descriptiveIconModel.colorConfiguration
if let color = descriptiveIconModel.iconColor?.uiColor {
descriptiveIcon.color = color
}
descriptiveIcon.size = descriptiveIconModel.size
descriptiveIcon.surface = backgroundColorSurface
showIconContainerView = true
@ -548,7 +550,9 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
if let directionalIconModel {
directionalIcon.name = directionalIconModel.iconType.iconName
directionalIcon.colorConfiguration = directionalIconModel.colorConfiguration
if let color = directionalIconModel.iconColor?.uiColor {
directionalIcon.color = color
}
directionalIcon.size = directionalIconModel.size.value
directionalIcon.surface = backgroundColorSurface
showIconContainerView = true

View File

@ -11,13 +11,33 @@ import VDSCoreTokens
extension Tilelet {
public enum IconColor: Equatable {
case token(UIColor.VDSColor)
case custom(UIColor)
private var reflectedValue: String { String(reflecting: self) }
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.reflectedValue == rhs.reflectedValue
}
public var uiColor: UIColor {
switch self {
case .token(let color):
return color.uiColor
case .custom(let color):
return color
}
}
}
/// Model that represents the options available for the descriptive icon.
public struct DescriptiveIcon {
/// A representation that will be used to render the icon with corresponding name.
public var name: Icon.Name
/// Color of the icon.
public var colorConfiguration: SurfaceColorConfiguration
public var iconColor: IconColor?
/// Enum for a preset height and width for the icon.
public var size: Icon.Size
@ -26,12 +46,12 @@ extension Tilelet {
public var accessibleText: String
public init(name: Icon.Name = .multipleDocuments,
colorConfiguration: SurfaceColorConfiguration = .init(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark),
iconColor: IconColor? = nil,
size: Icon.Size = .medium,
accessibleText: String? = nil) {
self.name = name
self.colorConfiguration = colorConfiguration
self.iconColor = iconColor
self.accessibleText = accessibleText ?? name.rawValue
self.size = size
}
@ -57,7 +77,7 @@ extension Tilelet {
}
/// Color of the icon.
public var colorConfiguration: SurfaceColorConfiguration
public var iconColor: IconColor?
/// Accessible Text for the Icon
public var accessibleText: String
@ -68,13 +88,13 @@ extension Tilelet {
/// Enum for a preset height and width for the icon.
public var size: IconSize
public init(iconType: IconType = .rightArrow,
colorConfiguration: SurfaceColorConfiguration = .init(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark),
public init(iconType: IconType = .rightArrow,
iconColor: IconColor? = nil,
size: IconSize = .medium,
accessibleText: String? = nil) {
self.iconType = iconType
self.colorConfiguration = colorConfiguration
self.iconColor = iconColor
self.accessibleText = accessibleText ?? iconType.iconName.rawValue
self.size = size
}

View File

@ -17,7 +17,8 @@ extension TitleLockup {
public enum TextColor: Equatable {
case primary
case secondary
case custom(UIColor, UIColor)
case token(UIColor.VDSColor)
case custom(UIColor)
private var reflectedValue: String { String(reflecting: self) }
@ -31,15 +32,18 @@ extension TitleLockup {
TitleLockup.textColorPrimaryConfiguration
case .secondary:
TitleLockup.textColorSecondaryConfiguration
case .custom(let lightColor, let darkColor):
SurfaceColorConfiguration(lightColor, darkColor).eraseToAnyColorable()
case .token(let color):
SurfaceColorConfiguration(color.uiColor, color.uiColor).eraseToAnyColorable()
case .custom(let color):
SurfaceColorConfiguration(color, color).eraseToAnyColorable()
}
}
}
public enum TitleTextColor: Equatable {
case primary
case custom(UIColor, UIColor)
case token(UIColor.VDSColor)
case custom(UIColor)
private var reflectedValue: String { String(reflecting: self) }
@ -51,8 +55,10 @@ extension TitleLockup {
switch self {
case .primary:
TitleLockup.textColorPrimaryConfiguration
case .custom(let lightColor, let darkColor):
SurfaceColorConfiguration(lightColor, darkColor).eraseToAnyColorable()
case .token(let color):
SurfaceColorConfiguration(color.uiColor, color.uiColor).eraseToAnyColorable()
case .custom(let color):
SurfaceColorConfiguration(color, color).eraseToAnyColorable()
}
}
}

View File

@ -1,8 +1,19 @@
1.0.66
----------------
- CXTDT-565087 - Input Field - Text - OnDark colors
- CXTDT-565112 - Input Field - Credit Card icons
- CXTDT-565117 - Input Field - Overflow not clipped
- ONEAPP-6325 - Table - Development finished
- CXTDT-565087 - InputField - Text - OnDark colors
- CXTDT-565112 - InputField - Credit Card icons
- CXTDT-565117 - InputField - Overflow not clipped
- CXTDT-565105 - InputField - Date - Typeover text not working
- CXTDT-565115 - InputField - CreditCard - China UnionPay does not allow longer numbers
- CXTDT-560823 TextArea Accessibility Labels/Error/ReadyOnly/Disabled
- CXTDT-553663 - DropdownSelect Accessibility
- CXTDT-544662 - Breadcrumbs - Text Wrapping
- CXTDT-568398 - Calendar - Saturday missing (on smaller screen size devices)
- CXTDT-568402 - Calendar - Extra row (on smaller screen size devices)
- CXTDT-568409 - Calendar - Width control missing
- CXTDT-568419 - Calendar - When hideContainerBorder=true, corner radius disappears
- CXTDT-568413 - Calendar - Missing option for Transparent Background
1.0.65
----------------