xtendui
Version:
Xtend UI is a powerful frontend library of Tailwind CSS components enhanced by vanilla js. It helps you build interfaces with advanced interactions and animations.
252 lines (237 loc) • 6.62 kB
JavaScript
/*!
* Xtend UI (https://xtendui.github.io/xtendui/)
* @copyright (c) 2017-2026 Riccardo Caroli
* @license MIT (https://github.com/xtendui/xtendui/blob/master/LICENSE.txt)
*/
import { Xt } from '../xt.mjs'
/**
* RippleInit
*/
export class RippleInit {
//
// init
//
/**
* init
*/
_init() {
const self = this
// init
self._initVars()
self._initLogic()
}
/**
* init vars
*/
_initVars() {
const self = this
// options
self._optionsDefault = Xt.merge([self.constructor.optionsDefault, Xt.options[self.componentName]])
self._optionsInitial = self.options = Xt.merge([self._optionsDefault, self._optionsCustom])
}
/**
* init logic
*/
_initLogic() {
const self = this
const options = self.options
// namespace
self.ns = self.ns ?? Xt.uniqueId()
// enable first for proper initial activation
self.enable()
// matches
Xt._initMatches({ self, optionsInitial: self._optionsInitial })
// vars
self.initial = true
// inner
if (!self.inner) {
self.container.append(Xt.node({ str: '<div class="xt-ripple-inner"></div>' }))
self.inner = self.container.querySelector(':scope > .xt-ripple-inner')
}
// on
const onHandler = Xt.dataStorage.put(self.container, `mousedown touchstart/${self.ns}`, self._eventStart.bind(self))
self.container.addEventListener('mousedown', onHandler)
self.container.addEventListener('touchstart', onHandler, { passive: true })
// setup
// dispatch event
self.container.dispatchEvent(new CustomEvent(`setup.${self._componentNs}`))
// needs frameDouble after ondone
Xt.frameDouble({
el: self.container,
ns: `${self.ns}Init`,
func: () => {
// initialized class
self.container.setAttribute(`data-${self.componentName}-init`, '')
// dispatch event
self.container.dispatchEvent(new CustomEvent(`init.${self._componentNs}`))
self.initial = false
// debug
if (options.debug) {
// eslint-disable-next-line no-console
console.debug(`${self.componentName} init`, self)
}
},
})
// disable last for proper options.disableDeactivate
if (self.options.disabled) {
self.disable()
}
}
//
// methods
//
/**
* eventStart
* @param {Event} e
*/
_eventStart(e) {
const self = this
const options = self.options
// disabled
if (self.disabled) {
return
}
// check if inside onlyInside
if (!options.onlyInside || e.target.closest(options.onlyInside)) {
self.inner.append(Xt.node({ str: '<div class="xt-ripple"></div>' }))
// fix prevent dragging links and images
if (e.type === 'mousedown') {
e.preventDefault()
}
// size
const h = self.container.offsetHeight
const w = self.container.offsetWidth
const sizeObject = h > w ? h : w
const size = sizeObject * options.sizeInitial + 1 // at least 1 pixel
// scale from diagonal
const sizeFinal = Math.sqrt(Math.pow(h, 2) + Math.pow(w, 2))
const scaleFinal = (sizeFinal / size) * 2
// offset (using clientX and clientY for touch so we need to get values with getBoundingClientRect also)
let y
let x
if (e.clientX !== undefined) {
y = e.clientY
x = e.clientX
} else if (e.touches && e.touches.length) {
y = e.touches[0].clientY
x = e.touches[0].clientX
}
const rectTarget = e.target.getBoundingClientRect()
y = y - rectTarget.top
x = x - rectTarget.left
// position
if (self.container !== e.target) {
const rectObject = self.container.getBoundingClientRect()
y += rectTarget.top - rectObject.top
x += rectTarget.left - rectObject.left
}
const top = y - size / 2
const left = x - size / 2
// dispatch event
self.size = size
self.top = top
self.left = left
self.sizeFinal = sizeFinal
self.scaleFinal = scaleFinal
// dispatch event
self.container.dispatchEvent(
new CustomEvent(`on.${self._componentNs}`, {
detail: e,
}),
)
// off
const endHandler = Xt.dataStorage.put(window, `mouseup touchend/${self.ns}`, self._eventEnd.bind(self))
addEventListener('mouseup', endHandler)
addEventListener('touchend', endHandler, { passive: true })
}
}
/**
* eventEnd
* @param {Event} e
*/
_eventEnd(e) {
const self = this
// disabled
if (self.disabled) {
return
}
// off
const endHandler = Xt.dataStorage.get(window, `mouseup touchend/${self.ns}`)
removeEventListener('mouseup', endHandler)
removeEventListener('touchend', endHandler)
// dispatch event
self.container.dispatchEvent(
new CustomEvent(`off.${self._componentNs}`, {
detail: e,
}),
)
}
//
// status
//
/**
* enable
*/
enable() {
const self = this
if (self.disabled) {
// enable
self.disabled = false
// dispatch event
self.container.dispatchEvent(new CustomEvent(`status.${self._componentNs}`))
}
}
/**
* disable
* @param {Object} params
* @param {Boolean} params.skipEvent Skip dispatch event
*/
disable({ skipEvent = false } = {}) {
const self = this
if (!self.disabled) {
// disable
self.disabled = true
// dispatch event
if (!skipEvent) {
self.container.dispatchEvent(new CustomEvent(`status.${self._componentNs}`))
}
}
}
//
// util
//
/**
* reinit
*/
reinit() {
const self = this
// reinit
self._initLogic()
}
/**
* destroy
*/
destroy() {
const self = this
// inner
self.inner.remove()
self.inner = null
// remove events
// on
const onHandler = Xt.dataStorage.get(self.container, `mousedown touchstart/${self.ns}`)
self.container.removeEventListener('mousedown', onHandler)
self.container.removeEventListener('touchstart', onHandler, { passive: true })
// off
const endHandler = Xt.dataStorage.get(window, `mouseup touchend/${self.ns}`)
removeEventListener('mouseup', endHandler)
removeEventListener('touchend', endHandler)
// initialized class
self.container.removeAttribute(`data-${self.componentName}-init`)
// set self
Xt._remove({ name: self.componentName, el: self.container })
// dispatch event
self.container.dispatchEvent(new CustomEvent(`destroy.${self._componentNs}`))
// delete
delete this
}
}