// ============================================================================
// Stylescape | Preloader
// ============================================================================
// Manages a preloader element that displays during page load.
// Supports data-ss-preloader attributes for declarative configuration.
// ============================================================================
/**
* Configuration options for Preloader
*/
export interface PreloaderOptions {
/** Timeout before hiding (ms) */
timeout?: number;
/** CSS class to add when hidden */
hiddenClass?: string;
/** Minimum display time (ms) */
minDisplayTime?: number;
/** Callback when preloader is hidden */
onHide?: () => void;
}
/**
* Preloader component that shows a loading indicator during page load.
*
* @example JavaScript
* ```typescript
* const preloader = new Preloader(".preloader", { timeout: 500 })
* ```
*
* @example HTML with data-ss
* ```html
*
* ```
*/
export class Preloader {
private element: HTMLElement | null;
private options: Required;
private startTime: number;
private isHidden: boolean = false;
constructor(
selectorOrElement: string | HTMLElement,
options: PreloaderOptions = {},
) {
this.element =
typeof selectorOrElement === "string"
? document.querySelector(selectorOrElement)
: selectorOrElement;
this.options = {
timeout: options.timeout ?? 500,
hiddenClass: options.hiddenClass ?? "preloader--hidden",
minDisplayTime: options.minDisplayTime ?? 0,
onHide: options.onHide ?? (() => {}),
};
this.startTime = Date.now();
if (!this.element) {
console.warn("[Stylescape] Preloader element not found");
return;
}
this.init();
}
// ========================================================================
// Public Methods
// ========================================================================
/**
* Manually show the preloader
*/
public show(): void {
if (!this.element) return;
this.isHidden = false;
this.startTime = Date.now();
this.element.classList.remove(this.options.hiddenClass);
this.element.setAttribute("aria-hidden", "false");
}
/**
* Manually hide the preloader
*/
public hide(): void {
if (!this.element || this.isHidden) return;
const elapsed = Date.now() - this.startTime;
const remaining = Math.max(0, this.options.minDisplayTime - elapsed);
setTimeout(() => {
if (!this.element) return;
this.element.classList.add(this.options.hiddenClass);
this.element.setAttribute("aria-hidden", "true");
this.isHidden = true;
this.options.onHide();
}, remaining);
}
/**
* Destroy the preloader instance
*/
public destroy(): void {
this.hide();
this.element = null;
}
// ========================================================================
// Private Methods
// ========================================================================
private init(): void {
// Set ARIA attributes for accessibility
this.element?.setAttribute("role", "progressbar");
this.element?.setAttribute("aria-busy", "true");
this.element?.setAttribute("aria-hidden", "false");
// Hide on window load with timeout
if (document.readyState === "complete") {
this.scheduleHide();
} else {
window.addEventListener("load", () => this.scheduleHide());
}
}
private scheduleHide(): void {
setTimeout(() => this.hide(), this.options.timeout);
}
}
export default Preloader;
// constructor(preloaderName: string, preloaderTimeout: number) {
// this.preloaderName = preloaderName
// this.preloaderElement = document.querySelector(preloaderName)
// this.preloaderTimeout = preloaderTimeout
// if (!this.preloaderElement) {
// // pass
// // console.warn(`Preloader element not found: ${preloaderName}`)
// } else {
// this.setPreloader()
// }
// }
// setPreloader(): void {
// window.addEventListener('load', this.handleLoadEvent.bind(this))
// }
// private handleLoadEvent(): void {
// if (this.preloaderElement) {
// setTimeout(() => this.hidePreloader(), this.preloaderTimeout)
// }
// }
// private hidePreloader(): void {
// if (this.preloaderElement) {
// this.preloaderElement.classList.add('preloader_hidden')
// }
// }
// // Optional: Method to update the preloader element dynamically
// updatePreloaderElement(selector: string): void {
// this.preloaderName = selector
// this.preloaderElement = document.querySelector(selector)
// if (!this.preloaderElement) {
// console.warn(`Updated preloader element not found: ${selector}`)
// }
// }
// }