term-kit-backend/Sources/TermKitBackend/View/Dialogs/Box.swift

126 lines
4.0 KiB
Swift

//
// Box.swift
// TermKitBackend
//
// Created by david-swift on 10.07.2024.
//
import TermKit
/// A dialog box, either a query, error, or info box.
struct Box: TermKitWidget {
/// The title.
var title: String
/// The message.
var message: String
/// The signal.
var signal: Signal
/// The buttons (info box if none).
var buttons: Body
/// The content behind the box.
var content: AnyView
/// Whether it is an error box.
var error = false
/// The identifier for the buttons of a box.
let boxButtonsID = "box-buttons"
/// The view storage.
/// - Parameters:
/// - modifiers: Modify views before being updated.
/// - type: The type of the app storage.
/// - Returns: The view storage.
func container<Data>(
data: WidgetData,
type: Data.Type
) -> ViewStorage where Data: ViewRenderData {
let storage = ViewStorage(nil)
let contentStorage = content.storage(data: data, type: type)
storage.pointer = contentStorage.pointer
return .init(contentStorage.pointer, content: [.mainContent: [contentStorage]])
}
/// Update the stored content.
/// - Parameters:
/// - storage: The storage to update.
/// - modifiers: Modify views before being updated
/// - updateProperties: Whether to update the view's properties.
/// - type: The type of the app storage.
func update<Data>(
_ storage: ViewStorage,
data: WidgetData,
updateProperties: Bool,
type: Data.Type
) where Data: ViewRenderData {
guard let storage = storage.content[.mainContent]?.first else {
return
}
content.updateStorage(storage, data: data, updateProperties: updateProperties, type: type)
let buttons = ButtonCollection { self.buttons }
.storage(data: data, type: ButtonContext.self).pointer as? [Button] ?? []
storage.fields[boxButtonsID] = buttons
if signal.update {
if buttons.isEmpty {
MessageBox.info(title, message: message)
} else if error {
MessageBox.error(title, message: message, buttons: buttons.map { $0.label }) { selection in
(storage.fields[boxButtonsID] as? [Button])?[safe: selection]?.action()
}
} else {
MessageBox.query(title, message: message, buttons: buttons.map { $0.label }) { selection in
(storage.fields[boxButtonsID] as? [Button])?[safe: selection]?.action()
}
}
}
}
}
/// Extend `AnyView`,
extension AnyView {
/// Add a query box.
/// - Parameters:
/// - title: The title.
/// - message: The message.
/// - signal: The box appears when the signal is emitted.
/// - buttons: The buttons.
/// - Returns: The view.
public func queryBox(
_ title: String,
message: String,
signal: Signal,
@ViewBuilder buttons: @escaping () -> Body
) -> AnyView {
Box(title: title, message: message, signal: signal, buttons: buttons(), content: self)
}
/// Add an error box.
/// - Parameters:
/// - title: The title.
/// - message: The message.
/// - signal: The box appears when the signal is emitted.
/// - buttons: The buttons.
/// - Returns: The view.
public func errorBox(
_ title: String,
message: String,
signal: Signal,
@ViewBuilder buttons: @escaping () -> Body
) -> AnyView {
Box(title: title, message: message, signal: signal, buttons: buttons(), content: self, error: true)
}
/// Add an info box.
/// - Parameters:
/// - title: The title.
/// - message: The message.
/// - signal: The box appears when the signal is emitted.
/// - Returns: The view.
public func infoBox(_ title: String, message: String, signal: Signal) -> AnyView {
Box(title: title, message: message, signal: signal, buttons: [], content: self)
}
}