Packages

Fully accessible unstyled Phoenix components

Current section

Files

Jump to
guillotine assets js spread-props.js
Raw

assets/js/spread-props.js

const prevAttrsMap = new WeakMap();
export function spreadProps(node, attrs) {
const oldAttrs = prevAttrsMap.get(node) || {};
const attrKeys = Object.keys(attrs);
const addEvent = (e, f) => {
node.addEventListener(e.toLowerCase(), f);
};
const removeEvent = (e, f) => {
node.removeEventListener(e.toLowerCase(), f);
};
const onEvents = (attr) => attr.startsWith("on");
const others = (attr) => !attr.startsWith("on");
const setup = (attr) => addEvent(attr.substring(2), attrs[attr]);
const teardown = (attr) => removeEvent(attr.substring(2), attrs[attr]);
const apply = (attrName) => {
let value = attrs[attrName];
const oldValue = oldAttrs[attrName];
if (value === oldValue) return;
if (typeof value === "boolean") {
value = value || undefined;
}
if (value != null) {
if (["value", "checked", "htmlFor"].includes(attrName)) {
node[attrName] = value;
} else {
node.setAttribute(attrName.toLowerCase(), value);
}
return;
}
node.removeAttribute(attrName.toLowerCase());
};
for (const key in oldAttrs) {
if (attrs[key] == null) {
node.removeAttribute(key.toLowerCase());
}
}
const oldEvents = Object.keys(oldAttrs).filter(onEvents);
oldEvents.forEach((evt) => {
removeEvent(evt.substring(2), oldAttrs[evt]);
});
attrKeys.filter(onEvents).forEach(setup);
attrKeys.filter(others).forEach(apply);
prevAttrsMap.set(node, attrs);
return function cleanup() {
attrKeys.filter(onEvents).forEach(teardown);
};
}