Source code for duck.html.components.progressbar
"""
Progress bar component.
"""
from duck.html.components.container import Container
from duck.html.components.script import Script
[docs]
class ProgressBar(Container):
"""
A modern loading bar component with interactive JavaScript toggles and Python toggles.
Attributes:
progress_bar (Container): The progress bar element.
"""
[docs]
def on_create(self):
"""
Initializes the loading bar component.
Sets up the styles and adds the progress bar element.
"""
super().on_create()
# Set up the styles for the loading bar
self.bg_color = "rgba(0, 255, 0, 0.1)"
self.style["width"] = "100%"
self.style["height"] = "3px"
self.style["border-radius"] = "3px"
self.style["overflow"] = "hidden"
self.style["display"] = "none"
self.style["transition"] = "display 0.1s ease"
# Create a progress bar
self._progress_bar = Container(bg_color="#4CAF50", id="progress-bar-inner")
self._progress_bar.style["width"] = "100%"
self._progress_bar.style["height"] = "100%"
self._progress_bar.style["transition"] = "transform 0.1s ease"
self._progress_bar.style["will-change"] = "transform"
self._progress_bar.style["transform-origin"] = "left"
# Add the progress bar element to the loading bar
self.add_child(self._progress_bar)
# Add a JavaScript function to toggle the loading bar
self.script = Script(
inner_html="""
function updateProgressBar(progressBar, progress, autoHideWhenZero = true) {
// Clamp progress value to [0, 100] for safety
progress = Math.max(0, Math.min(100, progress));
// Find the inner progress bar element by id or class
const progressBarInner =
progressBar.querySelector('#progress-bar-inner') ||
progressBar.querySelector('.progress-bar-inner');
// Schedule updates for the next animation frame for smooth UI
requestAnimationFrame(() => {
if (!progressBarInner) return;
if (progress > 0) {
// Show the progress bar if progress is nonzero
progressBar.style.display = 'inline-block';
// Animate the width (scaleX) to represent progress
progressBarInner.style.transform = `scaleX(${progress / 100})`;
} else if (autoHideWhenZero) {
// Hide the bar and reset the inner transform if enabled
hideProgressBar(progressBar, false);
} else {
// If not hiding, reset inner bar to zero width
progressBarInner.style.transform = 'scaleX(0)';
}
});
}
/**
* Hides the progress bar and resets inner bar transform.
* Useful for manual control or after navigation/transition.
* @param {HTMLElement} progressBar - The outer progress bar element.
* @param {boolean} nextAnimationFrame - Whether to run in the next animation frame. Defaults to true.
*/
function hideProgressBar(progressBar, nextAnimationFrame = true) {
// Find inner bar element
const progressBarInner =
progressBar.querySelector('#progress-bar-inner') ||
progressBar.querySelector('.progress-bar-inner');
if (nextAnimationFrame) {
requestAnimationFrame(() => {
// Hide the outer bar
progressBar.style.display = 'none';
// Reset inner bar transform for future use
if (progressBarInner) {
progressBarInner.style.transform = 'scaleX(0)';
}
});
}
else {
// Hide the outer bar
progressBar.style.display = 'none';
// Reset inner bar transform for future use
if (progressBarInner) {
progressBarInner.style.transform = 'scaleX(0)';
}
}
}
"""
)
self.add_child(self.script)
[docs]
def update_progress(self, progress: int):
"""
Updates the progress of the loading bar.
Args:
progress (int): The progress percentage.
"""
assert isinstance(progress, int), "Progress must be an integer."
if progress > 0 and self.style.get("display") == "none":
self.style["display"] = "inline-block"
elif progress <= 0:
self.style["display"] = "none"
# Update the progress bar width
self._progress_bar.style["width"] = f"{progress}%"