feat: improve planty ux

This commit is contained in:
2026-04-20 21:23:55 +02:00
parent 7ebb1297ac
commit 58d39cd101
12 changed files with 141 additions and 98 deletions

View File

@@ -1,3 +1,4 @@
import { animate, lerp } from '$lib/helpers';
import type { NodeInstance, Socket } from '@nodarium/types';
import { getContext, setContext } from 'svelte';
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
@@ -124,6 +125,9 @@ export class GraphState {
activeNodeId = $state(-1);
selectedNodes = new SvelteSet<number>();
activeSocket = $state<Socket | null>(null);
safePadding = $state<{ left?: number; right?: number; bottom?: number; top?: number } | null>(
null
);
hoveredSocket = $state<Socket | null>(null);
possibleSockets = $state<Socket[]>([]);
possibleSocketIds = $derived(
@@ -236,6 +240,37 @@ export class GraphState {
};
}
centerNode(node?: NodeInstance) {
const average = [0, 0, 4];
if (node) {
average[0] = node.position[0] + (this.safePadding?.right || 0) / 10;
average[1] = node.position[1];
average[2] = 10;
} else {
for (const node of this.graph.nodes.values()) {
average[0] += node.position[0];
average[1] += node.position[1];
}
average[0] = (average[0] / this.graph.nodes.size)
+ (this.safePadding?.right || 0) / (average[2] * 2);
average[1] /= this.graph.nodes.size;
}
const camX = this.cameraPosition[0];
const camY = this.cameraPosition[1];
const camZ = this.cameraPosition[2];
const ease = (t: number) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t);
const easeZoom = (t: number) => t * t * (3 - 2 * t);
animate(500, (a: number) => {
this.cameraPosition[0] = lerp(camX, average[0], ease(a));
this.cameraPosition[1] = lerp(camY, average[1], ease(a));
this.cameraPosition[2] = lerp(camZ, average[2], easeZoom(a));
if (this.mouseDown) return false;
});
}
pasteNodes() {
if (!this.clipboard) return;

View File

@@ -19,10 +19,10 @@
const {
keymap,
addMenuPadding
safePadding
}: {
keymap: ReturnType<typeof createKeyMap>;
addMenuPadding?: { left?: number; right?: number; bottom?: number; top?: number };
safePadding?: { left?: number; right?: number; bottom?: number; top?: number };
} = $props();
const graph = getGraphManager();
@@ -172,10 +172,10 @@
{#if graphState.addMenuPosition}
<AddMenu
onnode={handleNodeCreation}
paddingTop={addMenuPadding?.top}
paddingRight={addMenuPadding?.right}
paddingBottom={addMenuPadding?.bottom}
paddingLeft={addMenuPadding?.left}
paddingTop={safePadding?.top}
paddingRight={safePadding?.right}
paddingBottom={safePadding?.bottom}
paddingLeft={safePadding?.left}
/>
{/if}

View File

@@ -18,7 +18,7 @@
showHelp?: boolean;
settingTypes?: Record<string, unknown>;
addMenuPadding?: { left?: number; right?: number; bottom?: number; top?: number };
safePadding?: { left?: number; right?: number; bottom?: number; top?: number };
onsave?: (save: Graph) => void;
onresult?: (result: unknown) => void;
@@ -27,7 +27,7 @@
let {
graph,
registry,
addMenuPadding,
safePadding,
settings = $bindable(),
activeNode = $bindable(),
backgroundType = $bindable('grid'),
@@ -44,29 +44,32 @@
export const manager = new GraphManager(registry);
setGraphManager(manager);
const graphState = new GraphState(manager);
export const state = new GraphState(manager);
$effect(() => {
graphState.backgroundType = backgroundType;
graphState.snapToGrid = snapToGrid;
graphState.showHelp = showHelp;
if (safePadding) {
state.safePadding = safePadding;
}
state.backgroundType = backgroundType;
state.snapToGrid = snapToGrid;
state.showHelp = showHelp;
});
setGraphState(graphState);
setGraphState(state);
setupKeymaps(keymap, manager, graphState);
setupKeymaps(keymap, manager, state);
$effect(() => {
if (graphState.activeNodeId !== -1) {
activeNode = manager.getNode(graphState.activeNodeId);
if (state.activeNodeId !== -1) {
activeNode = manager.getNode(state.activeNodeId);
} else if (activeNode) {
activeNode = undefined;
}
});
$effect(() => {
if (!graphState.addMenuPosition) {
graphState.edgeEndPosition = null;
graphState.activeSocket = null;
if (!state.addMenuPosition) {
state.edgeEndPosition = null;
state.activeSocket = null;
}
});
@@ -86,4 +89,4 @@
});
</script>
<GraphEl {keymap} {addMenuPadding} />
<GraphEl {keymap} {safePadding} />

View File

@@ -67,28 +67,7 @@ export function setupKeymaps(keymap: Keymap, graph: GraphManager, graphState: Gr
description: 'Center camera',
callback: () => {
if (!graphState.isBodyFocused()) return;
const average = [0, 0];
for (const node of graph.nodes.values()) {
average[0] += node.position[0];
average[1] += node.position[1];
}
average[0] = (average[0] / graph.nodes.size) + 10;
average[1] /= graph.nodes.size;
const camX = graphState.cameraPosition[0];
const camY = graphState.cameraPosition[1];
const camZ = graphState.cameraPosition[2];
const ease = (t: number) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t);
const easeZoom = (t: number) => t * t * (3 - 2 * t);
animate(500, (a: number) => {
graphState.cameraPosition[0] = lerp(camX, average[0], ease(a));
graphState.cameraPosition[1] = lerp(camY, average[1], ease(a));
graphState.cameraPosition[2] = lerp(camZ, 4, easeZoom(a));
if (graphState.mouseDown) return false;
});
graphState.centerNode(graph.getNode(graphState.activeNodeId));
}
});