mirror of
https://github.com/nvms/soma3.git
synced 2025-12-13 14:40:52 +00:00
114 lines
3.1 KiB
TypeScript
114 lines
3.1 KiB
TypeScript
import { Context, evalGet } from "..";
|
|
import { classNames, extractAttributeName } from "../util";
|
|
|
|
interface AttributeDirectiveOptions {
|
|
element: Element;
|
|
context: Context;
|
|
attr: Attr;
|
|
}
|
|
|
|
interface Is {
|
|
sameNameProperty: boolean;
|
|
bound: boolean;
|
|
spread: boolean;
|
|
componentProp: boolean;
|
|
}
|
|
|
|
export class AttributeDirective {
|
|
element: Element;
|
|
context: Context;
|
|
expression: string;
|
|
attr: Attr;
|
|
extractedAttributeName: string;
|
|
|
|
previousClasses: string[] = [];
|
|
previousStyles: { [key: string]: string } = {};
|
|
|
|
is: Is = {
|
|
sameNameProperty: false,
|
|
bound: false,
|
|
spread: false,
|
|
componentProp: false,
|
|
};
|
|
|
|
constructor({ element, context, attr }: AttributeDirectiveOptions) {
|
|
this.element = element;
|
|
this.context = context;
|
|
this.expression = attr.value;
|
|
this.attr = attr;
|
|
this.extractedAttributeName = extractAttributeName(attr.name);
|
|
|
|
this.is = {
|
|
sameNameProperty: attr.name.startsWith("{") && attr.name.endsWith("}"),
|
|
bound: attr.name.includes(":bind"),
|
|
spread: attr.name.startsWith("..."),
|
|
componentProp: false,
|
|
};
|
|
|
|
if (this.is.sameNameProperty) {
|
|
this.expression = this.extractedAttributeName;
|
|
}
|
|
|
|
if (this.is.spread) {
|
|
this.expression = this.extractedAttributeName;
|
|
}
|
|
|
|
console.log("attribute", attr.name, "spread?", this.is.spread)
|
|
|
|
element.removeAttribute(attr.name);
|
|
|
|
if (this.is.bound) {
|
|
context.effect(this.update.bind(this));
|
|
} else {
|
|
this.update();
|
|
}
|
|
}
|
|
|
|
update() {
|
|
let value = evalGet(this.context.scope, this.expression);
|
|
|
|
if (this.is.spread && typeof value === "object") {
|
|
for (const [key, val] of Object.entries(value)) {
|
|
this.element.setAttribute(key, String(val));
|
|
}
|
|
} else if ((typeof value === "object" || Array.isArray(value)) && this.extractedAttributeName === "class") {
|
|
value = classNames(value);
|
|
const next = value.split(" ");
|
|
|
|
// If we now have classes that are not already on the element, add them now.
|
|
// Remove classes that are no longer on the element.
|
|
const diff = next.filter((c: string) => !this.previousClasses.includes(c)).filter(Boolean);
|
|
const rm = this.previousClasses.filter((c) => !next.includes(c));
|
|
|
|
diff.forEach((c: string) => {
|
|
this.previousClasses.push(c);
|
|
this.element.classList.add(c);
|
|
});
|
|
|
|
rm.forEach((c) => {
|
|
this.previousClasses = this.previousClasses.filter((addedClass) => addedClass !== c);
|
|
this.element.classList.remove(c);
|
|
});
|
|
} else if (typeof value === "object" && this.extractedAttributeName === "style") {
|
|
const next = Object.keys(value);
|
|
const rm = Object.keys(this.previousStyles).filter((style) => !next.includes(style));
|
|
|
|
next.forEach((style) => {
|
|
this.previousStyles[style] = value[style];
|
|
// @ts-ignore
|
|
this.element.style[style] = value[style];
|
|
});
|
|
|
|
rm.forEach((style) => {
|
|
this.previousStyles[style] = "";
|
|
// @ts-ignore
|
|
this.element.style[style] = "";
|
|
});
|
|
|
|
this.previousStyles = value;
|
|
} else {
|
|
this.element.setAttribute(this.extractedAttributeName, value);
|
|
}
|
|
}
|
|
}
|