UNPKG

@selenite/graph-editor

Version:

A graph editor for visual programming, based on rete and svelte.

189 lines (188 loc) 5.38 kB
import { cloneDeep } from 'lodash-es'; import { ClassicPreset, getUID } from 'rete'; import { valueConverters } from '../common'; import { untrack } from 'svelte'; export function applyParams(target, constructor, params) { if (Object.keys(params).length === 0) return; const ref = new constructor(); Object.assign(target, Object.fromEntries(Object.entries(params).filter(([k]) => k in ref))); } /** * A control represents widgets the user can interact with. */ export class Control extends ClassicPreset.Control { placeInHeader = $state(false); constructor(params = {}) { super(); applyParams(this, Control, params); } } /** * Supported types of input control. */ export const inputControlTypes = [ 'text', 'number', 'checkbox', 'textarea', 'integer', 'vector', 'remote-file', 'select', 'group-name-ref' ]; export function isInputControlType(type) { return inputControlTypes.includes(type); } /** * Default values for each type of input control. */ export const defaultInputControlValues = { text: '', number: 0, checkbox: true, textarea: '', integer: 0, vector: { x: 0, y: 0, z: 0 }, 'remote-file': '', select: '', 'group-name-ref': '' }; export const inputControlSocketType = { 'group-name-ref': 'groupNameRef', 'remote-file': 'path', checkbox: 'boolean', integer: 'integer', number: 'number', select: 'options', text: 'string', textarea: 'string', vector: 'vector' }; export const socketToControl = { path: 'remote-file', string: 'text', integer: 'integer', number: 'number', boolean: 'checkbox', vector: 'vector', any: 'text', groupNameRef: 'group-name-ref' }; export const socketTypesWithControl = Object.keys(socketToControl); let testa; export function assignControl(socketType, default_) { return socketType in socketToControl ? socketToControl[socketType] : default_; } export function getDatastructure({ datastructure, type, values }) { const defaultValue = defaultInputControlValues[type]; if (!values || values.length === 0) values = [defaultValue]; switch (datastructure) { case 'scalar': return values[0]; case 'array': return [...values]; default: throw new Error('Invalid datastructure'); } } export function getDatastructureValues({ datastructure, data }) { switch (datastructure) { case 'scalar': return [data]; case 'array': return [...data]; default: throw new Error('Invalid datastructure'); } } export class InputControl extends Control { #value = $state(); readonly = $state(false); onChange; label = $state(''); type = $state('text'); #datastructure = $state('scalar'); #socketType = $state('any'); changeType = $state(); canChangeType = $state(false); props = $state({}); options = $state(); autoType = false; constructor(params) { super(); this.props = params.props ?? {}; this.id = getUID(); this.#datastructure = params.datastructure; this.readonly = params.readonly ?? false; this.label = params.label ?? ''; this.onChange = params.onChange; this.#value = params.initial ?? getDatastructure(params); // untrack(() => { // if (params.onChange) { // params.onChange(this.#value); // } // }); this.type = params.type; this.changeType = params.changeType; this.canChangeType = params.canChangeType ?? false; this.#socketType = params.socketType; this.options = params.options; } get value() { return this.#value; } set value(v) { this.#value = v; if (this.onChange && !this.readonly) this.onChange(v); } get socketType() { return this.#socketType; } set socketType(t) { console.debug(`Converting values from ${this.#socketType} to ${t}`); const converter = valueConverters[t]; let values; if (!converter) { console.warn('No converter for', t); } else { values = getDatastructureValues({ datastructure: this.datastructure, data: this.value }).map(converter); } this.value = getDatastructure({ datastructure: this.datastructure, values, type: socketToControl[t] }); this.type = assignControl(t, this.type); this.#socketType = t; } get datastructure() { return this.#datastructure; } set datastructure(d) { if (d === this.#datastructure) return; if (d === 'scalar') { if (this.#value?.length > 0) this.#value = this.#value[0]; else this.#value = defaultInputControlValues[this.type]; } else { if (this.value === undefined) this.#value = []; else this.#value = [this.#value]; } this.#datastructure = d; this.onChange?.($state.snapshot(this.#value)); } } // const test = new InputControl({ type: 'checkbox', initial: true });