|
| 1 | +import ComponentsKit |
| 2 | +import SwiftUI |
| 3 | +import UIKit |
| 4 | + |
| 5 | +struct ModalPreviewHelpers { |
| 6 | + // MARK: - Enums |
| 7 | + |
| 8 | + enum ContentBody { |
| 9 | + case shortText |
| 10 | + case longText |
| 11 | + } |
| 12 | + enum ContentFooter { |
| 13 | + case button |
| 14 | + case buttonAndCheckbox |
| 15 | + } |
| 16 | + |
| 17 | + // MARK: - Preview Sections |
| 18 | + |
| 19 | + struct ContentSection<VM: ModalVM>: View { |
| 20 | + @Binding var model: VM |
| 21 | + @Binding var hasHeader: Bool |
| 22 | + @Binding var contentBody: ContentBody |
| 23 | + @Binding var contentFooter: ContentFooter? |
| 24 | + |
| 25 | + var body: some View { |
| 26 | + Section("Content") { |
| 27 | + Picker("Header", selection: self.$hasHeader) { |
| 28 | + Text("Text").tag(true) |
| 29 | + Text("None").tag(false) |
| 30 | + } |
| 31 | + Picker("Body", selection: self.$contentBody) { |
| 32 | + Text("Short Text").tag(ContentBody.shortText) |
| 33 | + Text("Long Text").tag(ContentBody.longText) |
| 34 | + } |
| 35 | + Picker("Footer", selection: .init( |
| 36 | + get: { |
| 37 | + return self.contentFooter |
| 38 | + }, |
| 39 | + set: { newValue in |
| 40 | + if newValue == nil { |
| 41 | + self.model.closesOnOverlayTap = true |
| 42 | + } |
| 43 | + self.contentFooter = newValue |
| 44 | + } |
| 45 | + )) { |
| 46 | + Text("Button").tag(ContentFooter.button) |
| 47 | + Text("Button and Checkbox").tag(ContentFooter.buttonAndCheckbox) |
| 48 | + Text("None").tag(Optional<ContentFooter>.none) |
| 49 | + } |
| 50 | + } |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + struct PropertiesSection<VM: ModalVM, Pickers: View>: View { |
| 55 | + @Binding var model: VM |
| 56 | + @Binding var footer: ContentFooter? |
| 57 | + @ViewBuilder var additionalPickers: () -> Pickers |
| 58 | + |
| 59 | + var body: some View { |
| 60 | + Section("Properties") { |
| 61 | + Picker("Background Color", selection: self.$model.backgroundColor) { |
| 62 | + Text("Default").tag(Optional<UniversalColor>.none) |
| 63 | + Text("Accent Background").tag(ComponentColor.accent.background) |
| 64 | + Text("Success Background").tag(ComponentColor.success.background) |
| 65 | + Text("Warning Background").tag(ComponentColor.warning.background) |
| 66 | + Text("Danger Background").tag(ComponentColor.danger.background) |
| 67 | + } |
| 68 | + BorderWidthPicker(selection: self.$model.borderWidth) |
| 69 | + Toggle("Closes On Overlay Tap", isOn: self.$model.closesOnOverlayTap) |
| 70 | + .disabled(self.footer == nil) |
| 71 | + Picker("Outer Paddings", selection: self.$model.outerPaddings) { |
| 72 | + Text("12px").tag(Paddings(padding: 12)) |
| 73 | + Text("16px").tag(Paddings(padding: 16)) |
| 74 | + Text("20px").tag(Paddings(padding: 20)) |
| 75 | + } |
| 76 | + Picker("Content Spacing", selection: self.$model.contentSpacing) { |
| 77 | + Text("8px").tag(CGFloat(8)) |
| 78 | + Text("12px").tag(CGFloat(12)) |
| 79 | + Text("16px").tag(CGFloat(16)) |
| 80 | + } |
| 81 | + Picker("Content Paddings", selection: self.$model.contentPaddings) { |
| 82 | + Text("12px").tag(Paddings(padding: 12)) |
| 83 | + Text("16px").tag(Paddings(padding: 16)) |
| 84 | + Text("20px").tag(Paddings(padding: 20)) |
| 85 | + } |
| 86 | + ContainerRadiusPicker(selection: self.$model.cornerRadius) { |
| 87 | + Text("Custom 30px").tag(ContainerRadius.custom(30)) |
| 88 | + } |
| 89 | + OverlayStylePicker(selection: self.$model.overlayStyle) |
| 90 | + Picker("Size", selection: self.$model.size) { |
| 91 | + Text("Small").tag(ModalSize.small) |
| 92 | + Text("Medium").tag(ModalSize.medium) |
| 93 | + Text("Large").tag(ModalSize.large) |
| 94 | + Text("Full").tag(ModalSize.full) |
| 95 | + } |
| 96 | + TransitionPicker(selection: self.$model.transition) |
| 97 | + self.additionalPickers() |
| 98 | + } |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + // MARK: - Shared UI |
| 103 | + |
| 104 | + private static let headerTitle = "Header" |
| 105 | + private static let headerFont: UniversalFont = .system(size: 20, weight: .bold) |
| 106 | + |
| 107 | + private static let bodyShortText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." |
| 108 | + private static let bodyLongText = """ |
| 109 | +Lorem ipsum odor amet, consectetuer adipiscing elit. Vitae vehicula pellentesque lectus orci fames. Cras suscipit dui tortor penatibus turpis ultrices. Laoreet montes adipiscing ante dapibus facilisis. Lorem per fames nec duis quis eleifend imperdiet. Tincidunt id interdum adipiscing eros dis quis platea varius. Potenti eleifend eu molestie laoreet varius sapien. Adipiscing nascetur platea penatibus curabitur tempus nibh laoreet porttitor. Augue et curabitur cras sed semper inceptos nunc montes mollis. |
| 110 | +
|
| 111 | +Lectus arcu pellentesque inceptos tempor fringilla nascetur. Erat curae convallis integer mi, quis facilisi tortor. Phasellus aliquam molestie vehicula odio in dis maximus diam elit. Rutrum gravida amet euismod feugiat fusce. Est egestas velit vulputate senectus sociosqu fringilla eget nibh. Nam pellentesque aenean mi platea tincidunt quam sem purus. Himenaeos suspendisse nec sapien habitasse ultricies maecenas libero odio. Rutrum senectus maximus ultrices, ad nam ultricies placerat. |
| 112 | +
|
| 113 | +Enim habitant laoreet inceptos scelerisque senectus, tellus molestie ut. Eros risus nibh morbi eu aenean. Velit ligula magnis aliquet at luctus. Dapibus vestibulum consectetur euismod vitae per ultrices litora quis. Aptent eleifend dapibus urna lacinia felis nisl. Sit amet fusce nullam feugiat posuere. Urna amet curae velit fermentum interdum vestibulum penatibus. Penatibus vivamus sem ultricies pellentesque congue id mattis diam. Aliquam efficitur mi gravida sollicitudin; amet imperdiet. Rutrum mollis risus justo tortor in duis cursus. |
| 114 | +""" |
| 115 | + private static let bodyFont: UniversalFont = .system(size: 18, weight: .regular) |
| 116 | + |
| 117 | + private static let footerButtonVM = ButtonVM { |
| 118 | + $0.title = "Close" |
| 119 | + $0.isFullWidth = true |
| 120 | + $0.color = .primary |
| 121 | + } |
| 122 | + private static let footerCheckboxVM = CheckboxVM { |
| 123 | + $0.title = "Agree and continue" |
| 124 | + } |
| 125 | + |
| 126 | + // MARK: - UIKit |
| 127 | + |
| 128 | + static func ukHeader(hasHeader: Bool) -> UKModalController.Content? { |
| 129 | + guard hasHeader else { |
| 130 | + return nil |
| 131 | + } |
| 132 | + |
| 133 | + return { _ in |
| 134 | + let title = UILabel() |
| 135 | + title.text = self.headerTitle |
| 136 | + title.font = self.headerFont.uiFont |
| 137 | + return title |
| 138 | + } |
| 139 | + } |
| 140 | + |
| 141 | + static func ukBody(body: ContentBody) -> UKModalController.Content { |
| 142 | + return { _ in |
| 143 | + let subtitle = UILabel() |
| 144 | + switch body { |
| 145 | + case .shortText: |
| 146 | + subtitle.text = self.bodyShortText |
| 147 | + case .longText: |
| 148 | + subtitle.text = self.bodyLongText |
| 149 | + } |
| 150 | + subtitle.numberOfLines = 0 |
| 151 | + subtitle.font = self.bodyFont.uiFont |
| 152 | + return subtitle |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + static func ukFooter(footer: ContentFooter?) -> UKModalController.Content? { |
| 157 | + return footer.map { footer in |
| 158 | + return { dismiss in |
| 159 | + let stackView = UIStackView() |
| 160 | + stackView.axis = .vertical |
| 161 | + stackView.spacing = 16 |
| 162 | + |
| 163 | + let button = UKButton( |
| 164 | + model: self.footerButtonVM, |
| 165 | + action: { dismiss(true) } |
| 166 | + ) |
| 167 | + stackView.addArrangedSubview(button) |
| 168 | + |
| 169 | + switch footer { |
| 170 | + case .button: |
| 171 | + button.model.isEnabled = true |
| 172 | + case .buttonAndCheckbox: |
| 173 | + button.model.isEnabled = false |
| 174 | + let checkbox = UKCheckbox( |
| 175 | + initialValue: false, |
| 176 | + model: self.footerCheckboxVM, |
| 177 | + onValueChange: { isSelected in |
| 178 | + button.model.isEnabled = isSelected |
| 179 | + } |
| 180 | + ) |
| 181 | + stackView.insertArrangedSubview(checkbox, at: 0) |
| 182 | + } |
| 183 | + |
| 184 | + return stackView |
| 185 | + } |
| 186 | + } |
| 187 | + } |
| 188 | + |
| 189 | + // MARK: - SwiftUI |
| 190 | + |
| 191 | + static func suHeader(hasHeader: Bool) -> some View { |
| 192 | + Group { |
| 193 | + if hasHeader { |
| 194 | + HStack { |
| 195 | + Text(self.headerTitle) |
| 196 | + .font(self.headerFont.font) |
| 197 | + Spacer() |
| 198 | + } |
| 199 | + } else { |
| 200 | + EmptyView() |
| 201 | + } |
| 202 | + } |
| 203 | + } |
| 204 | + |
| 205 | + static func suBody(body: ContentBody) -> some View { |
| 206 | + Group { |
| 207 | + switch body { |
| 208 | + case .shortText: |
| 209 | + Text(self.bodyShortText) |
| 210 | + case .longText: |
| 211 | + Text(self.bodyLongText) |
| 212 | + } |
| 213 | + } |
| 214 | + .font(self.bodyFont.font) |
| 215 | + .multilineTextAlignment(.leading) |
| 216 | + } |
| 217 | + |
| 218 | + static func suFooter( |
| 219 | + isPresented: Binding<Bool>, |
| 220 | + isCheckboxSelected: Binding<Bool>, |
| 221 | + footer: ContentFooter? |
| 222 | + ) -> some View { |
| 223 | + Group { |
| 224 | + switch footer { |
| 225 | + case .none: |
| 226 | + EmptyView() |
| 227 | + case .button: |
| 228 | + SUButton(model: self.footerButtonVM) { |
| 229 | + isPresented.wrappedValue = false |
| 230 | + } |
| 231 | + case .buttonAndCheckbox: |
| 232 | + VStack(alignment: .leading, spacing: 16) { |
| 233 | + SUCheckbox( |
| 234 | + isSelected: isCheckboxSelected, |
| 235 | + model: self.footerCheckboxVM |
| 236 | + ) |
| 237 | + SUButton(model: self.footerButtonVM.updating { |
| 238 | + $0.isEnabled = isCheckboxSelected.wrappedValue |
| 239 | + }) { |
| 240 | + isPresented.wrappedValue = false |
| 241 | + } |
| 242 | + } |
| 243 | + } |
| 244 | + } |
| 245 | + } |
| 246 | +} |
0 commit comments