@selenite/graph-editor
Version:
A graph editor for visual programming, based on rete and svelte.
128 lines (127 loc) • 5.68 kB
JavaScript
import { ClassicPreset, Scope } from 'rete';
import { classicConnectionPath, getDOMSocketPosition, loopConnectionPath } from 'rete-render-utils';
import Connection from './components/Connection.svelte';
import ConnectionWrapper from './components/ConnectionWrapper.svelte';
import Control from './components/Control.svelte';
import Node from './components/Node.svelte';
import Socket from './components/Socket.svelte';
export { default as Connection } from './components/Connection.svelte';
export { default as Control } from './components/Control.svelte';
export { default as Socket } from './components/Socket.svelte';
export { default as Button } from './components/Button.svelte';
// export {default as InputControl} from './components/InputControl.svelte'
export { default as InputControl } from './components/InputControl.svelte';
export { default as ExecSocket } from './components/ExecSocket.svelte';
export { default as AddXmlAttributeControl } from '../../AddXmlAttributeControl.svelte';
/**
* Classic preset for rendering nodes, connections, controls and sockets.
*/
export function setup(props) {
const positionWatcher = typeof props?.socketPositionWatcher === 'undefined'
? getDOMSocketPosition()
: props?.socketPositionWatcher;
const { node, connection, socket, control } = props?.customize || {};
return {
attach(plugin) {
positionWatcher.attach(plugin);
},
update(context, plugin) {
const { payload } = context.data;
const parent = plugin.parentScope();
if (!parent)
throw new Error('parent');
const emit = parent.emit.bind(parent);
if (context.data.type === 'node') {
return { data: payload, emit };
}
else if (context.data.type === 'connection') {
const { start, end } = context.data;
// console.log("Connection")
return {
data: payload,
...(start ? { start } : {}),
...(end ? { end } : {})
};
}
return { data: payload };
},
// eslint-disable-next-line max-statements, complexity
render(context, plugin) {
const parent = plugin.parentScope();
const emit = parent.emit.bind(parent);
if (context.data.type === 'node') {
const component = node ? node(context.data) : Node;
return (component && {
component,
props: {
data: context.data.payload,
emit
}
});
}
else if (context.data.type === 'connection') {
const component = connection ? connection(context.data) : Connection;
const { payload } = context.data;
const { source, target, sourceOutput, targetInput } = payload;
return (component && {
component: ConnectionWrapper,
props: {
data: context.data.payload,
component,
start: context.data.start ||
((change) => positionWatcher.listen(source, 'output', sourceOutput, change)),
end: context.data.end ||
((change) => positionWatcher.listen(target, 'input', targetInput, change)),
path: async (start, end) => {
const response = await plugin.emit({
type: 'connectionpath',
data: { payload, points: [start, end] }
});
if (!response)
return '';
const { path, points } = response.data;
const curvature = 0.3;
if (!path && points.length !== 2)
throw new Error('cannot render connection with a custom number of points');
if (!path)
return payload.isLoop
? loopConnectionPath(points, curvature, 120)
: classicConnectionPath(points, curvature);
return path;
}
}
});
}
else if (context.data.type === 'socket') {
const { payload } = context.data;
const component = socket ? socket(context.data) : Socket;
return (component && {
component,
props: {
data: payload
}
});
}
else if (context.data.type === 'control') {
const { payload } = context.data;
if (control) {
const component = control(context.data);
return (component && {
component,
props: {
data: payload
}
});
}
return context.data.payload instanceof ClassicPreset.InputControl
? {
component: Control,
props: {
data: payload
}
}
: null;
}
}
};
}