238 lines
8.4 KiB
JavaScript
238 lines
8.4 KiB
JavaScript
import { WidgetSyncManager } from "./WidgetSyncManager.js";
|
|
import { Logger } from "../../shared/logger.js";
|
|
import { createDropdownTrigger, CustomDropdown, updateDropdownTriggerValue } from "../../shared/CustomDropdown.js";
|
|
class InlineControlFactory {
|
|
/**
|
|
* Check if a widget type should use inline controls
|
|
*/
|
|
static shouldUseInlineControl(widgetType) {
|
|
const type = widgetType.toLowerCase();
|
|
return type === "toggle" || type === "boolean" || type === "combo" || type === "button";
|
|
}
|
|
/**
|
|
* Create an inline control for the given configuration
|
|
*/
|
|
static createControl(config) {
|
|
const type = config.widgetType.toLowerCase();
|
|
switch (type) {
|
|
case "toggle":
|
|
case "boolean":
|
|
return this.createInlineToggle(config);
|
|
case "combo":
|
|
return this.createInlineDropdown(config);
|
|
case "button":
|
|
return this.createInlineButton(config);
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
/**
|
|
* Create compact inline toggle for boolean values
|
|
*/
|
|
static createInlineToggle(config) {
|
|
const container = document.createElement("div");
|
|
container.className = "inline-control inline-toggle";
|
|
const label = document.createElement("label");
|
|
label.className = "inline-toggle-switch";
|
|
const checkbox = document.createElement("input");
|
|
checkbox.type = "checkbox";
|
|
checkbox.className = "inline-toggle-input";
|
|
const boolValue = config.currentValue === true || config.currentValue === "true";
|
|
checkbox.checked = boolValue;
|
|
const slider = document.createElement("span");
|
|
slider.className = "inline-toggle-slider";
|
|
container.addEventListener("click", (e) => e.stopPropagation());
|
|
checkbox.addEventListener("change", () => {
|
|
var _a;
|
|
WidgetSyncManager.syncWidgetValue(config.nodeId, config.widgetName, checkbox.checked);
|
|
(_a = config.onChange) == null ? void 0 : _a.call(config, checkbox.checked);
|
|
Logger.debug(`[InlineControl] Toggle ${config.widgetName}: ${checkbox.checked}`);
|
|
});
|
|
label.appendChild(checkbox);
|
|
label.appendChild(slider);
|
|
container.appendChild(label);
|
|
return {
|
|
element: container,
|
|
getValue: () => checkbox.checked,
|
|
setValue: (v) => {
|
|
checkbox.checked = Boolean(v);
|
|
},
|
|
destroy: () => {
|
|
container.remove();
|
|
}
|
|
};
|
|
}
|
|
/**
|
|
* Create compact inline dropdown for combo values
|
|
* Uses custom dropdown for viewport-aware positioning
|
|
*/
|
|
static createInlineDropdown(config) {
|
|
var _a;
|
|
const container = document.createElement("div");
|
|
container.className = "inline-control inline-dropdown";
|
|
const options = ((_a = config.constraints) == null ? void 0 : _a.options) ?? [];
|
|
let currentValue = String(config.currentValue);
|
|
const trigger = createDropdownTrigger(currentValue, "inline-dropdown-trigger");
|
|
trigger.style.cssText = `
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 4px 10px;
|
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
font-size: 14px;
|
|
background: rgba(160, 212, 104, 0.12);
|
|
border: 1px solid transparent;
|
|
border-radius: 6px;
|
|
color: var(--info-panel-accent-color, #74b9ff);
|
|
cursor: pointer;
|
|
min-width: 100px;
|
|
max-width: 180px;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
transition: all 0.2s ease;
|
|
`;
|
|
let activeDropdown = null;
|
|
const stopProp = (e) => e.stopPropagation();
|
|
container.addEventListener("click", stopProp);
|
|
container.addEventListener("mousedown", stopProp);
|
|
container.addEventListener("mouseup", stopProp);
|
|
trigger.addEventListener("click", (e) => {
|
|
e.stopPropagation();
|
|
if (activeDropdown) {
|
|
activeDropdown.hide();
|
|
activeDropdown = null;
|
|
return;
|
|
}
|
|
activeDropdown = new CustomDropdown({
|
|
options: options.map(String),
|
|
currentValue,
|
|
anchor: trigger,
|
|
onChange: (value) => {
|
|
var _a2;
|
|
currentValue = value;
|
|
updateDropdownTriggerValue(trigger, value);
|
|
WidgetSyncManager.syncWidgetValue(config.nodeId, config.widgetName, value);
|
|
(_a2 = config.onChange) == null ? void 0 : _a2.call(config, value);
|
|
Logger.debug(`[InlineControl] Dropdown ${config.widgetName}: ${value}`);
|
|
},
|
|
onClose: () => {
|
|
activeDropdown = null;
|
|
}
|
|
});
|
|
activeDropdown.show();
|
|
});
|
|
trigger.addEventListener("mouseenter", () => {
|
|
trigger.style.background = "rgba(160, 212, 104, 0.18)";
|
|
trigger.style.boxShadow = "0 2px 8px rgba(0, 0, 0, 0.15)";
|
|
});
|
|
trigger.addEventListener("mouseleave", () => {
|
|
trigger.style.background = "rgba(160, 212, 104, 0.12)";
|
|
trigger.style.boxShadow = "0 1px 3px rgba(0, 0, 0, 0.1)";
|
|
});
|
|
trigger.addEventListener("keydown", (e) => {
|
|
if (e.key === "Enter" || e.key === " ") {
|
|
e.preventDefault();
|
|
trigger.click();
|
|
}
|
|
});
|
|
container.appendChild(trigger);
|
|
return {
|
|
element: container,
|
|
getValue: () => currentValue,
|
|
setValue: (v) => {
|
|
currentValue = String(v);
|
|
updateDropdownTriggerValue(trigger, currentValue);
|
|
},
|
|
destroy: () => {
|
|
activeDropdown == null ? void 0 : activeDropdown.hide();
|
|
container.remove();
|
|
}
|
|
};
|
|
}
|
|
/**
|
|
* Create inline button for action widgets
|
|
* Invokes the widget's callback when clicked
|
|
*/
|
|
static createInlineButton(config) {
|
|
const container = document.createElement("div");
|
|
container.className = "inline-control inline-button";
|
|
const button = document.createElement("button");
|
|
button.className = "inline-action-button";
|
|
let buttonLabel = "Click";
|
|
const val = config.currentValue;
|
|
if (typeof val === "string" && val.length > 0 && val !== "null" && val !== "true" && val !== "false") {
|
|
buttonLabel = val;
|
|
}
|
|
button.textContent = buttonLabel;
|
|
button.type = "button";
|
|
button.style.cssText = `
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 4px 12px;
|
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
font-size: 13px;
|
|
background: var(--info-panel-accent-color, #a0d468);
|
|
color: var(--comfy-menu-bg, #1a1a1a);
|
|
border: none;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
transition: all 0.2s ease;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
`;
|
|
container.addEventListener("click", (e) => e.stopPropagation());
|
|
container.addEventListener("mousedown", (e) => e.stopPropagation());
|
|
container.addEventListener("mouseup", (e) => e.stopPropagation());
|
|
button.addEventListener("click", (e) => {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
button.style.transform = "scale(0.95)";
|
|
setTimeout(() => {
|
|
button.style.transform = "";
|
|
}, 100);
|
|
const app = window.app;
|
|
if (!(app == null ? void 0 : app.graph)) {
|
|
Logger.warn(`[InlineControl] No app.graph available`);
|
|
return;
|
|
}
|
|
const node = app.graph.getNodeById(config.nodeId);
|
|
if (!(node == null ? void 0 : node.widgets)) {
|
|
Logger.warn(`[InlineControl] Node ${config.nodeId} not found or has no widgets`);
|
|
return;
|
|
}
|
|
const widget = node.widgets.find((w) => w.name === config.widgetName);
|
|
if (widget && typeof widget.callback === "function") {
|
|
try {
|
|
widget.callback(widget.value, app.canvas, node, [0, 0], null);
|
|
Logger.debug(`[InlineControl] Button ${config.widgetName} clicked successfully`);
|
|
} catch (error) {
|
|
Logger.warn(`[InlineControl] Button callback failed:`, error);
|
|
}
|
|
} else {
|
|
Logger.warn(`[InlineControl] Widget ${config.widgetName} not found or has no callback`);
|
|
}
|
|
});
|
|
button.addEventListener("mouseenter", () => {
|
|
button.style.transform = "translateY(-1px)";
|
|
button.style.boxShadow = "0 3px 8px rgba(0, 0, 0, 0.25)";
|
|
});
|
|
button.addEventListener("mouseleave", () => {
|
|
button.style.transform = "";
|
|
button.style.boxShadow = "0 1px 3px rgba(0, 0, 0, 0.2)";
|
|
});
|
|
container.appendChild(button);
|
|
return {
|
|
element: container,
|
|
getValue: () => config.currentValue,
|
|
setValue: () => {
|
|
},
|
|
destroy: () => {
|
|
container.remove();
|
|
}
|
|
};
|
|
}
|
|
}
|
|
export {
|
|
InlineControlFactory
|
|
};
|