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