import SwiftUI import SFSafeSymbols final class InsertableFileButton: ObservableObject { @Published var label: ContentLabel = .init(icon: .buttonDownload, value: "Text") @Published var file: FileResource? @Published var downloadedFileName: String? = nil var command: String? { guard let file else { return nil } let result = """ icon: \(label.icon.rawValue) text: \(label.value) file: \(file.id) """ guard let downloadedFileName else { return result } return result + "\n" + "name: \(downloadedFileName)" } } final class InsertableUrlButton: ObservableObject { @Published var label: ContentLabel = .init(icon: .buttonDownload, value: "Text") @Published var url: String = "Url" var command: String? { """ icon: \(label.icon.rawValue) text: \(label.value) url: \(url) """ } } final class InsertableEventButton: ObservableObject { @Published var label: ContentLabel = .init(icon: .buttonDownload, value: "Text") @Published var event: String = "Javascript" var command: String? { """ icon: \(label.icon.rawValue) text: \(label.value) event: \(event) """ } } struct InsertableButtons: View, InsertableCommandView { final class Model: ObservableObject, InsertableCommandModel { enum AnyButton: Identifiable { case file(InsertableFileButton) case url(InsertableUrlButton) case event(InsertableEventButton) var command: String? { switch self { case .file(let file): return file.command case .url(let url): return url.command case .event(let event): return event.command } } var id: String { switch self { case .file(let file): return "file-\(file.file?.id ?? "none")" case .url(let url): return "url-\(url.url)" case .event(let event): return "event-\(event.event)" } } } @Published var buttons: [AnyButton] = [] var isReady: Bool { !buttons.isEmpty } var command: String? { let result = buttons.compactMap { $0.command } guard result.count == buttons.count else { return nil } return result.map { "```buttons\n\($0)\n```" }.joined(separator: "\n\n") } } static let title = "Buttons" static let sheetTitle = "Insert buttons" static let icon: SFSymbol = .rectangleAndHandPointUpLeft @ObservedObject private var model: Model init(model: Model) { self.model = model } var body: some View { VStack(alignment: .leading) { List(model.buttons) { button in switch button { case .file(let file): FileButtonView(content: file) case .url(let url): UrlButtonView(content: url) case .event(let event): EventButtonView(content: event) } } .frame(minHeight: 300) HStack { Spacer() Button("File", action: { model.buttons.append(.file(.init())) }) Button("Url", action: { model.buttons.append(.url(.init())) }) Button("Event", action: { model.buttons.append(.event(.init())) }) Spacer() } } } } private struct FileButtonView: View { @ObservedObject var content: InsertableFileButton @State private var showFileSelectionSheet = false var body: some View { HStack { LabelEditingView(label: content.label) Button("\(content.file?.id ?? "Select file")", action: { showFileSelectionSheet = true }) OptionalTextField("", text: $content.downloadedFileName, prompt: "Downloaded file name") .textFieldStyle(.roundedBorder) } .sheet(isPresented: $showFileSelectionSheet) { FileSelectionView(selectedFile: $content.file) } } } private struct UrlButtonView: View { @ObservedObject var content: InsertableUrlButton var body: some View { HStack { LabelEditingView(label: content.label) TextField("", text: $content.url, prompt: Text("URL")) .textFieldStyle(.roundedBorder) } } } private struct EventButtonView: View { @ObservedObject var content: InsertableEventButton var body: some View { HStack { LabelEditingView(label: content.label) TextField("", text: $content.event, prompt: Text("Javascript")) .textFieldStyle(.roundedBorder) } } }