// // EntryField.swift // VDS // // Created by Matt Bruce on 10/3/22. // import Foundation import UIKit import VDSColorTokens import Combine open class EntryField: Control { //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- private var stackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .vertical $0.distribution = .fill } }() private var containerStackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false $0.axis = .horizontal $0.distribution = .fill $0.spacing = 12 } }() private var titleLabel = Label() private var errorLabel = Label() private var helperLabel = Label() private var successLabel = Label() internal var containerView: UIView = { return UIView().with { $0.translatesAutoresizingMaskIntoConstraints = false } }() //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- // Sizes are from InVision design specs. public let containerSize = CGSize(width: 45, height: 45) //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @Proxy(\.model.labelText) open var labelText: String? @Proxy(\.model.helperText) open var helperText: String? @Proxy(\.model.showError) open var showError: Bool @Proxy(\.model.errorText) open var errorText: String? @Proxy(\.model.showSuccess) open var showSuccess: Bool @Proxy(\.model.successText) open var successText: String? @Proxy(\.model.helperTextPlacement) open var helperTextPlacement: HelperTextPlacement @Proxy(\.model.transparentBackground) open var transparentBackground: Bool @Proxy(\.model.width) open var width: CGFloat? @Proxy(\.model.maxLength) open var maxLength: Int? @Proxy(\.model.inputId) open var inputId: String? @Proxy(\.model.value) open var value: AnyHashable? @Proxy(\.model.defaultVaue) open var defaultValue: AnyHashable? @Proxy(\.model.required) open var required: Bool @Proxy(\.model.readOnly) open var readOnly: Bool //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- internal var heightConstraint: NSLayoutConstraint? internal var widthConstraint: NSLayoutConstraint? internal var minWidthConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- open override func setup() { super.setup() //add tapGesture to self publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in self?.sendActions(for: .touchUpInside) }.store(in: &subscribers) isAccessibilityElement = true accessibilityTraits = .button addSubview(stackView) //create the wrapping view containerView.widthAnchor.constraint(equalToConstant: containerSize.width).isActive = true containerView.heightAnchor.constraint(equalToConstant: containerSize.height).isActive = true heightConstraint = containerView.heightAnchor.constraint(equalToConstant: containerSize.height) heightConstraint?.isActive = true widthConstraint = containerView.widthAnchor.constraint(equalToConstant: containerSize.width) minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: containerSize.width) minWidthConstraint?.isActive = true containerStackView.addArrangedSubview(containerView) stackView.addArrangedSubview(titleLabel) stackView.addArrangedSubview(containerStackView) stackView.addArrangedSubview(errorLabel) stackView.addArrangedSubview(successLabel) stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true stackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true stackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true stackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true } public override func reset() { super.reset() setAccessibilityLabel() } //-------------------------------------------------- // MARK: - State //-------------------------------------------------- open override func updateView(viewModel: ModelType) { titleLabel.set(with: viewModel.labelModel) //show error or success if viewModel.showError, let errorLabelModel = viewModel.errorLabelModel { errorLabel.set(with: errorLabelModel) errorLabel.isHidden = false successLabel.isHidden = true } else if viewModel.showSuccess, let successLabelModel = viewModel.successLabelModel { successLabel.set(with: successLabelModel) errorLabel.isHidden = true successLabel.isHidden = false } else { errorLabel.isHidden = true successLabel.isHidden = true } //set the helper label position if let helperLabelModel = viewModel.helperLabelModel { helperLabel.removeFromSuperview() if viewModel.helperTextPlacement == .right { containerStackView.spacing = 12 containerStackView.addArrangedSubview(helperLabel) } else { containerStackView.spacing = 0 stackView.addArrangedSubview(helperLabel) } helperLabel.set(with: helperLabelModel) helperLabel.isHidden = false } else { helperLabel.isHidden = true } setAccessibilityHint(!viewModel.disabled) backgroundColor = viewModel.surface.color setNeedsLayout() layoutIfNeeded() } }