Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,53 @@ 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 or focusing (tabindex="0",
// keyboard/AT-reachable) pauses the timer so a message being read
// doesn't vanish; leaving or blurring resumes it. The flash keeps a
// fixed DOM id, so a second flash of the same kind patches into this
// same element rather than mount/destroy — updated() restarts the
// countdown against the fresh data-timeout so a stale timer can't
// dismiss the new message early, unless the element is currently
// paused (hovered/focused), in which case it stays paused.
AutoDismissFlash: {
mounted() {
this.hovered = false
this.focused = false
this.startTimer()
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.hovered || this.focused) {
this.clearTimer()
} else {
this.startTimer()
}
},
Comment thread
bbangert marked this conversation as resolved.
destroyed() { this.clearTimer() },
pause(flag) {
this[flag] = true
this.clearTimer()
},
resume(flag) {
this[flag] = false
if (!this.hovered && !this.focused) this.startTimer()
},
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)
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions lib/universal_proxy_web/components/layouts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,27 @@ defmodule UniversalProxyWeb.Layouts do
<div :if={Phoenix.Flash.get(@flash, :info) || Phoenix.Flash.get(@flash, :error)} class="px-6 pt-4">
<div
:if={msg = Phoenix.Flash.get(@flash, :info)}
id="flash-info"
class="rounded-md bg-success-soft text-success px-3.5 py-2.5 text-sm flex items-center gap-2"
phx-click="lv:clear-flash"
phx-value-key="info"
phx-hook="AutoDismissFlash"
data-timeout="4000"
role="status"
tabindex="0"
>
{msg}
</div>
<div
:if={msg = Phoenix.Flash.get(@flash, :error)}
id="flash-error"
class="rounded-md bg-danger-soft text-danger px-3.5 py-2.5 text-sm flex items-center gap-2 mt-2"
phx-click="lv:clear-flash"
phx-value-key="error"
phx-hook="AutoDismissFlash"
data-timeout="8000"
role="alert"
tabindex="0"
>
{msg}
</div>
Expand Down
Loading