Files
ComfyBox/src/lib/nodes/widgets/ComfyComboNode.ts
2023-06-03 00:38:09 -05:00

184 lines
5.9 KiB
TypeScript

import type IComfyInputSlot from "$lib/IComfyInputSlot";
import { BuiltInSlotType, LiteGraph, type INodeInputSlot, type LGraphNode, type SerializedLGraphNode, type SlotLayout } from "@litegraph-ts/core";
import { get, writable, type Writable } from "svelte/store";
import ComboWidget from "$lib/widgets/ComboWidget.svelte";
import type { ComfyWidgetProperties } from "./ComfyWidgetNode";
import ComfyWidgetNode from "./ComfyWidgetNode";
export interface ComfyComboProperties extends ComfyWidgetProperties {
values: string[]
/* JS Function body that takes a parameter named "value" as a parameter and returns the label for each combo entry */
convertValueToLabelCode: string
}
export default class ComfyComboNode extends ComfyWidgetNode<string> {
override properties: ComfyComboProperties = {
tags: [],
defaultValue: "A",
values: ["A", "B", "C", "D"],
convertValueToLabelCode: "",
excludeFromJourney: false,
}
static slotLayout: SlotLayout = {
inputs: [
{ name: "store", type: BuiltInSlotType.ACTION }
],
outputs: [
{ name: "value", type: "string" },
{ name: "changed", type: BuiltInSlotType.EVENT }
]
}
override svelteComponentType = ComboWidget
override defaultValue = "A";
override saveUserState = false;
// True if at least one combo box refresh has taken place
// Wait until the initial graph load for combo to be valid.
firstLoad: Writable<boolean>;
lightUp: Writable<boolean>;
valuesForCombo: Writable<any[]>; // Changed when the combo box has values.
maxLabelWidthChars: number = 0;
constructor(name?: string) {
super(name, "A")
this.firstLoad = writable(false)
this.lightUp = writable(true)
this.valuesForCombo = writable([])
}
override onPropertyChanged(property: any, value: any) {
if (property === "values" || property === "convertValueToLabelCode") {
// this.formatValues(this.properties.values)
}
}
formatValues(values: string[], defaultValue?: string, lightUp: boolean = false) {
if (values == null)
return;
let changed = this.properties.values != values;
this.properties.values = values;
const oldValue = get(this.value)
if (this.properties.values.indexOf(oldValue) === -1) {
changed = true;
this.value.set(defaultValue || this.properties.values[0])
}
if (lightUp && get(this.firstLoad) && changed)
this.lightUp.set(true)
let formatter: any;
if (this.properties.convertValueToLabelCode)
formatter = new Function("value", this.properties.convertValueToLabelCode) as (v: string) => string;
else
formatter = (value: any) => `${value}`;
this.maxLabelWidthChars = 0;
let valuesForCombo = []
try {
valuesForCombo = this.properties.values.map((value, index) => {
const label = formatter(value);
this.maxLabelWidthChars = Math.max(this.maxLabelWidthChars, label.length)
return {
value,
label,
index
}
})
}
catch (err) {
console.error("Failed formatting!", err)
valuesForCombo = this.properties.values.map((value, index) => {
const label = `${value}`
this.maxLabelWidthChars = Math.max(this.maxLabelWidthChars, label.length)
return {
value,
label,
index
}
})
}
this.firstLoad.set(true)
this.valuesForCombo.set(valuesForCombo);
}
onConnectOutput(
outputIndex: number,
inputType: INodeInputSlot["type"],
input: INodeInputSlot,
inputNode: LGraphNode,
inputIndex: number
): boolean {
if (!super.onConnectOutput(outputIndex, inputType, input, inputNode, inputIndex))
return false;
const thisProps = this.properties;
if (!("config" in input))
return true;
const comfyInput = input as IComfyInputSlot;
const otherProps = comfyInput.config;
console.warn("CHECK COMBO CONNECTION", otherProps, thisProps)
// Ensure combo options match
if (!(otherProps.values instanceof Array) || otherProps.values.length === 0)
return false;
thisProps.values = Array.from(otherProps.values);
const value = get(this.value)
if (thisProps.values.indexOf(value) === -1)
this.setValue(otherProps.defaultValue || thisProps.values[0])
console.warn("PASSED")
return true;
}
override parseValue(value: any): string {
if (typeof value !== "string" || this.properties.values.indexOf(value) === -1)
return this.properties.values[0]
return value
}
override clampOneConfig(input: IComfyInputSlot) {
if (!input.config.values)
this.setValue("")
else if (input.config.values.indexOf(this.properties.value) === -1) {
if (input.config.values.length === 0)
this.setValue("")
else
this.setValue(input.config.defaultValue || input.config.values[0])
}
}
override onSerialize(o: SerializedLGraphNode) {
super.onSerialize(o);
// TODO fix saving combo nodes with huge values lists
o.properties.values = []
}
override stripUserState(o: SerializedLGraphNode) {
super.stripUserState(o);
o.properties.values = []
o.properties.defaultValue = null;
(o as any).comfyValue = null
}
}
LiteGraph.registerNodeType({
class: ComfyComboNode,
title: "UI.Combo",
desc: "Combo box outputting a string value",
type: "ui/combo"
})