Middleware+View+UserState: Add toast messages
This commit is contained in:
@@ -17,7 +17,10 @@ struct TodosTableComponent: HTML {
|
||||
tr {
|
||||
th { "#" }
|
||||
th { "ID" }
|
||||
th { "Title" }
|
||||
th {
|
||||
"Title "
|
||||
i(.class("bi bi-arrow-down-circle")) {}
|
||||
}
|
||||
th { "Modifier" }
|
||||
}
|
||||
}
|
||||
@@ -32,10 +35,12 @@ struct TodosTableComponent: HTML {
|
||||
td { todo.title ?? "" }
|
||||
td {
|
||||
if let id = todo.id {
|
||||
button(
|
||||
.class("btn btn-danger"),
|
||||
i(
|
||||
.class("bi bi-trash3 text-danger"),
|
||||
.data("bs-toggle", value: "tooltip"),
|
||||
.data("bs-title", value: "Delete"),
|
||||
.hx.delete("/\(name)/\(id.uuidString)")
|
||||
) { "Delete" }
|
||||
) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,17 +19,24 @@ struct MainLayout<Body: HTML>: HTMLDocument {
|
||||
.href("https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css"),
|
||||
.integrity("sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"),
|
||||
.crossorigin(.anonymous))
|
||||
|
||||
link(
|
||||
.rel(.stylesheet),
|
||||
.href("https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css"),
|
||||
.integrity("sha384-XGjxtQfXaH2tnPFa9x+ruJTuLE3Aa6LhHSWRr1XeTyhezb4abCG4ccI5AkVDxqC+"),
|
||||
.crossorigin(.anonymous))
|
||||
|
||||
link(.rel(.stylesheet), .href("/css/style.css"))
|
||||
|
||||
// ---------------------------------
|
||||
// Style
|
||||
|
||||
style {
|
||||
"""
|
||||
body {
|
||||
padding-top: 56px;
|
||||
}
|
||||
"""
|
||||
"""
|
||||
body {
|
||||
padding-top: 56px;
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +57,9 @@ body {
|
||||
pageContent
|
||||
}
|
||||
}
|
||||
|
||||
// Placeholder for all toast messages
|
||||
ToastView()
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
@@ -76,6 +86,7 @@ body {
|
||||
.integrity("sha384-yhWpPsq2os1hEnx1I8cH7Ius6rclwTm3G2fhXDLF6Pzv7UnSsXY7BAj4fB6PIgSz"),
|
||||
.crossorigin(.anonymous)
|
||||
) {}
|
||||
|
||||
script(.src("/js/site.js")) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import Elementary
|
||||
import ElementaryHTMX
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// let state = try getState(request: req)
|
||||
// state.toast = ToastState(message: "", title: "", level: ToastState.Level.error)
|
||||
// throw Abort(.badRequest, headers: ["HX-Trigger": "toast"])
|
||||
//
|
||||
// The header "HX-Trigger" will make the part refresh and show the toast message
|
||||
|
||||
struct ToastView: HTML {
|
||||
|
||||
var state: ToastState = ToastState()
|
||||
|
||||
// -------------------------------------
|
||||
|
||||
var content: some HTML {
|
||||
div(
|
||||
.class("toast-container"), .id("cdiv_toast"),
|
||||
.hx.get("/toast"),
|
||||
.hx.trigger(HTMLAttributeValue.HTMX.EventTrigger(rawValue: "toast from:body")),
|
||||
.hx.swap(.outerHTML)
|
||||
) {
|
||||
div(.class("toast"), .id("toast")) {
|
||||
div(.class("toast-header bg-\(state.level.rawValue) text-white")) {
|
||||
strong(.class("me-auto")) { state.title }
|
||||
button(
|
||||
.class("btn-close btn-close-white"), .type(.button),
|
||||
.data("bs-dismiss", value: "toast")
|
||||
) {}
|
||||
}
|
||||
div(.class("toast-body")) { state.message }
|
||||
}
|
||||
if !state.message.isEmpty {
|
||||
script {
|
||||
"""
|
||||
runOnceAfterSettle(function () {
|
||||
const element = document.getElementById("toast");
|
||||
const toast = new bootstrap.Toast(element, { autohide: true, delay: 5000 });
|
||||
toast.show();
|
||||
|
||||
element.addEventListener("hidden.bs.toast", function () {
|
||||
element.remove();
|
||||
});
|
||||
});
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user