UNPKG

@selenite/graph-editor

Version:

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

144 lines (143 loc) 5.11 kB
import { assignConnectionPath } from '../../connection-path'; import { Scope } from 'rete'; import { ConnectionPathPlugin } from 'rete-connection-path-plugin'; import { get } from 'svelte/store'; import { getSvelteRenderer } from './renderer.svelte'; import { ButtonControl, InputControl, Socket } from '../../socket'; import { getDOMSocketPosition } from '../sockets-position.ts/dom-socket-position'; class SveltePlugin extends Scope { renderer; presets = []; owners = new WeakMap(); constructor() { super('svelte-render'); this.renderer = getSvelteRenderer(); this.addPipe((context) => { if (!context || typeof context !== 'object' || !('type' in context)) return context; if (context.type === 'unmount') { this.unmount(context.data.element); } else if (context.type === 'render') { if ('filled' in context.data && context.data.filled) { return context; } if (this.mount(context.data.element, context)) { return { ...context, data: { ...context.data, filled: true } }; } } return context; }); } apps = new Map(); setParent(scope) { super.setParent(scope); this.presets.forEach((preset) => { if (preset.attach) preset.attach(this); }); } unmount(element) { this.owners.delete(element); this.renderer.unmount(element); } // apps = new Map<HTMLElement, Node>(); mount(element, context) { // console.log('mount', element); const existing = this.renderer.get(element); const parent = this.parentScope(); if (existing) { this.presets.forEach((preset) => { if (this.owners.get(element) !== preset) return; const result = preset.update(context, this); if (result) { this.renderer.update(existing, result); } }); return true; } for (const preset of this.presets) { const result = preset.render(context, this); if (!result) continue; this.renderer.mount(element, result.component, result.props, () => parent?.emit({ type: 'rendered', data: context.data })); this.owners.set(element, preset); return true; } return false; } /** * Adds a preset to the plugin. * @param preset Preset that can render nodes, connections and other elements. */ addPreset(preset) { const local = preset; if (local.attach) local.attach(this); this.presets.push(local); } } export const setupSvelteRender = async (params) => { const { area, factory, editor } = params; if (!area) { console.warn('Could not setup svelte render, area not found'); return params; } console.log('Setting up svelte render'); const sveltePlugin = new SveltePlugin(); const pathPlugin = new ConnectionPathPlugin({ curve: (conn) => assignConnectionPath(get(factory.connectionPathType)) }); factory.connectionPathType.subscribe(() => { const area = factory.getArea(); if (!area) { console.warn('Area not found'); return; } for (const conn of editor.getConnections()) { area.update('connection', conn.id); } }); // @ts-expect-error: Ignore type error sveltePlugin.use(pathPlugin); const { AddXmlAttributeControl } = await import('../../nodes/XML'); const Presets = await import('./presets'); // sveltePlugin.addPreset(Presets.contextMenu.setup()) sveltePlugin.addPreset(Presets.classic.setup({ socketPositionWatcher: getDOMSocketPosition(), customize: { socket(context) { if (context.payload instanceof Socket) { // if (context.payload.type === 'exec') return Presets.classic.ExecSocket; return Presets.classic.Socket; } return Presets.classic.Socket; }, control(data) { if (data.payload instanceof InputControl) { return Presets.classic.InputControl; } if (data.payload instanceof ButtonControl) { return Presets.classic.Button; } if (data.payload instanceof AddXmlAttributeControl) return Presets.classic.AddXmlAttributeControl; return Presets.classic.Control; } } })); area.use(sveltePlugin); return params; }; export const svelteRenderSetup = { name: 'Svelte Render', type: 'area', setup: setupSvelteRender };