preline
Version:
Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.
162 lines (126 loc) • 4.07 kB
text/typescript
/*
* HSToggleCount
* @version: 3.0.1
* @author: Preline Labs Ltd.
* @license: Licensed under MIT and Preline UI Fair Use License (https://preline.co/docs/license.html)
* Copyright 2024 Preline Labs Ltd.
*/
import { IToggleCountOptions, IToggleCount } from '../toggle-count/interfaces';
import HSBasePlugin from '../base-plugin';
import { ICollectionItem } from '../../interfaces';
class HSToggleCount
extends HSBasePlugin<IToggleCountOptions>
implements IToggleCount
{
private readonly target: HTMLInputElement | null;
private readonly min: number | null;
private readonly max: number | null;
private readonly duration: number | null;
private isChecked: boolean;
private onToggleChangeListener: () => void;
constructor(el: HTMLElement, options?: IToggleCountOptions) {
super(el, options);
const data = el.getAttribute('data-hs-toggle-count');
const dataOptions: IToggleCountOptions = data ? JSON.parse(data) : {};
const concatOptions = {
...dataOptions,
...options,
};
this.target = concatOptions?.target
? typeof concatOptions?.target === 'string'
? (document.querySelector(concatOptions.target) as HTMLInputElement)
: concatOptions.target
: null;
this.min = concatOptions?.min || 0;
this.max = concatOptions?.max || 0;
this.duration = concatOptions?.duration || 700;
this.isChecked = (this.target as HTMLInputElement).checked || false;
if (this.target) this.init();
}
private toggleChange() {
this.isChecked = !this.isChecked;
this.toggle();
}
private init() {
this.createCollection(window.$hsToggleCountCollection, this);
if (this.isChecked) this.el.innerText = String(this.max);
this.onToggleChangeListener = () => this.toggleChange();
this.target.addEventListener('change', this.onToggleChangeListener);
}
private toggle() {
if (this.isChecked) this.countUp();
else this.countDown();
}
private animate(from: number, to: number) {
let startTimestamp = 0;
const step = (timestamp: number) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min(
(timestamp - startTimestamp) / this.duration,
1,
);
this.el.innerText = String(Math.floor(progress * (to - from) + from));
if (progress < 1) window.requestAnimationFrame(step);
};
window.requestAnimationFrame(step);
}
// Public methods
public countUp() {
this.animate(this.min, this.max);
}
public countDown() {
this.animate(this.max, this.min);
}
public destroy() {
// Remove listeners
this.target.removeEventListener('change', this.onToggleChangeListener);
window.$hsToggleCountCollection = window.$hsToggleCountCollection.filter(
({ element }) => element.el !== this.el,
);
}
// Static methods
static getInstance(target: HTMLElement | string, isInstance?: boolean) {
const elInCollection = window.$hsToggleCountCollection.find(
(el) =>
el.element.el ===
(typeof target === 'string' ? document.querySelector(target) : target),
);
return elInCollection
? isInstance
? elInCollection
: elInCollection.element
: null;
}
static autoInit() {
if (!window.$hsToggleCountCollection) window.$hsToggleCountCollection = [];
if (window.$hsToggleCountCollection)
window.$hsToggleCountCollection = window.$hsToggleCountCollection.filter(
({ element }) => document.contains(element.el),
);
document
.querySelectorAll('[data-hs-toggle-count]:not(.--prevent-on-load-init)')
.forEach((el: HTMLElement) => {
if (
!window.$hsToggleCountCollection.find(
(elC) => (elC?.element?.el as HTMLElement) === el,
)
)
new HSToggleCount(el);
});
}
}
declare global {
interface Window {
HSToggleCount: Function;
$hsToggleCountCollection: ICollectionItem<HSToggleCount>[];
}
}
window.addEventListener('load', () => {
HSToggleCount.autoInit();
// Uncomment for debug
// console.log('Toggle count collection:', window.$hsToggleCountCollection);
});
if (typeof window !== 'undefined') {
window.HSToggleCount = HSToggleCount;
}
export default HSToggleCount;