forked from exadel-inc/esl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
class.ts
97 lines (89 loc) · 3.78 KB
/
class.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/** Describe locker elements collection per class name */
type Locks = Map<string, Set<HTMLElement>>;
/** Store locks for key element classes*/
const lockStore = new WeakMap<HTMLElement, Locks>();
/** Mange className lock for the element */
const lock = (el: HTMLElement, className: string, locker: HTMLElement) => {
const elLocks: Locks = lockStore.get(el) || new Map();
const classLocks: Set<HTMLElement> = 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 lockes
*/
const unlock = (el: HTMLElement, className: string, locker: HTMLElement) => {
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: HTMLElement, className: string, locker?: HTMLElement): void => {
if (className[0] === '!') return CSSClassUtils.remove(el, className.substr(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: HTMLElement, className: string, locker?: HTMLElement): void => {
if (className[0] === '!') return CSSClassUtils.add(el, className.substr(1), locker);
if (locker && !unlock(el, className, locker)) return;
if (!locker) CSSClassUtils.unlock(el, className);
el.classList.remove(className);
};
/**
* CSS class manipulation utilities.
*
* Allows to manipulate 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 abstract class CSSClassUtils {
/** Splitting passed token string into CSS class names array. */
public static splitTokens(tokenString: string | null | undefined): string[] {
return (tokenString || '').split(' ').filter((str) => !!str);
}
/**
* Add all classes from the class token string to the element.
* @see CSSClassUtils
* */
public static add(el: HTMLElement, cls: string | null | undefined, locker?: HTMLElement) {
CSSClassUtils.splitTokens(cls).forEach((className) => add(el, className, locker));
}
/**
* Remove all classes from the class token string to the element.
* @see CSSClassUtils
* */
public static remove(el: HTMLElement, cls: string | null | undefined, locker?: HTMLElement) {
CSSClassUtils.splitTokens(cls).forEach((className) => remove(el, className, locker));
}
/**
* Toggle all classes from the class token string on the element to the passed state.
* @see CSSClassUtils
* */
public static toggle(el: HTMLElement, cls: string | null | undefined, state: boolean, locker?: HTMLElement) {
(state ? CSSClassUtils.add : CSSClassUtils.remove)(el, cls, locker);
}
/** Remove all lockers for the element or passed element className */
public static unlock(el: HTMLElement, className?: string) {
if (className) {
lockStore.get(el)?.delete(className);
} else {
lockStore.delete(el);
}
}
}