feat(svelte): add snapGrid

This commit is contained in:
moklick
2023-03-14 15:43:14 +01:00
parent ae244526bd
commit 9c7bd6dc68
7 changed files with 36 additions and 16 deletions
+22 -5
View File
@@ -5,6 +5,7 @@ import type { XYPosition, CoordinateExtent, Transform } from '@reactflow/system'
import { getDragItems, hasSelector, calcNextPosition } from './utils';
import type { Node } from '$lib/types';
import type { SvelteFlowStore } from '$lib/store/types';
export type UseDragData = { dx: number; dy: number };
export type UseDragEvent = D3DragEvent<HTMLDivElement, null, SubjectPosition>;
@@ -25,13 +26,21 @@ type UseDragParams = {
handleSelector?: string;
nodeId?: string;
updateNodePositions: (dragItems: NodeDragItem[], d: boolean, p: boolean) => void;
nodes: Writable<Node[]>;
transform: Writable<Transform>;
nodes: SvelteFlowStore['nodes'];
transform: SvelteFlowStore['transform'];
snapGrid: SvelteFlowStore['snapGrid'];
};
export default function drag(
nodeRef: Element,
{ handleSelector, nodeId, updateNodePositions, nodes, transform: transformStore }: UseDragParams
{
handleSelector,
nodeId,
updateNodePositions,
nodes,
transform: transformStore,
snapGrid: snapGridStore
}: UseDragParams
) {
let dragging = false;
let dragItems: NodeDragItem[] = [];
@@ -43,6 +52,7 @@ export default function drag(
const x = sourceEvent.touches ? sourceEvent.touches[0].clientX : sourceEvent.clientX;
const y = sourceEvent.touches ? sourceEvent.touches[0].clientY : sourceEvent.clientY;
const transform = get(transformStore);
const snapGrid = get(snapGridStore);
const pointerPos = {
x: (x - transform[0]) / transform[2],
@@ -51,17 +61,24 @@ export default function drag(
// we need the snapped position in order to be able to skip unnecessary drag events
return {
xSnapped: pointerPos.x,
ySnapped: pointerPos.y,
xSnapped: snapGrid ? snapGrid[0] * Math.round(pointerPos.x / snapGrid[0]) : pointerPos.x,
ySnapped: snapGrid ? snapGrid[1] * Math.round(pointerPos.y / snapGrid[1]) : pointerPos.y,
...pointerPos
};
};
const updateNodes = ({ x, y }: XYPosition) => {
let hasChange = false;
const snapGrid = get(snapGridStore);
dragItems = dragItems.map((n) => {
const nextPosition = { x: x - n.distance.x, y: y - n.distance.y };
if (snapGrid) {
nextPosition.x = snapGrid[0] * Math.round(nextPosition.x / snapGrid[0]);
nextPosition.y = snapGrid[1] * Math.round(nextPosition.y / snapGrid[1]);
}
const updatedPos = calcNextPosition(n, nextPosition, get(nodes));
// we want to make sure that we only fire a change event when there is a changes
@@ -5,7 +5,7 @@
import { Selection } from '$lib/components/Selection';
import drag from '$lib/actions/drag';
const { selectionRectMode, nodes, transform, updateNodePositions } = useStore();
const { selectionRectMode, nodes, transform, snapGrid, updateNodePositions } = useStore();
$: selectedNodes = $nodes.filter((n) => n.selected);
$: rect = getRectOfNodes(selectedNodes);
@@ -15,7 +15,7 @@
<div
class="selection-wrapper nopan"
style={`width: ${rect.width}px; height: ${rect.height}px; transform: translate(${rect.x}px, ${rect.y}px)`}
use:drag={{ nodes, transform, updateNodePositions }}
use:drag={{ nodes, snapGrid, transform, updateNodePositions }}
/>
<Selection
isVisible={$selectionRectMode === 'nodes'}
@@ -30,7 +30,7 @@
let className: string = '';
export { className as class };
const { nodes, transform, nodeTypes, updateNodePositions, addSelectedNodes } = useStore();
const { nodes, transform, nodeTypes, snapGrid, updateNodePositions, addSelectedNodes } = useStore();
let nodeRef: HTMLDivElement;
const nodeTypeValid = !!$nodeTypes[type!];
@@ -74,7 +74,7 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
use:drag={{ nodeId: id, nodes, transform, updateNodePositions }}
use:drag={{ nodeId: id, nodes, snapGrid, transform, updateNodePositions }}
bind:this={nodeRef}
data-id={id}
class={cc(['svelte-flow__node', `svelte-flow__node-${type}`, className])}
@@ -26,6 +26,7 @@
export let edgeTypes: $$Props['edgeTypes'] = undefined;
export let selectionKey: $$Props['selectionKey'] = undefined;
export let selectionMode: $$Props['selectionMode'] = undefined;
export let snapGrid: $$Props['snapGrid'] = undefined;
export let deleteKey: $$Props['deleteKey'] = undefined;
export let connectionRadius: $$Props['connectionRadius'] = undefined;
export let connectionLineType: $$Props['connectionLineType'] = undefined;
@@ -56,6 +57,7 @@
connectionLineType,
connectionRadius,
selectionMode,
snapGrid,
isValidConnection
};
@@ -4,7 +4,8 @@ import type {
NodeOrigin,
OnConnectStartParams,
Viewport,
SelectionMode
SelectionMode,
SnapGrid
} from '@reactflow/system';
import type {
@@ -29,6 +30,7 @@ export type SvelteFlowProps = {
initialViewport?: Viewport;
connectionRadius?: number;
selectionMode?: SelectionMode;
snapGrid?: SnapGrid;
class?: string;
style?: string;
@@ -6,7 +6,8 @@ import {
ConnectionMode,
ConnectionLineType,
type SelectionRect,
type Transform
type Transform,
type SnapGrid
} from '@reactflow/system';
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
@@ -60,6 +61,7 @@ export const initialStoreState = {
zoom: null,
selection: null
}),
snapGrid: writable<SnapGrid | null>(null),
dragging: writable<boolean>(false),
selectionRect: writable<SelectionRect | null>(null),
selectionKeyPressed: writable<boolean>(false),
@@ -63,7 +63,7 @@
data: { label: 'Custom Node' },
position: { x: 150, y: 300 }
}
]);
], { style: 'width: 125px;' });
const edges = createEdges([
{
@@ -116,6 +116,7 @@
maxZoom={2.5}
selectionMode={SelectionMode.Full}
initialViewport={{ x: 100, y: 100, zoom: 2 }}
snapGrid={[25, 25]}
on:node:click={(event) => console.log('on node click', event)}
on:node:mouseenter={(event) => console.log('on node enter', event)}
on:node:mouseleave={(event) => console.log('on node leave', event)}
@@ -136,10 +137,6 @@
</SvelteFlowProvider>
<style>
:root {
--node-width: 50;
}
:global(.svelte-flow .custom-style) {
background: #ff5050;
color: white;