flyonui
Version:
The easiest, free and open-source Tailwind CSS component library with semantic classes.
141 lines (105 loc) • 4.06 kB
text/typescript
/*
* HSToggleCount
* @version: 3.2.2
* @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 './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-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-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