Merge branch 'feature/parentViewProtocol' into 'develop'

Added Protocol to denote child views

See merge request BPHV_MIPS/vds_ios!292
This commit is contained in:
Bruce, Matt R 2024-08-23 13:54:03 +00:00
commit 6147568bc3
23 changed files with 123 additions and 23 deletions

View File

@ -101,6 +101,7 @@
EA6642952BCEBF9500D81DC4 /* TextLinkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6642942BCEBF9500D81DC4 /* TextLinkModel.swift */; };
EA6F330E2B911E9000BACAB9 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6F330D2B911E9000BACAB9 /* TextView.swift */; };
EA78C7962C00CAC200430AD1 /* Groupable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA78C7952C00CAC200430AD1 /* Groupable.swift */; };
EA7AE5592C78C7D000107C74 /* ParentViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7AE5582C78C7D000107C74 /* ParentViewProtocol.swift */; };
EA81410B2A0E8E3C004F60D2 /* ButtonIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */; };
EA8141102A127066004F60D2 /* UIColor+VDSColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */; };
EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */; };
@ -323,6 +324,7 @@
EA78C7952C00CAC200430AD1 /* Groupable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Groupable.swift; sourceTree = "<group>"; };
EA78C7A12C0E63D200430AD1 /* vds-dev.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "vds-dev.xcconfig"; sourceTree = "<group>"; };
EA78C7A22C0E63DD00430AD1 /* vds.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = vds.xcconfig; sourceTree = "<group>"; };
EA7AE5582C78C7D000107C74 /* ParentViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParentViewProtocol.swift; sourceTree = "<group>"; };
EA81410A2A0E8E3C004F60D2 /* ButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonIcon.swift; sourceTree = "<group>"; };
EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+VDSColor.swift"; sourceTree = "<group>"; };
EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = "<group>"; };
@ -777,6 +779,7 @@
EA78C7952C00CAC200430AD1 /* Groupable.swift */,
EA33624628931B050071C351 /* Initable.swift */,
EA471F392A95587500CE9E58 /* LayoutConstraintable.swift */,
EA7AE5582C78C7D000107C74 /* ParentViewProtocol.swift */,
EA985C7C297DAED300F2FF2E /* Primitive.swift */,
EAF7F0A5289B0CE000B287F5 /* Resetable.swift */,
EA3361C8289054C50071C351 /* Surfaceable.swift */,
@ -1335,6 +1338,7 @@
EAF7F0A4289B017C00B287F5 /* LabelAttributeModel.swift in Sources */,
EA0B18022A9E236900F2D0CD /* SelectorGroupBase.swift in Sources */,
EA5F86D02A1F936100BC83E4 /* TabsContainer.swift in Sources */,
EA7AE5592C78C7D000107C74 /* ParentViewProtocol.swift in Sources */,
EAF7F0B1289B177F00B287F5 /* ColorLabelAttribute.swift in Sources */,
EAC9258F2911C9DE00091998 /* EntryFieldBase.swift in Sources */,
18B9763F2C11BA4A009271DF /* CarouselPaginationModel.swift in Sources */,

View File

@ -30,7 +30,8 @@ public protocol SelectorControlable: Control, Changeable {
/// Base Class used to build out a Selector control.
@objcMembers
@objc(VDSSelectorBase)
open class SelectorBase: Control, SelectorControlable {
open class SelectorBase: Control, SelectorControlable, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
@ -49,6 +50,8 @@ open class SelectorBase: Control, SelectorControlable {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [selectorView] }
open var onChangeSubscriber: AnyCancellable?
open var size = CGSize(width: 20, height: 20) { didSet { setNeedsUpdate() } }

View File

@ -39,7 +39,7 @@ extension SelectorGroupSingleSelect {
}
/// Base Class used for any Grouped Form Control of a Selector Type.
open class SelectorGroupBase<SelectorItemType: Groupable>: Control, SelectorGroup, Changeable {
open class SelectorGroupBase<SelectorItemType: Groupable>: Control, SelectorGroup, Changeable, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Private Properties
@ -57,6 +57,8 @@ open class SelectorGroupBase<SelectorItemType: Groupable>: Control, SelectorGrou
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { items }
/// Array of the HandlerType registered.
/// Array of HandlerType that the user will have the ability to select from.
open var items: [SelectorItemType] = [] {

View File

@ -11,7 +11,7 @@ import Combine
import VDSCoreTokens
/// Base Class used to build out a SelectorControlable control.
open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changeable, Groupable {
open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changeable, Groupable, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
@ -61,7 +61,9 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var onChangeSubscriber: AnyCancellable?
open var children: [any ViewProtocol] { [label, childLabel, errorLabel, selectorView] }
open var onChangeSubscriber: AnyCancellable?
/// Label used to render labelText.
open var label = Label().with {

View File

@ -17,7 +17,7 @@ import Combine
/// to its parent this object will stretch to the parent's width.
@objcMembers
@objc(VDSBadge)
open class Badge: View {
open class Badge: View, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
@ -45,6 +45,8 @@ open class Badge: View {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [label] }
/// Label used to render text
open var label = Label().with {
$0.isAccessibilityElement = false

View File

@ -13,7 +13,7 @@ import Combine
/// A badge indicator is a visual label used to convey status or highlight supplemental information.
@objcMembers
@objc(VDSBadgeIndicator)
open class BadgeIndicator: View {
open class BadgeIndicator: View, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
@ -135,6 +135,8 @@ open class BadgeIndicator: View {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [label, badgeView] }
/// Label used for the numeric kind.
open var label = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical)

View File

@ -15,11 +15,13 @@ import Combine
/// Breadcrumbs are secondary navigation that use a hierarchy of internal links to tell customers where they are in an experience. Each breadcrumb links to its respective page, except for that of current page.
@objcMembers
@objc(VDSBreadcrumbs)
open class Breadcrumbs: View {
open class Breadcrumbs: View, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { breadcrumbs }
/// Array of ``BreadcrumbItem`` views for the Breadcrumbs.
open var breadcrumbs: [BreadcrumbItem] = [] { didSet { setNeedsUpdate() } }

View File

@ -94,6 +94,12 @@ open class DatePicker: EntryFieldBase<String> {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open override var children: [any ViewProtocol] {
var current = super.children
current.append(selectedDateLabel)
return current
}
open var calendarIcon = Icon().with {
$0.name = .calendar
$0.size = .medium

View File

@ -31,7 +31,7 @@ open class DropdownSelect: EntryFieldBase<String> {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
//--------------------------------------------------
/// If true, the label will be displayed inside the dropdown containerView. Otherwise, the label will be above the dropdown containerView like a normal text input.
open var showInlineLabel: Bool = false { didSet { setNeedsUpdate() }}

View File

@ -14,7 +14,7 @@ import Combine
/// It usually represents a supplementary or utilitarian action. A button icon can stand alone, but often
/// exists in a group when there are several actions that can be performed.
@objc(VDSButtonIcon)
open class ButtonIcon: Control, Changeable {
open class ButtonIcon: Control, Changeable, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
@ -109,6 +109,8 @@ open class ButtonIcon: Control, Changeable {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [icon] }
public var onChangeSubscriber: AnyCancellable?
///Badge Indicator object used to render for the ButtonIcon.

View File

@ -70,6 +70,12 @@ open class InputStepper: EntryFieldBase<Int> {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open override var children: [any ViewProtocol] {
var current = super.children
current.append(contentsOf: [decrementButton, incrementButton, textLabel])
return current
}
/// If there is a width that is larger than this size's minimumWidth, the input stepper will resize to this width.
open var controlWidth: ControlWidth? {
get { _controlWidth }

View File

@ -16,7 +16,7 @@ import Combine
/// experience-wide.
@objcMembers
@objc(VDSNotification)
open class Notification: View {
open class Notification: View, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
@ -101,6 +101,8 @@ open class Notification: View {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [typeIcon, closeButton, titleLabel, subTitleLabel, primaryButton, secondaryButton] }
/// Icon used for denoting type.
open var typeIcon = Icon().with {
$0.name = .infoBold

View File

@ -11,7 +11,7 @@ import VDSCoreTokens
@objcMembers
@objc(VDSPriceLockup)
open class PriceLockup: View {
open class PriceLockup: View, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
@ -90,6 +90,7 @@ open class PriceLockup: View {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [priceLockupLabel] }
/// If true, the component will render as bold.
open var bold: Bool = false { didSet { setNeedsUpdate() } }

View File

@ -14,7 +14,7 @@ import VDSCoreTokens
/// that are used within a ``RadioBoxGroup``.
@objcMembers
@objc(VDSRadioBoxItem)
open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
@ -53,6 +53,8 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [textLabel, subTextLabel, subTextRightLabel, selectorView] }
open var onChangeSubscriber: AnyCancellable?
/// Label used to render the text.

View File

@ -12,7 +12,7 @@ import VDSCoreTokens
/// Tabs are organizational components that group content and allow customers to navigate its display. Use them to separate content when the content is related but doesnt need to be compared.
@objcMembers
@objc(VDSTabs)
open class Tabs: View {
open class Tabs: View, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
@ -84,6 +84,8 @@ open class Tabs: View {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { tabViews }
/// A callback when the selectedIndex changes. Passes parameters (tabIndex).
open var onTabDidSelect: ((Int) -> Void)?

View File

@ -11,7 +11,7 @@ import VDSCoreTokens
import Combine
/// Base Class used to build out a Input controls.
open class EntryFieldBase<ValueType>: Control, Changeable, FormFieldInternalValidatable {
open class EntryFieldBase<ValueType>: Control, Changeable, FormFieldInternalValidatable, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
@ -155,6 +155,8 @@ open class EntryFieldBase<ValueType>: Control, Changeable, FormFieldInternalVali
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [titleLabel, helperLabel, errorLabel, statusIcon] }
/// This is the view that will be wrapped with the border for userInteraction.
/// The only subview of this view is the fieldStackView
open var containerView = View().with {

View File

@ -44,7 +44,7 @@ open class TileContainer: TileContainerBase<TileContainer.Padding> {
}
}
open class TileContainerBase<PaddingType: DefaultValuing & Valuing>: View where PaddingType.ValueType == CGFloat {
open class TileContainerBase<PaddingType: DefaultValuing & Valuing>: View where PaddingType.ValueType == CGFloat {
//--------------------------------------------------
// MARK: - Initializers
@ -122,7 +122,7 @@ open class TileContainerBase<PaddingType: DefaultValuing & Valuing>: View where
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
//--------------------------------------------------
/// This takes an image source url and applies it as a background image.
open var backgroundImage: UIImage? { didSet { setNeedsUpdate() } }
@ -340,21 +340,21 @@ open class TileContainerBase<PaddingType: DefaultValuing & Valuing>: View where
open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
if let onClickSubscriber {
if onClickSubscriber != nil {
isHighlighted = true
}
}
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
if let onClickSubscriber {
if onClickSubscriber != nil {
isHighlighted = false
}
}
open override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
if let onClickSubscriber {
if onClickSubscriber != nil {
isHighlighted = false
}
}

View File

@ -17,7 +17,7 @@ import Combine
/// function.
@objcMembers
@objc(VDSTilelet)
open class Tilelet: TileContainerBase<Tilelet.Padding> {
open class Tilelet: TileContainerBase<Tilelet.Padding>, ParentViewProtocol {
/// Enum used to describe the padding choices used for this component.
public enum Padding: String, DefaultValuing, Valuing, CaseIterable {
@ -110,6 +110,8 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [badge, titleLockup, descriptiveIcon, directionalIcon] }
/// Title lockup positioned in the contentView.
open var titleLockup = TitleLockup().with {
$0.standardStyleConfiguration = .init(styleConfigurations: [

View File

@ -14,7 +14,7 @@ import Combine
/// with approved built in text size configurations.
@objcMembers
@objc(VDSTitleLockup)
open class TitleLockup: View {
open class TitleLockup: View, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
@ -62,6 +62,8 @@ open class TitleLockup: View {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [eyebrowLabel, titleLabel, subTitleLabel] }
/// Aligns TitleLockup's subcomponent's text
open var textAlignment: TextAlignment = .left { didSet { setNeedsUpdate() } }

View File

@ -14,7 +14,7 @@ import Combine
/// or turn off a single option, setting or function.
@objcMembers
@objc(VDSToggle)
open class Toggle: Control, Changeable, FormFieldable {
open class Toggle: Control, Changeable, FormFieldable, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
@ -90,6 +90,8 @@ open class Toggle: Control, Changeable, FormFieldable {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [toggleView, label] }
open var onChangeSubscriber: AnyCancellable?
/// Actual toggle used in this component.

View File

@ -11,7 +11,7 @@ import VDSCoreTokens
@objcMembers
@objc(VDSTooltipDialog)
open class TooltipDialog: View, UIScrollViewDelegate {
open class TooltipDialog: View, UIScrollViewDelegate, ParentViewProtocol {
//--------------------------------------------------
// MARK: - Initializers
@ -55,6 +55,8 @@ open class TooltipDialog: View, UIScrollViewDelegate {
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var children: [any ViewProtocol] { [titleLabel, contentLabel] }
open var tooltipModel = Tooltip.TooltipModel() { didSet { setNeedsUpdate() } }
open var titleLabel = Label().with { label in

View File

@ -0,0 +1,37 @@
//
// ParentViewProtocol.swift
// VDS
//
// Created by Matt Bruce on 8/23/24.
//
import Foundation
/// This is used in a View or Control to denote subviews of the ViewProtocol
/// type, more or less for composite views/controls.
public protocol ParentViewProtocol: ViewProtocol {
var children: [any ViewProtocol] { get }
}
extension ParentViewProtocol {
/// This will get all of the children for yourself as well as all
/// of the children within the full tree hierarchy.
/// - Returns: All children within the hierachy
public func getAllChildren() -> [any ViewProtocol] {
var allChildren = [any ViewProtocol]()
func traverse(view: any ViewProtocol) {
if let parentView = view as? any ParentViewProtocol {
for child in parentView.children {
allChildren.append(child)
traverse(view: child)
}
}
}
traverse(view: self)
return children
}
}

View File

@ -38,8 +38,23 @@ extension ViewProtocol {
public func setNeedsUpdate() {
if shouldUpdateView {
shouldUpdateView = false
//see if this is a view that has children
let parent = self as? any ParentViewProtocol
let children = parent?.getAllChildren()
//if so turn off the shouldUpdate to keep UI
//from blocking
children?.forEach{ $0.shouldUpdateView = false }
updateView()
updateAccessibility()
//if so turn on
children?.forEach{
$0.updateView()
$0.updateAccessibility()
$0.shouldUpdateView = true
}
shouldUpdateView = true
}
}