fix(editor): route loops from endpoint heights
This commit is contained in:
parent
09abd7e3f6
commit
11bcfb5973
|
|
@ -6,7 +6,7 @@ import { DEFAULT_NODE_CAPABILITIES, RESOURCE_NODE_CAPABILITIES } from '@models/f
|
|||
import { HFNode } from '@models/nodes';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
import { isResourceNode, resolveNodeCapabilities, ReteRuntimeContext, syncResourceLinkPresentation } from './rete-editor';
|
||||
import { isResourceNode, refreshLoopMarkers, resolveNodeCapabilities, ReteRuntimeContext, syncResourceLinkPresentation } from './rete-editor';
|
||||
|
||||
function makeNode(typeName: string, nodeFamily: 'block' | 'container' | 'resource'): HFNode {
|
||||
return { data: { typeName, nodeFamily } } as unknown as HFNode;
|
||||
|
|
@ -87,6 +87,39 @@ describe('isResourceNode', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('loop route placement', () => {
|
||||
it('starts 48px above its highest endpoint, irrespective of a higher node between them', async () => {
|
||||
const start = { id: 'start', data: { typeName: 'Start', nodeFamily: 'block' } } as any;
|
||||
const router = { id: 'router', data: { typeName: 'Router', nodeFamily: 'block' } } as any;
|
||||
const intermediate = { id: 'intermediate', data: { typeName: 'Step', nodeFamily: 'block' } } as any;
|
||||
const forward = { id: 'forward', source: 'start', sourceOutput: 'out', target: 'router', targetInput: 'in' } as any;
|
||||
const loop = { id: 'loop', source: 'router', sourceOutput: 'out', target: 'start', targetInput: 'in', loop: {} } as any;
|
||||
const editor = {
|
||||
getNodes: () => [start, router, intermediate],
|
||||
getConnections: () => [forward, loop]
|
||||
} as any;
|
||||
const area = {
|
||||
nodeViews: new Map([
|
||||
['start', { position: { x: 0, y: 500 }, element: { offsetWidth: 100 } }],
|
||||
['router', { position: { x: 400, y: 400 }, element: { offsetWidth: 100 } }],
|
||||
['intermediate', { position: { x: 200, y: 100 }, element: { offsetWidth: 100 } }]
|
||||
]),
|
||||
update: vi.fn().mockResolvedValue(undefined)
|
||||
} as any;
|
||||
const runtime = makeRuntime({
|
||||
blocksService: {
|
||||
peekBlockType: vi.fn().mockImplementation((type: string) =>
|
||||
type === 'Router' ? { capabilities: { ...DEFAULT_NODE_CAPABILITIES, routesExclusively: true } } : null)
|
||||
} as any
|
||||
});
|
||||
|
||||
await refreshLoopMarkers(editor, area, runtime);
|
||||
|
||||
expect(loop.__loopBack).toBe(true);
|
||||
expect(loop.__loopTop).toBe(352);
|
||||
});
|
||||
});
|
||||
|
||||
describe('the link between a resource and a step that uses it', () => {
|
||||
it('replaces an S3 operation choice with PostgreSQL choices when its resource changes', async () => {
|
||||
const { NodeEditor, ClassicPreset } = await import('rete');
|
||||
|
|
|
|||
|
|
@ -977,21 +977,15 @@ export async function refreshLoopMarkers(
|
|||
}
|
||||
|
||||
/**
|
||||
* The initial route of a loop: above the nodes it has to avoid when the loop is first established.
|
||||
* The initial route of a loop: 48px above its highest endpoint. Nodes in between are deliberately
|
||||
* ignored, since they may subsequently be moved across this fixed route.
|
||||
*/
|
||||
function loopTop(area: AreaPlugin<HFSchemes, AreaExtra>, connection: LoopAwareConnection): number | undefined {
|
||||
const ends = [connection.source, connection.target]
|
||||
.map((id) => area.nodeViews.get(id))
|
||||
.filter((view): view is NonNullable<typeof view> => view != null);
|
||||
if (!ends.length) return undefined;
|
||||
// The way back runs across everything between its two ends, so it has to clear the tallest of
|
||||
// those nodes too - the loop's own steps usually sit there - not only the two it connects.
|
||||
const left = Math.min(...ends.map((view) => view.position.x));
|
||||
const right = Math.max(...ends.map((view) => view.position.x + view.element.offsetWidth));
|
||||
const tops = [...area.nodeViews.values()]
|
||||
.filter((view) => view.position.x + view.element.offsetWidth >= left && view.position.x <= right)
|
||||
.map((view) => view.position.y);
|
||||
return Math.min(...tops, ...ends.map((view) => view.position.y)) - 48;
|
||||
return Math.min(...ends.map((view) => view.position.y)) - 48;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue