// ============================================================================ // Stylescape | Progress Bar Manager // ============================================================================ // Controls progress bar UI elements with animation support. // Supports data-ss-progress attributes for declarative configuration. // ============================================================================ /** * Configuration options for ProgressBarManager */ export interface ProgressBarOptions { /** Initial value (0-100) */ value?: number; /** Minimum value */ min?: number; /** Maximum value */ max?: number; /** Whether to animate changes */ animate?: boolean; /** Animation duration in ms */ animationDuration?: number; /** Callback when value changes */ onChange?: (value: number, percentage: number) => void; /** Callback when reaching 100% */ onComplete?: () => void; /** CSS property to animate (width, height, or custom) */ property?: string; } /** * Progress bar manager with animation and accessibility support. * * @example JavaScript * ```typescript * const progress = new ProgressBarManager("#progress", { animate: true }) * progress.setProgress(50) * progress.increment(10) * ``` * * @example HTML with data-ss * ```html *
*
*
* ``` */ export class ProgressBarManager { private element: HTMLElement | null; private bar: HTMLElement | null; private options: Required; private currentValue: number; constructor( selectorOrElement: string | HTMLElement, options: ProgressBarOptions = {}, ) { this.element = typeof selectorOrElement === "string" ? document.querySelector(selectorOrElement) : selectorOrElement; this.options = { value: options.value ?? 0, min: options.min ?? 0, max: options.max ?? 100, animate: options.animate !== false, animationDuration: options.animationDuration ?? 300, onChange: options.onChange ?? (() => {}), onComplete: options.onComplete ?? (() => {}), property: options.property ?? "width", }; this.currentValue = this.options.value; // Find bar element (child with class containing 'bar' or the element itself) this.bar = this.element?.querySelector("[class*='bar']") || this.element; if (!this.element) { console.warn("[Stylescape] ProgressBarManager element not found"); return; } this.init(); } // ======================================================================== // Public Methods // ======================================================================== /** * Set progress value (0-100 or within min-max range) */ public setProgress(value: number): void { const clamped = Math.min( Math.max(value, this.options.min), this.options.max, ); const percentage = this.calculatePercentage(clamped); this.currentValue = clamped; this.updateDisplay(percentage); this.options.onChange(clamped, percentage); if (percentage >= 100) { this.options.onComplete(); } } /** * Get current progress value */ public getProgress(): number { return this.currentValue; } /** * Get current percentage (0-100) */ public getPercentage(): number { return this.calculatePercentage(this.currentValue); } /** * Increment progress by amount */ public increment(amount: number = 1): void { this.setProgress(this.currentValue + amount); } /** * Decrement progress by amount */ public decrement(amount: number = 1): void { this.setProgress(this.currentValue - amount); } /** * Reset progress to 0 */ public reset(): void { this.setProgress(this.options.min); } /** * Set progress to complete (100%) */ public complete(): void { this.setProgress(this.options.max); } /** * Set indeterminate state (for unknown progress) */ public setIndeterminate(indeterminate: boolean): void { if (!this.element) return; this.element.classList.toggle( "progress--indeterminate", indeterminate, ); this.element.removeAttribute("aria-valuenow"); } /** * Destroy the progress bar manager */ public destroy(): void { this.element = null; this.bar = null; } // ======================================================================== // Private Methods // ======================================================================== private init(): void { if (!this.element) return; // Set ARIA attributes this.element.setAttribute("role", "progressbar"); this.element.setAttribute("aria-valuemin", String(this.options.min)); this.element.setAttribute("aria-valuemax", String(this.options.max)); // Set transition for animation if (this.bar && this.options.animate) { this.bar.style.transition = `${this.options.property} ${this.options.animationDuration}ms ease`; } // Set initial value this.setProgress(this.currentValue); } private calculatePercentage(value: number): number { const range = this.options.max - this.options.min; return ((value - this.options.min) / range) * 100; } private updateDisplay(percentage: number): void { if (!this.bar || !this.element) return; // Update visual (this.bar.style as unknown as Record)[ this.options.property ] = `${percentage}%`; // Update ARIA this.element.setAttribute("aria-valuenow", String(this.currentValue)); // Update data attribute for CSS styling this.element.setAttribute( "data-progress", String(Math.round(percentage)), ); } } export default ProgressBarManager;