Merge branch 'mbruce/bugfixes' into 'develop'
comments and tabs fix See merge request BPHV_MIPS/vds_ios!90
This commit is contained in:
commit
270bf89b9f
@ -406,8 +406,8 @@
|
|||||||
EA33619D288B1E330071C351 /* Components */ = {
|
EA33619D288B1E330071C351 /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
EAD062AE2A3B87210015965D /* BadgeIndicator */,
|
|
||||||
EA4DB2FE28DCBC1900103EE3 /* Badge */,
|
EA4DB2FE28DCBC1900103EE3 /* Badge */,
|
||||||
|
EAD062AE2A3B87210015965D /* BadgeIndicator */,
|
||||||
EA0FC2BE2912D18200DF80B4 /* Buttons */,
|
EA0FC2BE2912D18200DF80B4 /* Buttons */,
|
||||||
EAF7F092289985E200B287F5 /* Checkbox */,
|
EAF7F092289985E200B287F5 /* Checkbox */,
|
||||||
EA985BF3296C609E00F2FF2E /* Icon */,
|
EA985BF3296C609E00F2FF2E /* Icon */,
|
||||||
@ -1131,7 +1131,7 @@
|
|||||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||||
CODE_SIGN_IDENTITY = "";
|
CODE_SIGN_IDENTITY = "";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 26;
|
CURRENT_PROJECT_VERSION = 27;
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
@ -1168,7 +1168,7 @@
|
|||||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||||
CODE_SIGN_IDENTITY = "";
|
CODE_SIGN_IDENTITY = "";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 26;
|
CURRENT_PROJECT_VERSION = 27;
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
|
|||||||
@ -23,7 +23,7 @@ public typealias ObjectColorable = Colorable & Initable & ObjectWithable
|
|||||||
/// let textColor = config.getColor(model) //returns .black
|
/// let textColor = config.getColor(model) //returns .black
|
||||||
///
|
///
|
||||||
|
|
||||||
/// You can pass in a Surfaceable object and it will give you the color responding.
|
/// You can pass in a Surfaceable object and this will return the corresponding Color based on the object's surface property.
|
||||||
open class SurfaceColorConfiguration: ObjectColorable {
|
open class SurfaceColorConfiguration: ObjectColorable {
|
||||||
public typealias ObjectType = Surfaceable
|
public typealias ObjectType = Surfaceable
|
||||||
public var lightColor: UIColor = .clear
|
public var lightColor: UIColor = .clear
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import UIKit
|
|||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
@objc(VDSControl)
|
@objc(VDSControl)
|
||||||
|
/// Base Class use to build Controls.
|
||||||
open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoable, Clickable {
|
open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoable, Clickable {
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import Combine
|
|||||||
|
|
||||||
|
|
||||||
@objc(VDSView)
|
@objc(VDSView)
|
||||||
|
/// Base Class used to build Views.
|
||||||
open class View: UIView, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
open class View: UIView, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -29,10 +30,10 @@ open class View: UIView, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
|||||||
/// Dictionary for keeping information for this Control use only Primitives
|
/// Dictionary for keeping information for this Control use only Primitives
|
||||||
open var userInfo = [String: Primitive]()
|
open var userInfo = [String: Primitive]()
|
||||||
|
|
||||||
/// Current Surface used within this Control and used to passdown to child views
|
/// Current Surface and this is used to pass down to child objects that implement Surfacable
|
||||||
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// Control is disabled or not
|
/// Whether this object is disabled or not
|
||||||
open var disabled: Bool = false { didSet { setNeedsUpdate(); isUserInteractionEnabled = !disabled } }
|
open var disabled: Bool = false { didSet { setNeedsUpdate(); isUserInteractionEnabled = !disabled } }
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -24,6 +24,8 @@ open class Badge: View {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Properties
|
// MARK: - Public Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Label used to render text
|
||||||
open var label = Label().with {
|
open var label = Label().with {
|
||||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
$0.adjustsFontSizeToFitWidth = false
|
$0.adjustsFontSizeToFitWidth = false
|
||||||
@ -32,12 +34,17 @@ open class Badge: View {
|
|||||||
$0.textStyle = .boldBodySmall
|
$0.textStyle = .boldBodySmall
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This will render the badges fill color based on the available options.
|
||||||
|
/// When used in conjunction with the surface prop, this fill color will change its tint automatically based on a light or dark surface.
|
||||||
open var fillColor: FillColor = .red { didSet { setNeedsUpdate() }}
|
open var fillColor: FillColor = .red { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// The text that will be shown in the label.
|
||||||
open var text: String = "" { didSet { setNeedsUpdate() }}
|
open var text: String = "" { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// When applied, this property takes a px value that will restrict the width at that point
|
||||||
open var maxWidth: CGFloat? { didSet { setNeedsUpdate() }}
|
open var maxWidth: CGFloat? { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// This will restrict the badge height to a specific number of lines. If the text overflows the allowable space, ellipsis will show.
|
||||||
open var numberOfLines: Int = 1 { didSet { setNeedsUpdate() }}
|
open var numberOfLines: Int = 1 { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -86,6 +93,8 @@ open class Badge: View {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Configuration
|
// MARK: - Configuration
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// ColorConfiguration that is mapped to the 'fillColor' for the surface
|
||||||
private var backgroundColorConfiguration: AnyColorable = {
|
private var backgroundColorConfiguration: AnyColorable = {
|
||||||
let config = KeyedColorConfiguration<Badge, FillColor>(keyPath: \.fillColor)
|
let config = KeyedColorConfiguration<Badge, FillColor>(keyPath: \.fillColor)
|
||||||
config.setSurfaceColors(VDSColor.backgroundBrandhighlight, VDSColor.backgroundBrandhighlight, forKey: .red)
|
config.setSurfaceColors(VDSColor.backgroundBrandhighlight, VDSColor.backgroundBrandhighlight, forKey: .red)
|
||||||
@ -98,8 +107,10 @@ open class Badge: View {
|
|||||||
return config.eraseToAnyColorable()
|
return config.eraseToAnyColorable()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
/// ColorConfiguration for the Text
|
||||||
private var textColorConfiguration = ViewColorConfiguration()
|
private var textColorConfiguration = ViewColorConfiguration()
|
||||||
|
|
||||||
|
/// Updates the textColorConfiguration based on the fillColor
|
||||||
public func updateTextColorConfig() {
|
public func updateTextColorConfig() {
|
||||||
textColorConfiguration.reset()
|
textColorConfiguration.reset()
|
||||||
|
|
||||||
|
|||||||
@ -12,27 +12,32 @@ import VDSFormControlsTokens
|
|||||||
import Combine
|
import Combine
|
||||||
import VDSTypographyTokens
|
import VDSTypographyTokens
|
||||||
|
|
||||||
/// Badges are visual labels used to convey status or highlight supplemental information.
|
/// Badge Indicators are visual labels .
|
||||||
@objc(VDSBadgeIndicator)
|
@objc(VDSBadgeIndicator)
|
||||||
open class BadgeIndicator: View {
|
open class BadgeIndicator: View {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Enums
|
// MARK: - Enums
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Enum type for fill color
|
||||||
public enum FillColor: String, CaseIterable {
|
public enum FillColor: String, CaseIterable {
|
||||||
case red, yellow, green, orange, blue, gray, grayLowContrast, black, white
|
case red, yellow, green, orange, blue, gray, grayLowContrast, black, white
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enum type for kind of BadgeIndicator
|
||||||
public enum Kind: String, CaseIterable {
|
public enum Kind: String, CaseIterable {
|
||||||
case simple, numbered
|
case simple, numbered
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum MaxDigits: String, CaseIterable {
|
/// Enum type for maximum number of digits
|
||||||
|
public enum MaximumDigits: String, CaseIterable {
|
||||||
case one
|
case one
|
||||||
case two
|
case two
|
||||||
case three
|
case three
|
||||||
case four
|
case four
|
||||||
case five
|
case five
|
||||||
case six
|
case six
|
||||||
|
case none
|
||||||
|
|
||||||
public var value: Int {
|
public var value: Int {
|
||||||
switch self {
|
switch self {
|
||||||
@ -48,10 +53,13 @@ open class BadgeIndicator: View {
|
|||||||
return 5
|
return 5
|
||||||
case .six:
|
case .six:
|
||||||
return 6
|
return 6
|
||||||
|
case .none:
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enum type describing size of Badge Indicator
|
||||||
public enum Size: String, CaseIterable {
|
public enum Size: String, CaseIterable {
|
||||||
case xxlarge = "2XLarge"
|
case xxlarge = "2XLarge"
|
||||||
case xlarge = "XLarge"
|
case xlarge = "XLarge"
|
||||||
@ -59,6 +67,7 @@ open class BadgeIndicator: View {
|
|||||||
case medium = "Medium"
|
case medium = "Medium"
|
||||||
case small = "Small"
|
case small = "Small"
|
||||||
|
|
||||||
|
/// Dynamic TextStyle for the size
|
||||||
public var textStyle: TextStyle {
|
public var textStyle: TextStyle {
|
||||||
let style = TextStyle.bodySmall
|
let style = TextStyle.bodySmall
|
||||||
var pointSize: CGFloat = VDSTypography.fontSizeBody12
|
var pointSize: CGFloat = VDSTypography.fontSizeBody12
|
||||||
@ -89,6 +98,7 @@ open class BadgeIndicator: View {
|
|||||||
letterSpacing: letterSpacing)
|
letterSpacing: letterSpacing)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//EdgeInsets for the label
|
||||||
public var edgeInset: UIEdgeInsets {
|
public var edgeInset: UIEdgeInsets {
|
||||||
var horizontalPadding: CGFloat = VDSLayout.Spacing.space1X.value
|
var horizontalPadding: CGFloat = VDSLayout.Spacing.space1X.value
|
||||||
let verticalPadding: CGFloat = 0
|
let verticalPadding: CGFloat = 0
|
||||||
@ -109,6 +119,8 @@ open class BadgeIndicator: View {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Properties
|
// MARK: - Public Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Label used for the numeric kind
|
||||||
open var label = Label().with {
|
open var label = Label().with {
|
||||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
$0.adjustsFontSizeToFitWidth = false
|
$0.adjustsFontSizeToFitWidth = false
|
||||||
@ -117,6 +129,7 @@ open class BadgeIndicator: View {
|
|||||||
$0.numberOfLines = 1
|
$0.numberOfLines = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// BorderColor for Surface.light
|
||||||
open var borderColorLight: UIColor? {
|
open var borderColorLight: UIColor? {
|
||||||
didSet {
|
didSet {
|
||||||
if let borderColorLight {
|
if let borderColorLight {
|
||||||
@ -128,6 +141,7 @@ open class BadgeIndicator: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// BorderColor for Surface.dark
|
||||||
open var borderColorDark: UIColor? {
|
open var borderColorDark: UIColor? {
|
||||||
didSet {
|
didSet {
|
||||||
if let borderColorDark {
|
if let borderColorDark {
|
||||||
@ -139,46 +153,97 @@ open class BadgeIndicator: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This will render the badges fill color based on the available options.
|
||||||
|
/// When used in conjunction with the surface prop, this fill color will change its tint automatically based on a light or dark surface.
|
||||||
open var fillColor: FillColor = .red { didSet { setNeedsUpdate() }}
|
open var fillColor: FillColor = .red { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// Badge Number that will be shown if you are using Kind.numbered
|
||||||
open var number: Int? { didSet { setNeedsUpdate() }}
|
open var number: Int? { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// Type of Badge Indicator, simple is a dot, whereas numbered shows a number.
|
||||||
open var kind: Kind = .simple { didSet { setNeedsUpdate() }}
|
open var kind: Kind = .simple { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// Character that is always at the begging. Accepts any character and if unaffected by maximumDigits
|
||||||
open var leadingCharacter: String? { didSet { setNeedsUpdate() }}
|
open var leadingCharacter: String? { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// Determines the size of the Badge Indicator as well as the textStyle and padding used.
|
||||||
open var size: Size = .xxlarge { didSet { setNeedsUpdate() }}
|
open var size: Size = .xxlarge { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// Pixel size of the dot when the kind is set to simple
|
||||||
open var dotSize: CGFloat? { didSet { setNeedsUpdate() }}
|
open var dotSize: CGFloat? { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// Sets the padding at the top/bottom of the label
|
||||||
open var verticalPadding: CGFloat? { didSet { setNeedsUpdate() }}
|
open var verticalPadding: CGFloat? { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// Sets the padding at the left/right of the label
|
||||||
open var horitonalPadding: CGFloat? { didSet { setNeedsUpdate() }}
|
open var horitonalPadding: CGFloat? { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// Hides the dot when you are in Kind.simple mode.
|
||||||
open var hideDot: Bool = false { didSet { setNeedsUpdate() }}
|
open var hideDot: Bool = false { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// Will not show the border
|
||||||
open var hideBorder: Bool = false { didSet { setNeedsUpdate() }}
|
open var hideBorder: Bool = false { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
open var maxDigits: MaxDigits = .two { didSet { setNeedsUpdate() }}
|
/// When in Kind.numbered this is the amount of digits that will show up when the user adds a number.
|
||||||
|
open var maximumDigits: MaximumDigits = .two { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// The Container's width
|
||||||
open var width: CGFloat? { didSet { setNeedsUpdate() }}
|
open var width: CGFloat? { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
/// The Container's height
|
||||||
open var height: CGFloat? { didSet { setNeedsUpdate() }}
|
open var height: CGFloat? { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private let borderWidth: CGFloat = 1.0
|
private let badgeView = View()
|
||||||
|
private var badgeSize: CGFloat { max(minSize, size.textStyle.font.lineHeight) }
|
||||||
|
private var labelEdgeInset: UIEdgeInsets {
|
||||||
|
var newInset = size.edgeInset
|
||||||
|
if let verticalPadding, let horitonalPadding {
|
||||||
|
newInset = .init(top: verticalPadding, left: horitonalPadding, bottom: verticalPadding, right: horitonalPadding)
|
||||||
|
} else if let verticalPadding {
|
||||||
|
newInset = .init(top: verticalPadding, left: newInset.left, bottom: verticalPadding, right: newInset.right)
|
||||||
|
} else if let horitonalPadding {
|
||||||
|
newInset = .init(top: newInset.top, left: horitonalPadding, bottom: newInset.bottom, right: horitonalPadding)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newInset
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Constraints
|
// MARK: - Constraints
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private let badgeView = View()
|
|
||||||
private var badgeSize: CGFloat { max(16.0, size.textStyle.font.lineHeight) }
|
|
||||||
private var widthConstraint: NSLayoutConstraint?
|
private var widthConstraint: NSLayoutConstraint?
|
||||||
private var heightConstraint: NSLayoutConstraint?
|
private var heightConstraint: NSLayoutConstraint?
|
||||||
private var labelContraints = NSLayoutConstraint.Container()
|
private var labelContraints = NSLayoutConstraint.Container()
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Configuration
|
||||||
|
//--------------------------------------------------
|
||||||
|
private let borderWidth: CGFloat = 1.0
|
||||||
|
private let minSize: CGFloat = 16.0
|
||||||
|
private let minDotSize: CGFloat = 4.0
|
||||||
|
private let dotRatio: CGFloat = 0.24
|
||||||
|
|
||||||
|
private var borderColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteBlack)
|
||||||
|
|
||||||
|
private var backgroundColorConfiguration: AnyColorable = {
|
||||||
|
let config = KeyedColorConfiguration<BadgeIndicator, FillColor>(keyPath: \.fillColor)
|
||||||
|
config.setSurfaceColors(VDSColor.backgroundBrandhighlight, VDSColor.backgroundBrandhighlight, forKey: .red)
|
||||||
|
config.setSurfaceColors(VDSColor.paletteYellow62, VDSColor.paletteYellow62, forKey: .yellow)
|
||||||
|
config.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen36, forKey: .green)
|
||||||
|
config.setSurfaceColors(VDSColor.paletteOrange41, VDSColor.paletteOrange58, forKey: .orange)
|
||||||
|
config.setSurfaceColors(VDSColor.paletteBlue38, VDSColor.paletteBlue46, forKey: .blue)
|
||||||
|
config.setSurfaceColors(VDSColor.paletteGray44, VDSColor.paletteGray65, forKey: .gray)
|
||||||
|
config.setSurfaceColors(VDSColor.paletteGray85, VDSColor.paletteGray20, forKey: .grayLowContrast)
|
||||||
|
config.setSurfaceColors(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryDark, forKey: .black)
|
||||||
|
config.setSurfaceColors(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryLight, forKey: .white)
|
||||||
|
return config.eraseToAnyColorable()
|
||||||
|
}()
|
||||||
|
|
||||||
|
private var textColorConfiguration = ViewColorConfiguration()
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -225,40 +290,6 @@ open class BadgeIndicator: View {
|
|||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var labelEdgeInset: UIEdgeInsets {
|
|
||||||
var newInset = size.edgeInset
|
|
||||||
if let verticalPadding, let horitonalPadding {
|
|
||||||
newInset = .init(top: verticalPadding, left: horitonalPadding, bottom: verticalPadding, right: horitonalPadding)
|
|
||||||
} else if let verticalPadding {
|
|
||||||
newInset = .init(top: verticalPadding, left: newInset.left, bottom: verticalPadding, right: newInset.right)
|
|
||||||
} else if let horitonalPadding {
|
|
||||||
newInset = .init(top: newInset.top, left: horitonalPadding, bottom: newInset.bottom, right: horitonalPadding)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newInset
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
|
||||||
// MARK: - Configuration
|
|
||||||
//--------------------------------------------------
|
|
||||||
private var borderColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteBlack)
|
|
||||||
|
|
||||||
private var backgroundColorConfiguration: AnyColorable = {
|
|
||||||
let config = KeyedColorConfiguration<BadgeIndicator, FillColor>(keyPath: \.fillColor)
|
|
||||||
config.setSurfaceColors(VDSColor.backgroundBrandhighlight, VDSColor.backgroundBrandhighlight, forKey: .red)
|
|
||||||
config.setSurfaceColors(VDSColor.paletteYellow62, VDSColor.paletteYellow62, forKey: .yellow)
|
|
||||||
config.setSurfaceColors(VDSColor.paletteGreen26, VDSColor.paletteGreen36, forKey: .green)
|
|
||||||
config.setSurfaceColors(VDSColor.paletteOrange41, VDSColor.paletteOrange58, forKey: .orange)
|
|
||||||
config.setSurfaceColors(VDSColor.paletteBlue38, VDSColor.paletteBlue46, forKey: .blue)
|
|
||||||
config.setSurfaceColors(VDSColor.paletteGray44, VDSColor.paletteGray65, forKey: .gray)
|
|
||||||
config.setSurfaceColors(VDSColor.paletteGray85, VDSColor.paletteGray20, forKey: .grayLowContrast)
|
|
||||||
config.setSurfaceColors(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryDark, forKey: .black)
|
|
||||||
config.setSurfaceColors(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryLight, forKey: .white)
|
|
||||||
return config.eraseToAnyColorable()
|
|
||||||
}()
|
|
||||||
|
|
||||||
private var textColorConfiguration = ViewColorConfiguration()
|
|
||||||
|
|
||||||
public func updateTextColorConfig() {
|
public func updateTextColorConfig() {
|
||||||
textColorConfiguration.reset()
|
textColorConfiguration.reset()
|
||||||
|
|
||||||
@ -323,19 +354,13 @@ open class BadgeIndicator: View {
|
|||||||
let badgeCount = number ?? 0
|
let badgeCount = number ?? 0
|
||||||
var text: String = ""
|
var text: String = ""
|
||||||
if kind == .numbered && badgeCount >= 0 {
|
if kind == .numbered && badgeCount >= 0 {
|
||||||
let maxBadgetCount = limitDigits(number: badgeCount, maxDigits: maxDigits.value)
|
let maxBadgetCount = maximumDigits == .none ? badgeCount : limitDigits(number: badgeCount, maxDigits: maximumDigits.value)
|
||||||
let formatter = NumberFormatter()
|
let formatter = NumberFormatter()
|
||||||
formatter.numberStyle = .decimal
|
formatter.numberStyle = .decimal
|
||||||
text = formatter.string(from: .init(integerLiteral: maxBadgetCount))!
|
text = formatter.string(from: .init(integerLiteral: maxBadgetCount))!
|
||||||
if maxDigits.value < "\(badgeCount)".count {
|
|
||||||
let formatter = NumberFormatter()
|
|
||||||
formatter.numberStyle = .decimal
|
|
||||||
text = "\(text)+"
|
|
||||||
}
|
|
||||||
if let leadingCharacter {
|
if let leadingCharacter {
|
||||||
text = "\(leadingCharacter)\(text)"
|
text = "\(leadingCharacter)\(text)"
|
||||||
} else {
|
|
||||||
text = "\(text)"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return text
|
return text
|
||||||
@ -368,7 +393,8 @@ open class BadgeIndicator: View {
|
|||||||
let frame = badgeView.frame
|
let frame = badgeView.frame
|
||||||
|
|
||||||
//default calculation if a dotSize isn't given
|
//default calculation if a dotSize isn't given
|
||||||
var dot: CGFloat = frame.height * 0.1875
|
let dotSizeRatio = frame.height * dotRatio
|
||||||
|
var dot: CGFloat = dotSizeRatio < minDotSize ? minDotSize : dotSizeRatio
|
||||||
if let dotSize, dotSize < frame.width, dotSize < frame.height {
|
if let dotSize, dotSize < frame.width, dotSize < frame.height {
|
||||||
dot = dotSize
|
dot = dotSize
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,12 +8,18 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
/// Protocol used for any object to be able to launch a loader
|
||||||
public protocol LoaderLaunchable {
|
public protocol LoaderLaunchable {
|
||||||
func presentLoader(surface: Surface, size: Int?)
|
func presentLoader(surface: Surface, size: Int?)
|
||||||
func dismissLoader()
|
func dismissLoader()
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LoaderLaunchable {
|
extension LoaderLaunchable {
|
||||||
|
|
||||||
|
/// Default implementation for presenting
|
||||||
|
/// - Parameters:
|
||||||
|
/// - surface: Surface for the Loader
|
||||||
|
/// - size: Size for the loader, nil is default.
|
||||||
public func presentLoader(surface: Surface, size: Int? = nil) {
|
public func presentLoader(surface: Surface, size: Int? = nil) {
|
||||||
if let presenting = UIApplication.topViewController() {
|
if let presenting = UIApplication.topViewController() {
|
||||||
let viewController = LoaderViewController(nibName: nil, bundle: nil).with {
|
let viewController = LoaderViewController(nibName: nil, bundle: nil).with {
|
||||||
@ -28,6 +34,7 @@ extension LoaderLaunchable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dismisses the Loader
|
||||||
public func dismissLoader() {
|
public func dismissLoader() {
|
||||||
if let presenting = UIApplication.topViewController() as? LoaderViewController {
|
if let presenting = UIApplication.topViewController() as? LoaderViewController {
|
||||||
presenting.dismiss(animated: true)
|
presenting.dismiss(animated: true)
|
||||||
|
|||||||
@ -9,23 +9,41 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
|
|
||||||
public class LoaderViewController: UIViewController, Surfaceable {
|
/// ViewController to show the Loader, this will be presented using the LoaderLaunchable Protocl.
|
||||||
|
open class LoaderViewController: UIViewController, Surfaceable {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Properties
|
||||||
|
//--------------------------------------------------
|
||||||
private var loader = Loader()
|
private var loader = Loader()
|
||||||
private var backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark)
|
private var backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark)
|
||||||
|
|
||||||
public var surface: Surface = .light
|
//--------------------------------------------------
|
||||||
public var size: Int?
|
// MARK: - Public Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
/// Current Surface used within this Control and used to passdown to child views
|
||||||
|
open var surface: Surface = .light { didSet { updateView() }}
|
||||||
|
|
||||||
public override func viewDidLoad() {
|
/// Size of the Height and Width of the Loader view.
|
||||||
|
open var size: Int? { didSet { updateView() }}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
//--------------------------------------------------
|
||||||
|
open override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
setup()
|
view.addSubview(loader)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
loader.centerXAnchor.constraint(equalTo: view.centerXAnchor),
|
||||||
|
loader.centerYAnchor.constraint(equalTo: view.centerYAnchor)
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func viewWillAppear(_ animated: Bool) {
|
open override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
updateView()
|
updateView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update this view based off of property chang
|
||||||
open func updateView() {
|
open func updateView() {
|
||||||
view.backgroundColor = backgroundColorConfiguration.getColor(self).withAlphaComponent(0.8)
|
view.backgroundColor = backgroundColorConfiguration.getColor(self).withAlphaComponent(0.8)
|
||||||
if let size {
|
if let size {
|
||||||
@ -33,12 +51,4 @@ public class LoaderViewController: UIViewController, Surfaceable {
|
|||||||
}
|
}
|
||||||
loader.surface = surface
|
loader.surface = surface
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setup() {
|
|
||||||
view.addSubview(loader)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
loader.centerXAnchor.constraint(equalTo: view.centerXAnchor),
|
|
||||||
loader.centerYAnchor.constraint(equalTo: view.centerYAnchor)
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ extension Tabs {
|
|||||||
open var selected: Bool = false { didSet { setNeedsUpdate() } }
|
open var selected: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
///The text label of the tab.
|
///The text label of the tab.
|
||||||
open var text: String = "Tab" { didSet { setNeedsUpdate() } }
|
open var text: String = "" { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
///Minimum width for the tab
|
///Minimum width for the tab
|
||||||
open var minWidth: CGFloat = 44.0 { didSet { setNeedsUpdate() } }
|
open var minWidth: CGFloat = 44.0 { didSet { setNeedsUpdate() } }
|
||||||
@ -52,6 +52,17 @@ extension Tabs {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
private var textStyle: TextStyle {
|
||||||
|
if size == .medium {
|
||||||
|
return .boldBodyLarge
|
||||||
|
} else {
|
||||||
|
//specs show that the font size shouldn't change however boldTitleSmall does
|
||||||
|
//change point size between iPad/iPhone. This is a "fix" so each device will
|
||||||
|
//load the correct pointSize
|
||||||
|
return UIDevice.isIPad ? .boldTitleSmall : .boldTitleMedium
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var labelWidthConstraint: NSLayoutConstraint?
|
private var labelWidthConstraint: NSLayoutConstraint?
|
||||||
private var labelLeadingConstraint: NSLayoutConstraint?
|
private var labelLeadingConstraint: NSLayoutConstraint?
|
||||||
private var labelTopConstraint: NSLayoutConstraint?
|
private var labelTopConstraint: NSLayoutConstraint?
|
||||||
@ -148,10 +159,12 @@ extension Tabs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
|
guard !text.isEmpty else { return }
|
||||||
|
|
||||||
//label properties
|
//label properties
|
||||||
label.text = text
|
label.text = text
|
||||||
|
label.textStyle = textStyle
|
||||||
label.textPosition = textPosition
|
label.textPosition = textPosition
|
||||||
label.textStyle = size.textStyle
|
|
||||||
label.textColor = textColorConfiguration.getColor(self)
|
label.textColor = textColorConfiguration.getColor(self)
|
||||||
|
|
||||||
//constaints
|
//constaints
|
||||||
|
|||||||
@ -47,17 +47,6 @@ open class Tabs: View {
|
|||||||
public enum Size: String, CaseIterable {
|
public enum Size: String, CaseIterable {
|
||||||
case medium
|
case medium
|
||||||
case large
|
case large
|
||||||
|
|
||||||
public var textStyle: TextStyle {
|
|
||||||
if self == .medium {
|
|
||||||
return .boldBodyLarge
|
|
||||||
} else {
|
|
||||||
//specs show that the font size shouldn't change however boldTitleSmall does
|
|
||||||
//change point size between iPad/iPhone. This is a "fix" so each device will
|
|
||||||
//load the correct pointSize
|
|
||||||
return UIDevice.isIPad ? .boldTitleSmall : .boldTitleMedium
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -220,6 +209,7 @@ open class Tabs: View {
|
|||||||
// Create new tab items from the models
|
// Create new tab items from the models
|
||||||
for model in models {
|
for model in models {
|
||||||
let tabItem = Tab()
|
let tabItem = Tab()
|
||||||
|
tabItem.size = size
|
||||||
tabItem.text = model.text
|
tabItem.text = model.text
|
||||||
tabItem.onClick = model.onClick
|
tabItem.onClick = model.onClick
|
||||||
tabItem.width = model.width
|
tabItem.width = model.width
|
||||||
@ -258,10 +248,10 @@ open class Tabs: View {
|
|||||||
|
|
||||||
// Update tab appearance based on properties
|
// Update tab appearance based on properties
|
||||||
for (index, tabItem) in tabViews.enumerated() {
|
for (index, tabItem) in tabViews.enumerated() {
|
||||||
|
tabItem.size = size
|
||||||
tabItem.selected = selectedIndex == index
|
tabItem.selected = selectedIndex == index
|
||||||
tabItem.index = index
|
tabItem.index = index
|
||||||
tabItem.minWidth = minWidth
|
tabItem.minWidth = minWidth
|
||||||
tabItem.size = size
|
|
||||||
tabItem.textPosition = textPosition
|
tabItem.textPosition = textPosition
|
||||||
tabItem.orientation = orientation
|
tabItem.orientation = orientation
|
||||||
tabItem.surface = surface
|
tabItem.surface = surface
|
||||||
|
|||||||
@ -119,6 +119,10 @@ open class Toggle: Control, Changeable {
|
|||||||
|
|
||||||
open var label = Label().with {
|
open var label = Label().with {
|
||||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
$0.textColorConfiguration = ViewColorConfiguration().with {
|
||||||
|
$0.setSurfaceColors(VDSColor.elementsSecondaryOnlight, VDSColor.elementsSecondaryOndark, forDisabled: true)
|
||||||
|
$0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false)
|
||||||
|
}.eraseToAnyColorable()
|
||||||
}
|
}
|
||||||
|
|
||||||
open var isOn: Bool {
|
open var isOn: Bool {
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import UIKit
|
|||||||
import Combine
|
import Combine
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
|
|
||||||
open class TooltipAlertViewController: UIViewController, Surfaceable, UIScrollViewDelegate {
|
open class TooltipAlertViewController: UIViewController, Surfaceable {
|
||||||
|
|
||||||
/// Set of Subscribers for any Publishers for this Control
|
/// Set of Subscribers for any Publishers for this Control
|
||||||
public var subscribers = Set<AnyCancellable>()
|
public var subscribers = Set<AnyCancellable>()
|
||||||
@ -26,69 +26,22 @@ open class TooltipAlertViewController: UIViewController, Surfaceable, UIScrollVi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var scrollView = UIScrollView().with {
|
private let tooltipDialog = TooltipDialog()
|
||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
$0.backgroundColor = .clear
|
|
||||||
}
|
|
||||||
|
|
||||||
private let modalView = View().with {
|
|
||||||
$0.layer.cornerRadius = 8
|
|
||||||
}
|
|
||||||
|
|
||||||
public var contentView: UIView? = nil
|
|
||||||
|
|
||||||
private let contentStackView = UIStackView().with {
|
|
||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
$0.axis = .vertical
|
|
||||||
$0.distribution = .fillProportionally
|
|
||||||
$0.spacing = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private var line = Line().with { instance in
|
|
||||||
instance.lineViewColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsLowcontrastOnlight, VDSColor.elementsLowcontrastOndark).eraseToAnyColorable()
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Public Properties
|
// MARK: - Public Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open var surface: Surface = .light { didSet { updateView() }}
|
open var surface: Surface = .light { didSet { updateView() }}
|
||||||
open var titleText: String? { didSet { updateView() }}
|
open var titleText: String? { didSet { updateView() }}
|
||||||
open var titleLabel = Label().with { label in
|
|
||||||
label.textStyle = .boldTitleMedium
|
|
||||||
}
|
|
||||||
|
|
||||||
open var contentText: String? { didSet { updateView() }}
|
open var contentText: String? { didSet { updateView() }}
|
||||||
open var contentLabel = Label().with { label in
|
open var contentView: UIView? { didSet { updateView() }}
|
||||||
label.textStyle = .bodyLarge
|
|
||||||
}
|
|
||||||
|
|
||||||
open var closeButtonText: String = "Close" { didSet { updateView() }}
|
open var closeButtonText: String = "Close" { didSet { updateView() }}
|
||||||
|
|
||||||
open lazy var closeButton: UIButton = {
|
|
||||||
let button = UIButton(type: .system)
|
|
||||||
button.backgroundColor = .clear
|
|
||||||
button.setTitle("Close", for: .normal)
|
|
||||||
button.titleLabel?.font = TextStyle.bodyLarge.font
|
|
||||||
button.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
return button
|
|
||||||
}()
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Configuration
|
// MARK: - Configuration
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private let containerViewBackgroundColorConfiguration = SurfaceColorConfiguration().with { instance in
|
|
||||||
instance.lightColor = .white
|
|
||||||
instance.darkColor = .black
|
|
||||||
}
|
|
||||||
|
|
||||||
private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryLight)
|
private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryLight)
|
||||||
|
|
||||||
private let closeButtonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark)
|
|
||||||
|
|
||||||
private let containerViewInset = VDSLayout.Spacing.space4X.value
|
|
||||||
private var containerBottomConstraint: NSLayoutConstraint?
|
|
||||||
private var containerHeightConstraint: NSLayoutConstraint?
|
|
||||||
private var contentStackViewBottomConstraint: NSLayoutConstraint?
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -114,57 +67,145 @@ open class TooltipAlertViewController: UIViewController, Surfaceable, UIScrollVi
|
|||||||
}.store(in: &subscribers)
|
}.store(in: &subscribers)
|
||||||
|
|
||||||
//clicking button
|
//clicking button
|
||||||
onClickSubscriber = closeButton.publisher(for: .touchUpInside)
|
onClickSubscriber = tooltipDialog.closeButton.publisher(for: .touchUpInside)
|
||||||
.sink {[weak self] button in
|
.sink {[weak self] button in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
self.dismiss(animated: true, completion: nil)
|
self.dismiss(animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentStackView.addArrangedSubview(titleLabel)
|
view.addSubview(tooltipDialog)
|
||||||
contentStackView.addArrangedSubview(contentLabel)
|
|
||||||
scrollView.addSubview(contentStackView)
|
|
||||||
modalView.addSubview(scrollView)
|
|
||||||
modalView.addSubview(line)
|
|
||||||
modalView.addSubview(closeButton)
|
|
||||||
view.addSubview(modalView)
|
|
||||||
|
|
||||||
// Activate constraints
|
// Activate constraints
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
// Constraints for the floating modal view
|
// Constraints for the floating modal view
|
||||||
modalView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
|
tooltipDialog.centerXAnchor.constraint(equalTo: view.centerXAnchor),
|
||||||
modalView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
|
tooltipDialog.centerYAnchor.constraint(equalTo: view.centerYAnchor),
|
||||||
modalView.widthAnchor.constraint(equalToConstant: 296),
|
tooltipDialog.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor),
|
||||||
modalView.heightAnchor.constraint(greaterThanOrEqualToConstant: 96),
|
tooltipDialog.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor),
|
||||||
modalView.heightAnchor.constraint(lessThanOrEqualToConstant: 312),
|
tooltipDialog.topAnchor.constraint(greaterThanOrEqualTo: view.topAnchor),
|
||||||
|
tooltipDialog.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor)
|
||||||
// Constraints for the scroll view
|
|
||||||
scrollView.topAnchor.constraint(equalTo: modalView.topAnchor, constant: VDSLayout.Spacing.space4X.value),
|
|
||||||
scrollView.leadingAnchor.constraint(equalTo: modalView.leadingAnchor),
|
|
||||||
scrollView.trailingAnchor.constraint(equalTo: modalView.trailingAnchor),
|
|
||||||
scrollView.bottomAnchor.constraint(equalTo: line.topAnchor),
|
|
||||||
|
|
||||||
line.leadingAnchor.constraint(equalTo: modalView.leadingAnchor),
|
|
||||||
line.trailingAnchor.constraint(equalTo: modalView.trailingAnchor),
|
|
||||||
|
|
||||||
closeButton.topAnchor.constraint(equalTo: line.bottomAnchor),
|
|
||||||
closeButton.leadingAnchor.constraint(equalTo: modalView.leadingAnchor),
|
|
||||||
closeButton.trailingAnchor.constraint(equalTo: modalView.trailingAnchor),
|
|
||||||
closeButton.bottomAnchor.constraint(equalTo: modalView.bottomAnchor),
|
|
||||||
closeButton.heightAnchor.constraint(equalToConstant: 44.0),
|
|
||||||
|
|
||||||
contentStackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
|
|
||||||
contentStackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: containerViewInset),
|
|
||||||
contentStackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -containerViewInset),
|
|
||||||
contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -containerViewInset),
|
|
||||||
contentStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -(containerViewInset * 2)),
|
|
||||||
contentStackView.heightAnchor.constraint(greaterThanOrEqualTo: scrollView.heightAnchor, constant: -(containerViewInset * 2))
|
|
||||||
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
open func updateView() {
|
open func updateView() {
|
||||||
view.backgroundColor = backgroundColorConfiguration.getColor(self).withAlphaComponent(0.3)
|
view.backgroundColor = backgroundColorConfiguration.getColor(self).withAlphaComponent(0.3)
|
||||||
modalView.backgroundColor = containerViewBackgroundColorConfiguration.getColor(self)
|
tooltipDialog.surface = surface
|
||||||
|
tooltipDialog.titleText = titleText
|
||||||
|
tooltipDialog.contentText = contentText
|
||||||
|
tooltipDialog.contentView = contentView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class TooltipDialog: View, UIScrollViewDelegate {
|
||||||
|
|
||||||
|
private var scrollView = UIScrollView().with {
|
||||||
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
$0.backgroundColor = .clear
|
||||||
|
}
|
||||||
|
|
||||||
|
private let contentStackView = UIStackView().with {
|
||||||
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
$0.axis = .vertical
|
||||||
|
$0.distribution = .fillProportionally
|
||||||
|
$0.spacing = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private var line = Line().with { instance in
|
||||||
|
instance.lineViewColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsLowcontrastOnlight, VDSColor.elementsLowcontrastOndark).eraseToAnyColorable()
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Public Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
open var titleText: String? { didSet { setNeedsUpdate() }}
|
||||||
|
open var titleLabel = Label().with { label in
|
||||||
|
label.textStyle = .boldTitleMedium
|
||||||
|
}
|
||||||
|
|
||||||
|
open var contentText: String? { didSet { setNeedsUpdate() }}
|
||||||
|
open var contentLabel = Label().with { label in
|
||||||
|
label.textStyle = .bodyLarge
|
||||||
|
}
|
||||||
|
|
||||||
|
open var contentView: UIView? = nil
|
||||||
|
|
||||||
|
open var closeButtonText: String = "Close" { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
|
open lazy var closeButton: UIButton = {
|
||||||
|
let button = UIButton(type: .system)
|
||||||
|
button.backgroundColor = .clear
|
||||||
|
button.setTitle("Close", for: .normal)
|
||||||
|
button.titleLabel?.font = TextStyle.bodyLarge.font
|
||||||
|
button.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Configuration
|
||||||
|
//--------------------------------------------------
|
||||||
|
private var closeButtonHeight: CGFloat = 44.0
|
||||||
|
private var fullWidth: CGFloat = 296
|
||||||
|
private var minHeight: CGFloat = 96.0
|
||||||
|
private var maxHeight: CGFloat = 312.0
|
||||||
|
private let containerViewInset = VDSLayout.Spacing.space4X.value
|
||||||
|
|
||||||
|
private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark)
|
||||||
|
private let closeButtonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark)
|
||||||
|
|
||||||
|
private var contentStackViewBottomConstraint: NSLayoutConstraint?
|
||||||
|
private var heightConstraint: NSLayoutConstraint?
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
//--------------------------------------------------
|
||||||
|
open override func setup() {
|
||||||
|
super.setup()
|
||||||
|
|
||||||
|
layer.cornerRadius = 8
|
||||||
|
|
||||||
|
contentStackView.addArrangedSubview(titleLabel)
|
||||||
|
contentStackView.addArrangedSubview(contentLabel)
|
||||||
|
scrollView.addSubview(contentStackView)
|
||||||
|
addSubview(scrollView)
|
||||||
|
addSubview(line)
|
||||||
|
addSubview(closeButton)
|
||||||
|
|
||||||
|
// Activate constraints
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
widthAnchor.constraint(equalToConstant: fullWidth),
|
||||||
|
|
||||||
|
// Constraints for the scroll view
|
||||||
|
scrollView.topAnchor.constraint(equalTo: topAnchor, constant: VDSLayout.Spacing.space4X.value),
|
||||||
|
scrollView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
|
scrollView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||||
|
scrollView.bottomAnchor.constraint(equalTo: line.topAnchor),
|
||||||
|
|
||||||
|
line.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
|
line.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||||
|
|
||||||
|
closeButton.topAnchor.constraint(equalTo: line.bottomAnchor),
|
||||||
|
closeButton.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
|
closeButton.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||||
|
closeButton.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||||
|
closeButton.heightAnchor.constraint(equalToConstant: closeButtonHeight),
|
||||||
|
|
||||||
|
contentStackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
|
||||||
|
contentStackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: containerViewInset),
|
||||||
|
contentStackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -containerViewInset),
|
||||||
|
contentStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -(containerViewInset * 2)),
|
||||||
|
|
||||||
|
])
|
||||||
|
contentStackViewBottomConstraint = contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
|
||||||
|
contentStackViewBottomConstraint?.activate()
|
||||||
|
|
||||||
|
heightConstraint = heightAnchor.constraint(equalToConstant: minHeight)
|
||||||
|
heightConstraint?.activate()
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func updateView() {
|
||||||
|
super.updateView()
|
||||||
|
|
||||||
|
backgroundColor = backgroundColorConfiguration.getColor(self)
|
||||||
scrollView.indicatorStyle = surface == .light ? .black : .white
|
scrollView.indicatorStyle = surface == .light ? .black : .white
|
||||||
|
|
||||||
titleLabel.removeFromSuperview()
|
titleLabel.removeFromSuperview()
|
||||||
@ -217,7 +258,31 @@ open class TooltipAlertViewController: UIViewController, Surfaceable, UIScrollVi
|
|||||||
closeButton.setTitleColor(closeButtonTextColor, for: .highlighted)
|
closeButton.setTitleColor(closeButtonTextColor, for: .highlighted)
|
||||||
closeButton.setTitle(closeButtonText, for: .normal)
|
closeButton.setTitle(closeButtonText, for: .normal)
|
||||||
|
|
||||||
|
contentStackView.setNeedsLayout()
|
||||||
|
contentStackView.layoutIfNeeded()
|
||||||
|
|
||||||
|
scrollView.setNeedsLayout()
|
||||||
scrollView.layoutIfNeeded()
|
scrollView.layoutIfNeeded()
|
||||||
|
|
||||||
|
//dealing with height
|
||||||
|
//we can't really use the minMax height and set constraints for
|
||||||
|
//greaterThan or lessThan on the heightAnchor due to scrollView/stackView intrinsic size
|
||||||
|
//therefore we can do a little math and manually set the height based off all of the content
|
||||||
|
var contentHeight = closeButtonHeight + scrollView.contentSize.height + (containerViewInset * 2)
|
||||||
|
|
||||||
|
//reset the bottomConstraint
|
||||||
|
contentStackViewBottomConstraint?.constant = 0
|
||||||
|
|
||||||
|
if contentHeight < minHeight {
|
||||||
|
contentHeight = minHeight
|
||||||
|
|
||||||
|
} else if contentHeight > maxHeight {
|
||||||
|
contentHeight = maxHeight
|
||||||
|
//since we are now scrolling, add padding to the bottom of the
|
||||||
|
//stackView between the bottom of the scrollView
|
||||||
|
contentStackViewBottomConstraint?.constant = -containerViewInset
|
||||||
|
}
|
||||||
|
|
||||||
|
heightConstraint?.constant = contentHeight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,12 +8,17 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
/// Protocol used to be implemented on a object that can return a color for a Generic ObjectType.
|
||||||
public protocol Colorable<ObjectType> {
|
public protocol Colorable<ObjectType> {
|
||||||
associatedtype ObjectType
|
associatedtype ObjectType
|
||||||
|
/// Method used to get the current color needed for the Generic ObjectType passed into the method.
|
||||||
func getColor(_ object: ObjectType) -> UIColor
|
func getColor(_ object: ObjectType) -> UIColor
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Colorable {
|
extension Colorable {
|
||||||
|
/// Helper method used where you don't know the ObjectType to return the color.
|
||||||
|
/// - Parameter object: Any object, but this objectType will be checked against the registered Generic ObjectType for the Colorable
|
||||||
|
/// - Returns: UIColor for the rule of this Colorable implementation for the object passed into the function
|
||||||
fileprivate func getColor(_ object: Any) -> UIColor {
|
fileprivate func getColor(_ object: Any) -> UIColor {
|
||||||
guard let model = object as? ObjectType else {
|
guard let model = object as? ObjectType else {
|
||||||
assertionFailure("Invalid ObjectType, Expecting type \(ObjectType.self), received \(object) ")
|
assertionFailure("Invalid ObjectType, Expecting type \(ObjectType.self), received \(object) ")
|
||||||
@ -22,21 +27,12 @@ extension Colorable {
|
|||||||
return getColor(model)
|
return getColor(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TypeErasure for the Colorable to AnyColorable
|
||||||
|
/// - Returns: AnyColorable
|
||||||
public func eraseToAnyColorable() -> AnyColorable { AnyColorable(self) }
|
public func eraseToAnyColorable() -> AnyColorable { AnyColorable(self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct GenericColorable<ObjectType>: Colorable, Withable {
|
/// TypeErased Struct of a Colorable Type. This type can be used anywhere as long as the ObjectType being passed into this method matches the wrapped Colorable ObjectType.
|
||||||
private var wrapped: any Colorable<ObjectType>
|
|
||||||
|
|
||||||
public init<C: Colorable>(colorable: C) where C.ObjectType == ObjectType {
|
|
||||||
wrapped = colorable
|
|
||||||
}
|
|
||||||
|
|
||||||
public func getColor(_ object: ObjectType) -> UIColor {
|
|
||||||
wrapped.getColor(object)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AnyColorable: Colorable, Withable {
|
public struct AnyColorable: Colorable, Withable {
|
||||||
private let wrapped: any Colorable
|
private let wrapped: any Colorable
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Any object that can be disabled, which may change the appearance
|
||||||
public protocol Disabling {
|
public protocol Disabling {
|
||||||
|
/// Whether this object is disabled or not
|
||||||
var disabled: Bool { get set }
|
var disabled: Bool { get set }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
|
|
||||||
|
/// The background tint that the component will be placed on. This will automatically adjust other elements as needed and takes "light" or "dark"
|
||||||
public enum Surface: String, Equatable {
|
public enum Surface: String, Equatable {
|
||||||
case light, dark
|
case light, dark
|
||||||
public var color: UIColor {
|
public var color: UIColor {
|
||||||
@ -17,5 +18,6 @@ public enum Surface: String, Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public protocol Surfaceable {
|
public protocol Surfaceable {
|
||||||
|
/// Current Surface and this is used to pass down to child objects that implement Surfacable
|
||||||
var surface: Surface { get set }
|
var surface: Surface { get set }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
1.0.27
|
1.0.27
|
||||||
=======
|
=======
|
||||||
- Added Loader View
|
- Added Loader View
|
||||||
|
- CXTDT-426628 - Toggle - Incorrect disabled text color
|
||||||
|
|
||||||
1.0.26
|
1.0.26
|
||||||
=======
|
=======
|
||||||
|
|||||||
@ -8,19 +8,50 @@ Using the system allows designers and developers to collaborate more easily and
|
|||||||
|
|
||||||
## Topics
|
## Topics
|
||||||
|
|
||||||
### Components
|
### BaseComponents
|
||||||
|
- ``ButtonBase``
|
||||||
|
- ``Control``
|
||||||
|
- ``View``
|
||||||
|
- ``SelfSizingCollectionView``
|
||||||
|
- ``SelectorBase``
|
||||||
|
- ``SelectorItemBase``
|
||||||
|
- ``SelectorGroupHandlerBase``
|
||||||
|
- ``SelectorGroupSelectedHandlerBase``
|
||||||
|
|
||||||
|
### Components
|
||||||
- ``Badge``
|
- ``Badge``
|
||||||
|
- ``BadgeIndicator``
|
||||||
- ``Button``
|
- ``Button``
|
||||||
- ``TextLink``
|
- ``ButtonIcon``
|
||||||
- ``TextLinkCaret``
|
- ``ButtonGroup``
|
||||||
- ``CheckboxGroup``
|
|
||||||
- ``Checkbox``
|
- ``Checkbox``
|
||||||
|
- ``CheckboxItem``
|
||||||
|
- ``CheckboxGroup``
|
||||||
|
- ``Icon``
|
||||||
- ``Label``
|
- ``Label``
|
||||||
|
- ``Line``
|
||||||
|
- ``Loader``
|
||||||
|
- ``RadioBoxItem``
|
||||||
- ``RadioBoxGroup``
|
- ``RadioBoxGroup``
|
||||||
- ``RadioBox``
|
|
||||||
- ``RadioButtonGroup``
|
|
||||||
- ``RadioButton``
|
- ``RadioButton``
|
||||||
|
- ``RadioButtonItem``
|
||||||
|
- ``RadioButtonGroup``
|
||||||
- ``RadioSwatchGroup``
|
- ``RadioSwatchGroup``
|
||||||
- ``RadioSwatch``
|
- ``RadioSwatch``
|
||||||
|
- ``Tabs``
|
||||||
|
- ``TextLink``
|
||||||
|
- ``TextLinkCaret``
|
||||||
|
- ``TileContainer``
|
||||||
|
- ``Tilelet``
|
||||||
|
- ``TitleLockup``
|
||||||
- ``Toggle``
|
- ``Toggle``
|
||||||
|
- ``Tooltip``
|
||||||
|
|
||||||
|
### ColorConfiguration
|
||||||
|
- ``Colorable``
|
||||||
|
- ``AnyColorable``
|
||||||
|
- ``SurfaceColorConfiguration``
|
||||||
|
- ``KeyColorConfiguration``
|
||||||
|
- ``KeyedColorConfiguration``
|
||||||
|
- ``ControlColorConfiguration``
|
||||||
|
- ``ViewColorConfiguration``
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user