implemented keyboard interactions

This commit is contained in:
peterkogo
2025-04-16 11:39:50 +02:00
parent 7c776a31a7
commit 3ef63ad10e
14 changed files with 201 additions and 54 deletions
+57 -3
View File
@@ -12,7 +12,9 @@ import {
type CoordinateExtent,
type UpdateConnection,
type ConnectionState,
updateAbsolutePositions
updateAbsolutePositions,
snapPosition,
calculateNodePosition
} from '@xyflow/system';
import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions } from '$lib/types';
@@ -247,7 +249,7 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
}
}
function handleNodeSelection(id: string) {
function handleNodeSelection(id: string, unselect?: boolean, nodeRef?: HTMLDivElement | null) {
const node = store.nodeLookup.get(id);
if (!node) {
@@ -260,8 +262,10 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
if (!node.selected) {
addSelectedNodes([id]);
} else if (node.selected && store.multiselectionKeyPressed) {
} else if (unselect || (node.selected && store.multiselectionKeyPressed)) {
unselectNodesAndEdges({ nodes: [node], edges: [] });
requestAnimationFrame(() => nodeRef?.blur());
}
}
@@ -288,6 +292,55 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
}
}
function moveSelectedNodes(direction: XYPosition, factor: number) {
const { nodeExtent, snapGrid, nodeOrigin, nodeLookup, nodesDraggable, onerror } = store;
const nodeUpdates = new Map();
/*
* by default a node moves 5px on each key press
* if snap grid is enabled, we use that for the velocity
*/
const xVelo = snapGrid?.[0] ?? 5;
const yVelo = snapGrid?.[1] ?? 5;
const xDiff = direction.x * xVelo * factor;
const yDiff = direction.y * yVelo * factor;
for (const node of nodeLookup.values()) {
const isSelected =
node.selected &&
(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined'));
if (!isSelected) {
continue;
}
let nextPosition = {
x: node.internals.positionAbsolute.x + xDiff,
y: node.internals.positionAbsolute.y + yDiff
};
if (snapGrid) {
nextPosition = snapPosition(nextPosition, snapGrid);
}
const { position, positionAbsolute } = calculateNodePosition({
nodeId: node.id,
nextPosition,
nodeLookup,
nodeExtent,
nodeOrigin,
onError: onerror
});
node.position = position;
node.internals.positionAbsolute = positionAbsolute;
nodeUpdates.set(node.id, node);
}
updateNodePositions(nodeUpdates);
}
function panBy(delta: XYPosition) {
return panBySystem({
delta,
@@ -335,6 +388,7 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
addSelectedEdges,
handleNodeSelection,
handleEdgeSelection,
moveSelectedNodes,
panBy,
updateConnection,
cancelConnection,
@@ -196,6 +196,10 @@ export const getInitialStore = (signals: StoreSignals) => {
nodesDraggable: boolean = $derived(signals.props.nodesDraggable ?? true);
nodesConnectable: boolean = $derived(signals.props.nodesConnectable ?? true);
elementsSelectable: boolean = $derived(signals.props.elementsSelectable ?? true);
nodesFocusable: boolean = $derived(signals.props.nodesFocusable ?? true);
edgesFocusable: boolean = $derived(signals.props.edgesFocusable ?? true);
disableKeyboardA11y: boolean = $derived(signals.props.disableKeyboardA11y ?? false);
minZoom: number = $derived(signals.props.minZoom ?? 0.5);
maxZoom: number = $derived(signals.props.maxZoom ?? 2);
+2 -1
View File
@@ -29,8 +29,9 @@ export type SvelteFlowStoreActions = {
unselectNodesAndEdges: (params?: { nodes?: Node[]; edges?: Edge[] }) => void;
addSelectedNodes: (ids: string[]) => void;
addSelectedEdges: (ids: string[]) => void;
handleNodeSelection: (id: string) => void;
handleNodeSelection: (id: string, unselect?: boolean, nodeRef?: HTMLDivElement | null) => void;
handleEdgeSelection: (id: string) => void;
moveSelectedNodes: (direction: XYPosition, factor: number) => void;
panBy: (delta: XYPosition) => Promise<boolean>;
updateConnection: UpdateConnection;
cancelConnection: () => void;