/** * source by `component-classes` * https://github.com/component/classes.git */ import indexOf from 'lodash-es/indexOf'; /** * Whitespace regexp. */ const re = /\s+/; export class ClassList { el: Element; list: DOMTokenList; constructor(el: Element) { if (!el || !el.nodeType) { throw new Error('A DOM element reference is required'); } this.el = el; this.list = el.classList; } array() { const className = this.el.getAttribute('class') || ''; const str = className.replace(/^\s+|\s+$/g, ''); const arr = str.split(re); if ('' === arr[0]) arr.shift(); return arr; } /** * Add class `name` if not already present. * * @param {String} name * @return {ClassList} * @api public */ add(name: string): ClassList { // classList if (this.list) { this.list.add(name); return this; } // fallback const arr = this.array(); const i = indexOf(arr, name); if (!~i) arr.push(name); this.el.className = arr.join(' '); return this; } /** * Remove class `name` when present, or * pass a regular expression to remove * any which match. * * @param {String|RegExp} name * @return {ClassList} * @api public */ remove(name: string | RegExp): ClassList { if ('[object RegExp]' === toString.call(name)) { return this._removeMatching(name as RegExp); } // classList if (this.list) { this.list.remove(name as string); return this; } // fallback const arr = this.array(); const i = indexOf(arr, name); if (~i) arr.splice(i, 1); this.el.className = arr.join(' '); return this; } /** * Remove all classes matching `re`. * * @param {RegExp} re * @return {ClassList} * @api private */ _removeMatching(re: RegExp): ClassList { const arr = this.array(); for (let i = 0; i < arr.length; i++) { if (re.test(arr[i])) { this.remove(arr[i]); } } return this; } /** * Toggle class `name`, can force state via `force`. * * For browsers that support classList, but do not support `force` yet, * the mistake will be detected and corrected. * * @param {String} name * @param {Boolean} force * @return {ClassList} * @api public */ toggle(name: string, force: boolean): ClassList { // classList if (this.list) { if ('undefined' !== typeof force) { if (force !== this.list.toggle(name, force)) { this.list.toggle(name); // toggle again to correct } } else { this.list.toggle(name); } return this; } // fallback if ('undefined' !== typeof force) { if (!force) { this.remove(name); } else { this.add(name); } } else { if (this.has(name)) { this.remove(name); } else { this.add(name); } } return this; } /** * Check if class `name` is present. * * @param {String} name * @api public */ has(name: string) { return this.list ? this.list.contains(name) : !!~indexOf(this.array(), name); } /** * Check if class `name` is present. * * @param {String} name * @api public */ contains(name: string) { return this.has(name); } } /** * Wrap `el` in a `ClassList`. * * @param {Element} el * @return {ClassList} * @api public */ export default function (el: Element): ClassList { return new ClassList(el); }