UNPKG

stylescape

Version:

Stylescape is a visual identity framework developed by Scape Agency.

165 lines 6.55 kB
export class RatingManager { constructor(selectorOrElement, options = {}) { this.hoverValue = null; this.handleClick = (e) => { if (this.options.readOnly) return; const star = e.currentTarget; const value = this.getStarValue(star, e); this.setValue(value); }; this.handleMouseEnter = (e) => { if (this.options.readOnly) return; const star = e.currentTarget; this.hoverValue = this.getStarValue(star, e); this.updateDisplay(); }; this.handleMouseLeave = () => { }; this.handleKeyDown = (e) => { if (this.options.readOnly) return; const step = this.options.half ? 0.5 : 1; switch (e.key) { case "ArrowRight": case "ArrowUp": e.preventDefault(); this.setValue(Math.min(this.currentValue + step, this.options.max)); break; case "ArrowLeft": case "ArrowDown": e.preventDefault(); this.setValue(Math.max(this.currentValue - step, 0)); break; case "Home": e.preventDefault(); this.setValue(0); break; case "End": e.preventDefault(); this.setValue(this.options.max); break; } }; this.container = typeof selectorOrElement === "string" ? document.querySelector(selectorOrElement) : selectorOrElement; this.options = { max: options.max ?? 5, value: options.value ?? 0, readOnly: options.readOnly ?? false, half: options.half ?? false, starSelector: options.starSelector ?? ".star, [data-value]", activeClass: options.activeClass ?? "rating__star--active", halfClass: options.halfClass ?? "rating__star--half", hoverClass: options.hoverClass ?? "rating__star--hover", onChange: options.onChange ?? (() => { }), }; this.currentValue = this.options.value; this.stars = []; if (!this.container) { console.warn("[Stylescape] RatingManager container not found"); return; } this.init(); } getValue() { return this.currentValue; } setValue(value) { const clamped = Math.min(Math.max(value, 0), this.options.max); this.currentValue = this.options.half ? clamped : Math.round(clamped); this.updateDisplay(); this.options.onChange(this.currentValue); } reset() { this.setValue(0); } setReadOnly(readOnly) { this.options.readOnly = readOnly; this.container?.classList.toggle("rating--readonly", readOnly); if (readOnly) { this.container?.setAttribute("aria-readonly", "true"); } else { this.container?.removeAttribute("aria-readonly"); } } destroy() { this.stars.forEach((star) => { star.removeEventListener("click", this.handleClick); star.removeEventListener("mouseenter", this.handleMouseEnter); star.removeEventListener("mouseleave", this.handleMouseLeave); }); this.container?.removeEventListener("keydown", this.handleKeyDown); this.container = null; this.stars = []; } init() { if (!this.container) return; this.stars = Array.from(this.container.querySelectorAll(this.options.starSelector)); if (this.stars.length === 0) { this.createStars(); } this.container.setAttribute("role", "slider"); this.container.setAttribute("aria-valuemin", "0"); this.container.setAttribute("aria-valuemax", String(this.options.max)); this.container.setAttribute("aria-valuenow", String(this.currentValue)); this.container.setAttribute("tabindex", "0"); if (!this.options.readOnly) { this.stars.forEach((star, index) => { star.addEventListener("click", this.handleClick); star.addEventListener("mouseenter", this.handleMouseEnter); star.addEventListener("mouseleave", this.handleMouseLeave); if (!star.hasAttribute("data-value")) { star.setAttribute("data-value", String(index + 1)); } }); this.container.addEventListener("keydown", this.handleKeyDown); this.container.addEventListener("mouseleave", () => { this.hoverValue = null; this.updateDisplay(); }); } this.updateDisplay(); } createStars() { if (!this.container) return; for (let i = 1; i <= this.options.max; i++) { const star = document.createElement("span"); star.className = "rating__star"; star.setAttribute("data-value", String(i)); star.textContent = "★"; this.container.appendChild(star); this.stars.push(star); } } getStarValue(star, event) { const baseValue = parseInt(star.getAttribute("data-value") || "0", 10); if (this.options.half) { const rect = star.getBoundingClientRect(); const isHalf = event.clientX < rect.left + rect.width / 2; return isHalf ? baseValue - 0.5 : baseValue; } return baseValue; } updateDisplay() { const displayValue = this.hoverValue ?? this.currentValue; this.stars.forEach((star) => { const value = parseInt(star.getAttribute("data-value") || "0", 10); const isActive = value <= displayValue; const isHalf = this.options.half && value - 0.5 === displayValue; const isHover = this.hoverValue !== null; star.classList.toggle(this.options.activeClass, isActive && !isHalf); star.classList.toggle(this.options.halfClass, isHalf); star.classList.toggle(this.options.hoverClass, isHover && value <= displayValue); }); this.container?.setAttribute("aria-valuenow", String(this.currentValue)); } } export default RatingManager; //# sourceMappingURL=RatingManager.js.map