Last active
August 21, 2021 14:00
-
-
Save jonathantneal/4812bae99b12bb677e9bf27e99977aee to your computer and use it in GitHub Desktop.
Tokens — manage space-separated tokens for any attribute — 990 bytes / 498 bytes gzipped
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* The **Tokens** interface represents a set of space-separated tokens. | |
* | |
* **Usage**: | |
* ```js | |
* let classTokens = new Tokens(document.body, 'class') | |
* classTokens.has('js') // boolean | |
* classTokens.add('js').has('js') // true | |
* classTokens.remove('js').has('js') // false | |
* ``` | |
* | |
* **Usage with `toggle`**: | |
* ```js | |
* classTokens.toggle('js') // boolean | |
* classTokens.toggle('js', true) // true | |
* classTokens.toggle('js', false) // false | |
* ``` | |
**/ | |
export class Tokens { | |
/** | |
* The **at()** method returns the token at the given index. | |
* | |
* **Example**: | |
* ```js | |
* let classTokens = new Tokens(document.body, 'class') | |
* | |
* classTokens.at(0) // first token | |
* classTokens.at(-1) // last token | |
* ``` | |
**/ | |
at(index: number): string | undefined | |
/** | |
* The **add()** method adds the given tokens and returns the current token set. | |
* | |
* **Example**: | |
* ```js | |
* let classTokens = new Tokens(document.body, 'class') | |
* | |
* classTokens.add('peace') | |
* ``` | |
**/ | |
add(...tokens: string[]): this | |
/** | |
* The **has()** method returns whether the current token set has the given token. | |
* | |
* **Example**: | |
* ```js | |
* let classTokens = new Tokens(document.body, 'class') | |
* | |
* classTokens.has('peace') | |
* ``` | |
**/ | |
has(token: string): boolean | |
/** | |
* The **remove()** method removes the given token and returns the current token set. | |
* | |
* **Example**: | |
* ```js | |
* let classTokens = new Tokens(document.body, 'class') | |
* | |
* classTokens.remove('worry') | |
* ``` | |
**/ | |
remove(...tokens: string[]): this | |
/** | |
* The **toggle()** method toggles the given token and returns whether the current token set has it. | |
* | |
* **Example**: | |
* ```js | |
* let classTokens = new Tokens(document.body, 'class') | |
* | |
* classTokens.toggle('smile') // adds or removes "smile" | |
* | |
* classTokens.toggle('smile', false) // removes "smile" | |
* classTokens.toggle('smile', true) // adds "smile" | |
* ``` | |
**/ | |
toggle(token: string, force?: boolean): boolean | |
/** | |
* The **size()** property returns the number of tokens in the current token set. | |
* | |
* **Example**: | |
* ```js | |
* let classTokens = new Tokens(document.body, 'class') | |
* | |
* classTokens.size // number of tokens | |
* ``` | |
**/ | |
size: boolean | |
} | |
/** | |
* The **tokens** method returns a set of **Tokens** for the given element and attribute. | |
* | |
* **Usage**: | |
* ```js | |
* Element.prototype._tokensFor = tokens | |
* | |
* let classTokens = document.body._tokensFor('class') | |
* classTokens.has('js') // boolean | |
* classTokens.add('js').has('js') // true | |
* classTokens.remove('js').has('js') // false | |
* ``` | |
* | |
* **Usage with `toggle`**: | |
* ```js | |
* classTokens.toggle('js') // boolean | |
* classTokens.toggle('js', true) // true | |
* classTokens.toggle('js', false) // false | |
* ``` | |
**/ | |
export function tokens(this: Element, name: string): Tokens |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let map = new WeakMap() | |
let getSet = (/** @type {Tokens} */ self, /** @type {boolean} */ make) => new Set(getAttr(self, make).value.split(/[\u0009\u000A\u000C\u000D\u0020]+/).filter(Boolean)) | |
let getAttr = (/** @type {Tokens} */ self, /** @type {boolean} */ make, /** @type {{ node: Element, name: string }} */ data = map.get(self)) => /** @type {Attr} */ ((make ? data.node.attributes.setNamedItem : Object).call(data.node.attributes, data.node.attributes[data.name] || document.createAttribute(data.name)) || data.node.attributes[data.name]) | |
export class Tokens { | |
constructor(/** @type {Element} */ node, /** @type {string} */ name) { | |
map.set(this, { node, name }) | |
} | |
at(/** @type {number} */ index) { | |
let set = [ ...getSet(this) ] | |
return set[index >= 0 ? index : set.length + index] | |
} | |
add(/** @type {string[]} */ ...tokens) { | |
let set = getSet(this, true) | |
for (let token of tokens) { | |
token = String(token).trim() | |
if (token) set.add(token) | |
} | |
getAttr(this).value = [ ...set ].join(' ') | |
return this | |
} | |
has(/** @type {string} */ token) { | |
return getSet(this).has(String(token).trim()) | |
} | |
remove(/** @type {string[]} */ ...tokens) { | |
let set = getSet(this, true) | |
for (let token of tokens) { | |
set.delete(String(token).trim()) | |
} | |
getAttr(this).value = [ ...set ].join(' ') | |
return this | |
} | |
toggle(/** @type {string} */ token, /** @type {boolean} */ force = null) { | |
let set = getSet(this, true) | |
token = String(token).trim() | |
force = force === null ? !set.has(token) : Boolean(force) | |
set[exists ? 'add' : 'delete'](token) | |
getAttr(this).value = [ ...set ].join(' ') | |
return force | |
} | |
get size() { | |
return getSet(this).size | |
} | |
[Symbol.iterator]() { | |
return getSet(this)[Symbol.iterator]() | |
} | |
[Symbol.toPrimitive]() { | |
return getAttr(this).value | |
} | |
} | |
export function tokens(/** @type {string} */ name) { | |
return new Tokens(this, name) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment