Source code for duck.html.components.snackbar
"""
Snackbar component for displaying brief messages at the top of the screen.
"""
from duck.html.components.container import FlexContainer
from duck.html.components.script import Script
[docs]
class Snackbar(FlexContainer):
"""
Snackbar component to show notifications.
"""
allowed_types = {"info", "success", "error"}
def __init__(self, text: str = None, type: str = "info", timeout: int = None, **kwargs):
self.type = type
self.timeout = timeout
assert self.type in self.allowed_types, f"Snackbar type must be one of {self.allowed_types}."
super().__init__(text=text or "", **kwargs)
[docs]
def on_create(self):
super().on_create()
self.color = "#222"
self.klass = f"snackbar snackbar-{self.type}"
# set default colors for different types
self.error_color = self.kwargs.get("error_color", "#f44336")
self.info_color = self.kwargs.get("info_color", "#2196f3")
self.success_color = self.kwargs.get("success_color", "#43a047")
self.warning_color = self.kwargs.get("warning_color", "#FFB300")
# Base styles for the snackbar
self.style.setdefaults({
"position": "fixed",
"top": "0",
"left": "0",
"right": "0",
"text-align": "center",
"padding": "12px 4px",
"z-index": "9999",
"transition": "transform 0.3s, opacity 0.3s",
"transform": "translateY(-100%)",
"opacity": "0",
"backdrop-filter": "blur(20px)",
"-webkit-backdrop-filter": "blur(20px)",
"will-change": "transform, opacity",
"flex-direction": "column",
"justify-content": "center",
"align-items": "center",
"margin-bottom": "2px",
})
bg = None
if self.type == "error":
bg = self.error_color
elif self.type == "info":
bg = self.info_color
elif self.type == "warning":
bg = self.warning_color
else:
bg = self.success_color
# Set background color based on type
self.style["background"] = bg
# Add script to handle show/hide logic.
self.script = Script(
inner_html=f"""
// Track the current auto-hide timer for the snackbar
if (!window._snackbarTimers) window._snackbarTimers = new WeakMap();
function showSnackbar(snackbar, type, timeout) {{
let bg = null;
// Set background color based on type
if (type === "error") bg = "{self.error_color}";
else if (type === "info") bg = "{self.info_color}";
else if (type === "success") bg = "{self.success_color}";
else if (type === "warning") bg = "{self.warning_color}";
if (bg) {{
snackbar.style.background = bg;
}}
// Clear any previous auto-hide timer for this snackbar
let prevTimer = window._snackbarTimers.get(snackbar);
if (prevTimer) {{
clearTimeout(prevTimer);
}}
// Show the snackbar (reset transition if hiding)
snackbar.style.display = "flex";
snackbar.style.transform = "translateY(0)";
snackbar.style.opacity = "1";
if (typeof timeout === "undefined" || timeout === null) timeout = {self.timeout or "null"};
if (timeout) {{
// Autohide: set new timer and track it
let timer = setTimeout(function() {{
hideSnackbar(snackbar);
window._snackbarTimers.delete(snackbar);
}}, timeout);
window._snackbarTimers.set(snackbar, timer);
}}
}}
function hideSnackbar(snackbar) {{
snackbar.style.transform = "translateY(-100%)";
snackbar.style.opacity = "0";
function onTransitionEnd(e) {{
if (e.propertyName === "opacity") {{
snackbar.style.display = "none";
snackbar.removeEventListener("transitionend", onTransitionEnd);
}}
}}
snackbar.addEventListener("transitionend", onTransitionEnd);
// Clear any pending auto-hide timer
let prevTimer = window._snackbarTimers.get(snackbar);
if (prevTimer) {{
clearTimeout(prevTimer);
window._snackbarTimers.delete(snackbar);
}}
}}
"""
)
self.add_child(self.script)
[docs]
def show(self):
"""
Show the snackbar.
"""
self.style["transform"] = "translateY(0)"
self.style["opacity"] = "1"
[docs]
def hide(self):
"""
Hide the snackbar.
"""
self.style["transform"] = "translateY(-100%)"
self.style["opacity"] = "0"