240 lines
8.7 KiB
Swift
240 lines
8.7 KiB
Swift
//
|
||
// ListUnordered.swift
|
||
// VDS
|
||
//
|
||
// Created by Vasavi Kanamarlapudi on 16/10/24.
|
||
//
|
||
|
||
import Foundation
|
||
import UIKit
|
||
import VDSCoreTokens
|
||
|
||
/// List unordered breaks up related content into distinct phrases or sentences, which improves scannability.
|
||
/// This component should be used when the text items don’t need to be in numeric order.
|
||
@objcMembers
|
||
@objc(VDSListUnordered)
|
||
open class ListUnordered: View {
|
||
|
||
//--------------------------------------------------
|
||
// MARK: - Initializers
|
||
//--------------------------------------------------
|
||
required public init() {
|
||
super.init(frame: .zero)
|
||
}
|
||
|
||
public override init(frame: CGRect) {
|
||
super.init(frame: .zero)
|
||
}
|
||
|
||
public required init?(coder: NSCoder) {
|
||
super.init(coder: coder)
|
||
}
|
||
|
||
//--------------------------------------------------
|
||
// MARK: - Enums
|
||
//--------------------------------------------------
|
||
/// Enum that represents the size availble for the component.
|
||
public enum Size: String, DefaultValuing, CaseIterable {
|
||
case large
|
||
case medium
|
||
case small
|
||
case micro
|
||
|
||
public static var defaultValue: Self { .large }
|
||
|
||
/// TextStyle relative to Size.
|
||
public var textStyle: TextStyle.StandardStyle {
|
||
switch self {
|
||
case .large:
|
||
return .bodyLarge
|
||
case .medium:
|
||
return .bodyMedium
|
||
case .small:
|
||
return .bodySmall
|
||
case .micro:
|
||
return .micro
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Enum that represents the type of spacing available for the component.
|
||
public enum Spacing: String, CaseIterable {
|
||
case standard, compact
|
||
}
|
||
|
||
//--------------------------------------------------
|
||
// MARK: - Public Properties
|
||
//--------------------------------------------------
|
||
/// Size of the component. The default size is Large.
|
||
open var size: Size = .defaultValue { didSet { setNeedsUpdate() } }
|
||
|
||
/// Spacing type of the component.
|
||
open var spacing: Spacing = .standard { didSet { setNeedsUpdate() } }
|
||
|
||
/// Lead-in text that shows as the top text for the component. This is optional.
|
||
open var leadInText: String? = nil { didSet { setNeedsUpdate() } }
|
||
|
||
/// Array of unordered list items to show for the component.
|
||
open var unorderedList: [ListUnorderedItemModel] = [] { didSet { setNeedsUpdate() }}
|
||
|
||
//--------------------------------------------------
|
||
// MARK: - Configuration Properties
|
||
//--------------------------------------------------
|
||
// It can be used for Glyph level 1.
|
||
private var disc = "•"
|
||
|
||
// It can be used for Glyph Level 2.
|
||
private var endash = "–"
|
||
|
||
// Spacing between the list items.
|
||
private var spaceBetweenItems: CGFloat {
|
||
switch (size, spacing) {
|
||
case (.large, .standard):
|
||
return VDSLayout.space4X
|
||
case (.medium, .standard), (.small, .standard), (.micro, .standard):
|
||
return VDSLayout.space3X
|
||
case (.large, .compact):
|
||
return VDSLayout.space2X
|
||
case (.medium, .compact), (.small, .compact), (.micro, .compact):
|
||
return VDSLayout.space1X
|
||
}
|
||
}
|
||
|
||
// Padding that can be used in an item between the glyph and the item text.
|
||
private var padding: CGFloat {
|
||
switch (size, spacing) {
|
||
case (.large, .standard), (.large, .compact):
|
||
return VDSLayout.space3X
|
||
case (.medium, .standard), (.small, .standard), (.micro, .standard), (.medium, .compact), (.small, .compact), (.micro, .compact):
|
||
return VDSLayout.space2X
|
||
}
|
||
}
|
||
|
||
private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark)
|
||
|
||
//--------------------------------------------------
|
||
// MARK: - Private Properties
|
||
//--------------------------------------------------
|
||
private lazy var listStackView = UIStackView().with {
|
||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||
$0.axis = .vertical
|
||
$0.distribution = .fill
|
||
$0.spacing = spaceBetweenItems
|
||
$0.backgroundColor = .clear
|
||
}
|
||
|
||
//--------------------------------------------------
|
||
// MARK: - Overrides
|
||
//--------------------------------------------------
|
||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and config.texturations.
|
||
open override func setup() {
|
||
super.setup()
|
||
|
||
// add stackview
|
||
addSubview(listStackView)
|
||
listStackView.heightGreaterThanEqualTo(constant:0)
|
||
listStackView.pinToSuperView()
|
||
}
|
||
|
||
open override func setDefaults() {
|
||
super.setDefaults()
|
||
leadInText = nil
|
||
unorderedList = []
|
||
}
|
||
|
||
/// Resets to default settings.
|
||
open override func reset() {
|
||
super.reset()
|
||
}
|
||
|
||
/// Used to make changes to the View based off a change events or from local properties.
|
||
open override func updateView() {
|
||
super.updateView()
|
||
listStackView.removeArrangedSubviews()
|
||
listStackView.subviews.forEach { $0.removeFromSuperview() }
|
||
listStackView.spacing = spaceBetweenItems
|
||
|
||
if leadInText != nil {
|
||
let listItem = getListItem(with:self.leadInText, surface: surface)
|
||
listStackView.addArrangedSubview(listItem)
|
||
}
|
||
|
||
unorderedList.forEach { item in
|
||
let listItem = getListItem(levelOneText: item.levelOneText, surface: surface)
|
||
listStackView.addArrangedSubview(listItem)
|
||
|
||
item.levelTwoText?.forEach { text in
|
||
let listItem = getListItem(levelTwoText: text, surface: surface)
|
||
listStackView.addArrangedSubview(listItem)
|
||
}
|
||
}
|
||
}
|
||
|
||
//--------------------------------------------------
|
||
// MARK: - Private Methods
|
||
//--------------------------------------------------
|
||
// Get Label with the required text and text formats.
|
||
func getLabel(with text: String?, surface: Surface) -> Label {
|
||
let textLabel = Label().with {
|
||
$0.isAccessibilityElement = true
|
||
$0.lineBreakMode = .byWordWrapping
|
||
$0.text = text
|
||
$0.textStyle = size.textStyle.regular
|
||
$0.textColor = textColorConfiguration.getColor(surface)
|
||
$0.surface = surface
|
||
}
|
||
return textLabel
|
||
}
|
||
|
||
// Get the list item with the required text (LeadInText, Level 1 Text, Level 2 Text).
|
||
func getListItem(with leadInText:String? = nil, levelOneText: String? = nil, levelTwoText: String? = nil, surface:Surface) -> UIView {
|
||
let itemStackView = UIStackView().with {
|
||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||
$0.axis = .horizontal
|
||
$0.alignment = .leading
|
||
$0.distribution = .fill
|
||
$0.spacing = padding
|
||
$0.backgroundColor = .clear
|
||
}
|
||
|
||
// StackView with LeadIntext if provided.
|
||
if leadInText != nil {
|
||
let leadTextLabel = getLabel(with: leadInText, surface: surface)
|
||
itemStackView.addArrangedSubview(leadTextLabel)
|
||
}
|
||
|
||
// StackView with Level 1 Text if provided.
|
||
if levelOneText != nil {
|
||
|
||
// Add level 1 glyph: 'disc, bold'
|
||
let discLabel = getLabel(with: disc, surface: surface)
|
||
discLabel.widthAnchor.constraint(equalToConstant: discLabel.intrinsicContentSize.width).activate()
|
||
itemStackView.addArrangedSubview(discLabel)
|
||
|
||
// Add level 1 Text
|
||
let levelOneLabel = getLabel(with: levelOneText, surface: surface)
|
||
itemStackView.addArrangedSubview(levelOneLabel)
|
||
}
|
||
|
||
// StackView with Level 2 Text if provided.
|
||
if levelTwoText != nil {
|
||
|
||
// Set level 2 leading space as needed for alignment.
|
||
let discSpaceView = View()
|
||
let discLabel = getLabel(with: disc, surface: surface)
|
||
discSpaceView.widthAnchor.constraint(equalToConstant: discLabel.intrinsicContentSize.width).activate()
|
||
itemStackView.addArrangedSubview(discSpaceView)
|
||
|
||
// Add level 2 glyph: 'en dash, regular'
|
||
let endashLabel = getLabel(with: endash, surface: surface)
|
||
endashLabel.widthAnchor.constraint(equalToConstant: endashLabel.intrinsicContentSize.width).activate()
|
||
itemStackView.addArrangedSubview(endashLabel)
|
||
|
||
// Add level 2 Text
|
||
let levelTwoLabel = getLabel(with: levelTwoText, surface: surface)
|
||
itemStackView.addArrangedSubview(levelTwoLabel)
|
||
}
|
||
return itemStackView
|
||
}
|
||
}
|