rvx
Version:
A signal based rendering library
99 lines (92 loc) • 2.2 kB
text/typescript
import type { ClassValue } from "../element-common.js";
import { teardown } from "../lifecycle.js";
import { watch } from "../signals.js";
class ClassBucket {
constructor(target: Element) {
this.
}
/**
* Increment the internal counter for the specified token until the current lifecycle is disposed.
*/
a(token: string): void {
const entries = this.
teardown(() => {
const removeQueue = this.
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
if (entry.t === token) {
if (--entry.c === 0) {
entries.splice(i, 1);
removeQueue.push(token);
}
return;
}
}
});
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
if (entry.t === token) {
entry.c++;
return;
}
}
entries.push({ t: token, c: 1 });
this.
}
/**
* Flush token additions & removals.
*/
f(): void {
const target = this.
const removeQueue = this.
const addQueue = this.
if (removeQueue.length > 0) {
target.classList.remove(...removeQueue);
removeQueue.length = 0;
}
if (addQueue.length > 0) {
if (target.hasAttribute("class")) {
target.classList.add(...addQueue);
} else {
target.setAttribute("class", addQueue.join(" "));
}
addQueue.length = 0;
}
}
}
function watchClass(value: ClassValue, bucket: ClassBucket, flush: boolean): void {
watch(value, value => {
if (typeof value === "string") {
bucket.a(value);
} else if (value) {
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
watchClass(value[i], bucket, false);
}
} else {
for (const token in value) {
watch(value[token], enable => {
if (enable) {
bucket.a(token);
}
if (flush) {
bucket.f();
}
});
}
}
}
if (flush) {
bucket.f();
} else {
flush = true;
}
});
}
export function setClass(elem: Element, value: ClassValue): void {
watchClass(value, new ClassBucket(elem), true);
}