feat(svelte): add event handlers and props, rename class names
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
import type { Writable } from 'svelte/store';
|
||||
import { select } from 'd3-selection';
|
||||
import { zoom as d3Zoom, zoomIdentity, type D3ZoomEvent } from 'd3-zoom';
|
||||
import type { D3SelectionInstance, D3ZoomInstance, Transform } from '@reactflow/system';
|
||||
import type { D3SelectionInstance, D3ZoomInstance, Transform, Viewport } from '@reactflow/system';
|
||||
import { clamp } from '@reactflow/utils';
|
||||
|
||||
const isWrappedWithClass = (event: any, className: string | undefined) =>
|
||||
event.target.closest(`.${className}`);
|
||||
@@ -14,7 +15,8 @@ function filter(event: any, params: ZoomParams): boolean {
|
||||
if (
|
||||
event.button === 1 &&
|
||||
event.type === 'mousedown' &&
|
||||
(isWrappedWithClass(event, 'react-flow__node') || isWrappedWithClass(event, 'react-flow__edge'))
|
||||
(isWrappedWithClass(event, 'svelte-flow__node') ||
|
||||
isWrappedWithClass(event, 'svelte-flow__edge'))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -80,15 +82,27 @@ type ZoomParams = {
|
||||
transform: Writable<Transform>;
|
||||
selecting: boolean;
|
||||
d3: Writable<{ zoom: D3ZoomInstance | null; selection: D3SelectionInstance | null }>;
|
||||
minZoom: number;
|
||||
maxZoom: number;
|
||||
initialViewport: Viewport;
|
||||
};
|
||||
|
||||
export default function zoom(domNode: Element, params: ZoomParams) {
|
||||
const { transform, d3 } = params;
|
||||
const d3ZoomInstance = d3Zoom();
|
||||
const { transform, d3, minZoom, maxZoom, initialViewport } = params;
|
||||
const d3ZoomInstance = d3Zoom().scaleExtent([minZoom, maxZoom]);
|
||||
const selection = select(domNode).call(d3ZoomInstance);
|
||||
|
||||
const updatedTransform = zoomIdentity
|
||||
.translate(initialViewport.x, initialViewport.y)
|
||||
.scale(clamp(initialViewport.zoom, minZoom, maxZoom));
|
||||
|
||||
const d3ZoomHandler = selection.on('wheel.zoom');
|
||||
|
||||
d3ZoomInstance.transform(selection, zoomIdentity);
|
||||
d3ZoomInstance.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
transform.set([event.transform.x, event.transform.y, event.transform.k]);
|
||||
});
|
||||
|
||||
d3ZoomInstance.transform(selection, updatedTransform);
|
||||
|
||||
selection.on('wheel.zoom', function (event: any, d: any) {
|
||||
if (isWrappedWithClass(event, 'nowheel')) {
|
||||
@@ -104,10 +118,6 @@ export default function zoom(domNode: Element, params: ZoomParams) {
|
||||
selection
|
||||
});
|
||||
|
||||
d3ZoomInstance.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
transform.set([event.transform.x, event.transform.y, event.transform.k]);
|
||||
});
|
||||
|
||||
d3ZoomInstance.filter((event: any) => filter(event, params));
|
||||
|
||||
return {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
export let interactionWidth: $$Props['interactionWidth'] = 20;
|
||||
</script>
|
||||
|
||||
<path d={path} fill="none" class="react-flow__edge-path" />
|
||||
<path d={path} fill="none" class="svelte-flow__edge-path" />
|
||||
|
||||
{#if interactionWidth}
|
||||
<path
|
||||
@@ -19,14 +19,14 @@
|
||||
fill="none"
|
||||
stroke-opacity={0}
|
||||
stroke-width={interactionWidth}
|
||||
class="react-flow__edge-interaction"
|
||||
class="svelte-flow__edge-interaction"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if label}
|
||||
<EdgeLabelRenderer>
|
||||
<div
|
||||
class="react-flow__edge-label"
|
||||
class="svelte-flow__edge-label"
|
||||
style:transform={`translate(-50%, -50%) translate(${labelX}px,${labelY}px)`}
|
||||
>
|
||||
{label}
|
||||
@@ -35,7 +35,7 @@
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.react-flow__edge-label {
|
||||
.svelte-flow__edge-label {
|
||||
position: absolute;
|
||||
background: white;
|
||||
}
|
||||
|
||||
@@ -7,15 +7,15 @@
|
||||
</script>
|
||||
|
||||
{#if $connectionPath}
|
||||
<svg width={$width} height={$height} class="react-flow__connectionline">
|
||||
<g class={cc(['react-flow__connection', $connection.status])}>
|
||||
<path d={$connectionPath} fill="none" class="react-flow__connection-path" />
|
||||
<svg width={$width} height={$height} class="svelte-flow__connectionline">
|
||||
<g class={cc(['svelte-flow__connection', $connection.status])}>
|
||||
<path d={$connectionPath} fill="none" class="svelte-flow__connection-path" />
|
||||
</g>
|
||||
</svg>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.react-flow__connectionline {
|
||||
.svelte-flow__connectionline {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@@ -26,12 +26,12 @@
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.react-flow__connection {
|
||||
.svelte-flow__connection {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.react-flow__edge-path,
|
||||
.react-flow__connection-path {
|
||||
.svelte-flow__edge-path,
|
||||
.svelte-flow__connection-path {
|
||||
stroke: #b1b1b7;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
type $$Props = {};
|
||||
</script>
|
||||
|
||||
<div use:portal={'.react-flow__edgelabel-renderer'}>
|
||||
<div use:portal={'.svelte-flow__edgelabel-renderer'}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { SvelteComponentTyped } from 'svelte';
|
||||
import { createEventDispatcher, type SvelteComponentTyped } from 'svelte';
|
||||
import { Position } from '@reactflow/system';
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
@@ -24,11 +24,23 @@
|
||||
export let selected: $$Props['selected'] = false;
|
||||
export let label: $$Props['label'] = undefined;
|
||||
|
||||
const { edgeTypes } = useStore();
|
||||
const { edgeTypes, edges } = useStore();
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const edgeComponent: typeof SvelteComponentTyped<EdgeProps> = $edgeTypes[type!] || BezierEdge;
|
||||
|
||||
function onClick() {
|
||||
const edge = $edges.find(e => e.id === id);
|
||||
dispatch('edge:click', edge);
|
||||
}
|
||||
</script>
|
||||
|
||||
<g class="react-flow__edge" class:animated data-id={id}>
|
||||
<g
|
||||
class="svelte-flow__edge"
|
||||
class:animated
|
||||
data-id={id}
|
||||
on:click={onClick}
|
||||
>
|
||||
<svelte:component
|
||||
this={edgeComponent}
|
||||
{id}
|
||||
@@ -47,12 +59,12 @@
|
||||
</g>
|
||||
|
||||
<style>
|
||||
.react-flow__edge {
|
||||
.svelte-flow__edge {
|
||||
pointer-events: visibleStroke;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.react-flow__edge :global(path) {
|
||||
.svelte-flow__edge :global(path) {
|
||||
stroke: #ccc;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import cc from 'classcat';
|
||||
import { Position, type Connection, type HandleProps } from '@reactflow/system';
|
||||
import { isMouseEvent } from '@reactflow/utils';
|
||||
@@ -18,6 +19,8 @@
|
||||
const isTarget = type === 'target';
|
||||
const nodeId = getContext<string>('rf_nodeid');
|
||||
const handleId = id || null;
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
|
||||
const {
|
||||
connectionMode,
|
||||
@@ -31,11 +34,16 @@
|
||||
updateConnection
|
||||
} = useStore();
|
||||
|
||||
function dispatchEvent(eventName: string) {
|
||||
dispatch(eventName, { nodeId, handleId, type });
|
||||
}
|
||||
|
||||
function onConnectExtended(params: Connection) {
|
||||
addEdge(params);
|
||||
// @todo add props
|
||||
// onConnectAction?.(edgeParams);
|
||||
// onConnect?.(edgeParams);
|
||||
dispatchEvent('connect')
|
||||
}
|
||||
|
||||
function onPointerDown(event: MouseEvent | TouchEvent) {
|
||||
@@ -56,7 +64,9 @@
|
||||
onConnect: onConnectExtended,
|
||||
updateConnection,
|
||||
cancelConnection,
|
||||
panBy
|
||||
panBy,
|
||||
onConnectStart: () => dispatchEvent('connect:start'),
|
||||
onConnectEnd: () => dispatchEvent('connect:end')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,8 +84,8 @@
|
||||
data-handlepos={position}
|
||||
data-id={`${nodeId}-${id}-${type}`}
|
||||
class={cc([
|
||||
'react-flow__handle',
|
||||
`react-flow__handle-${position}`,
|
||||
'svelte-flow__handle',
|
||||
`svelte-flow__handle-${position}`,
|
||||
'nodrag',
|
||||
'nopan',
|
||||
position
|
||||
@@ -90,7 +100,7 @@
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.react-flow__handle {
|
||||
.svelte-flow__handle {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
min-width: 5px;
|
||||
|
||||
@@ -43,7 +43,9 @@ export function handlePointerDown({
|
||||
cancelConnection,
|
||||
isValidConnection,
|
||||
edgeUpdaterType,
|
||||
onEdgeUpdateEnd
|
||||
onEdgeUpdateEnd,
|
||||
onConnectStart,
|
||||
onConnectEnd
|
||||
}: {
|
||||
event: MouseEvent | TouchEvent;
|
||||
handleId: string | null;
|
||||
@@ -61,8 +63,10 @@ export function handlePointerDown({
|
||||
panBy: (delta: XYPosition) => void;
|
||||
edgeUpdaterType?: HandleType;
|
||||
onEdgeUpdateEnd?: (evt: MouseEvent | TouchEvent) => void;
|
||||
onConnectStart: () => void;
|
||||
onConnectEnd: () => void;
|
||||
}): void {
|
||||
// when react-flow is used inside a shadow root we can't use document
|
||||
// when svelte-flow is used inside a shadow root we can't use document
|
||||
const doc = getHostForElement(event.target as HTMLElement);
|
||||
let autoPanId = 0;
|
||||
let prevClosestHandle: ConnectionHandle | null;
|
||||
@@ -113,7 +117,9 @@ export function handlePointerDown({
|
||||
});
|
||||
|
||||
// @todo add prop
|
||||
|
||||
// onConnectStart?.(event, { nodeId, handleId, handleType });
|
||||
onConnectStart();
|
||||
|
||||
function onPointerMove(event: MouseEvent | TouchEvent) {
|
||||
const transform = get(transformStore);
|
||||
@@ -166,7 +172,7 @@ export function handlePointerDown({
|
||||
if (connection.source !== connection.target && handleDomNode) {
|
||||
resetRecentHandle(prevActiveHandle);
|
||||
prevActiveHandle = handleDomNode;
|
||||
// @todo: remove the old class names "react-flow__handle-" in the next major version
|
||||
// @todo: remove the old class names "svelte-flow__handle-" in the next major version
|
||||
handleDomNode.classList.add('connecting');
|
||||
handleDomNode.classList.toggle('valid', isValid);
|
||||
}
|
||||
@@ -179,8 +185,8 @@ export function handlePointerDown({
|
||||
|
||||
// it's important to get a fresh reference from the store here
|
||||
// in order to get the latest state of onConnectEnd
|
||||
// @todo add onConnectEnd prop
|
||||
// getState().onConnectEnd?.(event);
|
||||
|
||||
onConnectEnd();
|
||||
|
||||
if (edgeUpdaterType) {
|
||||
onEdgeUpdateEnd?.(event);
|
||||
|
||||
@@ -81,11 +81,11 @@ export function isValidHandle(
|
||||
) {
|
||||
const isTarget = fromType === 'target';
|
||||
const handleDomNode = doc.querySelector(
|
||||
`.react-flow__handle[data-id="${handle?.nodeId}-${handle?.id}-${handle?.type}"]`
|
||||
`.svelte-flow__handle[data-id="${handle?.nodeId}-${handle?.id}-${handle?.type}"]`
|
||||
);
|
||||
const { x, y } = getEventPosition(event);
|
||||
const handleBelow = doc.elementFromPoint(x, y);
|
||||
const handleToCheck = handleBelow?.classList.contains('react-flow__handle')
|
||||
const handleToCheck = handleBelow?.classList.contains('svelte-flow__handle')
|
||||
? handleBelow
|
||||
: handleDomNode;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
$: rect = getRectOfNodes(selectedNodes, $nodeOrigin);
|
||||
</script>
|
||||
|
||||
{#if selectedNodes}
|
||||
{#if selectedNodes && $selectionRectMode === 'nodes'}
|
||||
<div
|
||||
class="selection-wrapper nopan"
|
||||
style={`width: ${rect.width}px; height: ${rect.height}px; transform: translate(${rect.x}px, ${rect.y}px)`}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { onMount, setContext, SvelteComponentTyped } from 'svelte';
|
||||
import { createEventDispatcher, onMount, setContext, SvelteComponentTyped } from 'svelte';
|
||||
import cc from 'classcat';
|
||||
import { type XYPosition, Position, errorMessages } from '@reactflow/system';
|
||||
|
||||
@@ -28,10 +28,9 @@
|
||||
let className: string = '';
|
||||
export { className as class };
|
||||
|
||||
let nodeRef: HTMLDivElement;
|
||||
|
||||
const { nodes, transform, nodeTypes, updateNodePositions, addSelectedNodes } = useStore();
|
||||
|
||||
let nodeRef: HTMLDivElement;
|
||||
const nodeTypeValid = !!$nodeTypes[type!];
|
||||
|
||||
if (!nodeTypeValid) {
|
||||
@@ -44,6 +43,7 @@
|
||||
const isSelectable = true;
|
||||
const selectNodesOnDrag = false;
|
||||
const isDraggable = true;
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
setContext('rf_nodeid', id);
|
||||
|
||||
@@ -55,30 +55,35 @@
|
||||
};
|
||||
});
|
||||
|
||||
function dispatchEvent(eventName: string) {
|
||||
const node = $nodes.find(n => n.id === id);
|
||||
dispatch(eventName, node);
|
||||
}
|
||||
|
||||
function onSelectNodeHandler(event: MouseEvent) {
|
||||
if (isSelectable && (!selectNodesOnDrag || !isDraggable)) {
|
||||
// this handler gets called within the drag start event when selectNodesOnDrag=true
|
||||
addSelectedNodes([id]);
|
||||
}
|
||||
|
||||
// if (onClick) {
|
||||
// const node = store.getState().nodeInternals.get(id)!;
|
||||
// onClick(event, { ...node });
|
||||
// }
|
||||
dispatchEvent('node:click')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
use:drag={{ nodeId: id, nodes, transform, updateNodePositions }}
|
||||
class={cc(['react-flow__node', `react-flow__node-${type}`, className])}
|
||||
data-id={id}
|
||||
class={cc(['svelte-flow__node', `svelte-flow__node-${type}`, className])}
|
||||
class:initializing={!width && !height}
|
||||
class:dragging
|
||||
class:selected
|
||||
bind:this={nodeRef}
|
||||
on:click={onSelectNodeHandler}
|
||||
style:transform={`translate(${positionAbsolute.x}px, ${positionAbsolute.y}px)`}
|
||||
{style}
|
||||
data-id={id}
|
||||
on:click={onSelectNodeHandler}
|
||||
on:mouseenter={() => dispatchEvent('node:mouseenter')}
|
||||
on:mouseleave={() => dispatchEvent('node:mouseleave')}
|
||||
on:mousemove={() => dispatchEvent('node:mousemove')}
|
||||
>
|
||||
<svelte:component
|
||||
this={nodeComponent}
|
||||
@@ -90,11 +95,14 @@
|
||||
isConnectable={true}
|
||||
xPos={positionAbsolute.x}
|
||||
yPos={positionAbsolute.y}
|
||||
on:connect:start
|
||||
on:connect
|
||||
on:connect:end
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.react-flow__node {
|
||||
.svelte-flow__node {
|
||||
border-radius: 3px;
|
||||
color: #222;
|
||||
text-align: center;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Node } from '$lib/types';
|
||||
|
||||
export type NodeWrapperProps = Node & {
|
||||
'on:nodeclick'?: (event: MouseEvent) => void;
|
||||
resizeObserver?: ResizeObserver | null;
|
||||
};
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
|
||||
{#if isVisible}
|
||||
<div
|
||||
class="react-flow__selection"
|
||||
class="svelte-flow__selection"
|
||||
style={`width: ${width}px; height: ${height}px; transform: translate(${x}px, ${y}px)`}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.react-flow__selection {
|
||||
.svelte-flow__selection {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@@ -23,8 +23,8 @@
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
.react-flow__selection:focus,
|
||||
.react-flow__selection:focus-visible {
|
||||
.svelte-flow__selection:focus,
|
||||
.svelte-flow__selection:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
export let selected: $$Props['selected'] = false;
|
||||
</script>
|
||||
|
||||
<Handle type="target" position={targetPosition} />
|
||||
<Handle type="target" position={targetPosition} on:connect:start on:connect on:connect:end />
|
||||
{data?.label}
|
||||
<Handle type="source" position={sourcePosition} />
|
||||
<Handle type="source" position={sourcePosition} on:connect:start on:connect on:connect:end />
|
||||
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Position } from '@reactflow/system';
|
||||
|
||||
import type { NodeProps } from '$lib/types';
|
||||
import { Handle } from '$lib/components/Handle';
|
||||
|
||||
export let data: { label: string } = { label: 'Node' };
|
||||
export let isConnectable: boolean = true;
|
||||
export let sourcePosition: Position = Position.Bottom;
|
||||
interface $$Props extends NodeProps<{ label: string }> {}
|
||||
|
||||
export let id: $$Props['id'];
|
||||
export let data: $$Props['data'] = { label: 'Node' };
|
||||
export let isConnectable: $$Props['isConnectable'] = true;
|
||||
export let targetPosition: $$Props['targetPosition'] = Position.Top;
|
||||
export let sourcePosition: $$Props['sourcePosition'] = Position.Bottom;
|
||||
export let xPos: $$Props['xPos'];
|
||||
export let yPos: $$Props['yPos'];
|
||||
export let selected: $$Props['selected'] = false;
|
||||
</script>
|
||||
|
||||
{data?.label}
|
||||
<Handle type="source" position={sourcePosition} />
|
||||
<Handle type="source" position={sourcePosition} on:connect:start on:connect on:connect:end />
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Position } from '@reactflow/system';
|
||||
|
||||
import type { NodeProps } from '$lib/types';
|
||||
import { Handle } from '$lib/components/Handle';
|
||||
|
||||
export let data: { label: string } = { label: 'Node' };
|
||||
export let isConnectable: boolean = true;
|
||||
export let targetPosition: Position = Position.Top;
|
||||
interface $$Props extends NodeProps<{ label: string }> {}
|
||||
|
||||
export let id: $$Props['id'];
|
||||
export let data: $$Props['data'] = { label: 'Node' };
|
||||
export let isConnectable: $$Props['isConnectable'] = true;
|
||||
export let targetPosition: $$Props['targetPosition'] = Position.Top;
|
||||
export let sourcePosition: $$Props['sourcePosition'] = Position.Bottom;
|
||||
export let xPos: $$Props['xPos'];
|
||||
export let yPos: $$Props['yPos'];
|
||||
export let selected: $$Props['selected'] = false;
|
||||
</script>
|
||||
|
||||
{data?.label}
|
||||
<Handle type="target" position={targetPosition} />
|
||||
<Handle type="target" position={targetPosition} on:connect:start on:connect on:connect:end />
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
const { edgesLayouted, width, height } = useStore();
|
||||
</script>
|
||||
|
||||
<svg width={$width} height={$height} class="react-flow__edges">
|
||||
<svg width={$width} height={$height} class="svelte-flow__edges">
|
||||
{#each $edgesLayouted as edge (edge.id)}
|
||||
<EdgeWrapper {...edge} />
|
||||
<EdgeWrapper {...edge} on:edge:click />
|
||||
{/each}
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
.react-flow__edges {
|
||||
.svelte-flow__edges {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
|
||||
@@ -21,16 +21,27 @@
|
||||
onDestroy(() => {
|
||||
resizeObserver?.disconnect();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<div class="react-flow__nodes">
|
||||
<div class="svelte-flow__nodes">
|
||||
{#each $nodes as node (node.id)}
|
||||
<NodeWrapper {...node} {resizeObserver} />
|
||||
<NodeWrapper
|
||||
{...node}
|
||||
{resizeObserver}
|
||||
on:node:click
|
||||
on:node:mouseenter
|
||||
on:node:mousemove
|
||||
on:node:mouseleave
|
||||
on:connect:start
|
||||
on:connect
|
||||
on:connect:end
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.react-flow__nodes {
|
||||
.svelte-flow__nodes {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
|
||||
@@ -29,13 +29,15 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { useStore } from '$lib/store';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { SelectionMode } from '@reactflow/system';
|
||||
import { getEventPosition, getNodesInside } from '@reactflow/utils';
|
||||
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
import { getConnectedEdges } from '$lib/utils';
|
||||
import type { Node, Edge } from '$lib/types';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const {
|
||||
nodes,
|
||||
edges,
|
||||
@@ -60,7 +62,7 @@
|
||||
$: hasActiveSelection = elementsSelectable && (isSelecting || $selectionRectMode === 'user');
|
||||
|
||||
function onClick(event: MouseEvent) {
|
||||
// onPaneClick?.(event);
|
||||
dispatch('pane:click');
|
||||
|
||||
resetSelectedElements();
|
||||
selectionRectMode.set(null);
|
||||
@@ -155,11 +157,21 @@
|
||||
|
||||
selectionRect.set(null);
|
||||
};
|
||||
|
||||
const onContextMenu = (event: MouseEvent) => {
|
||||
// if (Array.isArray(panOnDrag) && panOnDrag?.includes(2)) {
|
||||
// event.preventDefault();
|
||||
// return;
|
||||
// }
|
||||
|
||||
dispatch('pane:contextmenu');
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={container}
|
||||
class="react-flow__pane"
|
||||
class="svelte-flow__pane"
|
||||
class:dragging={$dragging}
|
||||
class:selection={isSelecting}
|
||||
on:click={hasActiveSelection ? undefined : wrapHandler(onClick, container)}
|
||||
@@ -167,12 +179,13 @@
|
||||
on:mousemove={hasActiveSelection ? onMouseMove : undefined}
|
||||
on:mouseup={hasActiveSelection ? onMouseUp : undefined}
|
||||
on:mouseleave={hasActiveSelection ? onMouseLeave : undefined}
|
||||
on:contextmenu={wrapHandler(onContextMenu, container)}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.react-flow__pane {
|
||||
.svelte-flow__pane {
|
||||
cursor: grab;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
||||
@@ -12,34 +12,34 @@
|
||||
$: positionClasses = `${position}`.split('-');
|
||||
</script>
|
||||
|
||||
<div class={cc(['react-flow__panel', className, ...positionClasses])} {style}>
|
||||
<div class={cc(['svelte-flow__panel', className, ...positionClasses])} {style}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.react-flow__panel {
|
||||
.svelte-flow__panel {
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
.react-flow__panel.top {
|
||||
.svelte-flow__panel.top {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.react-flow__panel.bottom {
|
||||
.svelte-flow__panel.bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.react-flow__panel.left {
|
||||
.svelte-flow__panel.left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.react-flow__panel.right {
|
||||
.svelte-flow__panel.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.react-flow__panel.center {
|
||||
.svelte-flow__panel.center {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { setContext, onMount } from 'svelte';
|
||||
import { ConnectionLineType } from '@reactflow/system';
|
||||
import { setContext, onMount, createEventDispatcher } from 'svelte';
|
||||
import cc from 'classcat';
|
||||
|
||||
import { key, createStore } from '$lib/store';
|
||||
@@ -13,21 +12,27 @@
|
||||
import { NodeSelection } from '$lib/components/NodeSelection';
|
||||
import { KeyHandler } from '$lib/components/KeyHandler';
|
||||
import { ConnectionLine } from '$lib/components/ConnectionLine';
|
||||
import type { SvelteFlowProps } from './types';
|
||||
import type { SvelteFlowProps, SvelteFlowEvents } from './types';
|
||||
|
||||
type $$Props = SvelteFlowProps;
|
||||
type $$Events = SvelteFlowEvents;
|
||||
|
||||
export let id: $$Props['id'] = '1';
|
||||
export let nodes: $$Props['nodes'] = [];
|
||||
export let edges: $$Props['edges'] = [];
|
||||
export let fitView: $$Props['fitView'] = undefined;
|
||||
export let minZoom: $$Props['minZoom'] = undefined;
|
||||
export let maxZoom: $$Props['maxZoom'] = undefined;
|
||||
export let initialViewport: $$Props['initialViewport'] = undefined;
|
||||
export let nodeTypes: $$Props['nodeTypes'] = undefined;
|
||||
export let edgeTypes: $$Props['edgeTypes'] = undefined;
|
||||
export let selectionKey: $$Props['selectionKey'] = undefined;
|
||||
export let deleteKey: $$Props['deleteKey'] = undefined;
|
||||
export let connectionLineType: $$Props['connectionLineType'] = ConnectionLineType.Bezier;
|
||||
|
||||
export let connectionLineType: $$Props['connectionLineType'] = undefined
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
const dispatch = createEventDispatcher<{ 'on:nodeclick': number }>();
|
||||
|
||||
|
||||
let domNode: HTMLDivElement;
|
||||
|
||||
@@ -51,8 +56,33 @@
|
||||
store.nodes.subscribe((ns) => {
|
||||
nodes = ns;
|
||||
});
|
||||
|
||||
store.edges.subscribe((es) => {
|
||||
edges = es;
|
||||
});
|
||||
});
|
||||
|
||||
$: {
|
||||
const updatableProps = {
|
||||
id,
|
||||
connectionLineType,
|
||||
};
|
||||
|
||||
Object.keys(updatableProps).forEach(prop => {
|
||||
// @ts-ignore
|
||||
if (updatableProps[prop] !== undefined) {
|
||||
// @ts-ignore
|
||||
if (!store[prop]) {
|
||||
// @ts-ignore
|
||||
console.warn(store[prop], prop, 'ups')
|
||||
} else {
|
||||
// @ts-ignore
|
||||
store[prop].set(updatableProps[prop]);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$: {
|
||||
store.setNodes(nodes);
|
||||
}
|
||||
@@ -60,23 +90,47 @@
|
||||
$: {
|
||||
store.setEdges(edges);
|
||||
}
|
||||
|
||||
$: {
|
||||
if (nodeTypes !== undefined) {
|
||||
store.setNodeTypes(nodeTypes);
|
||||
}
|
||||
|
||||
if (edgeTypes !== undefined) {
|
||||
store.setEdgeTypes(edgeTypes);
|
||||
}
|
||||
|
||||
if (minZoom !== undefined) {
|
||||
store.setMinZoom(minZoom);
|
||||
}
|
||||
|
||||
if (maxZoom !== undefined) {
|
||||
store.setMaxZoom(maxZoom);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
{...$$restProps}
|
||||
class={cc(['react-flow', className])}
|
||||
class={cc(['svelte-flow', className])}
|
||||
data-testid="rf__wrapper"
|
||||
{id}
|
||||
bind:this={domNode}
|
||||
>
|
||||
<KeyHandler {selectionKey} {deleteKey} />
|
||||
<Zoom>
|
||||
<Pane>
|
||||
<Zoom {initialViewport}>
|
||||
<Pane on:pane:click>
|
||||
<Viewport>
|
||||
<EdgeRenderer />
|
||||
<EdgeRenderer on:edge:click />
|
||||
<ConnectionLine />
|
||||
<div class="react-flow__edgelabel-renderer" />
|
||||
<NodeRenderer />
|
||||
<div class="svelte-flow__edgelabel-renderer" />
|
||||
<NodeRenderer
|
||||
on:node:click
|
||||
on:node:mouseenter
|
||||
on:node:mousemove
|
||||
on:node:mouseleave
|
||||
on:connect:start
|
||||
on:connect
|
||||
on:connect:end
|
||||
/>
|
||||
<NodeSelection />
|
||||
</Viewport>
|
||||
<UserSelection />
|
||||
@@ -86,7 +140,7 @@
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.react-flow {
|
||||
.svelte-flow {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
@@ -94,13 +148,13 @@
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.react-flow :global(.react-flow__node-default),
|
||||
.react-flow :global(.react-flow__node-input),
|
||||
.react-flow :global(.react-flow__node-output) {
|
||||
.svelte-flow :global(.svelte-flow__node-default),
|
||||
.svelte-flow :global(.svelte-flow__node-input),
|
||||
.svelte-flow :global(.svelte-flow__node-output) {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.react-flow__edgelabel-renderer {
|
||||
.svelte-flow__edgelabel-renderer {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { default as SvelteFlow } from './SvelteFlow.svelte';
|
||||
export type { SvelteFlowProps } from './types';
|
||||
export * from './types';
|
||||
|
||||
@@ -1,17 +1,49 @@
|
||||
import type { ConnectionLineType } from '@reactflow/system';
|
||||
import type { ConnectionLineType, NodeOrigin, Viewport } from '@reactflow/system';
|
||||
|
||||
import type { Edge, Node, NodeTypes, KeyDefinition } from '$lib/types';
|
||||
import type {
|
||||
Edge,
|
||||
Node,
|
||||
NodeTypes,
|
||||
KeyDefinition,
|
||||
EdgeTypes,
|
||||
DefaultEdgeOptions
|
||||
} from '$lib/types';
|
||||
|
||||
export type SvelteFlowProps = {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
|
||||
connectionLineType?: ConnectionLineType;
|
||||
id?: string;
|
||||
nodeTypes?: NodeTypes;
|
||||
edgeTypes?: EdgeTypes;
|
||||
selectionKey?: KeyDefinition;
|
||||
deleteKey?: KeyDefinition;
|
||||
nodeTypes?: NodeTypes;
|
||||
fitView?: boolean;
|
||||
nodeOrigin?: NodeOrigin;
|
||||
minZoom?: number;
|
||||
maxZoom?: number;
|
||||
initialViewport?: Viewport;
|
||||
defaultEdgeOptions?: DefaultEdgeOptions;
|
||||
|
||||
class?: string;
|
||||
style?: string;
|
||||
id?: string;
|
||||
|
||||
connectionLineType?: ConnectionLineType;
|
||||
};
|
||||
|
||||
export type SvelteFlowEvents = {
|
||||
'node:click': CustomEvent<Node>;
|
||||
'node:mouseenter': CustomEvent<Node>;
|
||||
'node:mousemove': CustomEvent<Node>;
|
||||
'node:mouseleave': CustomEvent<Node>;
|
||||
'edge:click': CustomEvent<Edge>;
|
||||
'connect:start': CustomEvent<number>;
|
||||
connect: CustomEvent<number>;
|
||||
'connect:end': CustomEvent<number>;
|
||||
'pane:click': CustomEvent;
|
||||
'pane:contextmenu': CustomEvent;
|
||||
};
|
||||
|
||||
export type SvelteFlowSlots = {
|
||||
default: { slotValue: string };
|
||||
};
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="react-flow__viewport"
|
||||
class="svelte-flow__viewport"
|
||||
style="transform: translate({$transform[0]}px, {$transform[1]}px) scale({$transform[2]})"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.react-flow__viewport {
|
||||
.svelte-flow__viewport {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { get } from 'svelte/store';
|
||||
import type { Viewport } from '@reactflow/system';
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
import zoom from '$lib/actions/zoom';
|
||||
|
||||
const { transform, d3, selectionKeyPressed, selectionRectMode } = useStore();
|
||||
export let initialViewport: Viewport = { x: 0, y: 0, zoom: 1 };
|
||||
|
||||
const { transform, d3, selectionKeyPressed, selectionRectMode, minZoom, maxZoom } = useStore();
|
||||
|
||||
$: selecting = $selectionKeyPressed || $selectionRectMode === 'user';
|
||||
</script>
|
||||
|
||||
<div class="react-flow__zoom" use:zoom={{ transform, d3, selecting }}>
|
||||
<div class="svelte-flow__zoom" use:zoom={{ transform, d3, selecting, minZoom: get(minZoom), maxZoom: get(maxZoom), initialViewport }}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.react-flow__zoom {
|
||||
.svelte-flow__zoom {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { SvelteFlow, type SvelteFlowProps } from '$lib/container/SvelteFlow';
|
||||
export { Controls, ControlButton } from '$lib/plugins/Controls';
|
||||
export { Background, BackgroundVariant } from '$lib/plugins/Background';
|
||||
export { Minimap } from '$lib/plugins/Minimap';
|
||||
export { Panel, type PanelProps } from '$lib/container/Panel';
|
||||
import { SvelteFlow } from '$lib/container/SvelteFlow';
|
||||
|
||||
export * from '$lib/container/SvelteFlow';
|
||||
export * from '$lib/container/Panel';
|
||||
|
||||
export * from '$lib/plugins/Controls';
|
||||
export * from '$lib/plugins/Background';
|
||||
export * from '$lib/plugins/Minimap';
|
||||
|
||||
export * from '$lib/types';
|
||||
export * from '$lib/utils';
|
||||
|
||||
export type { SvelteFlowProps };
|
||||
export default SvelteFlow;
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
: [patternDimensions[0] / 2, patternDimensions[1] / 2];
|
||||
</script>
|
||||
|
||||
<svg class={cc(['react-flow__background', className])} style={style}>
|
||||
<svg class={cc(['svelte-flow__background', className])} style={style}>
|
||||
<pattern
|
||||
id={patternId}
|
||||
x={$transform[0] % scaledGap[0]}
|
||||
@@ -67,7 +67,7 @@
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
.react-flow__background {
|
||||
.svelte-flow__background {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
<button
|
||||
type="button"
|
||||
on:click
|
||||
class={cc(['react-flow__controls-button', className])}
|
||||
class={cc(['svelte-flow__controls-button', className])}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot class="button-svg" />
|
||||
</button>
|
||||
|
||||
<style>
|
||||
.react-flow__controls-button {
|
||||
.svelte-flow__controls-button {
|
||||
border: none;
|
||||
background: #fefefe;
|
||||
border-bottom: 1px solid #eee;
|
||||
@@ -31,7 +31,7 @@
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.react-flow__controls-button :global(svg) {
|
||||
.svelte-flow__controls-button :global(svg) {
|
||||
width: 100%;
|
||||
max-width: 12px;
|
||||
max-height: 12px;
|
||||
|
||||
@@ -43,11 +43,11 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<Panel class="react-flow__controls" {position}>
|
||||
<Panel class="svelte-flow__controls" {position}>
|
||||
{#if showZoom}
|
||||
<ControlButton
|
||||
on:click={onZoomInHandler}
|
||||
class="react-flow__controls-zoomin"
|
||||
class="svelte-flow__controls-zoomin"
|
||||
title="zoom in"
|
||||
aria-label="zoom in"
|
||||
>
|
||||
@@ -56,7 +56,7 @@
|
||||
<svelte:component
|
||||
this={ControlButton}
|
||||
on:click={onZoomOutHandler}
|
||||
class="react-flow__controls-zoomout"
|
||||
class="svelte-flow__controls-zoomout"
|
||||
title="zoom out"
|
||||
aria-label="zoom out"
|
||||
>
|
||||
@@ -66,7 +66,7 @@
|
||||
{#if showFitView}
|
||||
<svelte:component
|
||||
this={ControlButton}
|
||||
class="react-flow__controls-fitview"
|
||||
class="svelte-flow__controls-fitview"
|
||||
on:click={onFitViewHandler}
|
||||
title="fit view"
|
||||
aria-label="fit view"
|
||||
@@ -77,7 +77,7 @@
|
||||
{#if showInteractive}
|
||||
<svelte:component
|
||||
this={ControlButton}
|
||||
class="react-flow__controls-interactive"
|
||||
class="svelte-flow__controls-interactive"
|
||||
on:click={onToggleInteractivity}
|
||||
title="toggle interactivity"
|
||||
aria-label="toggle interactivity"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { default as Controls } from './Controls.svelte';
|
||||
export { default as ControlButton } from './ControlButton.svelte';
|
||||
export { type ControlsProps } from './types';
|
||||
export * from './types';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { PanelPosition } from '@reactflow/system';
|
||||
|
||||
export type ControlsProps = {
|
||||
position: PanelPosition;
|
||||
showZoom: boolean;
|
||||
showFitView: boolean;
|
||||
showInteractive: boolean;
|
||||
position?: PanelPosition;
|
||||
showZoom?: boolean;
|
||||
showFitView?: boolean;
|
||||
showInteractive?: boolean;
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
const nodeClassNameFunc = getAttrFunction(nodeClassName);
|
||||
const shapeRendering =
|
||||
typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision';
|
||||
const labelledBy = `react-flow__minimap-desc-${$id}`;
|
||||
const labelledBy = `svelte-flow__minimap-desc-${$id}`;
|
||||
|
||||
$: viewBB = {
|
||||
x: -$transform[0] / $transform[2],
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
<Panel
|
||||
{position}
|
||||
class={cc(['react-flow__minimap', className])}
|
||||
class={cc(['svelte-flow__minimap', className])}
|
||||
style={`background-color: ${bgColor}; ${style}`}
|
||||
>
|
||||
<svg
|
||||
@@ -100,7 +100,7 @@
|
||||
{/if}
|
||||
{/each}
|
||||
<path
|
||||
class="react-flow__minimap-mask"
|
||||
class="svelte-flow__minimap-mask"
|
||||
d={`M${x - offset},${y - offset}h${viewboxWidth + offset * 2}v${viewboxHeight + offset * 2}h${
|
||||
-viewboxWidth - offset * 2
|
||||
}z
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</script>
|
||||
|
||||
<rect
|
||||
class={cc(['react-flow__minimap-node', className])}
|
||||
class={cc(['svelte-flow__minimap-node', className])}
|
||||
{x}
|
||||
{y}
|
||||
rx={borderRadius}
|
||||
|
||||
@@ -18,7 +18,12 @@ import { getHandleBounds, getConnectedEdges, addEdge as addEdgeUtil } from '$lib
|
||||
import type { EdgeTypes, NodeTypes, Node, Edge, ConnectionData } from '$lib/types';
|
||||
import { getEdgesLayouted } from './edges-layouted';
|
||||
import { getConnectionPath } from './connection-path';
|
||||
import { initConnectionData, initialStoreState } from './initial-store';
|
||||
import {
|
||||
initConnectionData,
|
||||
initialEdgeTypes,
|
||||
initialNodeTypes,
|
||||
initialStoreState
|
||||
} from './initial-store';
|
||||
import type { SvelteFlowStore } from './types';
|
||||
|
||||
export const key = Symbol();
|
||||
@@ -32,20 +37,27 @@ type CreateStoreProps = {
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export function createStore({
|
||||
transform = [0, 0, 1],
|
||||
nodeOrigin = [0, 0],
|
||||
fitView: fitViewOnInit = false,
|
||||
nodeTypes = {},
|
||||
edgeTypes = {},
|
||||
id = '1'
|
||||
}: CreateStoreProps): SvelteFlowStore {
|
||||
export function createStore({ fitView: fitViewOnInit = false }: CreateStoreProps): SvelteFlowStore {
|
||||
const store = {
|
||||
...initialStoreState
|
||||
};
|
||||
|
||||
let fitViewOnInitDone = false;
|
||||
|
||||
function setNodeTypes(nodeTypes: NodeTypes) {
|
||||
store.nodeTypes.set({
|
||||
...initialNodeTypes,
|
||||
...nodeTypes
|
||||
});
|
||||
}
|
||||
|
||||
function setEdgeTypes(edgeTypes: EdgeTypes) {
|
||||
store.edgeTypes.set({
|
||||
...initialEdgeTypes,
|
||||
...edgeTypes
|
||||
});
|
||||
}
|
||||
|
||||
function setEdges(edges: Edge[]) {
|
||||
store.edges.set(edges);
|
||||
}
|
||||
@@ -91,7 +103,7 @@ export function createStore({
|
||||
}
|
||||
|
||||
function updateNodeDimensions(updates: NodeDimensionUpdate[]) {
|
||||
const viewportNode = document?.querySelector('.react-flow__viewport');
|
||||
const viewportNode = document?.querySelector('.svelte-flow__viewport');
|
||||
|
||||
if (!viewportNode) {
|
||||
return;
|
||||
@@ -153,6 +165,25 @@ export function createStore({
|
||||
}
|
||||
}
|
||||
|
||||
function setMinZoom(minZoom: number) {
|
||||
const d3Zoom = get(store.d3).zoom;
|
||||
|
||||
if (d3Zoom) {
|
||||
d3Zoom?.scaleExtent([minZoom, get(store.maxZoom)]);
|
||||
|
||||
store.minZoom.set(minZoom);
|
||||
}
|
||||
}
|
||||
|
||||
function setMaxZoom(maxZoom: number) {
|
||||
const d3Zoom = get(store.d3).zoom;
|
||||
if (d3Zoom) {
|
||||
d3Zoom?.scaleExtent([get(store.minZoom), maxZoom]);
|
||||
|
||||
store.maxZoom.set(maxZoom);
|
||||
}
|
||||
}
|
||||
|
||||
function fitView() {
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
|
||||
|
||||
@@ -309,12 +340,16 @@ export function createStore({
|
||||
// actions
|
||||
setNodes,
|
||||
setEdges,
|
||||
setNodeTypes,
|
||||
setEdgeTypes,
|
||||
addEdge,
|
||||
updateNodePositions,
|
||||
updateNodeDimensions,
|
||||
zoomIn,
|
||||
zoomOut,
|
||||
fitView,
|
||||
setMinZoom,
|
||||
setMaxZoom,
|
||||
resetSelectedElements,
|
||||
addSelectedNodes,
|
||||
panBy,
|
||||
|
||||
@@ -13,10 +13,10 @@ import {
|
||||
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
||||
import InputNode from '$lib/components/nodes/InputNode.svelte';
|
||||
import OutputNode from '$lib/components/nodes/OutputNode.svelte';
|
||||
import type { Node, Edge, ConnectionData, NodeTypes, EdgeTypes, EdgeLayouted } from '$lib/types';
|
||||
import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
|
||||
import StraightEdge from '$lib/components/edges/StraightEdge.svelte';
|
||||
import SmoothStepEdge from '$lib/components/edges/SmoothStepEdge.svelte';
|
||||
import type { Node, Edge, ConnectionData, NodeTypes, EdgeTypes, EdgeLayouted } from '$lib/types';
|
||||
|
||||
export const initConnectionData = {
|
||||
nodeId: null,
|
||||
@@ -26,18 +26,32 @@ export const initConnectionData = {
|
||||
status: null
|
||||
};
|
||||
|
||||
export const initialNodeTypes = {
|
||||
input: InputNode,
|
||||
output: OutputNode,
|
||||
default: DefaultNode
|
||||
};
|
||||
|
||||
export const initialEdgeTypes = {
|
||||
straight: StraightEdge,
|
||||
smoothstep: SmoothStepEdge,
|
||||
default: BezierEdge
|
||||
};
|
||||
|
||||
export const initialStoreState = {
|
||||
id: writable<string | null>(null),
|
||||
nodes: writable<Node[]>([]),
|
||||
edges: writable<Edge[]>([]),
|
||||
edgesLayouted: readable<EdgeLayouted[]>([]),
|
||||
height: writable<number>(500),
|
||||
width: writable<number>(500),
|
||||
nodeOrigin: writable<NodeOrigin>([0.5, 0.5]),
|
||||
minZoom: writable<number>(0.5),
|
||||
maxZoom: writable<number>(2),
|
||||
nodeOrigin: writable<NodeOrigin>([0, 0]),
|
||||
d3: writable<{ zoom: D3ZoomInstance | null; selection: D3SelectionInstance | null }>({
|
||||
zoom: null,
|
||||
selection: null
|
||||
}),
|
||||
id: writable<string | null>(null),
|
||||
dragging: writable<boolean>(false),
|
||||
selectionRect: writable<SelectionRect | null>(null),
|
||||
selectionKeyPressed: writable<boolean>(false),
|
||||
@@ -45,16 +59,8 @@ export const initialStoreState = {
|
||||
deleteKeyPressed: writable<boolean>(false),
|
||||
selectionRectMode: writable<string | null>(null),
|
||||
selectionMode: writable<SelectionMode>(SelectionMode.Partial),
|
||||
nodeTypes: writable<NodeTypes>({
|
||||
input: InputNode,
|
||||
output: OutputNode,
|
||||
default: DefaultNode
|
||||
}),
|
||||
edgeTypes: writable<EdgeTypes>({
|
||||
straight: StraightEdge,
|
||||
smoothstep: SmoothStepEdge,
|
||||
default: BezierEdge
|
||||
}),
|
||||
nodeTypes: writable<NodeTypes>(initialNodeTypes),
|
||||
edgeTypes: writable<EdgeTypes>(initialEdgeTypes),
|
||||
transform: writable<Transform>([0, 0, 1]),
|
||||
connectionMode: writable<ConnectionMode>(ConnectionMode.Strict),
|
||||
domNode: writable<HTMLDivElement | null>(null),
|
||||
|
||||
@@ -7,14 +7,18 @@ import type {
|
||||
} from '@reactflow/system';
|
||||
|
||||
import type { initialStoreState } from './initial-store';
|
||||
import type { Node, Edge, ConnectionData } from '$lib/types';
|
||||
import type { Node, Edge, ConnectionData, NodeTypes, EdgeTypes } from '$lib/types';
|
||||
|
||||
export type SvelteFlowStoreActions = {
|
||||
setNodes: (nodes: Node[]) => void;
|
||||
setEdges: (edges: Edge[]) => void;
|
||||
setNodeTypes: (nodeTypes: NodeTypes) => void;
|
||||
setEdgeTypes: (edgeTypes: EdgeTypes) => void;
|
||||
addEdge: (edge: Edge | Connection) => void;
|
||||
zoomIn: (options?: ViewportHelperFunctionOptions) => void;
|
||||
zoomOut: (options?: ViewportHelperFunctionOptions) => void;
|
||||
setMinZoom: (minZoom: number) => void;
|
||||
setMaxZoom: (maxZoom: number) => void;
|
||||
fitView: (options?: ViewportHelperFunctionOptions) => boolean;
|
||||
updateNodePositions: (
|
||||
nodeDragItems: NodeDragItem[],
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { SvelteComponentTyped } from 'svelte';
|
||||
import type {
|
||||
BaseEdge,
|
||||
BezierPathOptions,
|
||||
DefaultEdgeOptionsBase,
|
||||
Position,
|
||||
SmoothStepPathOptions
|
||||
} from '@reactflow/system';
|
||||
@@ -58,3 +59,5 @@ export type EdgeProps = Pick<
|
||||
>;
|
||||
|
||||
export type EdgeTypes = Record<string, typeof SvelteComponentTyped<EdgeProps>>;
|
||||
|
||||
export type DefaultEdgeOptions = DefaultEdgeOptionsBase<Edge>;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { Node, Edge, NodeTypes } from '../lib/types';
|
||||
import SvelteFlow, {
|
||||
Controls,
|
||||
Background,
|
||||
BackgroundVariant,
|
||||
Minimap,
|
||||
Panel
|
||||
Panel,
|
||||
type Node, type Edge, type NodeTypes
|
||||
} from '../lib/index';
|
||||
import CustomNode from '../customnodes/Custom.svelte';
|
||||
|
||||
@@ -120,7 +120,24 @@
|
||||
// }
|
||||
</script>
|
||||
|
||||
<SvelteFlow bind:nodes bind:edges {nodeTypes} fitView>
|
||||
<SvelteFlow
|
||||
bind:nodes
|
||||
bind:edges
|
||||
{nodeTypes}
|
||||
fitView
|
||||
minZoom={0.1}
|
||||
maxZoom={2.5}
|
||||
initialViewport={{ x: 100, y: 100, zoom: 2 }}
|
||||
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)}
|
||||
on:edge:click={(event) => console.log('edge click', event)}
|
||||
on:connect:start={(event) => console.log('on connect start', event)}
|
||||
on:connect={(event) => console.log('on connect', event)}
|
||||
on:connect:end={(event) => console.log('on connect end', event)}
|
||||
on:pane:click={(event) => console.log('on pane click', event)}
|
||||
on:pane:contextmenu={(event) => { console.log('on pane contextmenu', event); }}
|
||||
>
|
||||
<Controls />
|
||||
<Background variant={BackgroundVariant.Dots} />
|
||||
<Minimap />
|
||||
@@ -134,7 +151,7 @@
|
||||
--node-width: 50;
|
||||
}
|
||||
|
||||
:global(.react-flow .custom-style) {
|
||||
:global(.svelte-flow .custom-style) {
|
||||
background: #ff5050;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"sourceMap": true,
|
||||
"strict": true
|
||||
}
|
||||
|
||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
||||
//
|
||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||
|
||||
Reference in New Issue
Block a user