diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 4d1107f5..1a695824 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; }; 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; }; 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; EA0FC2C62914222900DF80B4 /* ButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0FC2C52914222900DF80B4 /* ButtonGroup.swift */; }; @@ -120,6 +121,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 445BA07729C07B3D0036A7C5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = ""; }; 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; EA0FC2C52914222900DF80B4 /* ButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroup.swift; sourceTree = ""; }; @@ -245,6 +247,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 445BA07629C07ABA0036A7C5 /* Notification */ = { + isa = PBXGroup; + children = ( + 445BA07729C07B3D0036A7C5 /* Notification.swift */, + ); + path = Notification; + sourceTree = ""; + }; 5FC35BE128D513EB004EBEAC /* Button */ = { isa = PBXGroup; children = ( @@ -349,6 +359,7 @@ EA985BF3296C609E00F2FF2E /* Icon */, EA1F265F28B945070033E859 /* RadioSwatch */, EA3362412892EF700071C351 /* Label */, + 445BA07629C07ABA0036A7C5 /* Notification */, EA89200B28B530F0006B9984 /* RadioBox */, EAF7F11428A1470D00B287F5 /* RadioButton */, EAC925852911C9DE00091998 /* TextFields */, @@ -747,6 +758,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */, EAF7F0B5289C126F00B287F5 /* UILabel.swift in Sources */, EA5E304C294CBDD00082B959 /* TileContainer.swift in Sources */, EAF7F0A6289B0CE000B287F5 /* Resetable.swift in Sources */, diff --git a/VDS/Components/Notification/Notification.swift b/VDS/Components/Notification/Notification.swift new file mode 100644 index 00000000..39c7b04f --- /dev/null +++ b/VDS/Components/Notification/Notification.swift @@ -0,0 +1,204 @@ +// +// Notification.swift +// VDS +// +// Created by Nadigadda, Sumanth on 14/03/23. +// + +import Foundation +import UIKit +import VDSColorTokens + +@objc(VDSNotification) +/// A VDS Component that will render a view with information +public class Notification: View { + + //-------------------------------------------------- + // MARK: - Enums + //-------------------------------------------------- + + public enum NotificationStyle: String, CaseIterable { + case info, success, warning, error + + func styleIconName() -> Icon.Name { + switch self { + case .info: + return .infoBold + case .success: + return .checkmarkAltBold + case .warning: + return .warningBold + case .error: + return .errorBold + } + } + } + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + + private var mainStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.alignment = .top + $0.axis = .horizontal + $0.spacing = VDSLayout.Spacing.space2X.value + } + + private var labelsView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.alignment = .top + $0.axis = .vertical + } + + private var edgeSpacing: CGFloat { + return UIDevice.isIPad ? VDSLayout.Spacing.space5X.value : VDSLayout.Spacing.space4X.value + } + + //-------------------------------------------------- + // MARK: - View Properties + //-------------------------------------------------- + + open var typeIcon = Icon().with { + $0.name = .infoBold + } + + open var closeButton = Icon().with { + $0.name = .close + } + + open var titleLabel = Label().with { + $0.textStyle = .boldBodySmall + } + + open var subTitleLabel = Label().with { + $0.textStyle = .bodySmall + } + + open var buttonsView = ButtonGroup() + + //-------------------------------------------------- + // MARK: - Modal Properties + //-------------------------------------------------- + + open var type: NotificationStyle = .info { didSet{didChange()}} + + //-------------------------------------------------- + // MARK: - Configuration + //-------------------------------------------------- + private var backgroundColorConfiguration: StateColorConfiguration = { + let config = StateColorConfiguration() + config.setSurfaceColors(VDSColor.feedbackInformationBackgroundOnlight, VDSColor.feedbackInformationBackgroundOndark, forState: .info) + config.setSurfaceColors(VDSColor.feedbackWarningBackgroundOnlight, VDSColor.feedbackWarningBackgroundOndark, forState: .warning) + config.setSurfaceColors(VDSColor.feedbackSuccessBackgroundOnlight, VDSColor.feedbackSuccessBackgroundOndark, forState: .success) + config.setSurfaceColors(VDSColor.feedbackErrorBackgroundOnlight, VDSColor.feedbackErrorBackgroundOndark, forState: .error) + return config + }() + + private var textColorConfig = ViewColorConfiguration() + public func updateTextColorConfig() { + textColorConfig.reset() + + switch type { + case .info, .success, .warning, .error: + textColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: false) + textColorConfig.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forDisabled: true) + } + } + + //-------------------------------------------------- + // 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: - Lifecycle + //-------------------------------------------------- + + open override func setup() { + super.setup() + addSubview(mainStackView) + mainStackView.pinToSuperView(.init(top: edgeSpacing, left: edgeSpacing, bottom: edgeSpacing, right: edgeSpacing)) + + mainStackView.addArrangedSubview(typeIcon) + mainStackView.addArrangedSubview(labelsView) + mainStackView.addArrangedSubview(closeButton) + + labelsView.addArrangedSubview(titleLabel) + labelsView.addArrangedSubview(subTitleLabel) + labelsView.addArrangedSubview(buttonsView) + + publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in + self?.didClickOnCloseButton() + }.store(in: &subscribers) + } + + open override func reset() { + super.reset() + } + + //-------------------------------------------------- + // MARK: - State + //-------------------------------------------------- + open override func updateView() { + updateTextColorConfig() + + backgroundColor = backgroundColorConfiguration.getColor(surface, forState: type) + typeIcon.name = type.styleIconName() + typeIcon.color = surface == .dark ? Icon.Color.white : Icon.Color.black + closeButton.color = surface == .dark ? Icon.Color.white : Icon.Color.black + titleLabel.textColorConfiguration = textColorConfig.eraseToAnyColorable() + subTitleLabel.textColorConfiguration = textColorConfig.eraseToAnyColorable() + + titleLabel.surface = surface + subTitleLabel.surface = surface + + } + + func didClickOnCloseButton() { + print("Notification close button clicked!!!") + } + + ///Temporary Place holder + public struct TitleModel { + public var text: String + public var textAttributes: [any LabelAttributeModel]? + public var textStyle: TextStyle = .boldBodySmall + public var numberOfLines: Int + + public init(text: String, + textAttributes: [any LabelAttributeModel]? = nil, + numberOfLines: Int = 0) { + self.text = text + self.textAttributes = textAttributes + self.numberOfLines = numberOfLines + } + } + + public struct SubTitleModel { + public var text: String + public var textAttributes: [any LabelAttributeModel]? + public var textStyle: TextStyle = .bodySmall + public var numberOfLines: Int + + public init(text: String, + textColor: Use = .primary, + textAttributes: [any LabelAttributeModel]? = nil, + numberOfLines: Int = 0) { + self.text = text + self.textAttributes = textAttributes + self.numberOfLines = numberOfLines + } + } +} +