x-widget
Version:
Adds the ability to define reusable Widgets (WebComponents) using Alpinejs.
83 lines (81 loc) • 2.91 kB
JavaScript
var __defProp = Object.defineProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
var __export = (target, all) => {
__markAsModule(target);
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/x-widget-data.mjs
__export(exports, {
xWidgetData: () => xWidgetData
});
var camelToSnake = (name) => name.replace(/[a-z][A-Z]/g, (m) => m[0] + "-" + m[1].toLowerCase());
function xWidgetData(spec) {
const Alpine = this;
const propDescriptorEntries = Object.entries(Object.getOwnPropertyDescriptors(spec));
return ($el, $data) => {
const widgetEl = findWidget($el);
console.assert(widgetEl, "widget not found");
const boundProps = Array.from(widgetEl.attributes).filter((attr) => attr.name.startsWith("x-prop:")).map(({ name }) => name.substr(7));
const observer = new MutationObserver((changes) => {
changes.forEach(({ attributeName, target }) => {
setProp(attributeName, target.getAttribute(attributeName));
});
});
observer.observe(widgetEl, {
attributes: true,
attributeFilter: Object.keys(spec).map(camelToSnake),
attributeOldValue: false
});
const proplessDescriptors = Object.fromEntries(propDescriptorEntries.filter(([name]) => !boundProps.includes(name)));
const data = Alpine.reactive(Object.assign(Object.create(Object.getPrototypeOf(spec), proplessDescriptors), {
destroy() {
observer.disconnect();
}
}));
const attribs = [...widgetEl.attributes];
for (const name of Object.getOwnPropertyNames(spec)) {
const attrib = attribs.find((attr) => attr.name.match(new RegExp(`^((x-(bind|prop))?:)?${camelToSnake(name)}$`)));
if (!attrib)
continue;
if (attrib.name.startsWith("x-prop:")) {
Object.defineProperty(data, name, {
configurable: true,
enumerable: true,
get() {
return $data[name];
},
set(newValue) {
$data[name] = newValue;
}
});
} else {
setProp(name, widgetEl.getAttribute(camelToSnake(name)));
}
}
function setProp(name, attribValue) {
const defaultValue = spec[name];
if (typeof defaultValue === "boolean") {
if (attribValue === "") {
data[name] = true;
} else if (attribValue === "false") {
data[name] = false;
} else {
data[name] = !!attribValue;
}
} else if (typeof defaultValue === "number") {
data[name] = parseFloat(attribValue);
} else if (typeof defaultValue === "string") {
data[name] = attribValue;
} else {
throw new Error("unsupported static attribute: " + name);
}
}
return data;
};
}
function findWidget(el) {
while (el && !el.tagName.includes("-"))
el = el.parentElement;
return el;
}