From 147b692beba2400224117316b4b4af640e408720 Mon Sep 17 00:00:00 2001 From: Ben Bangert <100193+bbangert@users.noreply.github.com> Date: Fri, 21 Aug 2026 16:46:10 +0000 Subject: [PATCH 1/3] Auto-dismiss flash messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flashes stayed until clicked or a page reload — the eject "safe to unplug" toast never cleared. A small hook dismisses info flashes after 4s and errors after 8s, click still works, hover pauses the timer. Co-Authored-By: Claude Fable 5 --- assets/js/app.js | 22 +++++++++++++++++++ lib/universal_proxy_web/components/layouts.ex | 6 +++++ 2 files changed, 28 insertions(+) diff --git a/assets/js/app.js b/assets/js/app.js index 99257c4..1ed7ce6 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -77,6 +77,28 @@ const Hooks = { if (typeof this.el.select === "function") this.el.select() }, 30) } + }, + + // Auto-dismisses a flash message after `data-timeout` ms by replaying + // a click on the element itself, which reuses the existing + // phx-click="lv:clear-flash" binding — same path the user gets by + // clicking the flash manually. Hovering pauses the timer so a message + // being read doesn't vanish underneath the cursor. + AutoDismissFlash: { + mounted() { + this.startTimer() + this.el.addEventListener("mouseenter", () => this.clearTimer()) + this.el.addEventListener("mouseleave", () => this.startTimer()) + }, + destroyed() { this.clearTimer() }, + startTimer() { + const timeout = parseInt(this.el.dataset.timeout, 10) || 4000 + this.clearTimer() + this.timer = setTimeout(() => this.el.click(), timeout) + }, + clearTimer() { + if (this.timer) clearTimeout(this.timer) + } } } diff --git a/lib/universal_proxy_web/components/layouts.ex b/lib/universal_proxy_web/components/layouts.ex index e193500..50db7ef 100644 --- a/lib/universal_proxy_web/components/layouts.ex +++ b/lib/universal_proxy_web/components/layouts.ex @@ -38,18 +38,24 @@ defmodule UniversalProxyWeb.Layouts do
{msg}
@@ -57,6 +58,7 @@ defmodule UniversalProxyWeb.Layouts do phx-hook="AutoDismissFlash" data-timeout="8000" role="alert" + tabindex="0" > {msg}
From 7e0c51bf97b7b38a97f6fd3871907ba002d3d3cb Mon Sep 17 00:00:00 2001 From: Ben Bangert <100193+bbangert@users.noreply.github.com> Date: Fri, 21 Aug 2026 21:02:27 +0000 Subject: [PATCH 3/3] Track hover and focus pauses independently Leaving one pause condition resumed the countdown while the other was still active; the timer now restarts only when neither the pointer nor focus is on the flash. Co-Authored-By: Claude Fable 5 --- assets/js/app.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index fe68d1b..4178a8d 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -92,28 +92,29 @@ const Hooks = { // paused (hovered/focused), in which case it stays paused. AutoDismissFlash: { mounted() { - this.paused = false + this.hovered = false + this.focused = false this.startTimer() - this.el.addEventListener("mouseenter", () => this.pause()) - this.el.addEventListener("mouseleave", () => this.resume()) - this.el.addEventListener("focusin", () => this.pause()) - this.el.addEventListener("focusout", () => this.resume()) + this.el.addEventListener("mouseenter", () => this.pause("hovered")) + this.el.addEventListener("mouseleave", () => this.resume("hovered")) + this.el.addEventListener("focusin", () => this.pause("focused")) + this.el.addEventListener("focusout", () => this.resume("focused")) }, updated() { - if (this.paused) { + if (this.hovered || this.focused) { this.clearTimer() } else { this.startTimer() } }, destroyed() { this.clearTimer() }, - pause() { - this.paused = true + pause(flag) { + this[flag] = true this.clearTimer() }, - resume() { - this.paused = false - this.startTimer() + resume(flag) { + this[flag] = false + if (!this.hovered && !this.focused) this.startTimer() }, startTimer() { const timeout = parseInt(this.el.dataset.timeout, 10) || 4000