// // Checkbox.swift // VDS // // Created by Matt Bruce on 7/22/22. // import Foundation import UIKit import VDSColorTokens import VDSFormControlsTokens import Combine public class Checkbox: CheckboxBase{} open class CheckboxBase: SelectorBase { //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- public let checkboxSize = CGSize(width: 20, height: 20) private var checkboxBackgroundColorConfiguration: CheckboxErrorColorConfiguration = { return CheckboxErrorColorConfiguration().with { $0.forTrue.enabled.lightColor = VDSColor.elementsPrimaryOnlight $0.forTrue.enabled.darkColor = VDSColor.elementsPrimaryOndark $0.forTrue.disabled.lightColor = VDSColor.interactiveDisabledOnlight $0.forTrue.disabled.darkColor = VDSColor.interactiveDisabledOndark //error doesn't care enabled/disable $0.error.forTrue.lightColor = VDSColor.elementsPrimaryOnlight $0.error.forTrue.darkColor = VDSColor.elementsPrimaryOndark $0.error.forFalse.lightColor = VDSColor.feedbackErrorBackgroundOnlight $0.error.forFalse.darkColor = VDSColor.feedbackErrorBackgroundOndark } }() private var checkboxBorderColorConfiguration: CheckboxErrorColorConfiguration = { return CheckboxErrorColorConfiguration().with { $0.forTrue.enabled.lightColor = VDSColor.elementsPrimaryOnlight $0.forTrue.enabled.darkColor = VDSColor.elementsPrimaryOndark $0.forFalse.enabled.lightColor = VDSFormControlsColor.borderOnlight $0.forFalse.enabled.darkColor = VDSFormControlsColor.borderOndark $0.forTrue.disabled.lightColor = VDSColor.interactiveDisabledOnlight $0.forTrue.disabled.darkColor = VDSColor.interactiveDisabledOndark $0.forFalse.disabled.lightColor = VDSColor.interactiveDisabledOnlight $0.forFalse.disabled.darkColor = VDSColor.interactiveDisabledOndark //error doesn't care enabled/disable $0.error.forTrue.lightColor = VDSColor.elementsPrimaryOnlight $0.error.forTrue.darkColor = VDSColor.elementsPrimaryOndark $0.error.forFalse.lightColor = VDSColor.feedbackErrorOnlight $0.error.forFalse.darkColor = VDSColor.feedbackErrorOndark } }() private var checkboxCheckColorConfiguration: BinarySurfaceColorConfiguration = { return BinarySurfaceColorConfiguration().with { $0.forTrue.lightColor = VDSColor.elementsPrimaryOndark $0.forTrue.darkColor = VDSColor.elementsPrimaryOnlight } }() //-------------------------------------------------- // MARK: - Checkbox View //-------------------------------------------------- /// Manages the appearance of the checkbox. private var shapeLayer: CAShapeLayer? open override func getSelectorSize() -> CGSize { return checkboxSize } open override func updateSelector(_ viewModel: ModelType) { //get the colors let backgroundColor = checkboxBackgroundColorConfiguration.getColor(viewModel) let borderColor = checkboxBorderColorConfiguration.getColor(viewModel) let checkColor = checkboxCheckColorConfiguration.getColor(viewModel) if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { shapeLayer.removeFromSuperlayer() self.shapeLayer = nil } selectorView.backgroundColor = backgroundColor selectorView.layer.borderColor = borderColor.cgColor selectorView.layer.cornerRadius = 2.0 selectorView.layer.borderWidth = 1.0 if shapeLayer == nil { let bounds = selectorView.bounds let length = max(bounds.size.height, bounds.size.width) guard length > 0.0, shapeLayer == nil else { return } //draw the checkmark layer let xInsetLeft = length * 0.25 let yInsetTop = length * 0.3 let innerWidth = length - (xInsetLeft + length * 0.25) // + Right X Inset let innerHeight = length - (yInsetTop + length * 0.35) // + Bottom Y Inset let startPoint = CGPoint(x: xInsetLeft, y: yInsetTop + (innerHeight / 2)) let pivotOffSet = CGPoint(x: xInsetLeft + (innerWidth * 0.33), y: yInsetTop + innerHeight) let endOffset = CGPoint(x: xInsetLeft + innerWidth, y: yInsetTop) let bezierPath = UIBezierPath() bezierPath.move(to: startPoint) bezierPath.addLine(to: pivotOffSet) bezierPath.addLine(to: endOffset) let shapeLayer = CAShapeLayer() self.shapeLayer = shapeLayer shapeLayer.frame = bounds layer.addSublayer(shapeLayer) shapeLayer.strokeColor = checkColor.cgColor shapeLayer.fillColor = UIColor.clear.cgColor shapeLayer.path = bezierPath.cgPath shapeLayer.lineJoin = .miter shapeLayer.lineWidth = 2 CATransaction.withDisabledAnimations { shapeLayer.strokeEnd = model.selected ? 1 : 0 } } } //-------------------------------------------------- // MARK: - Color Class Configurations //-------------------------------------------------- private class CheckboxErrorColorConfiguration: BinaryDisabledSurfaceColorConfiguration { public let error = BinarySurfaceColorConfiguration() override func getColor(_ viewModel: ModelType) -> UIColor { //only show error is enabled and showError == true let showErrorColor = !viewModel.disabled && viewModel.hasError if showErrorColor { return error.getColor(viewModel) } else { return super.getColor(viewModel) } } } }