feat(svelte): add snapGrid
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user