feat: move add context menu within view if outside
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m59s
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m59s
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { HTML } from "@threlte/extras";
|
import type { NodeId, NodeInstance } from '@nodarium/types';
|
||||||
import { onMount } from "svelte";
|
import { HTML } from '@threlte/extras';
|
||||||
import type { NodeInstance, NodeId } from "@nodarium/types";
|
import { onMount } from 'svelte';
|
||||||
import { getGraphManager, getGraphState } from "../graph-state.svelte";
|
import { getGraphManager, getGraphState } from '../graph-state.svelte';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onnode: (n: NodeInstance) => void;
|
onnode: (n: NodeInstance) => void;
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
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>();
|
||||||
|
|
||||||
@@ -22,10 +23,10 @@
|
|||||||
: graph.getNodeDefinitions();
|
: graph.getNodeDefinitions();
|
||||||
|
|
||||||
function filterNodes() {
|
function filterNodes() {
|
||||||
return allNodes.filter((node) => node.id.includes(value ?? ""));
|
return allNodes.filter((node) => node.id.includes(value ?? ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodes = $derived(value === "" ? allNodes : filterNodes());
|
const nodes = $derived(value === '' ? allNodes : filterNodes());
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (nodes) {
|
if (nodes) {
|
||||||
if (activeNodeId === undefined) {
|
if (activeNodeId === undefined) {
|
||||||
@@ -39,38 +40,38 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleNodeCreation(nodeType: NodeInstance["type"]) {
|
function handleNodeCreation(nodeType: NodeInstance['type']) {
|
||||||
if (!graphState.addMenuPosition) return;
|
if (!graphState.addMenuPosition) return;
|
||||||
onnode?.({
|
onnode?.({
|
||||||
id: -1,
|
id: -1,
|
||||||
type: nodeType,
|
type: nodeType,
|
||||||
position: [...graphState.addMenuPosition],
|
position: [...graphState.addMenuPosition],
|
||||||
props: {},
|
props: {},
|
||||||
state: {},
|
state: {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyDown(event: KeyboardEvent) {
|
function handleKeyDown(event: KeyboardEvent) {
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
|
|
||||||
if (event.key === "Escape") {
|
if (event.key === 'Escape') {
|
||||||
graphState.addMenuPosition = null;
|
graphState.addMenuPosition = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === "ArrowDown") {
|
if (event.key === 'ArrowDown') {
|
||||||
const index = nodes.findIndex((node) => node.id === activeNodeId);
|
const index = nodes.findIndex((node) => node.id === activeNodeId);
|
||||||
activeNodeId = nodes[(index + 1) % nodes.length].id;
|
activeNodeId = nodes[(index + 1) % nodes.length].id;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === "ArrowUp") {
|
if (event.key === 'ArrowUp') {
|
||||||
const index = nodes.findIndex((node) => node.id === activeNodeId);
|
const index = nodes.findIndex((node) => node.id === activeNodeId);
|
||||||
activeNodeId = nodes[(index - 1 + nodes.length) % nodes.length].id;
|
activeNodeId = nodes[(index - 1 + nodes.length) % nodes.length].id;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === "Enter") {
|
if (event.key === 'Enter') {
|
||||||
if (activeNodeId && graphState.addMenuPosition) {
|
if (activeNodeId && graphState.addMenuPosition) {
|
||||||
handleNodeCreation(activeNodeId);
|
handleNodeCreation(activeNodeId);
|
||||||
}
|
}
|
||||||
@@ -81,6 +82,16 @@
|
|||||||
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>
|
||||||
|
|
||||||
@@ -89,7 +100,7 @@
|
|||||||
position.z={graphState.addMenuPosition?.[1]}
|
position.z={graphState.addMenuPosition?.[1]}
|
||||||
transform={false}
|
transform={false}
|
||||||
>
|
>
|
||||||
<div class="add-menu-wrapper">
|
<div class="add-menu-wrapper" bind:this={wrapper}>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<input
|
<input
|
||||||
id="add-menu"
|
id="add-menu"
|
||||||
@@ -112,7 +123,7 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
aria-selected={node.id === activeNodeId}
|
aria-selected={node.id === activeNodeId}
|
||||||
onkeydown={(event) => {
|
onkeydown={(event) => {
|
||||||
if (event.key === "Enter") {
|
if (event.key === 'Enter') {
|
||||||
handleNodeCreation(node.id);
|
handleNodeCreation(node.id);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -125,7 +136,7 @@
|
|||||||
activeNodeId = node.id;
|
activeNodeId = node.id;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{node.id.split("/").at(-1)}
|
{node.id.split('/').at(-1)}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
@@ -167,6 +178,8 @@
|
|||||||
min-height: none;
|
min-height: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result {
|
.result {
|
||||||
|
|||||||
Reference in New Issue
Block a user