Files
JaredTherriault-ComfyUI-JNodes/web/js/common/ModelEditForm.js
T

242 lines
5.6 KiB
JavaScript

// Dynamic form generator for editing model (LoRA/embedding) metadata in the Image Drawer.
// Renders form fields from FormObject definitions and submits changes via the /jnodes_edit_model_info API.
import { $el } from "/scripts/ui.js";
import { utilitiesInstance } from "../common/Utilities.js";
export class FormObject {
constructor(
name,
type,
defaultValue,
displayName = "",
tooltipText = "",
options = {},
onClickResetToDefault = undefined,
customWidget = undefined
) {
this._name = name;
this._displayName = displayName;
this._type = type;
this._defaultValue = defaultValue;
this._tooltipText = tooltipText;
this._options = options;
this._onClickResetToDefault = onClickResetToDefault;
this._customWidget = customWidget;
}
}
export class FormInfo {
constructor() {
this.itemToEdit;
this.buttonText;
this.onButtonClickFunction;
}
}
export class ModelEditForm {
constructor(imageDrawerInstance) {
this.imageDrawerInstance = imageDrawerInstance;
this._form;
this._formContainer;
}
_getFormResponse(){
let json = {};
if (this._form) {
for(const row of this._form.childNodes) {
const label = row.childNodes[0].childNodes[0];
let widget = row.childNodes[1].childNodes[0];
if (widget && widget.id == "inputContainer") {
widget = widget.childNodes[0];
}
if (label && widget) {
let widgetValue = undefined;
if (widget.getValue != undefined) {
widgetValue = widget.getValue();
} else if (widget.type == "checkbox") {
widgetValue = widget.checked;
} else if (widget.value != undefined) {
widgetValue = widget.value;
}
if (widgetValue != undefined) {
json[label.htmlFor] = widgetValue;
}
}
}
}
return JSON.stringify(json);
}
focusFirstTextElement() {
const textInput = this._formContainer.querySelector('input[type="text"]');
if (textInput) {
textInput.focus();
}
}
createForm(formObjects, formInfo) {
// Create the form element
this._formContainer = $el('div',
{
style: {
backgroundColor: "rgba(0,0,0,1)",
padding: "0% 25px",
borderStyle: "double",
border: "deepskyblue",
borderCollapse: "separate",
display: "flex",
flexDirection: "column",
alignItems: "center"
}
}
);
const headerText = $el("h2", {
textContent: `Editing${formInfo?.itemToEdit ? ' ' + formInfo.itemToEdit : ''}`,
});
this._formContainer.appendChild(headerText);
this._form = $el("div");
this._formContainer.appendChild(this._form);
// Loop through each FormObject and create inputs
formObjects.forEach((formObject) => {
// Create a label for the input
const label = $el('label', { style: { padding: "0px 25px 0px 0px" } });
label.setAttribute('for', formObject._name);
label.textContent =
formObject._displayName ? formObject._displayName :
utilitiesInstance.convertToTitleCase(formObject._name);
// Create the input element
let input;
if (formObject._customWidget) {
// If a custom widget is provided, use it
input = formObject._customWidget;
} else {
// Create a standard input element based on the type
input = $el('input');
input.setAttribute('type', formObject._type);
if (formObject._type == "checkbox") {
input.checked = formObject._defaultValue;
} else {
input.setAttribute('value', formObject._defaultValue);
}
if (formObject._type == "text") {
input.style.width = "25vw";
}
}
// Set the name and ID for the input
input.setAttribute('name', formObject._name);
input.setAttribute('id', formObject._name);
if (formObject._options) {
for (const key in formObject._options) {
input.setAttribute(key, formObject._options[key]);
}
}
let finalInputWidget = input;
if (formObject._onClickResetToDefault) {
const inputContainer = $el("div", {
id: "inputContainer",
style: {
display: "flex"
}
});
inputContainer.style.width = input.style.width;
input.style.width = "inherit";
const button = $el("button", {
onclick: () => { formObject._onClickResetToDefault(input); },
textContent: "↩️",
title: "Reset to default",
style: {
border: "none",
background: "transparent",
cursor: "pointer",
fontSize: "1rem",
padding: "2px",
}
});
inputContainer.appendChild(input);
inputContainer.appendChild(button);
finalInputWidget = inputContainer;
}
// Append the label and input to the form
let row = $el("tr",
{
style: {
verticalAlign: "middle",
}
},
[
$el("td", { style:{ textAlign: "left" } }, [label]),
$el("td", { style:{ textAlign: "right" } }, [finalInputWidget])
]
);
if (formObject._tooltipText) { row.title = formObject._tooltipText; }
this._form.appendChild(row);
if (this._form.childNodes.length % 2 == 0) {
row.style.background = "rgba(255,255,255,0.1)"
}
});
// Add a submit button to the form
if (formInfo.buttonText && formInfo.onButtonClickFunction) {
const button = $el('button', { style: { margin: "5%" } });
button.classList.add("JNodes-interactive-container");
button.textContent = formInfo.buttonText;
button.addEventListener('click',
async () => { formInfo.onButtonClickFunction(this._getFormResponse()); });
this._formContainer.appendChild(button);
this._formContainer.addEventListener("keydown", (event) => {
if (event.key == "Enter") {
event.preventDefault();
button.click();
}
});
}
return this._formContainer;
}
}