@exadel/esl
Version:
Exadel Smart Library (ESL) is the lightweight custom elements library that provide a set of super-flexible components
116 lines (115 loc) • 4.27 kB
JavaScript
import { wrap } from '../misc/array';
/** Store locks for key element classes*/
const lockStore = new WeakMap();
/** Mange className lock for the element */
const lock = (el, className, locker) => {
const elLocks = lockStore.get(el) || new Map();
const classLocks = elLocks.get(className) || new Set();
classLocks.add(locker);
elLocks.set(className, classLocks);
lockStore.set(el, elLocks);
};
/**
* Manage className unlock for the element
* @returns true if className have no locks
*/
const unlock = (el, className, locker) => {
const elLocks = lockStore.get(el);
if (!elLocks)
return true;
const classLocks = elLocks.get(className);
if (!classLocks)
return true;
classLocks.delete(locker);
return !classLocks.size;
};
/**
* Add single class to the element.
* Supports inversion and locker management.
*/
const add = (el, className, locker) => {
if (className[0] === '!')
return remove(el, className.substring(1), locker);
if (locker)
lock(el, className, locker);
el.classList.add(className);
};
/**
* Remove single class from the element.
* Supports inversion and locker management.
*/
const remove = (el, className, locker) => {
if (className[0] === '!')
return add(el, className.substring(1), locker);
if (locker && !unlock(el, className, locker))
return;
if (!locker)
CSSClassUtils.unlock(el, className);
el.classList.remove(className);
};
/**
* Check if the element matches passed CSS class.
* Supports inversion.
*/
const has = (el, className) => {
if (className[0] === '!')
return !has(el, className.substring(1));
return el.classList.contains(className);
};
/**
* CSS class manipulation utilities.
*
* Allows manipulating with CSS classes with the following set of sub-features:
* - JQuery-like enumeration - you can pass multiple tokens separated by space
* - safe checks - empty or falsy token sting will be ignored without throwing an error
* - inversion syntax - tokens that start from '!' will be processed with inverted action
* (e.g. addCls(el, '!class') - will remove 'class' from the element, while removeCls(el, '!class') adds 'class' to the element)
* - class locks - you can manipulate with classes using `locker` option that takes into account the modification initiator.
* That means the class added in 'locker' mode will not be removed until all initiators that requested add class have requested its removal.
* */
export class CSSClassUtils {
/** Splitting passed token string into CSS class names array. */
static splitTokens(tokenString) {
return (tokenString || '').split(' ').filter((str) => !!str);
}
/**
* Add all classes from the class token string to the element.
* @see CSSClassUtils
* */
static add(els, cls, locker) {
const tokens = CSSClassUtils.splitTokens(cls);
wrap(els).forEach((el) => tokens.forEach((className) => add(el, className, locker)));
}
/**
* Remove all classes from the class token string to the element.
* @see CSSClassUtils
* */
static remove(els, cls, locker) {
const tokens = CSSClassUtils.splitTokens(cls);
wrap(els).forEach((el) => tokens.forEach((className) => remove(el, className, locker)));
}
/**
* Toggle all classes from the class token string on the element to the passed state.
* @see CSSClassUtils
* */
static toggle(els, cls, state, locker) {
(state ? CSSClassUtils.add : CSSClassUtils.remove)(els, cls, locker);
}
/**
* Check if all class from token string matches to the element or elements.
* @see CSSClassUtils
* */
static has(els, cls) {
const tokens = CSSClassUtils.splitTokens(cls);
return wrap(els).every((el) => tokens.every((className) => has(el, className)));
}
/** Remove all lockers for the element or passed element className */
static unlock(els, className) {
if (className) {
wrap(els).forEach((el) => { var _a; return (_a = lockStore.get(el)) === null || _a === void 0 ? void 0 : _a.delete(className); });
}
else {
wrap(els).forEach((el) => lockStore.delete(el));
}
}
}