From e951e82b88c19428cc8fe6785fc1782987a8b234 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 20 Sep 2024 12:39:41 +0530 Subject: [PATCH 01/15] Digital ACT-191 ONEAPP-10928 story: added new file for Modal --- VDS/Components/Modal/Modal.swift | 120 +++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 VDS/Components/Modal/Modal.swift diff --git a/VDS/Components/Modal/Modal.swift b/VDS/Components/Modal/Modal.swift new file mode 100644 index 00000000..6f28175c --- /dev/null +++ b/VDS/Components/Modal/Modal.swift @@ -0,0 +1,120 @@ +// +// Modal.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 5/09/24. +// + +import Foundation +import UIKit +import VDSCoreTokens +import Combine + +/// A Modal is an overlay that interrupts the user flow +/// to force the customer to provide information or a response. +/// After the customer interacts with the modal, they can return to the parent content. +@objcMembers +@objc(VDSModal) +open class Modal: Control, ModalLaunchable { + + //-------------------------------------------------- + // 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: - Private Properties + //-------------------------------------------------- + internal var showModalButton = Button().with { + $0.use = .primary + $0.text = "Show Modal" + } + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + /// Text rendered for the title of the modal + open var title: String? { didSet { setNeedsUpdate() } } + + /// Text rendered for the content of the modal + open var content: String? { didSet { setNeedsUpdate() } } + + /// UIView rendered for the content area of the modal + open var contentView: UIView? { didSet { setNeedsUpdate() } } + + //-------------------------------------------------- + // MARK: - Overrides + //-------------------------------------------------- + /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. + open override func setup() { + super.setup() + + addSubview(showModalButton) + showModalButton.pinToSuperView() + backgroundColor = .clear + + isAccessibilityElement = true + accessibilityTraits = .button + } + + open override func setDefaults() { + super.setDefaults() + title = nil + content = nil + contentView = nil + + showModalButton.onClick = { _ in self.showModalButtonClick() } + } + + internal func showModalButtonClick() { + print("Clicked showModalButton") + self.presentModal(surface: self.surface, + modalModel: .init(closeButtonText: showModalButton.text ?? "", + title: title, + content: content, + contentView: contentView), + 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 { + label = title + } + if let content { + if !label.isEmpty { + label += "," + } + label += content + } + return label + } +} + + +// MARK: AppleGuidelinesTouchable +extension Modal: AppleGuidelinesTouchable { + /// Overrides to ensure that the touch point meets a minimum of the minimumTappableArea. + override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + Self.acceptablyOutsideBounds(point: point, bounds: bounds) + } + +} From aa092023de45fe9b8316406dee5b5da0cad88a06 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 20 Sep 2024 12:40:57 +0530 Subject: [PATCH 02/15] Digital ACT-191 ONEAPP-10928 story: added class for docs --- VDS/VDS.docc/VDS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/VDS/VDS.docc/VDS.md b/VDS/VDS.docc/VDS.md index 0a494234..ac8d39e7 100755 --- a/VDS/VDS.docc/VDS.md +++ b/VDS/VDS.docc/VDS.md @@ -38,6 +38,7 @@ Using the system allows designers and developers to collaborate more easily and - ``Label`` - ``Line`` - ``Loader`` +- ``Modal`` - ``Notification`` - ``Pagination`` - ``PriceLockup`` From 1f37597117e91c500dc6ccc45d3739e56a678d78 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 20 Sep 2024 13:04:47 +0530 Subject: [PATCH 03/15] Digital ACT-191 ONEAPP-10928 story: added change log file --- VDS/Components/Modal/Modal.swift | 20 +++++++- VDS/Components/Modal/ModalChangeLog.txt | 67 +++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 VDS/Components/Modal/ModalChangeLog.txt diff --git a/VDS/Components/Modal/Modal.swift b/VDS/Components/Modal/Modal.swift index 6f28175c..c0c28392 100644 --- a/VDS/Components/Modal/Modal.swift +++ b/VDS/Components/Modal/Modal.swift @@ -2,7 +2,7 @@ // Modal.swift // VDS // -// Created by Kanamarlapudi, Vasavi on 5/09/24. +// Created by Kanamarlapudi, Vasavi on 05/09/24. // import Foundation @@ -75,6 +75,24 @@ open class Modal: Control, ModalLaunchable { contentView = nil showModalButton.onClick = { _ in self.showModalButtonClick() } + + bridge_accessibilityLabelBlock = { [weak self] in + guard let self else { return "" } + var label = title + if label == nil { + label = content + } + if let label, !label.isEmpty { + return label + } else { + return "Modal" + } + } + + bridge_accessibilityHintBlock = { [weak self] in + guard let self else { return "" } + return isEnabled ? "Double tap to open." : "" + } } internal func showModalButtonClick() { diff --git a/VDS/Components/Modal/ModalChangeLog.txt b/VDS/Components/Modal/ModalChangeLog.txt new file mode 100644 index 00000000..7716b8b2 --- /dev/null +++ b/VDS/Components/Modal/ModalChangeLog.txt @@ -0,0 +1,67 @@ +MM/DD/YYYY +---------------- +- Initial Brand 3.0 handoff + +12/17/2021 +---------------- +- Replaced focusring colors (previously interactive/onlight/ondark) with accessibility/onlight/ondark colors +- Updated focus border name (previously interactive.focusring.onlight) with focusring.onlight/ondark + +12/31/2021 +---------------- +- Updated Hover and Active state trigger specs. If triggered by mouse, Active same as Hover. If not, Active same as Default. + +03/01/2022 +---------------- +- Replaced Close Non-Scaling icon with VDS Icon. +- Removed “vector effect” from Anatomy. +- Removed “weight” from Configurations. + +08/10/2022 +---------------- +- Updated default and inverted prop to light and dark surface. +- Noted that button is optional within anatomy + +09/06/2022 +---------------- +- Updated Anatomy element names to remove the word “Modal” from text elements, updated Button to be Button Group, +and noted Button Group as optional across all visuals within Anatomy. + +11/30/2022 +---------------- +- Added "(web only)" to any instance of "keyboard focus" + +12/13/2022 +---------------- +- Replaced focus border pixel and style & spacing values with tokens. + +04/24/2023 +---------------- +- Updated all instances of Close Button (VDS Icon) with VDS Button Icon (size small) +- Button Icon placed 8px from top/right edge +- Use the Ghost variant of Button Icon +- Added Button Icon props to Elements spec + +10/17/2023 +---------------- +- Added component tokens table +- Applied component tokens to light, dark surface configurations + +11/22/2023 +---------------- +- Updated tab/desk visuals to reflect new corner radius value - 12px +- Updated border radius value in Anatomy + +11/27/2023 +---------------- +- Updated ‘border radius” to “corner radius” in Anatomy + +12/1/2023 +---------------- +- Applied palette tokens instead of hardcoded values where component tokens included an opacity +- Removed layer opacity annotation for instances where opacity is built into a component token + +07/18/2024 +---------------- +- Added Scrollbar hit area with z-index specifications to the Behaviors page +- Decreased the height of the Grab zone to equal the height of the scrollbar thumb on the Behaviors page From 3c1caa7f5a066edb9bb0763f803cb493ffb2dc77 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Tue, 24 Sep 2024 10:52:09 +0530 Subject: [PATCH 04/15] Digital ACT-191 ONEAPP-10928 story: launching modal with fullscreen for mobile --- VDS.xcodeproj/project.pbxproj | 32 +++ VDS/Components/Modal/Modal.swift | 8 +- VDS/Components/Modal/ModalDialog.swift | 239 ++++++++++++++++++ .../Modal/ModalDialogViewController.swift | 131 ++++++++++ VDS/Components/Modal/ModalLaunchable.swift | 28 ++ VDS/Components/Modal/ModalModel.swift | 36 +++ 6 files changed, 469 insertions(+), 5 deletions(-) create mode 100644 VDS/Components/Modal/ModalDialog.swift create mode 100644 VDS/Components/Modal/ModalDialogViewController.swift create mode 100644 VDS/Components/Modal/ModalLaunchable.swift create mode 100644 VDS/Components/Modal/ModalModel.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 7090dcfd..3ad5de73 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -10,6 +10,10 @@ 180636C72C29B0A400C92D86 /* InputStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 180636C62C29B0A400C92D86 /* InputStepper.swift */; }; 180636C92C29B0DF00C92D86 /* InputStepperLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 180636C82C29B0DF00C92D86 /* InputStepperLog.txt */; }; 1808BEBC2BA41C3200129230 /* CarouselScrollbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */; }; + 1818D04D2C9BD2170053E73C /* ModalDialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1818D04C2C9BD2170053E73C /* ModalDialogViewController.swift */; }; + 1818D04F2C9BD3F60053E73C /* ModalDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1818D04E2C9BD3F60053E73C /* ModalDialog.swift */; }; + 1818D0512C9BD4090053E73C /* ModalLaunchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1818D0502C9BD4090053E73C /* ModalLaunchable.swift */; }; + 1818D0532C9BD47C0053E73C /* ModalModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1818D0522C9BD47C0053E73C /* ModalModel.swift */; }; 1832AC572BA0791D008AE476 /* BreadcrumbCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */; }; 183B16F32C78CF7C00BA6A10 /* CarouselSlotCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183B16F22C78CF7C00BA6A10 /* CarouselSlotCell.swift */; }; 183B16F72C80B32200BA6A10 /* FootnoteGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183B16F62C80B32200BA6A10 /* FootnoteGroup.swift */; }; @@ -30,6 +34,8 @@ 18B42AC62C09D197008D6262 /* CarouselSlotAlignmentModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B42AC52C09D197008D6262 /* CarouselSlotAlignmentModel.swift */; }; 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */; }; 18B9763F2C11BA4A009271DF /* CarouselPaginationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B9763E2C11BA4A009271DF /* CarouselPaginationModel.swift */; }; + 18C0F9462C98175900E1DD71 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C0F9452C98175900E1DD71 /* Modal.swift */; }; + 18C0F94A2C9817C100E1DD71 /* ModalChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 18C0F9492C9817C100E1DD71 /* ModalChangeLog.txt */; }; 18FEA1AD2BDD137500A56439 /* CalendarIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */; }; 18FEA1B52BE0E63600A56439 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */; }; 445BA07829C07B3D0036A7C5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445BA07729C07B3D0036A7C5 /* Notification.swift */; }; @@ -219,6 +225,10 @@ 180636C82C29B0DF00C92D86 /* InputStepperLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = InputStepperLog.txt; sourceTree = ""; }; 1808BEBB2BA41C3200129230 /* CarouselScrollbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselScrollbar.swift; sourceTree = ""; }; 1808BEBF2BA456B700129230 /* CarouselScrollbarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CarouselScrollbarChangeLog.txt; sourceTree = ""; }; + 1818D04C2C9BD2170053E73C /* ModalDialogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalDialogViewController.swift; sourceTree = ""; }; + 1818D04E2C9BD3F60053E73C /* ModalDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalDialog.swift; sourceTree = ""; }; + 1818D0502C9BD4090053E73C /* ModalLaunchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalLaunchable.swift; sourceTree = ""; }; + 1818D0522C9BD47C0053E73C /* ModalModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalModel.swift; sourceTree = ""; }; 1832AC562BA0791D008AE476 /* BreadcrumbCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbCellItem.swift; sourceTree = ""; }; 183B16F22C78CF7C00BA6A10 /* CarouselSlotCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselSlotCell.swift; sourceTree = ""; }; 183B16F62C80B32200BA6A10 /* FootnoteGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FootnoteGroup.swift; sourceTree = ""; }; @@ -244,6 +254,8 @@ 18B463A32BBD3C46005C4528 /* DropdownOptionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownOptionModel.swift; sourceTree = ""; }; 18B9763E2C11BA4A009271DF /* CarouselPaginationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselPaginationModel.swift; sourceTree = ""; }; 18BDEE812B75316E00452358 /* ButtonIconChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ButtonIconChangeLog.txt; sourceTree = ""; }; + 18C0F9452C98175900E1DD71 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = ""; }; + 18C0F9492C9817C100E1DD71 /* ModalChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ModalChangeLog.txt; sourceTree = ""; }; 18FEA1AC2BDD137500A56439 /* CalendarIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarIndicatorModel.swift; sourceTree = ""; }; 18FEA1B42BE0E63600A56439 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = ""; }; 18FEA1B82BE1301700A56439 /* CalendarChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CalendarChangeLog.txt; sourceTree = ""; }; @@ -550,6 +562,19 @@ path = Carousel; sourceTree = ""; }; + 18C0F9442C980CE500E1DD71 /* Modal */ = { + isa = PBXGroup; + children = ( + 18C0F9452C98175900E1DD71 /* Modal.swift */, + 1818D04E2C9BD3F60053E73C /* ModalDialog.swift */, + 1818D04C2C9BD2170053E73C /* ModalDialogViewController.swift */, + 1818D0502C9BD4090053E73C /* ModalLaunchable.swift */, + 1818D0522C9BD47C0053E73C /* ModalModel.swift */, + 18C0F9492C9817C100E1DD71 /* ModalChangeLog.txt */, + ); + path = Modal; + sourceTree = ""; + }; 440B84C82BD8E0CE004A732A /* Table */ = { isa = PBXGroup; children = ( @@ -731,6 +756,7 @@ EA3362412892EF700071C351 /* Label */, 44604AD529CE195300E62B51 /* Line */, EAD0688C2A55F801002E3A2D /* Loader */, + 18C0F9442C980CE500E1DD71 /* Modal */, 445BA07629C07ABA0036A7C5 /* Notification */, 71B23C2B2B91FA510027F7D9 /* Pagination */, 184023432C61E78D00A412C8 /* PriceLockup */, @@ -1239,6 +1265,7 @@ EA3362062891E14D0071C351 /* VerizonNHGeTX-Regular.otf in Resources */, EA3362052891E14D0071C351 /* VerizonNHGeDS-Bold.otf in Resources */, 180636C92C29B0DF00C92D86 /* InputStepperLog.txt in Resources */, + 18C0F94A2C9817C100E1DD71 /* ModalChangeLog.txt in Resources */, EAA5EEB928ECD24B003B3210 /* Icons.xcassets in Resources */, EAA5EEE428F5B855003B3210 /* VerizonNHGDS-Light.otf in Resources */, ); @@ -1324,6 +1351,7 @@ EA8141102A127066004F60D2 /* UIColor+VDSColor.swift in Sources */, EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */, EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */, + 18C0F9462C98175900E1DD71 /* Modal.swift in Sources */, EAC925842911C63100091998 /* Colorable.swift in Sources */, 18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */, EAF2F4762C231EAA007BFEDC /* AccessibilityActionElement.swift in Sources */, @@ -1341,6 +1369,7 @@ EA596ABD2A16B4EC00300C4B /* Tab.swift in Sources */, 71ACE89E2BA1CC1700FB6ADC /* TiletEyebrowModel.swift in Sources */, EAF7F11728A1475A00B287F5 /* RadioButtonItem.swift in Sources */, + 1818D0512C9BD4090053E73C /* ModalLaunchable.swift in Sources */, EA985BEE2968A92400F2FF2E /* TitleLockupSubTitleModel.swift in Sources */, EA2DC9B22BE175E6004F58C5 /* CharacterCountRule.swift in Sources */, EA985BF22968B5BB00F2FF2E /* TitleLockupTextStyle.swift in Sources */, @@ -1392,6 +1421,7 @@ EAF2F4892C2A1075007BFEDC /* AlertViewController.swift in Sources */, EA0D1C3D2A6AD57600E5C127 /* Typography+Enums.swift in Sources */, EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */, + 1818D04F2C9BD3F60053E73C /* ModalDialog.swift in Sources */, EAC58C0C2BED01D500BA39FA /* Telephone.swift in Sources */, EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */, EA8E40912A7D3F6300934ED3 /* UIView+Accessibility.swift in Sources */, @@ -1443,6 +1473,7 @@ EAB5FED429267EB300998C17 /* UIView+NSLayoutConstraint.swift in Sources */, EAB2376829E9992800AABE9A /* TooltipAlertViewController.swift in Sources */, EA33623E2892EE950071C351 /* UIDevice.swift in Sources */, + 1818D04D2C9BD2170053E73C /* ModalDialogViewController.swift in Sources */, EA985C692971B90B00F2FF2E /* IconSize.swift in Sources */, 71FC86E02B973AE500700965 /* DropShadowConfiguration.swift in Sources */, EA3362302891EB4A0071C351 /* Font.swift in Sources */, @@ -1451,6 +1482,7 @@ EAB5FEF12927F4AA00998C17 /* SelfSizingCollectionView.swift in Sources */, 184023452C61E7AD00A412C8 /* PriceLockup.swift in Sources */, EA3361B8288B2AAA0071C351 /* ViewProtocol.swift in Sources */, + 1818D0532C9BD47C0053E73C /* ModalModel.swift in Sources */, EA3361A8288B23300071C351 /* UIColor.swift in Sources */, EA2DC9B42BE2C6FE004F58C5 /* TextField.swift in Sources */, EAC58C182BED0E2300BA39FA /* SecurityCode.swift in Sources */, diff --git a/VDS/Components/Modal/Modal.swift b/VDS/Components/Modal/Modal.swift index c0c28392..19696b15 100644 --- a/VDS/Components/Modal/Modal.swift +++ b/VDS/Components/Modal/Modal.swift @@ -10,8 +10,7 @@ import UIKit import VDSCoreTokens import Combine -/// A Modal is an overlay that interrupts the user flow -/// to force the customer to provide information or a response. +/// A Modal is an overlay that interrupts the user flow to force the customer to provide information or a response. /// After the customer interacts with the modal, they can return to the parent content. @objcMembers @objc(VDSModal) @@ -52,7 +51,7 @@ open class Modal: Control, ModalLaunchable { /// UIView rendered for the content area of the modal open var contentView: UIView? { didSet { setNeedsUpdate() } } - + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -75,7 +74,7 @@ open class Modal: Control, ModalLaunchable { contentView = nil showModalButton.onClick = { _ in self.showModalButtonClick() } - + bridge_accessibilityLabelBlock = { [weak self] in guard let self else { return "" } var label = title @@ -96,7 +95,6 @@ open class Modal: Control, ModalLaunchable { } internal func showModalButtonClick() { - print("Clicked showModalButton") self.presentModal(surface: self.surface, modalModel: .init(closeButtonText: showModalButton.text ?? "", title: title, diff --git a/VDS/Components/Modal/ModalDialog.swift b/VDS/Components/Modal/ModalDialog.swift new file mode 100644 index 00000000..50b4e361 --- /dev/null +++ b/VDS/Components/Modal/ModalDialog.swift @@ -0,0 +1,239 @@ +// +// ModalDialog.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 09/09/24. +// + +import Foundation +import UIKit +import VDSCoreTokens + +@objcMembers +@objc(VDSModalDialog) +open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { + + //-------------------------------------------------- + // 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: - Private Properties + //-------------------------------------------------- + private var scrollView = UIScrollView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.backgroundColor = .clear + } + + private let contentStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .vertical + $0.alignment = .leading + $0.distribution = .fillProportionally + $0.spacing = 0 + } + + lazy var primaryAccessibilityElement = UIAccessibilityElement(accessibilityContainer: self).with { + $0.accessibilityLabel = "Modal" + } + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + open var children: [any ViewProtocol] { [closeCrossButton, titleLabel, contentLabel, closeButton] } + + open var modalModel = Modal.ModalModel() { didSet { setNeedsUpdate() } } + + open var titleLabel = Label().with { label in + label.isAccessibilityElement = true + label.textStyle = .boldTitleMedium + } + + open var contentLabel = Label().with { label in + label.isAccessibilityElement = true + label.textStyle = .bodyLarge + } + + open lazy var closeCrossButton = ButtonIcon().with { + $0.kind = .ghost + $0.surfaceType = .colorFill + $0.iconName = .close + $0.size = .small + $0.customContainerSize = UIDevice.isIPad ? 48 : 48 + $0.customIconSize = UIDevice.isIPad ? 32 : 32 + } + open lazy var closeButton = Button().with{ $0.use = .secondary; $0.text = "Close"} + + //-------------------------------------------------- + // 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.space4X //UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space4X + private let contentLabelTopSpace = UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space6X + private let contentLabelBottomSpace = UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space12X + private let gapBetweenButtonItems = VDSLayout.space3X + + 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: - Overrides + //-------------------------------------------------- + /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. + open override func setup() { + super.setup() + titleLabel.accessibilityTraits = .header + + layer.cornerRadius = 12 + contentStackView.addArrangedSubview(titleLabel) + contentStackView.addArrangedSubview(contentLabel) + scrollView.addSubview(contentStackView) + addSubview(closeCrossButton) + addSubview(scrollView) + addSubview(closeButton) + + // Activate constraints + NSLayoutConstraint.activate([ + widthAnchor.constraint(equalToConstant: fullWidth), + + // Constraints for the scroll view + scrollView.topAnchor.constraint(equalTo: topAnchor, constant: VDSLayout.space4X), + 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: scrollView.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() + } + + /// Used to make changes to the View based off a change events or from local properties. + open override func updateView() { + super.updateView() + + backgroundColor = backgroundColorConfiguration.getColor(self) + scrollView.indicatorStyle = surface == .light ? .black : .white + + contentStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } + + titleLabel.surface = surface + contentLabel.surface = surface + + titleLabel.text = modalModel.title + contentLabel.text = modalModel.content + + titleLabel.sizeToFit() + contentLabel.sizeToFit() + + var addedTitle = false + + if let titleText = modalModel.title, !titleText.isEmpty { + contentStackView.addArrangedSubview(titleLabel) + addedTitle = true + } + + var addedContent = false + if let contentText = modalModel.content, !contentText.isEmpty { + contentStackView.addArrangedSubview(contentLabel) + addedContent = true + } else if let contentView = modalModel.contentView { + contentView.translatesAutoresizingMaskIntoConstraints = false + if var surfaceable = contentView as? Surfaceable { + surfaceable.surface = surface + } + contentStackView.addArrangedSubview(contentView) + addedContent = true + } + + if addedTitle && addedContent { + contentStackView.setCustomSpacing(VDSLayout.space1X, after: titleLabel) + } + + let closeButtonTextColor = closeButtonTextColorConfiguration.getColor(self) + closeButton.setTitleColor(closeButtonTextColor, for: .normal) + closeButton.setTitleColor(closeButtonTextColor, for: .highlighted) + closeButton.setTitle(modalModel.closeButtonText, for: .normal) + closeButton.accessibilityLabel = modalModel.closeButtonText + + contentStackView.setNeedsLayout() + contentStackView.layoutIfNeeded() + + scrollView.setNeedsLayout() + 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 + } + + /// Used to update any Accessibility properties. + open override func updateAccessibility() { + super.updateAccessibility() + primaryAccessibilityElement.accessibilityHint = "Double tap on the \(modalModel.closeButtonText) button to close." + primaryAccessibilityElement.accessibilityFrameInContainerSpace = .init(origin: .zero, size: frame.size) + } + + open override var accessibilityElements: [Any]? { + get { + var elements: [Any] = [primaryAccessibilityElement] + contentStackView.arrangedSubviews.forEach{ elements.append($0) } + elements.append(closeButton) + + return elements + } + set {} + } + +} diff --git a/VDS/Components/Modal/ModalDialogViewController.swift b/VDS/Components/Modal/ModalDialogViewController.swift new file mode 100644 index 00000000..69103484 --- /dev/null +++ b/VDS/Components/Modal/ModalDialogViewController.swift @@ -0,0 +1,131 @@ +// +// ModalDialogViewController.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 09/09/24. +// + +import Foundation +import UIKit +import Combine +import VDSCoreTokens + +@objcMembers +@objc(VDSModalDialogViewController) +open class ModalDialogViewController: UIViewController, Surfaceable { + + /// Set of Subscribers for any Publishers for this Control. + open var subscribers = Set() + + //-------------------------------------------------- + // MARK: - Private Properties + //-------------------------------------------------- + private var onClickSubscriber: AnyCancellable? { + willSet { + if let onClickSubscriber { + onClickSubscriber.cancel() + } + } + } + + private var onClickCloseSubscriber: AnyCancellable? { + willSet { + if let onClickCloseSubscriber { + onClickCloseSubscriber.cancel() + } + } + } + + private let modalDialog = ModalDialog() + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + /// Current Surface and this is used to pass down to child objects that implement Surfacable + open var surface: Surface = .light { didSet { updateView() }} + open var modalModel = Modal.ModalModel() { didSet { updateView() }} + open var presenter: UIView? { didSet { updateView() }} + + //-------------------------------------------------- + // MARK: - Configuration + //-------------------------------------------------- + private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite) + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func viewDidLoad() { + super.viewDidLoad() + isModalInPresentation = UIDevice.isIPad ? true : false + setup() + } + open override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + UIAccessibility.post(notification: .screenChanged, argument: modalDialog) + } + + private func dismiss() { + dismiss(animated: true) { [weak self] in + guard let self, let presenter else { return } + UIAccessibility.post(notification: .layoutChanged, argument: presenter) + } + } + + open func setup() { + view.accessibilityElements = [modalDialog] + + //left-right swipe + view.publisher(for: UISwipeGestureRecognizer().with{ $0.direction = .right }) + .sink { [weak self] swipe in + guard let self, !UIAccessibility.isVoiceOverRunning else { return } + self.dismiss() + }.store(in: &subscribers) + + //tapping in background + view.publisher(for: UITapGestureRecognizer().with{ $0.numberOfTapsRequired = 1 }) + .sink { [weak self] swipe in + guard let self, !UIAccessibility.isVoiceOverRunning else { return } + self.dismiss() + }.store(in: &subscribers) + + //clicking button + onClickSubscriber = modalDialog.closeButton.publisher(for: .touchUpInside) + .sink {[weak self] button in + guard let self else { return } + self.dismiss() + } + + onClickCloseSubscriber = modalDialog.closeCrossButton.publisher(for: .touchUpInside) + .sink {[weak self] button in + guard let self else { return } + self.dismiss() + } + + view.addSubview(modalDialog) + + // Activate constraints + UIDevice.isIPad ? + NSLayoutConstraint.activate([ + // Constraints for the floating modal view + modalDialog.centerXAnchor.constraint(equalTo: view.centerXAnchor), + modalDialog.centerYAnchor.constraint(equalTo: view.centerYAnchor), + modalDialog.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor), + modalDialog.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor), + modalDialog.topAnchor.constraint(greaterThanOrEqualTo: view.topAnchor), + modalDialog.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor) + ]) : NSLayoutConstraint.activate([ + // Constraints for the floating modal view + modalDialog.leadingAnchor.constraint(equalTo: view.leadingAnchor), + modalDialog.trailingAnchor.constraint(equalTo: view.trailingAnchor), + modalDialog.topAnchor.constraint(equalTo: view.topAnchor), + modalDialog.bottomAnchor.constraint(equalTo: view.bottomAnchor) + ]) + } + + /// Used to make changes to the View based off a change events or from local properties. + open func updateView() { + view.backgroundColor = backgroundColorConfiguration.getColor(self).withAlphaComponent(0.8) + modalDialog.surface = surface + modalDialog.modalModel = modalModel + } +} diff --git a/VDS/Components/Modal/ModalLaunchable.swift b/VDS/Components/Modal/ModalLaunchable.swift new file mode 100644 index 00000000..b99b638c --- /dev/null +++ b/VDS/Components/Modal/ModalLaunchable.swift @@ -0,0 +1,28 @@ +// +// ModalLaunchable.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 09/09/24. +// + +import Foundation +import UIKit + +public protocol ModalLaunchable { + func presentModal(surface: Surface, modalModel: Modal.ModalModel, presenter: UIView?) +} + +extension ModalLaunchable { + public func presentModal(surface: Surface, modalModel: Modal.ModalModel, presenter: UIView? = nil) { + if let presenting = UIApplication.topViewController() { + let modalViewController = ModalDialogViewController(nibName: nil, bundle: nil).with { + $0.surface = surface + $0.modalModel = modalModel + $0.presenter = presenter + $0.modalPresentationStyle = UIDevice.isIPad ? .overCurrentContext : .fullScreen + $0.modalTransitionStyle = .crossDissolve + } + presenting.present(modalViewController, animated: true) + } + } +} diff --git a/VDS/Components/Modal/ModalModel.swift b/VDS/Components/Modal/ModalModel.swift new file mode 100644 index 00000000..1e653ab9 --- /dev/null +++ b/VDS/Components/Modal/ModalModel.swift @@ -0,0 +1,36 @@ +// +// ModalModel.swift +// VDS +// +// Created by Kanamarlapudi, Vasavi on 09/09/24. +// + +import Foundation +import UIKit + +extension Modal { + + /// Model used to represent the modal. + public struct ModalModel: Equatable { + /// Current Surface and this is used to pass down to child objects that implement Surfacable + public var closeButtonText: String + public var title: String? + public var content: String? + public var contentView: UIView? + public var accessibleText: String? + public var contentViewAlignment: UIStackView.Alignment? + public init(closeButtonText: String = "Close", + title: String? = nil, + content: String? = nil, + contentView: UIView? = nil, + accessibleText: String? = "Modal", + contentViewAlignment: UIStackView.Alignment = .leading) { + self.closeButtonText = closeButtonText + self.title = title + self.content = content + self.contentView = contentView + self.accessibleText = accessibleText + self.contentViewAlignment = contentViewAlignment + } + } +} From 56155abbb08ae4e33844fdab61b250742183b71a Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Wed, 25 Sep 2024 12:02:33 +0530 Subject: [PATCH 05/15] Digital ACT-191 ONEAPP-10928 story: updated modal with alignments and spacing --- VDS/Components/Modal/ModalDialog.swift | 101 +++++++++--------- .../Modal/ModalDialogViewController.swift | 18 +--- 2 files changed, 54 insertions(+), 65 deletions(-) diff --git a/VDS/Components/Modal/ModalDialog.swift b/VDS/Components/Modal/ModalDialog.swift index 50b4e361..a54a208f 100644 --- a/VDS/Components/Modal/ModalDialog.swift +++ b/VDS/Components/Modal/ModalDialog.swift @@ -74,16 +74,17 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { $0.customIconSize = UIDevice.isIPad ? 32 : 32 } open lazy var closeButton = Button().with{ $0.use = .secondary; $0.text = "Close"} - + //-------------------------------------------------- // 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.space4X //UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space4X + private var fullWidth: CGFloat = 0.0 + private var minHeight: CGFloat = 232.0 + private var maxHeight: CGFloat = 0.0 + private var minWidth: CGFloat = 560.0 + private var maxWidth: CGFloat = 0.0 + + private let containerViewInset = UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space4X private let contentLabelTopSpace = UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space6X private let contentLabelBottomSpace = UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space12X private let gapBetweenButtonItems = VDSLayout.space3X @@ -99,46 +100,67 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { //-------------------------------------------------- /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { - super.setup() + super.setup() + + fullWidth = UIScreen.main.bounds.size.width + + // Max Width: 70% of viewport width. + // Maximum width is only applied to a provided width and not to the default. + maxWidth = fullWidth * (70/100) + + // 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) + titleLabel.accessibilityTraits = .header layer.cornerRadius = 12 contentStackView.addArrangedSubview(titleLabel) contentStackView.addArrangedSubview(contentLabel) + contentStackView.setCustomSpacing(contentLabelTopSpace, after: titleLabel) + scrollView.addSubview(contentStackView) addSubview(closeCrossButton) addSubview(scrollView) addSubview(closeButton) + self.bringSubviewToFront(closeCrossButton) + let trailingSpace = UIDevice.isIPad ? containerViewInset/2 : containerViewInset // Activate constraints NSLayoutConstraint.activate([ - widthAnchor.constraint(equalToConstant: fullWidth), + widthAnchor.constraint(equalToConstant: maxWidth), - // Constraints for the scroll view - scrollView.topAnchor.constraint(equalTo: topAnchor, constant: VDSLayout.space4X), - scrollView.leadingAnchor.constraint(equalTo: leadingAnchor), - scrollView.trailingAnchor.constraint(equalTo: trailingAnchor), -// scrollView.bottomAnchor.constraint(equalTo: line.topAnchor), + // Constraints for the closeCrossButton + closeCrossButton.topAnchor.constraint(equalTo: topAnchor), + closeCrossButton.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor), + closeCrossButton.trailingAnchor.constraint(equalTo: trailingAnchor), + closeCrossButton.heightAnchor.constraint(equalToConstant: 48.0), + closeCrossButton.widthAnchor.constraint(equalToConstant: 48.0), -// line.leadingAnchor.constraint(equalTo: leadingAnchor), -// line.trailingAnchor.constraint(equalTo: trailingAnchor), - - closeButton.topAnchor.constraint(equalTo: scrollView.bottomAnchor), - closeButton.leadingAnchor.constraint(equalTo: leadingAnchor), - closeButton.trailingAnchor.constraint(equalTo: trailingAnchor), - closeButton.bottomAnchor.constraint(equalTo: bottomAnchor), - closeButton.heightAnchor.constraint(equalToConstant: closeButtonHeight), + // 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), + // Constraints for the scrollView + scrollView.topAnchor.constraint(equalTo: topAnchor, constant: containerViewInset), + scrollView.leadingAnchor.constraint(equalTo: leadingAnchor, constant:containerViewInset), + scrollView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -(trailingSpace)), + scrollView.bottomAnchor.constraint(equalTo: closeButton.topAnchor, constant: -contentLabelBottomSpace), + + // Constraints for the contentStackView 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)), - + contentStackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), + contentStackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), + contentStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -trailingSpace), + contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor) ]) + contentStackViewBottomConstraint = contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor) contentStackViewBottomConstraint?.activate() - - heightConstraint = heightAnchor.constraint(equalToConstant: minHeight) + heightConstraint = heightAnchor.constraint(equalToConstant: maxHeight) heightConstraint?.activate() } @@ -181,7 +203,7 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { } if addedTitle && addedContent { - contentStackView.setCustomSpacing(VDSLayout.space1X, after: titleLabel) + contentStackView.setCustomSpacing(contentLabelTopSpace, after: titleLabel) } let closeButtonTextColor = closeButtonTextColorConfiguration.getColor(self) @@ -195,27 +217,6 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { scrollView.setNeedsLayout() 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 } /// Used to update any Accessibility properties. diff --git a/VDS/Components/Modal/ModalDialogViewController.swift b/VDS/Components/Modal/ModalDialogViewController.swift index 69103484..ba113b52 100644 --- a/VDS/Components/Modal/ModalDialogViewController.swift +++ b/VDS/Components/Modal/ModalDialogViewController.swift @@ -27,15 +27,7 @@ open class ModalDialogViewController: UIViewController, Surfaceable { } } } - - private var onClickCloseSubscriber: AnyCancellable? { - willSet { - if let onClickCloseSubscriber { - onClickCloseSubscriber.cancel() - } - } - } - + private let modalDialog = ModalDialog() //-------------------------------------------------- @@ -56,7 +48,7 @@ open class ModalDialogViewController: UIViewController, Surfaceable { //-------------------------------------------------- open override func viewDidLoad() { super.viewDidLoad() - isModalInPresentation = UIDevice.isIPad ? true : false + isModalInPresentation = true setup() } open override func viewDidAppear(_ animated: Bool) { @@ -95,11 +87,7 @@ open class ModalDialogViewController: UIViewController, Surfaceable { self.dismiss() } - onClickCloseSubscriber = modalDialog.closeCrossButton.publisher(for: .touchUpInside) - .sink {[weak self] button in - guard let self else { return } - self.dismiss() - } + modalDialog.closeCrossButton.onClick = { _ in self.dismiss() } view.addSubview(modalDialog) From 4d5704d47b0382e732ccfbeaa9c57fcbbc81f1c9 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Wed, 25 Sep 2024 16:08:13 +0530 Subject: [PATCH 06/15] Digital ACT-191 ONEAPP-10928 story: content text style and surface updates --- VDS/Components/Modal/ModalDialog.swift | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/VDS/Components/Modal/ModalDialog.swift b/VDS/Components/Modal/ModalDialog.swift index a54a208f..7ac34ba8 100644 --- a/VDS/Components/Modal/ModalDialog.swift +++ b/VDS/Components/Modal/ModalDialog.swift @@ -57,7 +57,7 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { open var titleLabel = Label().with { label in label.isAccessibilityElement = true - label.textStyle = .boldTitleMedium + label.textStyle = .boldTitleLarge } open var contentLabel = Label().with { label in @@ -79,10 +79,11 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { // MARK: - Configuration //-------------------------------------------------- private var fullWidth: CGFloat = 0.0 - private var minHeight: CGFloat = 232.0 + private var minHeight: CGFloat = 232.0 //min content area height 136 //min window height 232 private var maxHeight: CGFloat = 0.0 private var minWidth: CGFloat = 560.0 private var maxWidth: CGFloat = 0.0 + private var closeCrossButtonSize = 48.0 private let containerViewInset = UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space4X private let contentLabelTopSpace = UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space6X @@ -127,17 +128,19 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { addSubview(closeButton) self.bringSubviewToFront(closeCrossButton) let trailingSpace = UIDevice.isIPad ? containerViewInset/2 : containerViewInset - + let crossTopSpace = UIDevice.isIPad ? 0 : VDSLayout.space12X + let scrollTopSpace = UIDevice.isIPad ? containerViewInset : (crossTopSpace + closeCrossButtonSize) + // Activate constraints NSLayoutConstraint.activate([ widthAnchor.constraint(equalToConstant: maxWidth), // Constraints for the closeCrossButton - closeCrossButton.topAnchor.constraint(equalTo: topAnchor), + closeCrossButton.topAnchor.constraint(equalTo: topAnchor, constant: crossTopSpace), closeCrossButton.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor), closeCrossButton.trailingAnchor.constraint(equalTo: trailingAnchor), - closeCrossButton.heightAnchor.constraint(equalToConstant: 48.0), - closeCrossButton.widthAnchor.constraint(equalToConstant: 48.0), + closeCrossButton.heightAnchor.constraint(equalToConstant: closeCrossButtonSize), + closeCrossButton.widthAnchor.constraint(equalToConstant: closeCrossButtonSize), // Constraints for the bottom button view closeButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant:containerViewInset), @@ -145,7 +148,7 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { closeButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -containerViewInset), // Constraints for the scrollView - scrollView.topAnchor.constraint(equalTo: topAnchor, constant: containerViewInset), + scrollView.topAnchor.constraint(equalTo: topAnchor, constant: scrollTopSpace), scrollView.leadingAnchor.constraint(equalTo: leadingAnchor, constant:containerViewInset), scrollView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -(trailingSpace)), scrollView.bottomAnchor.constraint(equalTo: closeButton.topAnchor, constant: -contentLabelBottomSpace), @@ -175,6 +178,8 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { titleLabel.surface = surface contentLabel.surface = surface + closeCrossButton.surface = surface + closeButton.surface = surface titleLabel.text = modalModel.title contentLabel.text = modalModel.content From 12ad80643d0eec5076fa2eaba9462aea32765584 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Wed, 25 Sep 2024 18:15:27 +0530 Subject: [PATCH 07/15] Digital ACT-191 ONEAPP-10928 story: documentation changes for modal dialog --- VDS/Components/Modal/ModalDialog.swift | 51 ++++++++++++++++++-------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/VDS/Components/Modal/ModalDialog.swift b/VDS/Components/Modal/ModalDialog.swift index 7ac34ba8..96b99d31 100644 --- a/VDS/Components/Modal/ModalDialog.swift +++ b/VDS/Components/Modal/ModalDialog.swift @@ -78,11 +78,22 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- - private var fullWidth: CGFloat = 0.0 - private var minHeight: CGFloat = 232.0 //min content area height 136 //min window height 232 + // full width : viewport width + private var fullWidth: CGFloat = UIScreen.main.bounds.size.width + + // Min height content area 136 px. Total window height 232 px + private var minHeight: CGFloat = 232.0 + + // Max height content area. total window height: 70% of viewport height private var maxHeight: CGFloat = 0.0 + + // Min default width private var minWidth: CGFloat = 560.0 + + // Max width: 70% of viewport width private var maxWidth: CGFloat = 0.0 + + // close button with the 48 x 48 px private var closeCrossButtonSize = 48.0 private let containerViewInset = UIDevice.isIPad ? VDSLayout.space12X : VDSLayout.space4X @@ -90,9 +101,16 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { private let contentLabelBottomSpace = UIDevice.isIPad ? VDSLayout.space8X : VDSLayout.space12X private let gapBetweenButtonItems = VDSLayout.space3X + //-------------------------------------------------- + // MARK: - Configuration Properties + //-------------------------------------------------- + private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark) private let closeButtonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- private var contentStackViewBottomConstraint: NSLayoutConstraint? private var heightConstraint: NSLayoutConstraint? @@ -103,8 +121,6 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { open override func setup() { super.setup() - fullWidth = UIScreen.main.bounds.size.width - // Max Width: 70% of viewport width. // Maximum width is only applied to a provided width and not to the default. maxWidth = fullWidth * (70/100) @@ -116,18 +132,21 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { maxHeight = UIScreen.main.bounds.size.height * (70/100) titleLabel.accessibilityTraits = .header - layer.cornerRadius = 12 + + // Add titleLabel, contentLabel to contentStack. contentStackView.addArrangedSubview(titleLabel) contentStackView.addArrangedSubview(contentLabel) contentStackView.setCustomSpacing(contentLabelTopSpace, after: titleLabel) - scrollView.addSubview(contentStackView) + + // Add crossButon, scrollView, closeButton. addSubview(closeCrossButton) addSubview(scrollView) addSubview(closeButton) self.bringSubviewToFront(closeCrossButton) - let trailingSpace = UIDevice.isIPad ? containerViewInset/2 : containerViewInset + + let contentTrailingSpace = UIDevice.isIPad ? containerViewInset/2 : containerViewInset let crossTopSpace = UIDevice.isIPad ? 0 : VDSLayout.space12X let scrollTopSpace = UIDevice.isIPad ? containerViewInset : (crossTopSpace + closeCrossButtonSize) @@ -150,14 +169,14 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { // Constraints for the scrollView scrollView.topAnchor.constraint(equalTo: topAnchor, constant: scrollTopSpace), scrollView.leadingAnchor.constraint(equalTo: leadingAnchor, constant:containerViewInset), - scrollView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -(trailingSpace)), + scrollView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -(contentTrailingSpace)), scrollView.bottomAnchor.constraint(equalTo: closeButton.topAnchor, constant: -contentLabelBottomSpace), // Constraints for the contentStackView contentStackView.topAnchor.constraint(equalTo: scrollView.topAnchor), contentStackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), contentStackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), - contentStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -trailingSpace), + contentStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -contentTrailingSpace), contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor) ]) @@ -171,22 +190,23 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { open override func updateView() { super.updateView() + // Update surface and background backgroundColor = backgroundColorConfiguration.getColor(self) scrollView.indicatorStyle = surface == .light ? .black : .white - - contentStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } - - titleLabel.surface = surface - contentLabel.surface = surface closeCrossButton.surface = surface closeButton.surface = surface + titleLabel.surface = surface + contentLabel.surface = surface + + // Re-arrange contentStack + contentStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } titleLabel.text = modalModel.title contentLabel.text = modalModel.content - titleLabel.sizeToFit() contentLabel.sizeToFit() + // Update title, content and contentview var addedTitle = false if let titleText = modalModel.title, !titleText.isEmpty { @@ -211,6 +231,7 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { contentStackView.setCustomSpacing(contentLabelTopSpace, after: titleLabel) } + // Update closeButton let closeButtonTextColor = closeButtonTextColorConfiguration.getColor(self) closeButton.setTitleColor(closeButtonTextColor, for: .normal) closeButton.setTitleColor(closeButtonTextColor, for: .highlighted) From e71b94bc4f9f5d46bb9365883f5ac0cd0cf041af Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Thu, 26 Sep 2024 13:40:35 +0530 Subject: [PATCH 08/15] Digital ACT-191 ONEAPP-10928 story: resolved constraint conflicts --- VDS/Components/Modal/ModalDialog.swift | 30 ++++++++++++++++---------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/VDS/Components/Modal/ModalDialog.swift b/VDS/Components/Modal/ModalDialog.swift index 96b99d31..f70fcda7 100644 --- a/VDS/Components/Modal/ModalDialog.swift +++ b/VDS/Components/Modal/ModalDialog.swift @@ -106,7 +106,7 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { //-------------------------------------------------- private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark) - private let closeButtonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) + private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) //-------------------------------------------------- // MARK: - Constraints @@ -146,10 +146,10 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { addSubview(closeButton) self.bringSubviewToFront(closeCrossButton) - let contentTrailingSpace = UIDevice.isIPad ? containerViewInset/2 : containerViewInset let crossTopSpace = UIDevice.isIPad ? 0 : VDSLayout.space12X let scrollTopSpace = UIDevice.isIPad ? containerViewInset : (crossTopSpace + closeCrossButtonSize) - + let contentTrailingSpace = UIDevice.isIPad ? (containerViewInset/2) - 6 : containerViewInset + // Activate constraints NSLayoutConstraint.activate([ widthAnchor.constraint(equalToConstant: maxWidth), @@ -159,7 +159,6 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { closeCrossButton.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor), closeCrossButton.trailingAnchor.constraint(equalTo: trailingAnchor), closeCrossButton.heightAnchor.constraint(equalToConstant: closeCrossButtonSize), - closeCrossButton.widthAnchor.constraint(equalToConstant: closeCrossButtonSize), // Constraints for the bottom button view closeButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant:containerViewInset), @@ -169,8 +168,9 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { // 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.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -contentTrailingSpace), scrollView.bottomAnchor.constraint(equalTo: closeButton.topAnchor, constant: -contentLabelBottomSpace), + scrollView.widthAnchor.constraint(equalToConstant: (maxWidth - (containerViewInset + contentTrailingSpace))), // Constraints for the contentStackView contentStackView.topAnchor.constraint(equalTo: scrollView.topAnchor), @@ -189,7 +189,11 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() - + + heightConstraint?.deactivate() + maxWidth = UIScreen.main.bounds.size.width * (70/100) + maxHeight = UIScreen.main.bounds.size.height * (70/100) + // Update surface and background backgroundColor = backgroundColorConfiguration.getColor(self) scrollView.indicatorStyle = surface == .light ? .black : .white @@ -199,10 +203,12 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { contentLabel.surface = surface // Re-arrange contentStack - contentStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } - + contentStackView.removeArrangedSubviews() + titleLabel.text = modalModel.title contentLabel.text = modalModel.content + titleLabel.textColor = textColorConfiguration.getColor(self) + contentLabel.textColor = textColorConfiguration.getColor(self) titleLabel.sizeToFit() contentLabel.sizeToFit() @@ -232,9 +238,9 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { } // Update closeButton - let closeButtonTextColor = closeButtonTextColorConfiguration.getColor(self) - closeButton.setTitleColor(closeButtonTextColor, for: .normal) - closeButton.setTitleColor(closeButtonTextColor, for: .highlighted) + 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 @@ -243,6 +249,8 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { scrollView.setNeedsLayout() scrollView.layoutIfNeeded() + heightConstraint?.constant = maxHeight + heightConstraint?.activate() } /// Used to update any Accessibility properties. From 8a63ea5c75eb00fb25005e9857965fd5c8d0be60 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 27 Sep 2024 17:05:19 +0530 Subject: [PATCH 09/15] Digital ACT-191 ONEAPP-10928 story: loading button data if provided --- VDS/Components/Modal/Modal.swift | 20 ++++++--- VDS/Components/Modal/ModalDialog.swift | 61 +++++++++++++------------- VDS/Components/Modal/ModalModel.swift | 3 ++ 3 files changed, 47 insertions(+), 37 deletions(-) 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 } } } From 08be77242df16d5a9ba8f5c28ca8ea5409e21b20 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 27 Sep 2024 17:15:33 +0530 Subject: [PATCH 10/15] Digital ACT-191 ONEAPP-10928 story: onClick for close button --- VDS/Components/Modal/ModalDialogViewController.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/VDS/Components/Modal/ModalDialogViewController.swift b/VDS/Components/Modal/ModalDialogViewController.swift index ba113b52..4d29a91f 100644 --- a/VDS/Components/Modal/ModalDialogViewController.swift +++ b/VDS/Components/Modal/ModalDialogViewController.swift @@ -81,13 +81,11 @@ open class ModalDialogViewController: UIViewController, Surfaceable { }.store(in: &subscribers) //clicking button - onClickSubscriber = modalDialog.closeButton.publisher(for: .touchUpInside) + onClickSubscriber = modalDialog.closeCrossButton.publisher(for: .touchUpInside) .sink {[weak self] button in guard let self else { return } self.dismiss() } - - modalDialog.closeCrossButton.onClick = { _ in self.dismiss() } view.addSubview(modalDialog) From 81725ffdeb86029117b9816f6ba4cadf0c20733b Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 27 Sep 2024 18:11:54 +0530 Subject: [PATCH 11/15] Digital ACT-191 ONEAPP-10928 story: Modal to be displayed at full screen if fullScreenDialog true --- VDS/Components/Modal/Modal.swift | 9 ++++---- VDS/Components/Modal/ModalDialog.swift | 24 ++++++++++------------ VDS/Components/Modal/ModalLaunchable.swift | 2 +- VDS/Components/Modal/ModalModel.swift | 3 +++ 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/VDS/Components/Modal/Modal.swift b/VDS/Components/Modal/Modal.swift index 7ced3ba0..b927d528 100644 --- a/VDS/Components/Modal/Modal.swift +++ b/VDS/Components/Modal/Modal.swift @@ -30,7 +30,7 @@ open class Modal: Control, ModalLaunchable { public required init?(coder: NSCoder) { super.init(coder: coder) } - + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -53,8 +53,8 @@ open class Modal: Control, ModalLaunchable { ///// 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. + + ///// If provided, the Modal has the option to be displayed at full screen. open var fullScreenDialog: Bool = false { didSet { setNeedsUpdate() } } //-------------------------------------------------- @@ -105,7 +105,8 @@ open class Modal: Control, ModalLaunchable { title: title, content: content, contentView: contentView, - buttonData: buttonData), + buttonData: buttonData, + fullScreenDialog: fullScreenDialog), presenter: self) } diff --git a/VDS/Components/Modal/ModalDialog.swift b/VDS/Components/Modal/ModalDialog.swift index 5221a92b..3c9c05c5 100644 --- a/VDS/Components/Modal/ModalDialog.swift +++ b/VDS/Components/Modal/ModalDialog.swift @@ -81,9 +81,6 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- - // full width : viewport width - private var fullWidth: CGFloat = UIScreen.main.bounds.size.width - // Min height content area 136 px. Total window height 232 px private var minHeight: CGFloat = 232.0 @@ -116,6 +113,7 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { //-------------------------------------------------- private var contentStackViewBottomConstraint: NSLayoutConstraint? private var heightConstraint: NSLayoutConstraint? + private var widthConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Overrides @@ -126,13 +124,13 @@ 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 = UIDevice.isIPad ? fullWidth * (70/100): fullWidth + maxWidth = UIDevice.isIPad && !modalModel.fullScreenDialog ? UIScreen.main.bounds.size.width * (70/100): UIScreen.main.bounds.size.width // 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 = UIDevice.isIPad ? UIScreen.main.bounds.size.height * (70/100) : UIScreen.main.bounds.size.height + maxHeight = UIDevice.isIPad && !modalModel.fullScreenDialog ? UIScreen.main.bounds.size.height * (70/100) : UIScreen.main.bounds.size.height titleLabel.accessibilityTraits = .header layer.cornerRadius = 12 @@ -149,14 +147,12 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { addSubview(buttonGroupData) self.bringSubviewToFront(closeCrossButton) - let crossTopSpace = UIDevice.isIPad ? 0 : VDSLayout.space12X - let scrollTopSpace = UIDevice.isIPad ? containerViewInset : (crossTopSpace + closeCrossButtonSize) + let crossTopSpace = UIDevice.isIPad && !modalModel.fullScreenDialog ? 0 : VDSLayout.space12X + let scrollTopSpace = UIDevice.isIPad && !modalModel.fullScreenDialog ? containerViewInset : (crossTopSpace + closeCrossButtonSize) let contentTrailingSpace = UIDevice.isIPad ? (containerViewInset/2) - 6 : containerViewInset // Activate constraints NSLayoutConstraint.activate([ - widthAnchor.constraint(equalToConstant: maxWidth), - // Constraints for the closeCrossButton closeCrossButton.topAnchor.constraint(equalTo: topAnchor, constant: crossTopSpace), closeCrossButton.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor), @@ -173,7 +169,6 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { scrollView.leadingAnchor.constraint(equalTo: leadingAnchor, constant:containerViewInset), scrollView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -contentTrailingSpace), 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), @@ -187,14 +182,16 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { contentStackViewBottomConstraint?.activate() heightConstraint = heightAnchor.constraint(equalToConstant: maxHeight) heightConstraint?.activate() + widthConstraint = widthAnchor.constraint(equalToConstant: maxWidth) + widthConstraint?.activate() } /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() - - 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 + + maxWidth = UIDevice.isIPad && !modalModel.fullScreenDialog ? UIScreen.main.bounds.size.width * (70/100) : UIScreen.main.bounds.size.width + maxHeight = UIDevice.isIPad && !modalModel.fullScreenDialog ? UIScreen.main.bounds.size.height * (70/100) : UIScreen.main.bounds.size.height // Update surface and background backgroundColor = backgroundColorConfiguration.getColor(self) @@ -252,6 +249,7 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { contentStackView.layoutIfNeeded() scrollView.setNeedsLayout() scrollView.layoutIfNeeded() + widthConstraint?.constant = maxWidth heightConstraint?.constant = maxHeight } diff --git a/VDS/Components/Modal/ModalLaunchable.swift b/VDS/Components/Modal/ModalLaunchable.swift index b99b638c..537de6c5 100644 --- a/VDS/Components/Modal/ModalLaunchable.swift +++ b/VDS/Components/Modal/ModalLaunchable.swift @@ -19,7 +19,7 @@ extension ModalLaunchable { $0.surface = surface $0.modalModel = modalModel $0.presenter = presenter - $0.modalPresentationStyle = UIDevice.isIPad ? .overCurrentContext : .fullScreen + $0.modalPresentationStyle = UIDevice.isIPad && !modalModel.fullScreenDialog ? .overCurrentContext : .fullScreen $0.modalTransitionStyle = .crossDissolve } presenting.present(modalViewController, animated: true) diff --git a/VDS/Components/Modal/ModalModel.swift b/VDS/Components/Modal/ModalModel.swift index 4a57c7de..85972bcc 100644 --- a/VDS/Components/Modal/ModalModel.swift +++ b/VDS/Components/Modal/ModalModel.swift @@ -20,11 +20,13 @@ extension Modal { public var accessibleText: String? public var contentViewAlignment: UIStackView.Alignment? public var buttonData: [ButtonBase]? + public var fullScreenDialog: Bool public init(closeButtonText: String = "Close", title: String? = nil, content: String? = nil, contentView: UIView? = nil, buttonData: [ButtonBase]? = nil, + fullScreenDialog: Bool = false, accessibleText: String? = "Modal", contentViewAlignment: UIStackView.Alignment = .leading) { self.closeButtonText = closeButtonText @@ -34,6 +36,7 @@ extension Modal { self.accessibleText = accessibleText self.contentViewAlignment = contentViewAlignment self.buttonData = buttonData + self.fullScreenDialog = fullScreenDialog } } } From b210135566136df8cf144c9c0e2900ec2ed05a17 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 27 Sep 2024 18:58:53 +0530 Subject: [PATCH 12/15] Digital ACT-191 ONEAPP-10928 story: show/hide close button based on flag --- VDS/Components/Modal/Modal.swift | 12 ++++++++---- VDS/Components/Modal/ModalDialog.swift | 2 ++ VDS/Components/Modal/ModalModel.swift | 3 +++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/VDS/Components/Modal/Modal.swift b/VDS/Components/Modal/Modal.swift index b927d528..494b71b7 100644 --- a/VDS/Components/Modal/Modal.swift +++ b/VDS/Components/Modal/Modal.swift @@ -51,12 +51,15 @@ 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. + /// 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 has the option to be displayed at full screen. + /// If provided, the Modal has the option to be displayed at full screen. open var fullScreenDialog: Bool = false { didSet { setNeedsUpdate() } } - + + /// If provided, close button can not be present. + open var hideCloseButton: Bool = false { didSet { setNeedsUpdate() } } + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -106,7 +109,8 @@ open class Modal: Control, ModalLaunchable { content: content, contentView: contentView, buttonData: buttonData, - fullScreenDialog: fullScreenDialog), + fullScreenDialog: fullScreenDialog, + hideCloseButton: hideCloseButton), presenter: self) } diff --git a/VDS/Components/Modal/ModalDialog.swift b/VDS/Components/Modal/ModalDialog.swift index 3c9c05c5..1f1987ce 100644 --- a/VDS/Components/Modal/ModalDialog.swift +++ b/VDS/Components/Modal/ModalDialog.swift @@ -245,6 +245,8 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { contentStackView.setCustomSpacing(contentLabelTopSpace, after: titleLabel) } + closeCrossButton.isHidden = modalModel.hideCloseButton + contentStackView.setNeedsLayout() contentStackView.layoutIfNeeded() scrollView.setNeedsLayout() diff --git a/VDS/Components/Modal/ModalModel.swift b/VDS/Components/Modal/ModalModel.swift index 85972bcc..c0bb6f79 100644 --- a/VDS/Components/Modal/ModalModel.swift +++ b/VDS/Components/Modal/ModalModel.swift @@ -21,12 +21,14 @@ extension Modal { public var contentViewAlignment: UIStackView.Alignment? public var buttonData: [ButtonBase]? public var fullScreenDialog: Bool + public var hideCloseButton: Bool public init(closeButtonText: String = "Close", title: String? = nil, content: String? = nil, contentView: UIView? = nil, buttonData: [ButtonBase]? = nil, fullScreenDialog: Bool = false, + hideCloseButton: Bool = false, accessibleText: String? = "Modal", contentViewAlignment: UIStackView.Alignment = .leading) { self.closeButtonText = closeButtonText @@ -37,6 +39,7 @@ extension Modal { self.contentViewAlignment = contentViewAlignment self.buttonData = buttonData self.fullScreenDialog = fullScreenDialog + self.hideCloseButton = hideCloseButton } } } From a3a714dfc1a576cf560023392063910ae1064904 Mon Sep 17 00:00:00 2001 From: Vasavi Kanamarlapudi Date: Fri, 27 Sep 2024 19:47:55 +0530 Subject: [PATCH 13/15] Digital ACT-191 ONEAPP-10928 story: documentation changes --- VDS/Components/Modal/Modal.swift | 3 ++ VDS/Components/Modal/ModalDialog.swift | 50 +++++++++---------- .../Modal/ModalDialogViewController.swift | 4 +- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/VDS/Components/Modal/Modal.swift b/VDS/Components/Modal/Modal.swift index 494b71b7..b99d99a8 100644 --- a/VDS/Components/Modal/Modal.swift +++ b/VDS/Components/Modal/Modal.swift @@ -80,6 +80,9 @@ open class Modal: Control, ModalLaunchable { title = nil content = nil contentView = nil + buttonData = nil + fullScreenDialog = false + hideCloseButton = false showModalButton.onClick = { _ in self.showModalButtonClick() } diff --git a/VDS/Components/Modal/ModalDialog.swift b/VDS/Components/Modal/ModalDialog.swift index 1f1987ce..ce03e88e 100644 --- a/VDS/Components/Modal/ModalDialog.swift +++ b/VDS/Components/Modal/ModalDialog.swift @@ -28,26 +28,6 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { super.init(coder: coder) } - //-------------------------------------------------- - // MARK: - Private Properties - //-------------------------------------------------- - private var scrollView = UIScrollView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.backgroundColor = .clear - } - - private let contentStackView = UIStackView().with { - $0.translatesAutoresizingMaskIntoConstraints = false - $0.axis = .vertical - $0.alignment = .leading - $0.distribution = .fillProportionally - $0.spacing = 0 - } - - lazy var primaryAccessibilityElement = UIAccessibilityElement(accessibilityContainer: self).with { - $0.accessibilityLabel = "Modal" - } - //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @@ -79,18 +59,35 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { } //-------------------------------------------------- - // MARK: - Configuration + // MARK: - Private Properties //-------------------------------------------------- - // Min height content area 136 px. Total window height 232 px + private var scrollView = UIScrollView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.backgroundColor = .clear + } + + private var contentStackView = UIStackView().with { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.axis = .vertical + $0.alignment = .leading + $0.distribution = .fillProportionally + $0.spacing = 0 + } + + lazy var primaryAccessibilityElement = UIAccessibilityElement(accessibilityContainer: self).with { + $0.accessibilityLabel = "Modal" + } + + // Min height for content area 136 px. Total window min height 232 px which is including top and bottom space. private var minHeight: CGFloat = 232.0 - // Max height content area. total window height: 70% of viewport height + // Max height is total window height. It is 70% of viewport height. private var maxHeight: CGFloat = 0.0 - // Min default width - private var minWidth: CGFloat = 560.0 + // Default width 560 px. A maximum width is only applied to a provided width and not to the default. + private var defaultWidth: CGFloat = 560.0 - // Max width: 70% of viewport width + // Max width: 70% of viewport width. private var maxWidth: CGFloat = 0.0 // close button with the 48 x 48 px @@ -104,7 +101,6 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { //-------------------------------------------------- // MARK: - Configuration Properties //-------------------------------------------------- - private let backgroundColorConfiguration = SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryDark) private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) diff --git a/VDS/Components/Modal/ModalDialogViewController.swift b/VDS/Components/Modal/ModalDialogViewController.swift index 4d29a91f..834a1212 100644 --- a/VDS/Components/Modal/ModalDialogViewController.swift +++ b/VDS/Components/Modal/ModalDialogViewController.swift @@ -92,7 +92,7 @@ open class ModalDialogViewController: UIViewController, Surfaceable { // Activate constraints UIDevice.isIPad ? NSLayoutConstraint.activate([ - // Constraints for the floating modal view + // Constraints for the floating modal view for Tablet. modalDialog.centerXAnchor.constraint(equalTo: view.centerXAnchor), modalDialog.centerYAnchor.constraint(equalTo: view.centerYAnchor), modalDialog.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor), @@ -100,7 +100,7 @@ open class ModalDialogViewController: UIViewController, Surfaceable { modalDialog.topAnchor.constraint(greaterThanOrEqualTo: view.topAnchor), modalDialog.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor) ]) : NSLayoutConstraint.activate([ - // Constraints for the floating modal view + // Constraints for fullscreen modal view for Phone. modalDialog.leadingAnchor.constraint(equalTo: view.leadingAnchor), modalDialog.trailingAnchor.constraint(equalTo: view.trailingAnchor), modalDialog.topAnchor.constraint(equalTo: view.topAnchor), From f741b3df8bc6265f51f50afd76b85a0ec37ea1b8 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 1 Oct 2024 16:32:17 -0500 Subject: [PATCH 14/15] removed width/height contraints in dialog and pushed down to the viewcontroller. Signed-off-by: Matt Bruce --- VDS/Components/Modal/ModalDialog.swift | 35 +-------------- .../Modal/ModalDialogViewController.swift | 43 +++++++++++-------- 2 files changed, 26 insertions(+), 52 deletions(-) diff --git a/VDS/Components/Modal/ModalDialog.swift b/VDS/Components/Modal/ModalDialog.swift index ce03e88e..bf95f9bf 100644 --- a/VDS/Components/Modal/ModalDialog.swift +++ b/VDS/Components/Modal/ModalDialog.swift @@ -77,19 +77,7 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { lazy var primaryAccessibilityElement = UIAccessibilityElement(accessibilityContainer: self).with { $0.accessibilityLabel = "Modal" } - - // Min height for content area 136 px. Total window min height 232 px which is including top and bottom space. - private var minHeight: CGFloat = 232.0 - - // Max height is total window height. It is 70% of viewport height. - private var maxHeight: CGFloat = 0.0 - - // Default width 560 px. A maximum width is only applied to a provided width and not to the default. - private var defaultWidth: CGFloat = 560.0 - - // Max width: 70% of viewport width. - private var maxWidth: CGFloat = 0.0 - + // close button with the 48 x 48 px private var closeCrossButtonSize = 48.0 @@ -108,8 +96,6 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { // MARK: - Constraints //-------------------------------------------------- private var contentStackViewBottomConstraint: NSLayoutConstraint? - private var heightConstraint: NSLayoutConstraint? - private var widthConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Overrides @@ -118,16 +104,6 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { open override func setup() { super.setup() - // Max Width: 70% of viewport width. - // Maximum width is only applied to a provided width and not to the default. - maxWidth = UIDevice.isIPad && !modalModel.fullScreenDialog ? UIScreen.main.bounds.size.width * (70/100): UIScreen.main.bounds.size.width - - // 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 = UIDevice.isIPad && !modalModel.fullScreenDialog ? UIScreen.main.bounds.size.height * (70/100) : UIScreen.main.bounds.size.height - titleLabel.accessibilityTraits = .header layer.cornerRadius = 12 @@ -176,19 +152,12 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { contentStackViewBottomConstraint = contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor) contentStackViewBottomConstraint?.activate() - heightConstraint = heightAnchor.constraint(equalToConstant: maxHeight) - heightConstraint?.activate() - widthConstraint = widthAnchor.constraint(equalToConstant: maxWidth) - widthConstraint?.activate() } /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() - maxWidth = UIDevice.isIPad && !modalModel.fullScreenDialog ? UIScreen.main.bounds.size.width * (70/100) : UIScreen.main.bounds.size.width - maxHeight = UIDevice.isIPad && !modalModel.fullScreenDialog ? 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 @@ -247,8 +216,6 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { contentStackView.layoutIfNeeded() scrollView.setNeedsLayout() scrollView.layoutIfNeeded() - widthConstraint?.constant = maxWidth - heightConstraint?.constant = maxHeight } /// Used to update any Accessibility properties. diff --git a/VDS/Components/Modal/ModalDialogViewController.swift b/VDS/Components/Modal/ModalDialogViewController.swift index 834a1212..8e0a2dcf 100644 --- a/VDS/Components/Modal/ModalDialogViewController.swift +++ b/VDS/Components/Modal/ModalDialogViewController.swift @@ -89,29 +89,36 @@ open class ModalDialogViewController: UIViewController, Surfaceable { view.addSubview(modalDialog) - // Activate constraints - UIDevice.isIPad ? - NSLayoutConstraint.activate([ - // Constraints for the floating modal view for Tablet. - modalDialog.centerXAnchor.constraint(equalTo: view.centerXAnchor), - modalDialog.centerYAnchor.constraint(equalTo: view.centerYAnchor), - modalDialog.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor), - modalDialog.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor), - modalDialog.topAnchor.constraint(greaterThanOrEqualTo: view.topAnchor), - modalDialog.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor) - ]) : NSLayoutConstraint.activate([ - // Constraints for fullscreen modal view for Phone. - modalDialog.leadingAnchor.constraint(equalTo: view.leadingAnchor), - modalDialog.trailingAnchor.constraint(equalTo: view.trailingAnchor), - modalDialog.topAnchor.constraint(equalTo: view.topAnchor), - modalDialog.bottomAnchor.constraint(equalTo: view.bottomAnchor) - ]) } /// Used to make changes to the View based off a change events or from local properties. open func updateView() { - view.backgroundColor = backgroundColorConfiguration.getColor(self).withAlphaComponent(0.8) modalDialog.surface = surface modalDialog.modalModel = modalModel + + + // Activate constraints + modalDialog.removeConstraints() + let isFullScreen = UIDevice.isIPad && !modalModel.fullScreenDialog ? false : true + + if isFullScreen { + view.backgroundColor = modalDialog.backgroundColor + modalDialog + .pinLeading() + .pinTrailing() + modalDialog.pinTop(anchor: UIDevice.isIPad ? view.safeAreaLayoutGuide.topAnchor : view.topAnchor) + modalDialog.pinBottom(UIDevice.isIPad ? view.bottomAnchor : view.safeAreaLayoutGuide.bottomAnchor) + + } else { + view.backgroundColor = backgroundColorConfiguration.getColor(self).withAlphaComponent(0.8) + NSLayoutConstraint.activate([ + // Constraints for the floating modal view for Tablet. + modalDialog.centerXAnchor.constraint(equalTo: view.centerXAnchor), + modalDialog.centerYAnchor.constraint(equalTo: view.centerYAnchor), + modalDialog.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.7), + modalDialog.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.7) + ]) + } + } } From 83ce02e879c00d34294c96a1a88b6db5b2c9dfed Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 4 Oct 2024 13:46:43 -0500 Subject: [PATCH 15/15] updated spacing issue Signed-off-by: Matt Bruce --- VDS/Components/Modal/ModalDialog.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VDS/Components/Modal/ModalDialog.swift b/VDS/Components/Modal/ModalDialog.swift index bf95f9bf..9c7dd42f 100644 --- a/VDS/Components/Modal/ModalDialog.swift +++ b/VDS/Components/Modal/ModalDialog.swift @@ -207,7 +207,7 @@ open class ModalDialog: View, UIScrollViewDelegate, ParentViewProtocol { } if addedTitle && addedContent { - contentStackView.setCustomSpacing(contentLabelTopSpace, after: titleLabel) + contentStackView.spacing = contentLabelTopSpace } closeCrossButton.isHidden = modalModel.hideCloseButton