153 lines
4.8 KiB
JavaScript
153 lines
4.8 KiB
JavaScript
import { COLORS, EMPTY_VALUE } from "./constants.js";
|
||
import { flashButton, patchFileDropdown } from "./utils.js";
|
||
import { ButtonRowWidget } from "./widgets/button_row_widget.js";
|
||
|
||
export const createLoaderCrud = (
|
||
node,
|
||
fileWidget,
|
||
{
|
||
baseEndpoint,
|
||
nodeLabel = "Node",
|
||
buildJson,
|
||
pushJson,
|
||
template,
|
||
newPlaceholder = "New file name:",
|
||
clonePlaceholder = "Clone to new file:",
|
||
onChange,
|
||
},
|
||
) => {
|
||
const handleNew = async () => {
|
||
const name = prompt(newPlaceholder);
|
||
if (!name?.trim()) return;
|
||
|
||
const res = await fetch(`${baseEndpoint}/new`, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ file: name.trim() }),
|
||
});
|
||
const data = await res.json();
|
||
|
||
if (data.ok) {
|
||
patchFileDropdown(fileWidget, EMPTY_VALUE, data.file);
|
||
pushJson(node, JSON.parse(data.content));
|
||
onChange?.();
|
||
if (node.graph) node.graph.setDirtyCanvas(true, true);
|
||
} else {
|
||
alert(`[${nodeLabel}] ${data.error}`);
|
||
}
|
||
};
|
||
|
||
const handleSave = async () => {
|
||
const file = fileWidget.value;
|
||
if (!file || file === EMPTY_VALUE) {
|
||
alert(`[${nodeLabel}] No file selected.`);
|
||
return;
|
||
}
|
||
|
||
const content = buildJson(node);
|
||
const res = await fetch(`${baseEndpoint}/save`, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ file, content }),
|
||
});
|
||
const data = await res.json();
|
||
|
||
if (data.ok) {
|
||
flashButton(node, 1, "✅ Saved!");
|
||
onChange?.();
|
||
} else {
|
||
alert(`[${nodeLabel}] Save failed: ${data.error}`);
|
||
}
|
||
};
|
||
|
||
const handleClone = async () => {
|
||
const name = prompt(clonePlaceholder);
|
||
if (!name?.trim()) return;
|
||
|
||
const content = buildJson(node);
|
||
const file = name.trim().endsWith(".json")
|
||
? name.trim()
|
||
: `${name.trim()}.json`;
|
||
|
||
const res = await fetch(`${baseEndpoint}/save`, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ file, content }),
|
||
});
|
||
const data = await res.json();
|
||
|
||
if (data.ok) {
|
||
patchFileDropdown(fileWidget, EMPTY_VALUE, file);
|
||
onChange?.();
|
||
if (node.graph) node.graph.setDirtyCanvas(true, true);
|
||
} else {
|
||
alert(`[${nodeLabel}] Clone failed: ${data.error}`);
|
||
}
|
||
};
|
||
|
||
const handleDelete = async () => {
|
||
const file = fileWidget.value;
|
||
if (!file || file === EMPTY_VALUE) {
|
||
alert(`[${nodeLabel}] No file selected.`);
|
||
return;
|
||
}
|
||
if (!confirm(`Delete "${file}"? This cannot be undone.`)) return;
|
||
|
||
const res = await fetch(`${baseEndpoint}/delete`, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ file }),
|
||
});
|
||
const data = await res.json();
|
||
|
||
if (data.ok) {
|
||
const values = fileWidget.options?.values ?? [];
|
||
const idx = values.indexOf(file);
|
||
if (idx !== -1) values.splice(idx, 1);
|
||
if (!values.length) values.push(EMPTY_VALUE);
|
||
|
||
fileWidget.options.values = values;
|
||
fileWidget.value = values[0];
|
||
fileWidget.callback?.(values[0]);
|
||
if (values[0] === EMPTY_VALUE) pushJson(node, template);
|
||
|
||
onChange?.();
|
||
if (node.graph) node.graph.setDirtyCanvas(true, true);
|
||
} else {
|
||
alert(`[${nodeLabel}] Delete failed: ${data.error}`);
|
||
}
|
||
};
|
||
|
||
const loadFileIntoWidgets = async (file) => {
|
||
if (!file || file === EMPTY_VALUE) return;
|
||
try {
|
||
const res = await fetch(
|
||
`${baseEndpoint}/load?file=${encodeURIComponent(file)}`,
|
||
);
|
||
const data = await res.json();
|
||
if (data.error) {
|
||
console.warn(`[${nodeLabel}] Load error:`, data.error);
|
||
return;
|
||
}
|
||
pushJson(node, JSON.parse(data.content));
|
||
} catch (e) {
|
||
console.warn(`[${nodeLabel}] Failed to load or parse file:`, e);
|
||
}
|
||
};
|
||
|
||
node.widgets.push(
|
||
new ButtonRowWidget("action_buttons", [
|
||
{ label: "➕ New", onClick: handleNew },
|
||
{ label: "💾 Save", onClick: handleSave },
|
||
{ label: "📋 Clone", onClick: handleClone },
|
||
{
|
||
label: "🗑️ Delete",
|
||
color: COLORS.dark_red,
|
||
onClick: handleDelete,
|
||
},
|
||
]),
|
||
);
|
||
|
||
return { loadFileIntoWidgets };
|
||
};
|