feat: introduce local-first SVG workbench
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
(() => {
|
||||
"use strict";
|
||||
|
||||
const controllerScript = document.currentScript;
|
||||
const channel = document.documentElement.dataset.svgToolsChannel;
|
||||
if (!channel) {
|
||||
controllerScript?.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
const send = (message) => parent.postMessage({ ...message, channel }, "*");
|
||||
const find = (key) =>
|
||||
typeof key === "string"
|
||||
? document.querySelector(`[data-svg-tools-node="${CSS.escape(key)}"]`)
|
||||
: null;
|
||||
const reportBox = (key) => {
|
||||
const element = find(key);
|
||||
let box = null;
|
||||
try {
|
||||
if (element && typeof element.getBBox === "function") {
|
||||
const candidate = element.getBBox();
|
||||
if (
|
||||
[candidate.x, candidate.y, candidate.width, candidate.height].every(
|
||||
Number.isFinite,
|
||||
)
|
||||
) {
|
||||
box = {
|
||||
x: candidate.x,
|
||||
y: candidate.y,
|
||||
width: candidate.width,
|
||||
height: candidate.height,
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Some SVG elements do not expose geometry in every browser.
|
||||
}
|
||||
send({ type: "selection-box", key, box });
|
||||
};
|
||||
|
||||
document.addEventListener("click", (event) => {
|
||||
const target =
|
||||
event.target instanceof Element
|
||||
? event.target.closest("[data-svg-tools-node]")
|
||||
: null;
|
||||
const key = target?.getAttribute("data-svg-tools-node");
|
||||
if (key) send({ type: "select", key });
|
||||
if (event.target instanceof Element && event.target.closest("a")) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
addEventListener("message", (event) => {
|
||||
if (event.source !== parent) return;
|
||||
const message = event.data;
|
||||
if (!message || message.channel !== channel) return;
|
||||
if (message.type === "ping") send({ type: "ready" });
|
||||
if (message.type === "selection" && typeof message.key === "string") {
|
||||
reportBox(message.key);
|
||||
}
|
||||
if (
|
||||
message.type === "path" &&
|
||||
typeof message.key === "string" &&
|
||||
typeof message.data === "string"
|
||||
) {
|
||||
find(message.key)?.setAttribute("d", message.data);
|
||||
reportBox(message.key);
|
||||
}
|
||||
if (
|
||||
message.type === "transform" &&
|
||||
typeof message.key === "string" &&
|
||||
(typeof message.value === "string" || message.value === null)
|
||||
) {
|
||||
const element = find(message.key);
|
||||
if (!element) return;
|
||||
if (message.value) element.setAttribute("transform", message.value);
|
||||
else element.removeAttribute("transform");
|
||||
reportBox(message.key);
|
||||
}
|
||||
});
|
||||
|
||||
send({ type: "ready" });
|
||||
controllerScript?.remove();
|
||||
})();
|
||||
Reference in New Issue
Block a user