You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
3.4 KiB
169 lines
3.4 KiB
/**
|
|
* 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);
|
|
}
|