UNPKG

@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
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)); } } }