diff --git a/VDS/Components/Modal/Modal.swift b/VDS/Components/Modal/Modal.swift index 19696b15..7ced3ba0 100644 --- a/VDS/Components/Modal/Modal.swift +++ b/VDS/Components/Modal/Modal.swift @@ -30,8 +30,7 @@ open class Modal: Control, ModalLaunchable { public required init?(coder: NSCoder) { super.init(coder: coder) } - - + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -52,6 +51,12 @@ open class Modal: Control, ModalLaunchable { /// UIView rendered for the content area of the modal open var contentView: UIView? { didSet { setNeedsUpdate() } } + ///// Array of Buttonable Views that are shown as Modal Footer. Primary and Close button data for modal button group. + open var buttonData: [ButtonBase]? { didSet { setNeedsUpdate() } } + + ///// If provided, the Modal Dialog will render at full screen. + open var fullScreenDialog: Bool = false { didSet { setNeedsUpdate() } } + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -87,7 +92,7 @@ open class Modal: Control, ModalLaunchable { return "Modal" } } - + bridge_accessibilityHintBlock = { [weak self] in guard let self else { return "" } return isEnabled ? "Double tap to open." : "" @@ -99,17 +104,18 @@ open class Modal: Control, ModalLaunchable { modalModel: .init(closeButtonText: showModalButton.text ?? "", title: title, content: content, - contentView: contentView), + contentView: contentView, + buttonData: buttonData), presenter: self) } - + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() showModalButton.surface = surface } - + public static func accessibleText(for title: String?, content: String?, closeButtonText: String) -> String { var label = "" if let title { @@ -132,5 +138,5 @@ extension Modal: AppleGuidelinesTouchable { override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { Self.acceptablyOutsideBounds(point: point, bounds: bounds) } - + } diff --git a/VDS/Components/Modal/ModalDialog.swift b/VDS/Components/Modal/ModalDialog.swift index f70fcda7..5221a92b 100644 --- a/VDS/Components/Modal/ModalDialog.swift +++ b/VDS/Components/Modal/ModalDialog.swift @@ -51,7 +51,7 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - open var children: [any ViewProtocol] { [closeCrossButton, titleLabel, contentLabel, closeButton] } + open var children: [any ViewProtocol] { [closeCrossButton, titleLabel, contentLabel, buttonGroupData] } open var modalModel = Modal.ModalModel() { didSet { setNeedsUpdate() } } @@ -73,8 +73,11 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { $0.customContainerSize = UIDevice.isIPad ? 48 : 48 $0.customIconSize = UIDevice.isIPad ? 32 : 32 } - open lazy var closeButton = Button().with{ $0.use = .secondary; $0.text = "Close"} + open var buttonGroupData = ButtonGroup().with { + $0.alignment = .left + } + //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- @@ -123,14 +126,14 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { // Max Width: 70% of viewport width. // Maximum width is only applied to a provided width and not to the default. - maxWidth = fullWidth * (70/100) - + maxWidth = UIDevice.isIPad ? fullWidth * (70/100): fullWidth + // Max Height: Total window height 70% of viewport height. // For Tablet: By default the model's height is dynamic and is based on the amount of content // it contains. it can expand between a minimum and maximum height. The minimum height is determined by the minimum // height of the content area plus the top and bottom padding. - maxHeight = UIScreen.main.bounds.size.height * (70/100) - + maxHeight = UIDevice.isIPad ? UIScreen.main.bounds.size.height * (70/100) : UIScreen.main.bounds.size.height + titleLabel.accessibilityTraits = .header layer.cornerRadius = 12 @@ -140,10 +143,10 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { contentStackView.setCustomSpacing(contentLabelTopSpace, after: titleLabel) scrollView.addSubview(contentStackView) - // Add crossButon, scrollView, closeButton. + // Add crossButon, scrollView, buttonsData. addSubview(closeCrossButton) addSubview(scrollView) - addSubview(closeButton) + addSubview(buttonGroupData) self.bringSubviewToFront(closeCrossButton) let crossTopSpace = UIDevice.isIPad ? 0 : VDSLayout.space12X @@ -161,21 +164,21 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { closeCrossButton.heightAnchor.constraint(equalToConstant: closeCrossButtonSize), // Constraints for the bottom button view - closeButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant:containerViewInset), - closeButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -containerViewInset), - closeButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -containerViewInset), + buttonGroupData.leadingAnchor.constraint(equalTo: leadingAnchor, constant:containerViewInset), + buttonGroupData.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -containerViewInset), + buttonGroupData.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -containerViewInset), // Constraints for the scrollView scrollView.topAnchor.constraint(equalTo: topAnchor, constant: scrollTopSpace), scrollView.leadingAnchor.constraint(equalTo: leadingAnchor, constant:containerViewInset), scrollView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -contentTrailingSpace), - scrollView.bottomAnchor.constraint(equalTo: closeButton.topAnchor, constant: -contentLabelBottomSpace), + scrollView.bottomAnchor.constraint(equalTo: buttonGroupData.topAnchor, constant: -contentLabelBottomSpace), scrollView.widthAnchor.constraint(equalToConstant: (maxWidth - (containerViewInset + contentTrailingSpace))), // Constraints for the contentStackView contentStackView.topAnchor.constraint(equalTo: scrollView.topAnchor), contentStackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), - contentStackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), + contentStackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -contentTrailingSpace), contentStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -contentTrailingSpace), contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor) ]) @@ -190,15 +193,14 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { open override func updateView() { super.updateView() - heightConstraint?.deactivate() - maxWidth = UIScreen.main.bounds.size.width * (70/100) - maxHeight = UIScreen.main.bounds.size.height * (70/100) - + maxWidth = UIDevice.isIPad ? UIScreen.main.bounds.size.width * (70/100) : UIScreen.main.bounds.size.width + maxHeight = UIDevice.isIPad ? UIScreen.main.bounds.size.height * (70/100) : UIScreen.main.bounds.size.height + // Update surface and background backgroundColor = backgroundColorConfiguration.getColor(self) scrollView.indicatorStyle = surface == .light ? .black : .white closeCrossButton.surface = surface - closeButton.surface = surface + buttonGroupData.surface = surface titleLabel.surface = surface contentLabel.surface = surface @@ -212,6 +214,15 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { titleLabel.sizeToFit() contentLabel.sizeToFit() + // Add buttons data if provided + if let buttons = modalModel.buttonData, buttons.count > 0 { + buttonGroupData.buttons = buttons + let percent = UIDevice.isIPad ? 50.0 : 100.0 + buttonGroupData.rowQuantityTablet = 2 + buttonGroupData.rowQuantityPhone = 1 + buttonGroupData.childWidth = .percentage(percent) + } + // Update title, content and contentview var addedTitle = false @@ -237,26 +248,17 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { contentStackView.setCustomSpacing(contentLabelTopSpace, after: titleLabel) } - // Update closeButton - let textColor = textColorConfiguration.getColor(self) - closeButton.setTitleColor(textColor, for: .normal) - closeButton.setTitleColor(textColor, for: .highlighted) - closeButton.setTitle(modalModel.closeButtonText, for: .normal) - closeButton.accessibilityLabel = modalModel.closeButtonText - contentStackView.setNeedsLayout() contentStackView.layoutIfNeeded() - scrollView.setNeedsLayout() scrollView.layoutIfNeeded() heightConstraint?.constant = maxHeight - heightConstraint?.activate() } /// Used to update any Accessibility properties. open override func updateAccessibility() { super.updateAccessibility() - primaryAccessibilityElement.accessibilityHint = "Double tap on the \(modalModel.closeButtonText) button to close." + primaryAccessibilityElement.accessibilityHint = "Double tap on the cross button to close." primaryAccessibilityElement.accessibilityFrameInContainerSpace = .init(origin: .zero, size: frame.size) } @@ -264,8 +266,7 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { get { var elements: [Any] = [primaryAccessibilityElement] contentStackView.arrangedSubviews.forEach{ elements.append($0) } - elements.append(closeButton) - + elements.append(buttonGroupData) return elements } set {} diff --git a/VDS/Components/Modal/ModalModel.swift b/VDS/Components/Modal/ModalModel.swift index 1e653ab9..4a57c7de 100644 --- a/VDS/Components/Modal/ModalModel.swift +++ b/VDS/Components/Modal/ModalModel.swift @@ -19,10 +19,12 @@ extension Modal { public var contentView: UIView? public var accessibleText: String? public var contentViewAlignment: UIStackView.Alignment? + public var buttonData: [ButtonBase]? public init(closeButtonText: String = "Close", title: String? = nil, content: String? = nil, contentView: UIView? = nil, + buttonData: [ButtonBase]? = nil, accessibleText: String? = "Modal", contentViewAlignment: UIStackView.Alignment = .leading) { self.closeButtonText = closeButtonText @@ -31,6 +33,7 @@ extension Modal { self.contentView = contentView self.accessibleText = accessibleText self.contentViewAlignment = contentViewAlignment + self.buttonData = buttonData } } }