commit dedabec83aec63d8171ad04a667d530dd8381fd8
parent 817f63eaad17ed22317eec173382fb73c1c94ce9
Author: brookjeynes <me@brookjeynes.dev>
Date: Sun, 7 Jun 2026 17:40:05 +1000
refactor: simplify toast component
Signed-off-by: brookjeynes <me@brookjeynes.dev>
Diffstat:
5 files changed, 67 insertions(+), 86 deletions(-)
diff --git a/internal/server/htmx/htmx.go b/internal/server/htmx/htmx.go
@@ -6,13 +6,16 @@ import (
)
func HxError(w http.ResponseWriter, status int, msg string) {
- toast := toast.Error(msg)
- toastJSON, err := toast.Marshal()
+ t := toast.Toast{
+ Type: toast.ToastTypeError,
+ Body: msg,
+ }
+ json, err := t.Marshal()
if err != nil {
- toastJSON = []byte(`{"HXToast":{"type":"error","body":"An error occurred"}}`)
+ json = []byte(`{"HXToast":{"type":"error","body":"An error occurred"}}`)
}
- w.Header().Set("HX-Trigger", string(toastJSON))
+ w.Header().Set("HX-Trigger", string(json))
w.Header().Set("HX-Reswap", "none")
w.WriteHeader(status)
_, _ = w.Write([]byte(msg))
diff --git a/internal/server/toast/toast.css b/internal/server/toast/toast.css
@@ -1,43 +1,43 @@
-#toast-container {
- position: fixed;
- right: 1rem;
- top: 1rem;
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
+.toast-container {
+ position: fixed;
+ right: 1rem;
+ top: 1rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
}
-.toast {
- background-color: white;
- border: 1px solid black;
- padding: 0.2rem 0.5rem;
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 0.75rem;
- opacity: 0;
- transform: translateY(8px);
- animation: toast-in 0.2s ease-out forwards;
+.toast-box {
+ background-color: white;
+ border: 1px solid black;
+ padding: 0.2rem 0.5rem;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.75rem;
+ opacity: 0;
+ transform: translateY(8px);
+ animation: toast-in 0.2s ease-out forwards;
}
-.toast button {
- background: none;
- border: none;
- cursor: pointer;
- padding: 0;
- font: inherit;
- line-height: 1;
- flex-shrink: 0;
+.toast-box > .dismiss-button {
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 0;
+ font: inherit;
+ line-height: 1;
+ flex-shrink: 0;
}
@keyframes toast-in {
- from {
- opacity: 0;
- transform: translateY(8px);
- }
+ from {
+ opacity: 0;
+ transform: translateY(8px);
+ }
- to {
- opacity: 1;
- transform: none;
- }
+ to {
+ opacity: 1;
+ transform: none;
+ }
}
diff --git a/internal/server/toast/toast.go b/internal/server/toast/toast.go
@@ -5,8 +5,7 @@ import "encoding/json"
type ToastType = string
const (
- ToastTypeError ToastType = "error"
- ToastTypeSuccess ToastType = "success"
+ ToastTypeError ToastType = "error"
)
type Toast struct {
@@ -14,20 +13,6 @@ type Toast struct {
Body string
}
-func Error(body string) Toast {
- return Toast{
- Type: ToastTypeError,
- Body: body,
- }
-}
-
-func Success(body string) Toast {
- return Toast{
- Type: ToastTypeSuccess,
- Body: body,
- }
-}
-
func (t *Toast) Marshal() ([]byte, error) {
trigger := map[string]any{
"HXToast": map[string]string{
diff --git a/internal/server/toast/toast.js b/internal/server/toast/toast.js
@@ -1,33 +1,31 @@
-class Toast {
- constructor(type, body) {
- this.type = type;
- this.body = body;
- }
+function makeToast(type, body) {
+ const isError = type === "error";
- make() {
- const isError = this.type === "error";
+ const container = document.createElement("div");
+ container.classList.add("toast-box", `toast-${type}`);
+ container.setAttribute("role", isError ? "alert" : "status");
- const container = document.createElement("div");
- container.classList.add("toast", `toast-${this.type}`);
- container.setAttribute("role", isError ? "alert" : "status");
+ const message = document.createElement("span");
+ message.textContent = body;
- const message = document.createElement("span");
- message.textContent = this.body;
+ const dismiss = document.createElement("button");
+ dismiss.classList.add("dismiss-button");
+ dismiss.setAttribute("type", "button");
+ dismiss.setAttribute("aria-label", "Dismiss");
+ dismiss.textContent = "x";
+ dismiss.addEventListener("click", () => container.remove());
- const dismiss = document.createElement("button");
- dismiss.setAttribute("type", "button");
- dismiss.setAttribute("aria-label", "Dismiss");
- dismiss.textContent = "x";
- dismiss.addEventListener("click", () => container.remove());
+ container.appendChild(message);
+ container.appendChild(dismiss);
- container.appendChild(message);
- container.appendChild(dismiss);
-
- return container;
- }
+ return container;
+}
- show() {
- const toastContainer = document.querySelector("#toast-container");
- toastContainer.appendChild(this.make());
- }
+function showToast(type, body) {
+ const toastContainer = document.querySelector(".toast-container");
+ toastContainer.appendChild(makeToast(type, body));
}
+
+document.body.addEventListener("HXToast", (evt) => {
+ showToast(evt.detail.type, evt.detail.body);
+});
diff --git a/internal/server/ui/layouts/base/base.templ b/internal/server/ui/layouts/base/base.templ
@@ -19,17 +19,12 @@ templ Base(params BaseParams) {
</head>
<body>
<main>
- <section id="toast-container"></section>
+ <section class="toast-container"></section>
{ children... }
</main>
</body>
<script>
initLucideIcons();
-
- document.body.addEventListener("HXToast", (evt) => {
- const toast = new Toast(evt.detail.type, evt.detail.body);
- toast.show();
- });
</script>
</html>
}