feat: clamp AddMenu to viewport
All checks were successful
🚀 Lint & Test & Deploy / release (push) Successful in 4m10s
All checks were successful
🚀 Lint & Test & Deploy / release (push) Successful in 4m10s
This commit is contained in:
@@ -5,19 +5,33 @@
|
|||||||
import { getGraphManager, getGraphState } from '../graph-state.svelte';
|
import { getGraphManager, getGraphState } from '../graph-state.svelte';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
paddingLeft?: number;
|
||||||
|
paddingRight?: number;
|
||||||
|
paddingTop?: number;
|
||||||
|
paddingBottom?: number;
|
||||||
onnode: (n: NodeInstance) => void;
|
onnode: (n: NodeInstance) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const { onnode }: Props = $props();
|
const padding = 10;
|
||||||
|
|
||||||
|
const {
|
||||||
|
paddingLeft = padding,
|
||||||
|
paddingRight = padding,
|
||||||
|
paddingTop = padding,
|
||||||
|
paddingBottom = padding,
|
||||||
|
onnode
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
const graph = getGraphManager();
|
const graph = getGraphManager();
|
||||||
const graphState = getGraphState();
|
const graphState = getGraphState();
|
||||||
|
|
||||||
let input: HTMLInputElement;
|
let input: HTMLInputElement;
|
||||||
let wrapper: HTMLDivElement;
|
|
||||||
let value = $state<string>();
|
let value = $state<string>();
|
||||||
let activeNodeId = $state<NodeId>();
|
let activeNodeId = $state<NodeId>();
|
||||||
|
|
||||||
|
const MENU_WIDTH = 150;
|
||||||
|
const MENU_HEIGHT = 350;
|
||||||
|
|
||||||
const allNodes = graphState.activeSocket
|
const allNodes = graphState.activeSocket
|
||||||
? graph.getPossibleNodes(graphState.activeSocket)
|
? graph.getPossibleNodes(graphState.activeSocket)
|
||||||
: graph.getNodeDefinitions();
|
: graph.getNodeDefinitions();
|
||||||
@@ -79,19 +93,52 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clampAddMenuPosition() {
|
||||||
|
if (!graphState.addMenuPosition) return;
|
||||||
|
|
||||||
|
const camX = graphState.cameraPosition[0];
|
||||||
|
const camY = graphState.cameraPosition[1];
|
||||||
|
const zoom = graphState.cameraPosition[2];
|
||||||
|
|
||||||
|
const halfViewportWidth = (graphState.width / 2) / zoom;
|
||||||
|
const halfViewportHeight = (graphState.height / 2) / zoom;
|
||||||
|
|
||||||
|
const halfMenuWidth = (MENU_WIDTH / 2) / zoom;
|
||||||
|
const halfMenuHeight = (MENU_HEIGHT / 2) / zoom;
|
||||||
|
|
||||||
|
const minX = camX - halfViewportWidth - halfMenuWidth + paddingLeft / zoom;
|
||||||
|
const maxX = camX + halfViewportWidth - halfMenuWidth - paddingRight / zoom;
|
||||||
|
const minY = camY - halfViewportHeight - halfMenuHeight + paddingTop / zoom;
|
||||||
|
const maxY = camY + halfViewportHeight - halfMenuHeight - paddingBottom / zoom;
|
||||||
|
|
||||||
|
const clampedX = Math.max(
|
||||||
|
minX + halfMenuWidth,
|
||||||
|
Math.min(graphState.addMenuPosition[0], maxX - halfMenuWidth)
|
||||||
|
);
|
||||||
|
const clampedY = Math.max(
|
||||||
|
minY + halfMenuHeight,
|
||||||
|
Math.min(graphState.addMenuPosition[1], maxY - halfMenuHeight)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (clampedX !== graphState.addMenuPosition[0] || clampedY !== graphState.addMenuPosition[1]) {
|
||||||
|
graphState.addMenuPosition = [clampedX, clampedY];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
const pos = graphState.addMenuPosition;
|
||||||
|
const zoom = graphState.cameraPosition[2];
|
||||||
|
const width = graphState.width;
|
||||||
|
const height = graphState.height;
|
||||||
|
|
||||||
|
if (pos && zoom && width && height) {
|
||||||
|
clampAddMenuPosition();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
input.disabled = false;
|
input.disabled = false;
|
||||||
setTimeout(() => input.focus(), 50);
|
setTimeout(() => input.focus(), 50);
|
||||||
|
|
||||||
const rect = wrapper.getBoundingClientRect();
|
|
||||||
const deltaY = rect.bottom - window.innerHeight;
|
|
||||||
const deltaX = rect.right - window.innerWidth;
|
|
||||||
if (deltaY > 0) {
|
|
||||||
wrapper.style.marginTop = `-${deltaY + 30}px`;
|
|
||||||
}
|
|
||||||
if (deltaX > 0) {
|
|
||||||
wrapper.style.marginLeft = `-${deltaX + 30}px`;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -100,7 +147,7 @@
|
|||||||
position.z={graphState.addMenuPosition?.[1]}
|
position.z={graphState.addMenuPosition?.[1]}
|
||||||
transform={false}
|
transform={false}
|
||||||
>
|
>
|
||||||
<div class="add-menu-wrapper" bind:this={wrapper}>
|
<div class="add-menu-wrapper">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<input
|
<input
|
||||||
id="add-menu"
|
id="add-menu"
|
||||||
|
|||||||
@@ -17,9 +17,11 @@
|
|||||||
import { MouseEventManager } from './mouse.events';
|
import { MouseEventManager } from './mouse.events';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
keymap
|
keymap,
|
||||||
|
addMenuPadding
|
||||||
}: {
|
}: {
|
||||||
keymap: ReturnType<typeof createKeyMap>;
|
keymap: ReturnType<typeof createKeyMap>;
|
||||||
|
addMenuPadding?: { left?: number; right?: number; bottom?: number; top?: number };
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
const graph = getGraphManager();
|
const graph = getGraphManager();
|
||||||
@@ -160,7 +162,13 @@
|
|||||||
|
|
||||||
{#if graph.status === 'idle'}
|
{#if graph.status === 'idle'}
|
||||||
{#if graphState.addMenuPosition}
|
{#if graphState.addMenuPosition}
|
||||||
<AddMenu onnode={handleNodeCreation} />
|
<AddMenu
|
||||||
|
onnode={handleNodeCreation}
|
||||||
|
paddingTop={addMenuPadding?.top}
|
||||||
|
paddingRight={addMenuPadding?.right}
|
||||||
|
paddingBottom={addMenuPadding?.bottom}
|
||||||
|
paddingLeft={addMenuPadding?.left}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if graphState.activeSocket}
|
{#if graphState.activeSocket}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
showHelp?: boolean;
|
showHelp?: boolean;
|
||||||
settingTypes?: Record<string, unknown>;
|
settingTypes?: Record<string, unknown>;
|
||||||
|
|
||||||
|
addMenuPadding?: { left?: number; right?: number; bottom?: number; top?: number };
|
||||||
|
|
||||||
onsave?: (save: Graph) => void;
|
onsave?: (save: Graph) => void;
|
||||||
onresult?: (result: unknown) => void;
|
onresult?: (result: unknown) => void;
|
||||||
};
|
};
|
||||||
@@ -25,6 +27,7 @@
|
|||||||
let {
|
let {
|
||||||
graph,
|
graph,
|
||||||
registry,
|
registry,
|
||||||
|
addMenuPadding,
|
||||||
settings = $bindable(),
|
settings = $bindable(),
|
||||||
activeNode = $bindable(),
|
activeNode = $bindable(),
|
||||||
backgroundType = $bindable('grid'),
|
backgroundType = $bindable('grid'),
|
||||||
@@ -83,4 +86,4 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<GraphEl {keymap} />
|
<GraphEl {keymap} {addMenuPadding} />
|
||||||
|
|||||||
@@ -2,7 +2,11 @@
|
|||||||
import { type Snippet } from 'svelte';
|
import { type Snippet } from 'svelte';
|
||||||
import { panelState as state } from './PanelState.svelte';
|
import { panelState as state } from './PanelState.svelte';
|
||||||
|
|
||||||
const { children } = $props<{ children?: Snippet }>();
|
let { children, open = $bindable(false) } = $props<{ children?: Snippet; open?: boolean }>();
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
open = !!state.activePanel.value;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="wrapper" class:visible={state.activePanel.value}>
|
<div class="wrapper" class:visible={state.activePanel.value}>
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
let activeNode = $state<NodeInstance | undefined>(undefined);
|
let activeNode = $state<NodeInstance | undefined>(undefined);
|
||||||
let scene = $state<Group>(null!);
|
let scene = $state<Group>(null!);
|
||||||
|
|
||||||
|
let sidebarOpen = $state(false);
|
||||||
let graphInterface = $state<ReturnType<typeof GraphInterface>>(null!);
|
let graphInterface = $state<ReturnType<typeof GraphInterface>>(null!);
|
||||||
let viewerComponent = $state<ReturnType<typeof Viewer>>();
|
let viewerComponent = $state<ReturnType<typeof Viewer>>();
|
||||||
const manager = $derived(graphInterface?.manager);
|
const manager = $derived(graphInterface?.manager);
|
||||||
@@ -171,6 +172,7 @@
|
|||||||
graph={pm.graph}
|
graph={pm.graph}
|
||||||
bind:this={graphInterface}
|
bind:this={graphInterface}
|
||||||
registry={nodeRegistry}
|
registry={nodeRegistry}
|
||||||
|
addMenuPadding={{ right: sidebarOpen ? 330 : undefined }}
|
||||||
backgroundType={appSettings.value.nodeInterface.backgroundType}
|
backgroundType={appSettings.value.nodeInterface.backgroundType}
|
||||||
snapToGrid={appSettings.value.nodeInterface.snapToGrid}
|
snapToGrid={appSettings.value.nodeInterface.snapToGrid}
|
||||||
bind:activeNode
|
bind:activeNode
|
||||||
@@ -181,7 +183,7 @@
|
|||||||
onresult={(result) => handleUpdate(result as Graph)}
|
onresult={(result) => handleUpdate(result as Graph)}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<Sidebar>
|
<Sidebar bind:open={sidebarOpen}>
|
||||||
<Panel id="general" title="General" icon="i-[tabler--settings]">
|
<Panel id="general" title="General" icon="i-[tabler--settings]">
|
||||||
<NestedSettings
|
<NestedSettings
|
||||||
id="general"
|
id="general"
|
||||||
|
|||||||
Reference in New Issue
Block a user