Merge branch 'next' into refactor/edge-rendering
This commit is contained in:
@@ -1,11 +1,27 @@
|
||||
export default function (node: Element, target = 'body') {
|
||||
const targetEl = document.querySelector(target);
|
||||
type PortalOptions = {
|
||||
target?: string;
|
||||
domNode: Element | null;
|
||||
};
|
||||
|
||||
function tryToMount(node: Element, domNode: Element | null, target: string | undefined) {
|
||||
if (!domNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetEl = target ? domNode.querySelector(target) : domNode;
|
||||
|
||||
if (targetEl) {
|
||||
targetEl.appendChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
export default function (node: Element, { target, domNode }: PortalOptions) {
|
||||
tryToMount(node, domNode, target);
|
||||
|
||||
return {
|
||||
async update({ target, domNode }: PortalOptions) {
|
||||
tryToMount(node, domNode, target);
|
||||
},
|
||||
destroy() {
|
||||
if (node.parentNode) {
|
||||
node.parentNode.removeChild(node);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<script lang="ts">
|
||||
import portal from '$lib/actions/portal';
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
type $$Props = {};
|
||||
|
||||
const { domNode } = useStore();
|
||||
</script>
|
||||
|
||||
<div use:portal={'.svelte-flow__edgelabel-renderer'}>
|
||||
<div use:portal={{ target: '.svelte-flow__edgelabel-renderer', domNode: $domNode }}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { errorMessages, getMarkerId } from '@xyflow/system';
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
|
||||
import { BezierEdgeInternal } from '$lib/components/edges';
|
||||
import type { EdgeLayouted, Edge } from '$lib/types';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
edgecontextmenu: { edge: Edge; event: MouseEvent };
|
||||
}>();
|
||||
|
||||
$: edgeComponent = $edgeTypes[type!] || BezierEdge;
|
||||
$: edgeComponent = $edgeTypes[type!] || BezierEdgeInternal;
|
||||
$: markerStartUrl = markerStart ? `url(#${getMarkerId(markerStart, $flowId)})` : undefined;
|
||||
$: markerEndUrl = markerEnd ? `url(#${getMarkerId(markerEnd, $flowId)})` : undefined;
|
||||
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
<script lang="ts">
|
||||
import { getContext, createEventDispatcher } from 'svelte';
|
||||
import { getContext } from 'svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import cc from 'classcat';
|
||||
import {
|
||||
Position,
|
||||
XYHandle,
|
||||
isMouseEvent,
|
||||
type Connection,
|
||||
type HandleType
|
||||
areConnectionMapsEqual,
|
||||
handleConnectionChange
|
||||
} from '@xyflow/system';
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
import type { HandleComponentProps } from '$lib/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
type $$Props = HandleComponentProps;
|
||||
|
||||
@@ -20,6 +21,8 @@
|
||||
export let position: $$Props['position'] = Position.Top;
|
||||
export let style: $$Props['style'] = undefined;
|
||||
export let isConnectable: $$Props['isConnectable'] = undefined;
|
||||
export let onconnect: $$Props['onconnect'] = undefined;
|
||||
export let ondisconnect: $$Props['ondisconnect'] = undefined;
|
||||
// export let isConnectableStart: $$Props['isConnectableStart'] = undefined;
|
||||
// export let isConnectableEnd: $$Props['isConnectableEnd'] = undefined;
|
||||
|
||||
@@ -32,18 +35,6 @@
|
||||
$: handleConnectable = isConnectable !== undefined ? isConnectable : $connectable;
|
||||
|
||||
const handleId = id || null;
|
||||
const dispatch = createEventDispatcher<{
|
||||
connect: { connection: Connection };
|
||||
connectstart: {
|
||||
event: MouseEvent | TouchEvent;
|
||||
nodeId: string | null;
|
||||
handleId: string | null;
|
||||
handleType: HandleType | null;
|
||||
};
|
||||
connectend: {
|
||||
event: MouseEvent | TouchEvent;
|
||||
};
|
||||
}>();
|
||||
|
||||
const store = useStore();
|
||||
const {
|
||||
@@ -55,10 +46,16 @@
|
||||
isValidConnection,
|
||||
lib,
|
||||
addEdge,
|
||||
onedgecreate,
|
||||
panBy,
|
||||
cancelConnection,
|
||||
updateConnection,
|
||||
autoPanOnConnect
|
||||
autoPanOnConnect,
|
||||
edges,
|
||||
connectionLookup,
|
||||
onconnect: onConnectAction,
|
||||
onconnectstart: onConnectStartAction,
|
||||
onconnectend: onConnectEndAction
|
||||
} = store;
|
||||
|
||||
function onPointerDown(event: MouseEvent | TouchEvent) {
|
||||
@@ -80,28 +77,50 @@
|
||||
cancelConnection,
|
||||
panBy,
|
||||
onConnect: (connection) => {
|
||||
addEdge(connection);
|
||||
const edge = $onedgecreate ? $onedgecreate(connection) : connection;
|
||||
|
||||
// @todo: should we change/ improve the stuff we are passing here?
|
||||
// instead of source/target we could pass fromNodeId, fromHandleId, etc
|
||||
dispatch('connect', { connection });
|
||||
if (!edge) {
|
||||
return;
|
||||
}
|
||||
|
||||
addEdge(edge);
|
||||
$onConnectAction?.(connection);
|
||||
},
|
||||
onConnectStart: (event, startParams) => {
|
||||
dispatch('connectstart', {
|
||||
event,
|
||||
$onConnectStartAction?.(event, {
|
||||
nodeId: startParams.nodeId,
|
||||
handleId: startParams.handleId,
|
||||
handleType: startParams.handleType
|
||||
});
|
||||
},
|
||||
onConnectEnd: (event) => {
|
||||
dispatch('connectend', { event });
|
||||
$onConnectEndAction?.(event);
|
||||
},
|
||||
getTransform: () => [$viewport.x, $viewport.y, $viewport.zoom]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let prevConnections: Map<string, Connection> | null = null;
|
||||
let connections: Map<string, Connection> | undefined;
|
||||
|
||||
$: if (onconnect || ondisconnect) {
|
||||
// connectionLookup is not reactive, so we use edges to get notified about updates
|
||||
$edges;
|
||||
connections = $connectionLookup.get(`${nodeId}-${type}-${id || null}`);
|
||||
}
|
||||
|
||||
$: {
|
||||
if (prevConnections && !areConnectionMapsEqual(connections, prevConnections)) {
|
||||
const _connections = connections ?? new Map();
|
||||
|
||||
handleConnectionChange(prevConnections, _connections, ondisconnect);
|
||||
handleConnectionChange(_connections, prevConnections, onconnect);
|
||||
}
|
||||
|
||||
prevConnections = connections ?? new Map();
|
||||
}
|
||||
|
||||
// @todo implement connectablestart, connectableend
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { shortcut, type ShortcutModifierDefinition } from '@svelte-put/shortcut';
|
||||
import { isInputDOMNode, isMacOs } from '@xyflow/system';
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
import type { KeyHandlerProps } from './types';
|
||||
import type { KeyDefinition, KeyDefinitionObject } from '$lib/types';
|
||||
import { isMacOs } from '@xyflow/system';
|
||||
|
||||
type $$Props = KeyHandlerProps;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
} = useStore();
|
||||
|
||||
function isKeyObject(key?: KeyDefinition | null): key is KeyDefinitionObject {
|
||||
return typeof key === 'object';
|
||||
return key !== null && typeof key === 'object';
|
||||
}
|
||||
|
||||
function getModifier(key?: KeyDefinition | null): ShortcutModifierDefinition {
|
||||
@@ -71,16 +71,19 @@
|
||||
trigger: [
|
||||
{
|
||||
...selectionKeyDefinition,
|
||||
callback: () => selectionKeyDefinition.key && selectionKeyPressed.set(true)
|
||||
enabled: selectionKeyDefinition.key !== null,
|
||||
callback: () => selectionKeyPressed.set(true)
|
||||
}
|
||||
],
|
||||
|
||||
type: 'keydown'
|
||||
}}
|
||||
use:shortcut={{
|
||||
trigger: [
|
||||
{
|
||||
...selectionKeyDefinition,
|
||||
callback: () => selectionKeyDefinition.key && selectionKeyPressed.set(false)
|
||||
enabled: selectionKeyDefinition.key !== null,
|
||||
callback: () => selectionKeyPressed.set(false)
|
||||
}
|
||||
],
|
||||
type: 'keyup'
|
||||
@@ -89,7 +92,8 @@
|
||||
trigger: [
|
||||
{
|
||||
...multiSelectionKeyDefinition,
|
||||
callback: () => multiSelectionKeyDefinition.key && multiselectionKeyPressed.set(true)
|
||||
enabled: multiSelectionKeyDefinition.key !== null,
|
||||
callback: () => multiselectionKeyPressed.set(true)
|
||||
}
|
||||
],
|
||||
type: 'keydown'
|
||||
@@ -98,7 +102,8 @@
|
||||
trigger: [
|
||||
{
|
||||
...multiSelectionKeyDefinition,
|
||||
callback: () => multiSelectionKeyDefinition.key && multiselectionKeyPressed.set(false)
|
||||
enabled: multiSelectionKeyDefinition.key !== null,
|
||||
callback: () => multiselectionKeyPressed.set(false)
|
||||
}
|
||||
],
|
||||
type: 'keyup'
|
||||
@@ -107,7 +112,8 @@
|
||||
trigger: [
|
||||
{
|
||||
...deleteKeyDefinition,
|
||||
callback: () => deleteKeyDefinition.key && deleteKeyPressed.set(true)
|
||||
enabled: deleteKeyDefinition.key !== null,
|
||||
callback: (detail) => !isInputDOMNode(detail.originalEvent) && deleteKeyPressed.set(true)
|
||||
}
|
||||
],
|
||||
type: 'keydown'
|
||||
@@ -116,7 +122,8 @@
|
||||
trigger: [
|
||||
{
|
||||
...deleteKeyDefinition,
|
||||
callback: () => deleteKeyDefinition.key && deleteKeyPressed.set(false)
|
||||
enabled: deleteKeyDefinition.key !== null,
|
||||
callback: () => deleteKeyPressed.set(false)
|
||||
}
|
||||
],
|
||||
type: 'keyup'
|
||||
@@ -125,7 +132,8 @@
|
||||
trigger: [
|
||||
{
|
||||
...panActivationKeyDefinition,
|
||||
callback: () => panActivationKeyDefinition.key && panActivationKeyPressed.set(true)
|
||||
enabled: panActivationKeyDefinition.key !== null,
|
||||
callback: () => panActivationKeyPressed.set(true)
|
||||
}
|
||||
],
|
||||
type: 'keydown'
|
||||
@@ -134,7 +142,8 @@
|
||||
trigger: [
|
||||
{
|
||||
...panActivationKeyDefinition,
|
||||
callback: () => panActivationKeyDefinition.key && panActivationKeyPressed.set(false)
|
||||
enabled: panActivationKeyDefinition.key !== null,
|
||||
callback: () => panActivationKeyPressed.set(false)
|
||||
}
|
||||
],
|
||||
type: 'keyup'
|
||||
@@ -143,7 +152,8 @@
|
||||
trigger: [
|
||||
{
|
||||
...zoomActivationKeyDefinition,
|
||||
callback: () => zoomActivationKeyDefinition.key && zoomActivationKeyPressed.set(true)
|
||||
enabled: zoomActivationKeyDefinition.key !== null,
|
||||
callback: () => zoomActivationKeyPressed.set(true)
|
||||
}
|
||||
],
|
||||
type: 'keydown'
|
||||
@@ -152,7 +162,8 @@
|
||||
trigger: [
|
||||
{
|
||||
...zoomActivationKeyDefinition,
|
||||
callback: () => zoomActivationKeyDefinition.key && zoomActivationKeyPressed.set(false)
|
||||
enabled: zoomActivationKeyDefinition.key !== null,
|
||||
callback: () => zoomActivationKeyPressed.set(false)
|
||||
}
|
||||
],
|
||||
type: 'keyup'
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { getNodesBounds } from '@xyflow/system';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
import { Selection } from '$lib/components/Selection';
|
||||
import drag from '$lib/actions/drag';
|
||||
import type { Node } from '$lib/types';
|
||||
|
||||
const store = useStore();
|
||||
const { selectionRectMode, nodes } = store;
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
selectioncontextmenu: { nodes: Node[]; event: MouseEvent | TouchEvent };
|
||||
selectionclick: { nodes: Node[]; event: MouseEvent | TouchEvent };
|
||||
}>();
|
||||
|
||||
$: selectedNodes = $nodes.filter((n) => n.selected);
|
||||
$: bounds = getNodesBounds(selectedNodes);
|
||||
|
||||
function onContextMenu(event: MouseEvent | TouchEvent) {
|
||||
dispatch('selectioncontextmenu', { nodes: selectedNodes, event });
|
||||
}
|
||||
|
||||
function onClick(event: MouseEvent | TouchEvent) {
|
||||
dispatch('selectionclick', { nodes: selectedNodes, event });
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if selectedNodes && $selectionRectMode === 'nodes'}
|
||||
@@ -17,6 +32,8 @@
|
||||
class="selection-wrapper nopan"
|
||||
style="width: {bounds.width}px; height: {bounds.height}px; transform: translate({bounds.x}px, {bounds.y}px)"
|
||||
use:drag={{ disabled: false, store }}
|
||||
on:contextmenu={onContextMenu}
|
||||
on:click={onClick}
|
||||
>
|
||||
<Selection width="100%" height="100%" x={0} y={0} />
|
||||
</div>
|
||||
|
||||
@@ -27,11 +27,15 @@
|
||||
export let style: NodeWrapperProps['style'] = undefined;
|
||||
export let type: NodeWrapperProps['type'] = 'default';
|
||||
export let isParent: NodeWrapperProps['isParent'] = false;
|
||||
export let positionAbsolute: NodeWrapperProps['positionAbsolute'] = undefined;
|
||||
export let positionOrigin: NodeWrapperProps['positionOrigin'] = undefined;
|
||||
export let positionX: NodeWrapperProps['positionX'];
|
||||
export let positionY: NodeWrapperProps['positionY'];
|
||||
export let positionOriginX: NodeWrapperProps['positionOriginX'];
|
||||
export let positionOriginY: NodeWrapperProps['positionOriginY'];
|
||||
export let sourcePosition: NodeWrapperProps['sourcePosition'] = undefined;
|
||||
export let targetPosition: NodeWrapperProps['targetPosition'] = undefined;
|
||||
export let zIndex: NodeWrapperProps['zIndex'];
|
||||
export let width: NodeWrapperProps['width'] = undefined;
|
||||
export let height: NodeWrapperProps['height'] = undefined;
|
||||
export let dragHandle: NodeWrapperProps['dragHandle'] = undefined;
|
||||
export let initialized: NodeWrapperProps['initialized'] = false;
|
||||
let className: string = '';
|
||||
@@ -164,11 +168,11 @@
|
||||
class:nopan={draggable}
|
||||
class:parent={isParent}
|
||||
style:z-index={zIndex}
|
||||
style:transform="translate({positionOrigin?.x ?? 0}px, {positionOrigin?.y ?? 0}px)"
|
||||
style:transform="translate({positionOriginX}px, {positionOriginY}px)"
|
||||
style:visibility={initialized ? 'visible' : 'hidden'}
|
||||
style="{style} {node.size?.width ? `;width=${node.size?.width}px` : ''} {node.size?.height
|
||||
? `;height=${node.size?.height}px;`
|
||||
: ''}"
|
||||
style="{style ?? ''}; {!width ? '' : `width:${width}px;`} {!height
|
||||
? ''
|
||||
: `height:${height}px;`}"
|
||||
on:click={onSelectNodeHandler}
|
||||
on:mouseenter={(event) => dispatch('nodemouseenter', { node, event })}
|
||||
on:mouseleave={(event) => dispatch('nodemouseleave', { node, event })}
|
||||
@@ -187,11 +191,10 @@
|
||||
{dragging}
|
||||
{dragHandle}
|
||||
isConnectable={connectable}
|
||||
xPos={positionAbsolute?.x ?? 0}
|
||||
yPos={positionAbsolute?.y ?? 0}
|
||||
on:connectstart
|
||||
on:connect
|
||||
on:connectend
|
||||
positionAbsoluteX={positionX}
|
||||
positionAbsoluteY={positionY}
|
||||
{width}
|
||||
{height}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { XYPosition } from '@xyflow/system';
|
||||
import type { Node } from '$lib/types';
|
||||
|
||||
export type NodeWrapperProps = Pick<
|
||||
@@ -9,19 +8,22 @@ export type NodeWrapperProps = Pick<
|
||||
| 'data'
|
||||
| 'draggable'
|
||||
| 'dragging'
|
||||
| 'positionAbsolute'
|
||||
| 'selected'
|
||||
| 'selectable'
|
||||
| 'style'
|
||||
| 'type'
|
||||
| 'width'
|
||||
| 'height'
|
||||
| 'sourcePosition'
|
||||
| 'targetPosition'
|
||||
| 'dragHandle'
|
||||
| 'hidden'
|
||||
> & {
|
||||
positionOrigin?: XYPosition;
|
||||
width?: number;
|
||||
height?: number;
|
||||
type: string;
|
||||
positionX: number;
|
||||
positionY: number;
|
||||
positionOriginX: number;
|
||||
positionOriginY: number;
|
||||
'on:nodeclick'?: (event: MouseEvent) => void;
|
||||
resizeObserver?: ResizeObserver | null;
|
||||
isParent?: boolean;
|
||||
|
||||
@@ -1,30 +1,48 @@
|
||||
<script lang="ts">
|
||||
import { getBezierPath } from '@xyflow/system';
|
||||
|
||||
import type { EdgeProps } from '$lib/types';
|
||||
import type { BezierEdgeProps } from '$lib/types';
|
||||
import { BaseEdge } from '$lib/components/BaseEdge';
|
||||
|
||||
type $$Props = EdgeProps;
|
||||
type $$Props = BezierEdgeProps;
|
||||
|
||||
export let id: $$Props['id'] = undefined;
|
||||
export let label: $$Props['label'] = undefined;
|
||||
export let labelStyle: $$Props['labelStyle'] = undefined;
|
||||
export let style: $$Props['style'] = undefined;
|
||||
export let markerStart: $$Props['markerStart'] = undefined;
|
||||
export let markerEnd: $$Props['markerEnd'] = undefined;
|
||||
export let pathOptions: $$Props['pathOptions'] = undefined;
|
||||
export let interactionWidth: $$Props['interactionWidth'] = undefined;
|
||||
|
||||
export let sourceX: $$Props['sourceX'];
|
||||
export let sourceY: $$Props['sourceY'];
|
||||
export let sourcePosition: $$Props['sourcePosition'];
|
||||
|
||||
export let targetX: $$Props['targetX'];
|
||||
export let targetY: $$Props['targetY'];
|
||||
export let targetPosition: $$Props['targetPosition'];
|
||||
|
||||
$: [path, labelX, labelY] = getBezierPath({
|
||||
sourceX: $$props.sourceX,
|
||||
sourceY: $$props.sourceY,
|
||||
targetX: $$props.targetX,
|
||||
targetY: $$props.targetY,
|
||||
sourcePosition: $$props.sourcePosition,
|
||||
targetPosition: $$props.targetPosition
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
curvature: pathOptions?.curvature
|
||||
});
|
||||
</script>
|
||||
|
||||
<BaseEdge
|
||||
{id}
|
||||
{path}
|
||||
{labelX}
|
||||
{labelY}
|
||||
id={$$props.id}
|
||||
label={$$props.label}
|
||||
labelStyle={$$props.labelStyle}
|
||||
markerStart={$$props.markerStart}
|
||||
markerEnd={$$props.markerEnd}
|
||||
interactionWidth={$$props.interactionWidth}
|
||||
style={$$props.style}
|
||||
{label}
|
||||
{labelStyle}
|
||||
{markerStart}
|
||||
{markerEnd}
|
||||
{interactionWidth}
|
||||
{style}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<script lang="ts">
|
||||
import { getBezierPath } from '@xyflow/system';
|
||||
|
||||
import type { EdgeProps } from '$lib/types';
|
||||
import { BaseEdge } from '$lib/components/BaseEdge';
|
||||
|
||||
type $$Props = EdgeProps;
|
||||
|
||||
// these props are not used in this edge, but passed to every custom edge component
|
||||
export let id: $$Props['id'] = '';
|
||||
export let source: $$Props['source'] = '';
|
||||
export let target: $$Props['target'] = '';
|
||||
export let animated: $$Props['animated'] = undefined;
|
||||
export let selected: $$Props['selected'] = undefined;
|
||||
export let label: $$Props['label'] = undefined;
|
||||
export let labelStyle: $$Props['labelStyle'] = undefined;
|
||||
export let data: $$Props['data'] = undefined;
|
||||
export let style: $$Props['style'] = undefined;
|
||||
export let markerStart: $$Props['markerStart'] = undefined;
|
||||
export let markerEnd: $$Props['markerEnd'] = undefined;
|
||||
export let interactionWidth: $$Props['interactionWidth'] = undefined;
|
||||
|
||||
export let sourceX: $$Props['sourceX'];
|
||||
export let sourceY: $$Props['sourceY'];
|
||||
export let sourcePosition: $$Props['sourcePosition'];
|
||||
export let sourceHandleId: $$Props['sourceHandleId'] = undefined;
|
||||
|
||||
export let targetX: $$Props['targetX'];
|
||||
export let targetY: $$Props['targetY'];
|
||||
export let targetPosition: $$Props['targetPosition'];
|
||||
export let targetHandleId: $$Props['targetHandleId'] = undefined;
|
||||
|
||||
$: [path, labelX, labelY] = getBezierPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition
|
||||
});
|
||||
|
||||
// hopefully with Svelte5, we don't need this kind of workaround anymore
|
||||
id;
|
||||
source;
|
||||
target;
|
||||
animated;
|
||||
selected;
|
||||
data;
|
||||
sourceHandleId;
|
||||
targetHandleId;
|
||||
</script>
|
||||
|
||||
<BaseEdge
|
||||
{path}
|
||||
{labelX}
|
||||
{labelY}
|
||||
{label}
|
||||
{labelStyle}
|
||||
{markerStart}
|
||||
{markerEnd}
|
||||
{interactionWidth}
|
||||
{style}
|
||||
/>
|
||||
@@ -1,30 +1,49 @@
|
||||
<script lang="ts">
|
||||
import { getSmoothStepPath } from '@xyflow/system';
|
||||
|
||||
import type { EdgeProps } from '$lib/types';
|
||||
import type { SmoothStepEdgeProps } from '$lib/types';
|
||||
import { BaseEdge } from '$lib/components/BaseEdge';
|
||||
|
||||
type $$Props = EdgeProps;
|
||||
type $$Props = SmoothStepEdgeProps;
|
||||
|
||||
export let id: $$Props['id'] = undefined;
|
||||
export let label: $$Props['label'] = undefined;
|
||||
export let labelStyle: $$Props['labelStyle'] = undefined;
|
||||
export let style: $$Props['style'] = undefined;
|
||||
export let markerStart: $$Props['markerStart'] = undefined;
|
||||
export let markerEnd: $$Props['markerEnd'] = undefined;
|
||||
export let pathOptions: $$Props['pathOptions'] = undefined;
|
||||
export let interactionWidth: $$Props['interactionWidth'] = undefined;
|
||||
|
||||
export let sourceX: $$Props['sourceX'];
|
||||
export let sourceY: $$Props['sourceY'];
|
||||
export let sourcePosition: $$Props['sourcePosition'];
|
||||
|
||||
export let targetX: $$Props['targetX'];
|
||||
export let targetY: $$Props['targetY'];
|
||||
export let targetPosition: $$Props['targetPosition'];
|
||||
|
||||
$: [path, labelX, labelY] = getSmoothStepPath({
|
||||
sourceX: $$props.sourceX,
|
||||
sourceY: $$props.sourceY,
|
||||
targetX: $$props.targetX,
|
||||
targetY: $$props.targetY,
|
||||
sourcePosition: $$props.sourcePosition,
|
||||
targetPosition: $$props.targetPosition
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
borderRadius: pathOptions?.borderRadius,
|
||||
offset: pathOptions?.offset
|
||||
});
|
||||
</script>
|
||||
|
||||
<BaseEdge
|
||||
{id}
|
||||
{path}
|
||||
{labelX}
|
||||
{labelY}
|
||||
id={$$props.id}
|
||||
label={$$props.label}
|
||||
labelStyle={$$props.labelStyle}
|
||||
markerStart={$$props.markerStart}
|
||||
markerEnd={$$props.markerEnd}
|
||||
interactionWidth={$$props.interactionWidth}
|
||||
style={$$props.style}
|
||||
{label}
|
||||
{labelStyle}
|
||||
{markerStart}
|
||||
{markerEnd}
|
||||
{interactionWidth}
|
||||
{style}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<script lang="ts">
|
||||
import { getSmoothStepPath } from '@xyflow/system';
|
||||
|
||||
import type { EdgeProps } from '$lib/types';
|
||||
import { BaseEdge } from '$lib/components/BaseEdge';
|
||||
|
||||
type $$Props = EdgeProps;
|
||||
|
||||
// these props are not used in this edge, but passed to every custom edge component
|
||||
export let id: $$Props['id'] = '';
|
||||
export let source: $$Props['source'] = '';
|
||||
export let target: $$Props['target'] = '';
|
||||
|
||||
export let animated: $$Props['animated'] = undefined;
|
||||
export let selected: $$Props['selected'] = undefined;
|
||||
export let label: $$Props['label'] = undefined;
|
||||
export let labelStyle: $$Props['labelStyle'] = undefined;
|
||||
export let data: $$Props['data'] = undefined;
|
||||
export let style: $$Props['style'] = undefined;
|
||||
export let markerStart: $$Props['markerStart'] = undefined;
|
||||
export let markerEnd: $$Props['markerEnd'] = undefined;
|
||||
export let interactionWidth: $$Props['interactionWidth'] = undefined;
|
||||
|
||||
export let sourceX: $$Props['sourceX'];
|
||||
export let sourceY: $$Props['sourceY'];
|
||||
export let sourcePosition: $$Props['sourcePosition'];
|
||||
export let sourceHandleId: $$Props['sourceHandleId'] = undefined;
|
||||
|
||||
export let targetX: $$Props['targetX'];
|
||||
export let targetY: $$Props['targetY'];
|
||||
export let targetPosition: $$Props['targetPosition'];
|
||||
export let targetHandleId: $$Props['targetHandleId'] = undefined;
|
||||
|
||||
$: [path, labelX, labelY] = getSmoothStepPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition
|
||||
});
|
||||
|
||||
id;
|
||||
source;
|
||||
target;
|
||||
animated;
|
||||
selected;
|
||||
data;
|
||||
sourceHandleId;
|
||||
targetHandleId;
|
||||
</script>
|
||||
|
||||
<BaseEdge
|
||||
{path}
|
||||
{labelX}
|
||||
{labelY}
|
||||
{label}
|
||||
{labelStyle}
|
||||
{markerStart}
|
||||
{markerEnd}
|
||||
{interactionWidth}
|
||||
{style}
|
||||
/>
|
||||
@@ -1,31 +1,49 @@
|
||||
<script lang="ts">
|
||||
import { getSmoothStepPath } from '@xyflow/system';
|
||||
|
||||
import type { EdgeProps } from '$lib/types';
|
||||
import type { StepEdgeProps } from '$lib/types';
|
||||
import { BaseEdge } from '$lib/components/BaseEdge';
|
||||
|
||||
type $$Props = EdgeProps;
|
||||
type $$Props = StepEdgeProps;
|
||||
|
||||
export let id: $$Props['id'] = undefined;
|
||||
export let label: $$Props['label'] = undefined;
|
||||
export let labelStyle: $$Props['labelStyle'] = undefined;
|
||||
export let style: $$Props['style'] = undefined;
|
||||
export let markerStart: $$Props['markerStart'] = undefined;
|
||||
export let markerEnd: $$Props['markerEnd'] = undefined;
|
||||
export let pathOptions: $$Props['pathOptions'] = undefined;
|
||||
export let interactionWidth: $$Props['interactionWidth'] = undefined;
|
||||
|
||||
export let sourceX: $$Props['sourceX'];
|
||||
export let sourceY: $$Props['sourceY'];
|
||||
export let sourcePosition: $$Props['sourcePosition'];
|
||||
|
||||
export let targetX: $$Props['targetX'];
|
||||
export let targetY: $$Props['targetY'];
|
||||
export let targetPosition: $$Props['targetPosition'];
|
||||
|
||||
$: [path, labelX, labelY] = getSmoothStepPath({
|
||||
sourceX: $$props.sourceX,
|
||||
sourceY: $$props.sourceY,
|
||||
targetX: $$props.targetX,
|
||||
targetY: $$props.targetY,
|
||||
sourcePosition: $$props.sourcePosition,
|
||||
targetPosition: $$props.targetPosition,
|
||||
borderRadius: 0
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
borderRadius: 0,
|
||||
offset: pathOptions?.offset
|
||||
});
|
||||
</script>
|
||||
|
||||
<BaseEdge
|
||||
{id}
|
||||
{path}
|
||||
{labelX}
|
||||
{labelY}
|
||||
id={$$props.id}
|
||||
label={$$props.label}
|
||||
labelStyle={$$props.labelStyle}
|
||||
markerStart={$$props.markerStart}
|
||||
markerEnd={$$props.markerEnd}
|
||||
interactionWidth={$$props.interactionWidth}
|
||||
style={$$props.style}
|
||||
{label}
|
||||
{labelStyle}
|
||||
{markerStart}
|
||||
{markerEnd}
|
||||
{interactionWidth}
|
||||
{style}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<script lang="ts">
|
||||
import { getSmoothStepPath } from '@xyflow/system';
|
||||
|
||||
import type { EdgeProps } from '$lib/types';
|
||||
import { BaseEdge } from '$lib/components/BaseEdge';
|
||||
|
||||
type $$Props = EdgeProps;
|
||||
|
||||
// these props are not used in this edge, but passed to every custom edge component
|
||||
export let id: $$Props['id'] = '';
|
||||
export let source: $$Props['source'] = '';
|
||||
export let target: $$Props['target'] = '';
|
||||
|
||||
export let animated: $$Props['animated'] = undefined;
|
||||
export let selected: $$Props['selected'] = undefined;
|
||||
export let label: $$Props['label'] = undefined;
|
||||
export let data: $$Props['data'] = undefined;
|
||||
export let labelStyle: $$Props['labelStyle'] = undefined;
|
||||
export let style: $$Props['style'] = undefined;
|
||||
export let markerStart: $$Props['markerStart'] = undefined;
|
||||
export let markerEnd: $$Props['markerEnd'] = undefined;
|
||||
export let interactionWidth: $$Props['interactionWidth'] = undefined;
|
||||
|
||||
export let sourceX: $$Props['sourceX'];
|
||||
export let sourceY: $$Props['sourceY'];
|
||||
export let sourcePosition: $$Props['sourcePosition'];
|
||||
export let sourceHandleId: $$Props['sourceHandleId'] = undefined;
|
||||
|
||||
export let targetX: $$Props['targetX'];
|
||||
export let targetY: $$Props['targetY'];
|
||||
export let targetPosition: $$Props['targetPosition'];
|
||||
export let targetHandleId: $$Props['targetHandleId'] = undefined;
|
||||
|
||||
$: [path, labelX, labelY] = getSmoothStepPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
borderRadius: 0
|
||||
});
|
||||
|
||||
id;
|
||||
source;
|
||||
target;
|
||||
animated;
|
||||
selected;
|
||||
data;
|
||||
sourceHandleId;
|
||||
targetHandleId;
|
||||
</script>
|
||||
|
||||
<BaseEdge
|
||||
{path}
|
||||
{labelX}
|
||||
{labelY}
|
||||
{label}
|
||||
{labelStyle}
|
||||
{markerStart}
|
||||
{markerEnd}
|
||||
{interactionWidth}
|
||||
{style}
|
||||
/>
|
||||
@@ -1,28 +1,42 @@
|
||||
<script lang="ts">
|
||||
import { getStraightPath } from '@xyflow/system';
|
||||
|
||||
import type { EdgeProps } from '$lib/types';
|
||||
import type { StraightEdgeProps } from '$lib/types';
|
||||
import { BaseEdge } from '$lib/components/BaseEdge';
|
||||
|
||||
type $$Props = EdgeProps;
|
||||
type $$Props = StraightEdgeProps;
|
||||
|
||||
export let id: $$Props['id'] = undefined;
|
||||
export let label: $$Props['label'] = undefined;
|
||||
export let labelStyle: $$Props['labelStyle'] = undefined;
|
||||
export let style: $$Props['style'] = undefined;
|
||||
export let markerStart: $$Props['markerStart'] = undefined;
|
||||
export let markerEnd: $$Props['markerEnd'] = undefined;
|
||||
export let interactionWidth: $$Props['interactionWidth'] = undefined;
|
||||
|
||||
export let sourceX: $$Props['sourceX'];
|
||||
export let sourceY: $$Props['sourceY'];
|
||||
|
||||
export let targetX: $$Props['targetX'];
|
||||
export let targetY: $$Props['targetY'];
|
||||
|
||||
$: [path, labelX, labelY] = getStraightPath({
|
||||
sourceX: $$props.sourceX,
|
||||
sourceY: $$props.sourceY,
|
||||
targetX: $$props.targetX,
|
||||
targetY: $$props.targetY
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY
|
||||
});
|
||||
</script>
|
||||
|
||||
<BaseEdge
|
||||
{id}
|
||||
{path}
|
||||
{labelX}
|
||||
{labelY}
|
||||
id={$$props.id}
|
||||
label={$$props.label}
|
||||
labelStyle={$$props.labelStyle}
|
||||
markerStart={$$props.markerStart}
|
||||
markerEnd={$$props.markerEnd}
|
||||
interactionWidth={$$props.interactionWidth}
|
||||
style={$$props.style}
|
||||
{label}
|
||||
{labelStyle}
|
||||
{markerStart}
|
||||
{markerEnd}
|
||||
{interactionWidth}
|
||||
{style}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<script lang="ts">
|
||||
import { getStraightPath } from '@xyflow/system';
|
||||
|
||||
import type { EdgeProps } from '$lib/types';
|
||||
import { BaseEdge } from '$lib/components/BaseEdge';
|
||||
|
||||
type $$Props = EdgeProps;
|
||||
|
||||
// these props are not used in this edge, but passed to every custom edge component
|
||||
export let id: $$Props['id'] = '';
|
||||
export let source: $$Props['source'] = '';
|
||||
export let target: $$Props['target'] = '';
|
||||
|
||||
export let animated: $$Props['animated'] = undefined;
|
||||
export let selected: $$Props['selected'] = undefined;
|
||||
export let label: $$Props['label'] = undefined;
|
||||
export let labelStyle: $$Props['labelStyle'] = undefined;
|
||||
export let data: $$Props['data'] = undefined;
|
||||
export let style: $$Props['style'] = undefined;
|
||||
export let markerStart: $$Props['markerStart'] = undefined;
|
||||
export let markerEnd: $$Props['markerEnd'] = undefined;
|
||||
export let interactionWidth: $$Props['interactionWidth'] = undefined;
|
||||
|
||||
export let sourceX: $$Props['sourceX'];
|
||||
export let sourceY: $$Props['sourceY'];
|
||||
export let sourcePosition: $$Props['sourcePosition'];
|
||||
export let sourceHandleId: $$Props['sourceHandleId'] = undefined;
|
||||
|
||||
export let targetX: $$Props['targetX'];
|
||||
export let targetY: $$Props['targetY'];
|
||||
export let targetPosition: $$Props['targetPosition'];
|
||||
export let targetHandleId: $$Props['targetHandleId'] = undefined;
|
||||
|
||||
$: [path, labelX, labelY] = getStraightPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY
|
||||
});
|
||||
|
||||
id;
|
||||
source;
|
||||
target;
|
||||
animated;
|
||||
selected;
|
||||
data;
|
||||
sourcePosition;
|
||||
targetPosition;
|
||||
sourceHandleId;
|
||||
targetHandleId;
|
||||
</script>
|
||||
|
||||
<BaseEdge
|
||||
{path}
|
||||
{labelX}
|
||||
{labelY}
|
||||
{label}
|
||||
{labelStyle}
|
||||
{markerStart}
|
||||
{markerEnd}
|
||||
{interactionWidth}
|
||||
{style}
|
||||
/>
|
||||
@@ -1,4 +1,17 @@
|
||||
// We distinguish between internal and exported edges
|
||||
// The internal edges are used directly like custom edges and always get an id, source and target props
|
||||
// If you import an edge from the library, the id is optional and source and target are not used at all
|
||||
|
||||
// @todo: how can we prevent this duplication in ...Edge/ ...EdgeInternal?
|
||||
// both are quite similar, it's just about 1-2 props that are different
|
||||
export { default as BezierEdge } from './BezierEdge.svelte';
|
||||
export { default as StraightEdge } from './StraightEdge.svelte';
|
||||
export { default as BezierEdgeInternal } from './BezierEdgeInternal.svelte';
|
||||
|
||||
export { default as SmoothStepEdge } from './SmoothStepEdge.svelte';
|
||||
export { default as SmoothStepEdgeInternal } from './SmoothStepEdgeInternal.svelte';
|
||||
|
||||
export { default as StraightEdge } from './StraightEdge.svelte';
|
||||
export { default as StraightEdgeInternal } from './StraightEdgeInternal.svelte';
|
||||
|
||||
export { default as StepEdge } from './StepEdge.svelte';
|
||||
export { default as StepEdgeInternal } from './StepEdgeInternal.svelte';
|
||||
|
||||
@@ -11,27 +11,31 @@
|
||||
|
||||
// unused props - we need to list them here in order to prevent warnings
|
||||
export let id: $$Props['id'] = '';
|
||||
export let width: $$Props['width'] = undefined;
|
||||
export let height: $$Props['height'] = undefined;
|
||||
export let selected: $$Props['selected'] = undefined;
|
||||
export let type: $$Props['type'] = undefined;
|
||||
export let zIndex: $$Props['zIndex'] = undefined;
|
||||
export let dragging: $$Props['dragging'] = false;
|
||||
export let dragHandle: $$Props['dragHandle'] = undefined;
|
||||
export let xPos: $$Props['xPos'] = 0;
|
||||
export let yPos: $$Props['yPos'] = 0;
|
||||
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
|
||||
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
|
||||
export let isConnectable: $$Props['isConnectable'] = undefined;
|
||||
|
||||
// @todo: there must be a better way to do this
|
||||
id;
|
||||
width;
|
||||
height;
|
||||
selected;
|
||||
type;
|
||||
zIndex;
|
||||
dragging;
|
||||
dragHandle;
|
||||
xPos;
|
||||
yPos;
|
||||
positionAbsoluteX;
|
||||
positionAbsoluteY;
|
||||
isConnectable;
|
||||
</script>
|
||||
|
||||
<Handle type="target" position={targetPosition} on:connectstart on:connect on:connectend />
|
||||
<Handle type="target" position={targetPosition} />
|
||||
{data?.label}
|
||||
<Handle type="source" position={sourcePosition} on:connectstart on:connect on:connectend />
|
||||
<Handle type="source" position={sourcePosition} />
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
// unused props - we need to list them here in order to prevent warnings
|
||||
export let id: $$Props['id'] = '';
|
||||
export let width: $$Props['width'] = undefined;
|
||||
export let height: $$Props['height'] = undefined;
|
||||
export let data: $$Props['data'] = {};
|
||||
export let selected: $$Props['selected'] = undefined;
|
||||
export let sourcePosition: $$Props['sourcePosition'] = undefined;
|
||||
@@ -13,12 +15,14 @@
|
||||
export let zIndex: $$Props['zIndex'] = undefined;
|
||||
export let dragging: $$Props['dragging'] = false;
|
||||
export let dragHandle: $$Props['dragHandle'] = undefined;
|
||||
export let xPos: $$Props['xPos'] = 0;
|
||||
export let yPos: $$Props['yPos'] = 0;
|
||||
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
|
||||
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
|
||||
export let isConnectable: $$Props['isConnectable'] = undefined;
|
||||
|
||||
// @todo: there must be a better way to do this
|
||||
id;
|
||||
width;
|
||||
height;
|
||||
data;
|
||||
selected;
|
||||
sourcePosition;
|
||||
@@ -27,7 +31,7 @@
|
||||
zIndex;
|
||||
dragging;
|
||||
dragHandle;
|
||||
xPos;
|
||||
yPos;
|
||||
positionAbsoluteX;
|
||||
positionAbsoluteY;
|
||||
isConnectable;
|
||||
</script>
|
||||
|
||||
@@ -10,28 +10,32 @@
|
||||
|
||||
// unused props - we need to list them here in order to prevent warnings
|
||||
export let id: $$Props['id'] = '';
|
||||
export let width: $$Props['width'] = undefined;
|
||||
export let height: $$Props['height'] = undefined;
|
||||
export let selected: $$Props['selected'] = undefined;
|
||||
export let targetPosition: $$Props['targetPosition'] = undefined;
|
||||
export let type: $$Props['type'] = undefined;
|
||||
export let zIndex: $$Props['zIndex'] = undefined;
|
||||
export let dragging: $$Props['dragging'] = false;
|
||||
export let dragHandle: $$Props['dragHandle'] = undefined;
|
||||
export let xPos: $$Props['xPos'] = 0;
|
||||
export let yPos: $$Props['yPos'] = 0;
|
||||
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
|
||||
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
|
||||
export let isConnectable: $$Props['isConnectable'] = undefined;
|
||||
|
||||
// @todo: there must be a better way to do this
|
||||
id;
|
||||
width;
|
||||
height;
|
||||
selected;
|
||||
targetPosition;
|
||||
type;
|
||||
zIndex;
|
||||
dragging;
|
||||
dragHandle;
|
||||
xPos;
|
||||
yPos;
|
||||
positionAbsoluteX;
|
||||
positionAbsoluteY;
|
||||
isConnectable;
|
||||
</script>
|
||||
|
||||
{data?.label}
|
||||
<Handle type="source" position={sourcePosition} on:connectstart on:connect on:connectend />
|
||||
<Handle type="source" position={sourcePosition} />
|
||||
|
||||
@@ -10,28 +10,32 @@
|
||||
|
||||
// unused props - we need to list them here in order to prevent warnings
|
||||
export let id: $$Props['id'] = '';
|
||||
export let width: $$Props['width'] = undefined;
|
||||
export let height: $$Props['height'] = undefined;
|
||||
export let selected: $$Props['selected'] = undefined;
|
||||
export let sourcePosition: $$Props['sourcePosition'] = undefined;
|
||||
export let type: $$Props['type'] = undefined;
|
||||
export let zIndex: $$Props['zIndex'] = undefined;
|
||||
export let dragging: $$Props['dragging'] = false;
|
||||
export let dragHandle: $$Props['dragHandle'] = undefined;
|
||||
export let xPos: $$Props['xPos'] = 0;
|
||||
export let yPos: $$Props['yPos'] = 0;
|
||||
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
|
||||
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
|
||||
export let isConnectable: $$Props['isConnectable'] = undefined;
|
||||
|
||||
// @todo: there must be a better way to do this
|
||||
id;
|
||||
width;
|
||||
height;
|
||||
selected;
|
||||
sourcePosition;
|
||||
type;
|
||||
zIndex;
|
||||
dragging;
|
||||
dragHandle;
|
||||
xPos;
|
||||
yPos;
|
||||
positionAbsoluteX;
|
||||
positionAbsoluteY;
|
||||
isConnectable;
|
||||
</script>
|
||||
|
||||
{data?.label}
|
||||
<Handle type="target" position={targetPosition} on:connectstart on:connect on:connectend />
|
||||
<Handle type="target" position={targetPosition} />
|
||||
|
||||
@@ -40,10 +40,10 @@
|
||||
<div class="svelte-flow__nodes">
|
||||
{#each $visibleNodes as node (node.id)}
|
||||
{@const posOrigin = getPositionWithOrigin({
|
||||
x: node.positionAbsolute?.x ?? 0,
|
||||
y: node.positionAbsolute?.y ?? 0,
|
||||
width: (node.size?.width || node.width) ?? 0,
|
||||
height: (node.size?.height || node.height) ?? 0,
|
||||
x: node.computed?.positionAbsolute?.x ?? 0,
|
||||
y: node.computed?.positionAbsolute?.y ?? 0,
|
||||
width: node.computed?.width ?? node.width ?? 0,
|
||||
height: node.computed?.height ?? node.height ?? 0,
|
||||
origin: node.origin
|
||||
})}
|
||||
<NodeWrapper
|
||||
@@ -61,26 +61,28 @@
|
||||
node.connectable ||
|
||||
($nodesConnectable && typeof node.connectable === 'undefined')
|
||||
)}
|
||||
positionAbsolute={node.positionAbsolute}
|
||||
positionOrigin={posOrigin}
|
||||
positionX={node.computed?.positionAbsolute?.x ?? 0}
|
||||
positionY={node.computed?.positionAbsolute?.y ?? 0}
|
||||
positionOriginX={posOrigin.x ?? 0}
|
||||
positionOriginY={posOrigin.y ?? 0}
|
||||
isParent={!!node[internalsSymbol]?.isParent}
|
||||
style={node.style}
|
||||
class={node.class}
|
||||
type={node.type}
|
||||
type={node.type ?? 'default'}
|
||||
sourcePosition={node.sourcePosition}
|
||||
targetPosition={node.targetPosition}
|
||||
dragging={node.dragging}
|
||||
zIndex={node[internalsSymbol]?.z ?? 0}
|
||||
dragHandle={node.dragHandle}
|
||||
initialized={(!!node.width && !!node.height) || (!!node.size?.width && !!node.size?.height)}
|
||||
width={node.width ?? undefined}
|
||||
height={node.height ?? undefined}
|
||||
initialized={(!!node.computed?.width && !!node.computed?.height) ||
|
||||
(!!node.width && !!node.height)}
|
||||
{resizeObserver}
|
||||
on:nodeclick
|
||||
on:nodemouseenter
|
||||
on:nodemousemove
|
||||
on:nodemouseleave
|
||||
on:connectstart
|
||||
on:connect
|
||||
on:connectend
|
||||
on:nodedrag
|
||||
on:nodedragstart
|
||||
on:nodedragstop
|
||||
|
||||
@@ -15,8 +15,14 @@
|
||||
import { Attribution } from '$lib/components/Attribution';
|
||||
import { key, useStore, createStoreContext } from '$lib/store';
|
||||
import type { SvelteFlowProps } from './types';
|
||||
import { updateStore, updateStoreByKeys, type UpdatableStoreProps } from './utils';
|
||||
import {
|
||||
updateStore,
|
||||
updateStoreByKeys,
|
||||
type UpdatableStoreProps,
|
||||
getColorModeClass
|
||||
} from './utils';
|
||||
import { get } from 'svelte/store';
|
||||
import { useColorModeClass } from '$lib/hooks/useColorModeClass';
|
||||
|
||||
type $$Props = SvelteFlowProps;
|
||||
|
||||
@@ -65,11 +71,16 @@
|
||||
export let autoPanOnNodeDrag: $$Props['autoPanOnNodeDrag'] = true;
|
||||
export let onerror: $$Props['onerror'] = undefined;
|
||||
export let ondelete: $$Props['ondelete'] = undefined;
|
||||
export let onedgecreate: $$Props['onedgecreate'] = undefined;
|
||||
export let attributionPosition: $$Props['attributionPosition'] = undefined;
|
||||
export let proOptions: $$Props['proOptions'] = undefined;
|
||||
export let defaultEdgeOptions: $$Props['defaultEdgeOptions'] = undefined;
|
||||
export let width: $$Props['width'] = undefined;
|
||||
export let height: $$Props['height'] = undefined;
|
||||
export let colorMode: $$Props['colorMode'] = 'light';
|
||||
export let onconnect: $$Props['onconnect'] = undefined;
|
||||
export let onconnectstart: $$Props['onconnectstart'] = undefined;
|
||||
export let onconnectend: $$Props['onconnectend'] = undefined;
|
||||
|
||||
export let defaultMarkerColor = '#b1b1b7';
|
||||
|
||||
@@ -142,8 +153,12 @@
|
||||
autoPanOnNodeDrag,
|
||||
onerror,
|
||||
ondelete,
|
||||
onedgecreate,
|
||||
connectionMode,
|
||||
nodeDragThreshold
|
||||
nodeDragThreshold,
|
||||
onconnect,
|
||||
onconnectstart,
|
||||
onconnectend
|
||||
};
|
||||
|
||||
updateStoreByKeys(store, updatableProps);
|
||||
@@ -156,6 +171,8 @@
|
||||
maxZoom,
|
||||
translateExtent
|
||||
});
|
||||
|
||||
$: colorModeClass = useColorModeClass(colorMode);
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -163,7 +180,7 @@
|
||||
bind:clientWidth
|
||||
bind:clientHeight
|
||||
{style}
|
||||
class={cc(['svelte-flow', className])}
|
||||
class={cc(['svelte-flow', className, $colorModeClass])}
|
||||
data-testid="svelte-flow__wrapper"
|
||||
on:dragover
|
||||
on:drop
|
||||
@@ -206,15 +223,12 @@
|
||||
on:nodemouseenter
|
||||
on:nodemousemove
|
||||
on:nodemouseleave
|
||||
on:connectstart
|
||||
on:connect
|
||||
on:connectend
|
||||
on:nodedragstart
|
||||
on:nodedrag
|
||||
on:nodedragstop
|
||||
on:nodecontextmenu
|
||||
/>
|
||||
<NodeSelection />
|
||||
<NodeSelection on:selectionclick on:selectioncontextmenu />
|
||||
</ViewportComponent>
|
||||
<UserSelection />
|
||||
</Pane>
|
||||
|
||||
@@ -14,7 +14,11 @@ import type {
|
||||
OnError,
|
||||
ConnectionMode,
|
||||
PanelPosition,
|
||||
ProOptions
|
||||
ProOptions,
|
||||
ColorMode,
|
||||
OnConnect,
|
||||
OnConnectStart,
|
||||
OnConnectEnd
|
||||
} from '@xyflow/system';
|
||||
|
||||
import type {
|
||||
@@ -25,7 +29,8 @@ import type {
|
||||
EdgeTypes,
|
||||
DefaultEdgeOptions,
|
||||
FitViewOptions,
|
||||
OnDelete
|
||||
OnDelete,
|
||||
OnEdgeCreate
|
||||
} from '$lib/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
@@ -75,6 +80,7 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
|
||||
defaultEdgeOptions?: DefaultEdgeOptions;
|
||||
width?: number;
|
||||
height?: number;
|
||||
colorMode?: ColorMode;
|
||||
|
||||
class?: string;
|
||||
style?: string;
|
||||
@@ -87,4 +93,10 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
|
||||
onMoveEnd?: OnMoveEnd;
|
||||
onerror?: OnError;
|
||||
ondelete?: OnDelete;
|
||||
|
||||
onedgecreate?: OnEdgeCreate;
|
||||
|
||||
onconnect?: OnConnect;
|
||||
onconnectstart?: OnConnectStart;
|
||||
onconnectend?: OnConnectEnd;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { SvelteFlowStore } from '$lib/store/types';
|
||||
import type { EdgeTypes, NodeTypes } from '$lib/types';
|
||||
import type { CoordinateExtent } from '@xyflow/system';
|
||||
import type { ColorMode, CoordinateExtent } from '@xyflow/system';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
// this is helper function for updating the store
|
||||
@@ -64,7 +64,11 @@ export type UpdatableStoreProps = {
|
||||
connectionMode?: UnwrapWritable<SvelteFlowStore['connectionMode']>;
|
||||
onerror?: UnwrapWritable<SvelteFlowStore['onerror']>;
|
||||
ondelete?: UnwrapWritable<SvelteFlowStore['ondelete']>;
|
||||
onedgecreate?: UnwrapWritable<SvelteFlowStore['onedgecreate']>;
|
||||
nodeDragThreshold?: UnwrapWritable<SvelteFlowStore['nodeDragThreshold']>;
|
||||
onconnect?: UnwrapWritable<SvelteFlowStore['onconnect']>;
|
||||
onconnectstart?: UnwrapWritable<SvelteFlowStore['onconnectstart']>;
|
||||
onconnectend?: UnwrapWritable<SvelteFlowStore['onconnectend']>;
|
||||
};
|
||||
|
||||
export function updateStoreByKeys(store: SvelteFlowStore, keys: UpdatableStoreProps) {
|
||||
@@ -77,3 +81,15 @@ export function updateStoreByKeys(store: SvelteFlowStore, keys: UpdatableStorePr
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getColorModeClass(colorMode?: ColorMode) {
|
||||
if (colorMode !== 'system') {
|
||||
return colorMode;
|
||||
}
|
||||
|
||||
if (!colorMode || typeof window === 'undefined' || !window.matchMedia) {
|
||||
return 'light';
|
||||
}
|
||||
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { ColorMode, ColorModeClass } from '@xyflow/system';
|
||||
import { readable, type Readable } from 'svelte/store';
|
||||
|
||||
function getMediaQuery() {
|
||||
if (typeof window === 'undefined' || !window.matchMedia) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return window.matchMedia('(prefers-color-scheme: dark)');
|
||||
}
|
||||
|
||||
export function useColorModeClass(colorMode: ColorMode = 'light'): Readable<ColorModeClass> {
|
||||
const colorModeClass = readable<ColorModeClass>('light', (set) => {
|
||||
if (colorMode !== 'system') {
|
||||
set(colorMode);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const mediaQuery = getMediaQuery();
|
||||
const updateColorModeClass = () => set(mediaQuery?.matches ? 'dark' : 'light');
|
||||
|
||||
set(mediaQuery?.matches ? 'dark' : 'light');
|
||||
mediaQuery?.addEventListener('change', updateColorModeClass);
|
||||
|
||||
return () => {
|
||||
mediaQuery?.removeEventListener('change', updateColorModeClass);
|
||||
};
|
||||
});
|
||||
|
||||
return colorModeClass;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { derived } from 'svelte/store';
|
||||
import { areConnectionMapsEqual, type Connection, type HandleType } from '@xyflow/system';
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
export type useHandleConnectionsParams = {
|
||||
nodeId: string;
|
||||
type: HandleType;
|
||||
id?: string | null;
|
||||
};
|
||||
|
||||
const initialConnections: Connection[] = [];
|
||||
|
||||
export function useHandleConnections({ nodeId, type, id = null }: useHandleConnectionsParams) {
|
||||
const { edges, connectionLookup } = useStore();
|
||||
let prevConnections: Map<string, Connection> | undefined = undefined;
|
||||
|
||||
return derived(
|
||||
[edges, connectionLookup],
|
||||
([, connectionLookup], set) => {
|
||||
const nextConnections = connectionLookup.get(`${nodeId}-${type}-${id || null}`);
|
||||
|
||||
if (!areConnectionMapsEqual(nextConnections, prevConnections)) {
|
||||
prevConnections = nextConnections;
|
||||
set(Array.from(prevConnections?.values() || []));
|
||||
}
|
||||
},
|
||||
initialConnections
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { derived, type Readable } from 'svelte/store';
|
||||
|
||||
import type { Node } from '$lib/types';
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
function areNodesDataEqual(a: Node['data'][] | null, b: Node['data'][] | null) {
|
||||
if ((!a && !b) || (!a?.length && !b?.length)) {
|
||||
true;
|
||||
}
|
||||
|
||||
if (!a || !b || a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function useNodesData<NodeType extends Node = Node>(
|
||||
nodeId: string
|
||||
): Readable<NodeType['data'] | null>;
|
||||
export function useNodesData<NodeType extends Node = Node>(
|
||||
nodeIds: string[]
|
||||
): Readable<NodeType['data'][]>;
|
||||
export function useNodesData<NodeType extends Node = Node>(
|
||||
nodeIds: string[],
|
||||
guard: (node: Node) => node is NodeType
|
||||
): Readable<NodeType['data'][]>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function useNodesData(nodeIds: any): any {
|
||||
const { nodes, nodeLookup } = useStore();
|
||||
let prevNodesData: (Node['data'] | null)[] | null = null;
|
||||
|
||||
return derived([nodes, nodeLookup], ([, nodeLookup], set) => {
|
||||
let nextNodesData: (Node['data'] | null)[] | null = null;
|
||||
const nodeIdArray = Array.isArray(nodeIds);
|
||||
|
||||
if (!nodeIdArray) {
|
||||
nextNodesData = [nodeLookup.get(nodeIds)?.data || null];
|
||||
} else {
|
||||
const data = [];
|
||||
|
||||
for (const nodeId of nodeIds) {
|
||||
const nodeData = nodeLookup.get(nodeId)?.data;
|
||||
|
||||
if (nodeData) {
|
||||
data.push(nodeData);
|
||||
}
|
||||
}
|
||||
|
||||
nextNodesData = data;
|
||||
}
|
||||
|
||||
if (!areNodesDataEqual(nextNodesData, prevNodesData)) {
|
||||
prevNodesData = nextNodesData;
|
||||
set(nodeIdArray ? nextNodesData : nextNodesData[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
import { get, type Writable } from 'svelte/store';
|
||||
import {
|
||||
getIncomersBase,
|
||||
getOutgoersBase,
|
||||
getOverlappingArea,
|
||||
isRectObject,
|
||||
nodeToRect,
|
||||
@@ -20,6 +18,7 @@ import {
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
import type { Edge, FitViewOptions, Node } from '$lib/types';
|
||||
import { isNode } from '$lib/utils';
|
||||
|
||||
export function useSvelteFlow(): {
|
||||
zoomIn: ZoomInOut;
|
||||
@@ -48,9 +47,16 @@ export function useSvelteFlow(): {
|
||||
screenToFlowPosition: (position: XYPosition) => XYPosition;
|
||||
flowToScreenPosition: (position: XYPosition) => XYPosition;
|
||||
viewport: Writable<Viewport>;
|
||||
getConnectedEdges: (id: string | (Node | { id: Node['id'] })[]) => Edge[];
|
||||
getIncomers: (node: string | Node | { id: Node['id'] }) => Node[];
|
||||
getOutgoers: (node: string | Node | { id: Node['id'] }) => Node[];
|
||||
updateNode: (
|
||||
id: string,
|
||||
nodeUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
|
||||
options?: { replace: boolean }
|
||||
) => void;
|
||||
updateNodeData: (
|
||||
id: string,
|
||||
dataUpdate: object | ((node: Node) => object),
|
||||
options?: { replace: boolean }
|
||||
) => void;
|
||||
toObject: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport };
|
||||
} {
|
||||
const {
|
||||
@@ -84,6 +90,24 @@ export function useSvelteFlow(): {
|
||||
return [nodeRect, node, isRect];
|
||||
};
|
||||
|
||||
const updateNode = (
|
||||
id: string,
|
||||
nodeUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
|
||||
options: { replace: boolean } = { replace: false }
|
||||
) => {
|
||||
nodes.update((nds) =>
|
||||
nds.map((node) => {
|
||||
if (node.id === id) {
|
||||
const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node as Node) : nodeUpdate;
|
||||
|
||||
return options.replace && isNode(nextNode) ? nextNode : { ...node, ...nextNode };
|
||||
}
|
||||
|
||||
return node;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
zoomIn,
|
||||
zoomOut,
|
||||
@@ -136,12 +160,12 @@ export function useSvelteFlow(): {
|
||||
) => {
|
||||
const [nodeRect, node, isRect] = getNodeRect(nodeOrRect);
|
||||
|
||||
if (!nodeRect || !node) {
|
||||
if (!nodeRect) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return (nodesToIntersect || get(nodes)).filter((n) => {
|
||||
if (!isRect && (n.id === node.id || !n.positionAbsolute)) {
|
||||
if (!isRect && (n.id === node!.id || !n.computed?.positionAbsolute)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -232,29 +256,6 @@ export function useSvelteFlow(): {
|
||||
y: rendererPosition.y + domY
|
||||
};
|
||||
},
|
||||
getConnectedEdges: (node) => {
|
||||
const nodeIds = new Set();
|
||||
|
||||
if (typeof node === 'string') {
|
||||
nodeIds.add(node);
|
||||
} else if (node.length >= 1) {
|
||||
node.forEach((n) => {
|
||||
nodeIds.add(n.id);
|
||||
});
|
||||
}
|
||||
|
||||
return get(edges).filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target));
|
||||
},
|
||||
getIncomers: (node) => {
|
||||
const _node = typeof node === 'string' ? { id: node } : node;
|
||||
|
||||
return getIncomersBase(_node, get(nodes), get(edges));
|
||||
},
|
||||
getOutgoers: (node) => {
|
||||
const _node = typeof node === 'string' ? { id: node } : node;
|
||||
|
||||
return getOutgoersBase(_node, get(nodes), get(edges));
|
||||
},
|
||||
toObject: () => {
|
||||
return {
|
||||
nodes: get(nodes).map((node) => ({
|
||||
@@ -268,6 +269,16 @@ export function useSvelteFlow(): {
|
||||
viewport: { ...get(viewport) }
|
||||
};
|
||||
},
|
||||
updateNode,
|
||||
updateNodeData: (id, dataUpdate, options) => {
|
||||
updateNode(id, (node) => {
|
||||
const nextData = typeof dataUpdate === 'function' ? dataUpdate(node) : dataUpdate;
|
||||
|
||||
return options?.replace
|
||||
? { ...node, data: nextData }
|
||||
: { ...node, data: { ...node.data, ...nextData } };
|
||||
});
|
||||
},
|
||||
viewport
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { get } from 'svelte/store';
|
||||
import type { UpdateNodeInternals, NodeDimensionUpdate } from '@xyflow/system';
|
||||
import type { UpdateNodeInternals } from '@xyflow/system';
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
|
||||
@@ -7,13 +7,14 @@ export * from '$lib/container/Panel';
|
||||
export * from '$lib/components/SvelteFlowProvider';
|
||||
export * from '$lib/components/EdgeLabelRenderer';
|
||||
export * from '$lib/components/BaseEdge';
|
||||
export * from '$lib/components/edges';
|
||||
export { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '$lib/components/edges';
|
||||
export * from '$lib/components/Handle';
|
||||
|
||||
// plugins
|
||||
export * from '$lib/plugins/Controls';
|
||||
export * from '$lib/plugins/Background';
|
||||
export * from '$lib/plugins/Minimap';
|
||||
export * from '$lib/plugins/NodeToolbar';
|
||||
|
||||
// store
|
||||
export { useStore } from '$lib/store';
|
||||
@@ -26,9 +27,20 @@ export * from '$lib/hooks/useSvelteFlow';
|
||||
export * from '$lib/hooks/useUpdateNodeInternals';
|
||||
export * from '$lib/hooks/useConnection';
|
||||
export * from '$lib/hooks/useNodesEdges';
|
||||
export * from '$lib/hooks/useHandleConnections';
|
||||
export * from '$lib/hooks/useNodesData';
|
||||
|
||||
// types
|
||||
export type { Edge, EdgeProps, EdgeTypes, DefaultEdgeOptions } from '$lib/types/edges';
|
||||
export type {
|
||||
Edge,
|
||||
EdgeProps,
|
||||
BezierEdgeProps,
|
||||
SmoothStepEdgeProps,
|
||||
StepEdgeProps,
|
||||
StraightEdgeProps,
|
||||
EdgeTypes,
|
||||
DefaultEdgeOptions
|
||||
} from '$lib/types/edges';
|
||||
export type { HandleComponentProps, FitViewOptions } from '$lib/types/general';
|
||||
export type { Node, NodeTypes, DefaultNodeOptions } from '$lib/types/nodes';
|
||||
export type { SvelteFlowStore } from '$lib/store/types';
|
||||
@@ -74,7 +86,9 @@ export {
|
||||
type Rect,
|
||||
type Box,
|
||||
type Transform,
|
||||
type CoordinateExtent
|
||||
type CoordinateExtent,
|
||||
type ColorMode,
|
||||
type ColorModeClass
|
||||
} from '@xyflow/system';
|
||||
|
||||
// system utils
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
<svg
|
||||
class={cc(['svelte-flow__background', className])}
|
||||
data-testid="svelte-flow__background"
|
||||
style:--background-color-props={bgColor}
|
||||
style:--background-pattern-color-props={patternColor}
|
||||
style:--xy-background-color-props={bgColor}
|
||||
style:--xy-background-pattern-color-props={patternColor}
|
||||
>
|
||||
<pattern
|
||||
id={patternId}
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
type="button"
|
||||
on:click
|
||||
class={cc(['svelte-flow__controls-button', className])}
|
||||
style:--controls-button-background-color-props={bgColor}
|
||||
style:--controls-button-background-color-hover-props={bgColorHover}
|
||||
style:--controls-button-color-props={color}
|
||||
style:--controls-button-color-hover-props={colorHover}
|
||||
style:--controls-button-border-color-props={borderColor}
|
||||
style:--xy-controls-button-background-color-props={bgColor}
|
||||
style:--xy-controls-button-background-color-hover-props={bgColorHover}
|
||||
style:--xy-controls-button-color-props={color}
|
||||
style:--xy-controls-button-color-hover-props={colorHover}
|
||||
style:--xy-controls-button-border-color-props={borderColor}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot class="button-svg" />
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
export let position: $$Props['position'] = 'bottom-right';
|
||||
export let ariaLabel: $$Props['ariaLabel'] = 'Mini map';
|
||||
export let nodeStrokeColor: $$Props['nodeStrokeColor'] = 'transparent';
|
||||
export let nodeColor: $$Props['nodeColor'] = '#e2e2e2';
|
||||
export let nodeColor: $$Props['nodeColor'] = undefined;
|
||||
export let nodeClass: $$Props['nodeClass'] = '';
|
||||
export let nodeBorderRadius: $$Props['nodeBorderRadius'] = 5;
|
||||
export let nodeStrokeWidth: $$Props['nodeStrokeWidth'] = 2;
|
||||
@@ -51,7 +51,7 @@
|
||||
translateExtent
|
||||
} = useStore();
|
||||
|
||||
const nodeColorFunc = getAttrFunction(nodeColor);
|
||||
const nodeColorFunc = nodeColor === undefined ? undefined : getAttrFunction(nodeColor);
|
||||
const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor);
|
||||
const nodeClassFunc = getAttrFunction(nodeClass);
|
||||
const shapeRendering =
|
||||
@@ -94,10 +94,10 @@
|
||||
viewBox="{x} {y} {viewboxWidth} {viewboxHeight}"
|
||||
role="img"
|
||||
aria-labelledby={labelledBy}
|
||||
style:--minimap-background-color-props={bgColor}
|
||||
style:--minimap-mask-color-props={maskColor}
|
||||
style:--minimap-mask-stroke-color-props={maskStrokeColor}
|
||||
style:--minimap-mask-stroke-width-props={maskStrokeWidth}
|
||||
style:--xy-minimap-background-color-props={bgColor}
|
||||
style:--xy-minimap-mask-color-props={maskColor}
|
||||
style:--xy-minimap-mask-stroke-color-props={maskStrokeColor}
|
||||
style:--xy-minimap-mask-stroke-width-props={maskStrokeWidth}
|
||||
use:interactive={{
|
||||
panZoom: $panZoom,
|
||||
viewport,
|
||||
@@ -114,15 +114,15 @@
|
||||
{#if ariaLabel}<title id={labelledBy}>{ariaLabel}</title>{/if}
|
||||
|
||||
{#each $nodes as node (node.id)}
|
||||
{#if node.width && node.height}
|
||||
{#if (node.computed?.width || node?.width) && (node.computed?.height || node.height)}
|
||||
{@const pos = getNodePositionWithOrigin(node).positionAbsolute}
|
||||
<MinimapNode
|
||||
x={pos.x}
|
||||
y={pos.y}
|
||||
width={node.width}
|
||||
height={node.height}
|
||||
width={node.computed?.width ?? node.width ?? 0}
|
||||
height={node.computed?.height ?? node.height ?? 0}
|
||||
selected={node.selected}
|
||||
color={nodeColorFunc(node)}
|
||||
color={nodeColorFunc?.(node)}
|
||||
borderRadius={nodeBorderRadius}
|
||||
strokeColor={nodeStrokeColorFunc(node)}
|
||||
strokeWidth={nodeStrokeWidth}
|
||||
@@ -142,27 +142,3 @@
|
||||
</svg>
|
||||
{/if}
|
||||
</Panel>
|
||||
|
||||
<style>
|
||||
svg {
|
||||
background-color: var(
|
||||
--minimap-background-color-props,
|
||||
var(--minimap-background-color, var(--minimap-background-color-default))
|
||||
);
|
||||
}
|
||||
|
||||
.svelte-flow__minimap-mask {
|
||||
fill: var(
|
||||
--minimap-mask-color-props,
|
||||
var(--minimap-mask-color, var(--minimap-mask-color-default))
|
||||
);
|
||||
stroke: var(
|
||||
--minimap-mask-stroke-color-props,
|
||||
var(--minimap-mask-stroke-color, var(--minimap-mask-stroke-color-default))
|
||||
);
|
||||
stroke-width: var(
|
||||
--minimap-mask-stroke-width-props,
|
||||
var(--minimap-mask-stroke-width, var(--minimap-mask-stroke-width-default))
|
||||
);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
export let width: number = 0;
|
||||
export let height: number = 0;
|
||||
export let borderRadius: number = 5;
|
||||
export let color: string;
|
||||
export let color: string | undefined = undefined;
|
||||
export let shapeRendering: string;
|
||||
export let strokeColor: string;
|
||||
export let strokeColor: string | undefined = undefined;
|
||||
export let strokeWidth: number = 2;
|
||||
export let selected: boolean = false;
|
||||
let className: string = '';
|
||||
@@ -24,8 +24,8 @@
|
||||
ry={borderRadius}
|
||||
{width}
|
||||
{height}
|
||||
fill={color}
|
||||
stroke={strokeColor}
|
||||
stroke-width={strokeWidth}
|
||||
style={`${color ? `fill: ${color};` : ''}${strokeColor ? `stroke: ${strokeColor};` : ''}${
|
||||
strokeWidth ? `stroke-width: ${strokeWidth};` : ''
|
||||
}`}
|
||||
shape-rendering={shapeRendering}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import {
|
||||
getNodesBounds,
|
||||
Position,
|
||||
type Rect,
|
||||
internalsSymbol,
|
||||
getNodeToolbarTransform
|
||||
} from '@xyflow/system';
|
||||
import portal from '$lib/actions/portal';
|
||||
import type { Node } from '$lib/types';
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
import type { NodeToolbarProps } from './types';
|
||||
|
||||
type $$Props = NodeToolbarProps;
|
||||
|
||||
export let nodeId: $$Props['nodeId'] = undefined;
|
||||
export let position: $$Props['position'] = undefined;
|
||||
export let align: $$Props['align'] = undefined;
|
||||
export let offset: $$Props['offset'] = undefined;
|
||||
export let isVisible: $$Props['isVisible'] = undefined;
|
||||
|
||||
const { domNode, viewport, nodeLookup, nodes, nodeOrigin } = useStore();
|
||||
const contextNodeId = getContext<string>('svelteflow__node_id');
|
||||
|
||||
let transform: string;
|
||||
let toolbarNodes: Node[] = [];
|
||||
let _offset = offset !== undefined ? offset : 10;
|
||||
let _position = position !== undefined ? position : Position.Top;
|
||||
let _align = align !== undefined ? align : 'center';
|
||||
|
||||
$: {
|
||||
// $nodes only needed to trigger updates, $nodeLookup is just a helper that does not trigger any updates
|
||||
if ($nodes) {
|
||||
const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId || contextNodeId];
|
||||
|
||||
toolbarNodes = nodeIds.reduce<Node[]>((res, nodeId) => {
|
||||
const node = $nodeLookup.get(nodeId);
|
||||
|
||||
if (node) {
|
||||
res.push(node);
|
||||
}
|
||||
|
||||
return res;
|
||||
}, []);
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
let nodeRect: Rect | undefined = undefined;
|
||||
|
||||
if (toolbarNodes.length === 1) {
|
||||
const toolbarNode = toolbarNodes[0];
|
||||
nodeRect = {
|
||||
...toolbarNode.position,
|
||||
width: toolbarNode.computed?.width ?? toolbarNode.width ?? 0,
|
||||
height: toolbarNode.computed?.height ?? toolbarNode.height ?? 0
|
||||
};
|
||||
} else if (toolbarNodes.length > 1) {
|
||||
nodeRect = getNodesBounds(toolbarNodes, $nodeOrigin);
|
||||
}
|
||||
|
||||
if (nodeRect) {
|
||||
transform = getNodeToolbarTransform(nodeRect, $viewport, _position, _offset, _align);
|
||||
}
|
||||
}
|
||||
|
||||
$: zIndex =
|
||||
toolbarNodes.length === 0
|
||||
? 1
|
||||
: Math.max(...toolbarNodes.map((node) => (node[internalsSymbol]?.z || 5) + 1));
|
||||
|
||||
//FIXME: Possible performance bottleneck
|
||||
$: selectedNodesCount = $nodes.filter((node) => node.selected).length;
|
||||
|
||||
// if isVisible is not set, we show the toolbar only if its node is selected and no other node is selected
|
||||
$: isActive =
|
||||
typeof isVisible === 'boolean'
|
||||
? isVisible
|
||||
: toolbarNodes.length === 1 && toolbarNodes[0].selected && selectedNodesCount === 1;
|
||||
</script>
|
||||
|
||||
{#if $domNode && isActive && toolbarNodes}
|
||||
<div
|
||||
data-id={toolbarNodes.reduce((acc, node) => `${acc}${node.id} `, '').trim()}
|
||||
class="svelte-flow__node-toolbar"
|
||||
use:portal={{ domNode: $domNode }}
|
||||
style:position="absolute"
|
||||
style:transform
|
||||
style:z-index={zIndex}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as NodeToolbar } from './NodeToolbar.svelte';
|
||||
export * from './types';
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Position, Align } from '@xyflow/system';
|
||||
|
||||
export type NodeToolbarProps = {
|
||||
nodeId?: string | string[];
|
||||
position?: Position;
|
||||
align?: Align;
|
||||
offset?: number;
|
||||
isVisible?: boolean;
|
||||
};
|
||||
@@ -80,10 +80,12 @@ export function getDerivedConnectionProps(
|
||||
: handleBounds[0];
|
||||
const fromHandleX = fromHandle
|
||||
? fromHandle.x + fromHandle.width / 2
|
||||
: (fromNode?.width ?? 0) / 2;
|
||||
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode?.height ?? 0;
|
||||
const fromX = (fromNode?.positionAbsolute?.x ?? 0) + fromHandleX;
|
||||
const fromY = (fromNode?.positionAbsolute?.y ?? 0) + fromHandleY;
|
||||
: (fromNode?.computed?.width ?? 0) / 2;
|
||||
const fromHandleY = fromHandle
|
||||
? fromHandle.y + fromHandle.height / 2
|
||||
: fromNode?.computed?.height ?? 0;
|
||||
const fromX = (fromNode?.computed?.positionAbsolute?.x ?? 0) + fromHandleX;
|
||||
const fromY = (fromNode?.computed?.positionAbsolute?.y ?? 0) + fromHandleY;
|
||||
const fromPosition = fromHandle?.position;
|
||||
const toPosition = fromPosition ? oppositePosition[fromPosition] : undefined;
|
||||
|
||||
|
||||
@@ -66,22 +66,25 @@ export function createStore({
|
||||
|
||||
const updateNodePositions: UpdateNodePositions = (nodeDragItems, dragging = false) => {
|
||||
store.nodes.update((nds) => {
|
||||
return nds.map((n) => {
|
||||
return nds.map((node) => {
|
||||
const nodeDragItem = (nodeDragItems as Array<NodeBase | NodeDragItem>).find(
|
||||
(ndi) => ndi.id === n.id
|
||||
(ndi) => ndi.id === node.id
|
||||
);
|
||||
|
||||
if (nodeDragItem) {
|
||||
return {
|
||||
...n,
|
||||
[internalsSymbol]: n[internalsSymbol],
|
||||
...node,
|
||||
dragging,
|
||||
positionAbsolute: nodeDragItem.positionAbsolute,
|
||||
position: nodeDragItem.position
|
||||
position: nodeDragItem.position,
|
||||
computed: {
|
||||
...node.computed,
|
||||
positionAbsolute: nodeDragItem.computed?.positionAbsolute
|
||||
},
|
||||
[internalsSymbol]: node[internalsSymbol]
|
||||
};
|
||||
}
|
||||
|
||||
return n;
|
||||
return node;
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -189,15 +192,20 @@ export function createStore({
|
||||
}
|
||||
|
||||
function unselectNodesAndEdges(params?: { nodes?: Node[]; edges?: Edge[] }) {
|
||||
const nodeIdsToUnselect = (params?.nodes ? params.nodes : get(store.nodes)).map(
|
||||
(item) => item.id
|
||||
);
|
||||
const edgeIdsToUnselect = (params?.edges ? params.edges : get(store.edges)).map(
|
||||
(item) => item.id
|
||||
);
|
||||
const selectedNodeIds = (params?.nodes ? params.nodes : get(store.nodes))
|
||||
.filter((node) => node.selected)
|
||||
.map((node) => node.id);
|
||||
const selectedEdgeIds = (params?.edges ? params.edges : get(store.edges))
|
||||
.filter((edge) => edge.selected)
|
||||
.map((edge) => edge.id);
|
||||
|
||||
store.nodes.update((ns) => ns.map(resetSelectedItem(nodeIdsToUnselect)));
|
||||
store.edges.update((es) => es.map(resetSelectedItem(edgeIdsToUnselect)));
|
||||
if (selectedNodeIds.length) {
|
||||
store.nodes.update((ns) => ns.map(resetSelectedItem(selectedNodeIds)));
|
||||
}
|
||||
|
||||
if (selectedEdgeIds.length) {
|
||||
store.edges.update((es) => es.map(resetSelectedItem(selectedEdgeIds)));
|
||||
}
|
||||
}
|
||||
|
||||
store.deleteKeyPressed.subscribe((deleteKeyPressed) => {
|
||||
|
||||
@@ -14,19 +14,28 @@ import {
|
||||
type OnError,
|
||||
devWarn,
|
||||
type Viewport,
|
||||
updateNodes,
|
||||
adoptUserProvidedNodes,
|
||||
getNodesBounds,
|
||||
getViewportForBounds
|
||||
getViewportForBounds,
|
||||
updateConnectionLookup,
|
||||
type ConnectionLookup,
|
||||
type OnConnect,
|
||||
type OnConnectStart,
|
||||
type OnConnectEnd
|
||||
} from '@xyflow/system';
|
||||
|
||||
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 GroupNode from '$lib/components/nodes/GroupNode.svelte';
|
||||
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 StepEdge from '$lib/components/edges/StepEdge.svelte';
|
||||
|
||||
import {
|
||||
BezierEdgeInternal,
|
||||
SmoothStepEdgeInternal,
|
||||
StraightEdgeInternal,
|
||||
StepEdgeInternal
|
||||
} from '$lib/components/edges';
|
||||
|
||||
import type {
|
||||
NodeTypes,
|
||||
EdgeTypes,
|
||||
@@ -34,7 +43,8 @@ import type {
|
||||
Node,
|
||||
Edge,
|
||||
FitViewOptions,
|
||||
OnDelete
|
||||
OnDelete,
|
||||
OnEdgeCreate
|
||||
} from '$lib/types';
|
||||
import { createNodesStore, createEdgesStore } from './utils';
|
||||
import { initConnectionProps, type ConnectionProps } from './derived-connection-props';
|
||||
@@ -47,10 +57,10 @@ export const initialNodeTypes = {
|
||||
};
|
||||
|
||||
export const initialEdgeTypes = {
|
||||
straight: StraightEdge,
|
||||
smoothstep: SmoothStepEdge,
|
||||
default: BezierEdge,
|
||||
step: StepEdge
|
||||
straight: StraightEdgeInternal,
|
||||
smoothstep: SmoothStepEdgeInternal,
|
||||
default: BezierEdgeInternal,
|
||||
step: StepEdgeInternal
|
||||
};
|
||||
|
||||
export const getInitialStore = ({
|
||||
@@ -66,20 +76,17 @@ export const getInitialStore = ({
|
||||
height?: number;
|
||||
fitView?: boolean;
|
||||
}) => {
|
||||
const nodeLookup = new Map<string, Node>();
|
||||
const nextNodes = updateNodes(nodes, nodeLookup, {
|
||||
const nodeLookup = new Map();
|
||||
const nextNodes = adoptUserProvidedNodes(nodes, nodeLookup, {
|
||||
nodeOrigin: [0, 0],
|
||||
elevateNodesOnSelect: false
|
||||
});
|
||||
const connectionLookup = updateConnectionLookup(new Map(), edges);
|
||||
|
||||
let viewport: Viewport = { x: 0, y: 0, zoom: 1 };
|
||||
|
||||
if (fitView && width && height) {
|
||||
const nodesWithDimensions = nextNodes.map((node) => ({
|
||||
...node,
|
||||
width: node.size?.width,
|
||||
height: node.size?.height
|
||||
}));
|
||||
const nodesWithDimensions = nextNodes.filter((node) => node.width && node.height);
|
||||
const bounds = getNodesBounds(nodesWithDimensions, [0, 0]);
|
||||
viewport = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
|
||||
}
|
||||
@@ -89,14 +96,15 @@ export const getInitialStore = ({
|
||||
nodes: createNodesStore(nextNodes, nodeLookup),
|
||||
nodeLookup: readable<Map<string, Node>>(nodeLookup),
|
||||
visibleNodes: readable<Node[]>([]),
|
||||
edges: createEdgesStore(edges),
|
||||
edges: createEdgesStore(edges, connectionLookup),
|
||||
visibleEdges: readable<EdgeLayouted[]>([]),
|
||||
connectionLookup: readable<ConnectionLookup>(connectionLookup),
|
||||
height: writable<number>(500),
|
||||
width: writable<number>(500),
|
||||
minZoom: writable<number>(0.5),
|
||||
maxZoom: writable<number>(2),
|
||||
nodeOrigin: writable<NodeOrigin>([0, 0]),
|
||||
nodeDragThreshold: writable<number>(0),
|
||||
nodeDragThreshold: writable<number>(1),
|
||||
nodeExtent: writable<CoordinateExtent>(infiniteExtent),
|
||||
translateExtent: writable<CoordinateExtent>(infiniteExtent),
|
||||
autoPanOnNodeDrag: writable<boolean>(true),
|
||||
@@ -133,6 +141,10 @@ export const getInitialStore = ({
|
||||
lib: readable<string>('svelte'),
|
||||
onlyRenderVisibleElements: writable<boolean>(false),
|
||||
onerror: writable<OnError>(devWarn),
|
||||
ondelete: writable<OnDelete>(undefined)
|
||||
ondelete: writable<OnDelete>(undefined),
|
||||
onedgecreate: writable<OnEdgeCreate>(undefined),
|
||||
onconnect: writable<OnConnect>(undefined),
|
||||
onconnectstart: writable<OnConnectStart>(undefined),
|
||||
onconnectend: writable<OnConnectEnd>(undefined)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -6,7 +6,13 @@ import {
|
||||
type Writable,
|
||||
get
|
||||
} from 'svelte/store';
|
||||
import { updateNodes, type Viewport, type PanZoomInstance } from '@xyflow/system';
|
||||
import {
|
||||
adoptUserProvidedNodes,
|
||||
updateConnectionLookup,
|
||||
type Viewport,
|
||||
type PanZoomInstance,
|
||||
type ConnectionLookup
|
||||
} from '@xyflow/system';
|
||||
|
||||
import type { DefaultEdgeOptions, DefaultNodeOptions, Edge, Node } from '$lib/types';
|
||||
|
||||
@@ -133,7 +139,7 @@ export const createNodesStore = (
|
||||
let elevateNodesOnSelect = true;
|
||||
|
||||
const _set = (nds: Node[]): Node[] => {
|
||||
const nextNodes = updateNodes(nds, nodeLookup, {
|
||||
const nextNodes = adoptUserProvidedNodes(nds, nodeLookup, {
|
||||
elevateNodesOnSelect,
|
||||
defaults
|
||||
});
|
||||
@@ -168,6 +174,7 @@ export const createNodesStore = (
|
||||
|
||||
export const createEdgesStore = (
|
||||
edges: Edge[],
|
||||
connectionLookup: ConnectionLookup,
|
||||
defaultOptions?: DefaultEdgeOptions
|
||||
): Writable<Edge[]> & { setDefaultOptions: (opts: DefaultEdgeOptions) => void } => {
|
||||
const { subscribe, set, update } = writable<Edge[]>([]);
|
||||
@@ -176,6 +183,9 @@ export const createEdgesStore = (
|
||||
|
||||
const _set: typeof set = (eds: Edge[]) => {
|
||||
const nextEdges = defaults ? eds.map((edge) => ({ ...defaults, ...edge })) : eds;
|
||||
|
||||
updateConnectionLookup(connectionLookup, nextEdges);
|
||||
|
||||
value = nextEdges;
|
||||
set(value);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,9 @@ import type {
|
||||
BezierPathOptions,
|
||||
DefaultEdgeOptionsBase,
|
||||
EdgePosition,
|
||||
SmoothStepPathOptions
|
||||
SmoothStepPathOptions,
|
||||
Optional,
|
||||
StepPathOptions
|
||||
} from '@xyflow/system';
|
||||
|
||||
import type { Node } from '$lib/types';
|
||||
@@ -29,6 +31,7 @@ type BezierEdgeType<T> = DefaultEdge<T> & {
|
||||
|
||||
type StepEdgeType<T> = DefaultEdge<T> & {
|
||||
type: 'step';
|
||||
pathOptions?: StepPathOptions;
|
||||
};
|
||||
|
||||
export type Edge<T = any> =
|
||||
@@ -37,7 +40,7 @@ export type Edge<T = any> =
|
||||
| BezierEdgeType<T>
|
||||
| StepEdgeType<T>;
|
||||
|
||||
export type EdgeProps = Omit<Edge, 'sourceHandle' | 'targetHandle'> &
|
||||
export type EdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandle' | 'type'> &
|
||||
EdgePosition & {
|
||||
markerStart?: string;
|
||||
markerEnd?: string;
|
||||
@@ -45,6 +48,31 @@ export type EdgeProps = Omit<Edge, 'sourceHandle' | 'targetHandle'> &
|
||||
targetHandleId?: string | null;
|
||||
};
|
||||
|
||||
export type EdgeComponentProps<T = any> = Optional<
|
||||
Omit<
|
||||
EdgeProps<T>,
|
||||
'source' | 'target' | 'sourceHandleId' | 'targetHandleId' | 'animated' | 'selected' | 'data'
|
||||
>,
|
||||
'id'
|
||||
>;
|
||||
|
||||
export type BezierEdgeProps<T = any> = EdgeComponentProps<T> & {
|
||||
pathOptions?: BezierPathOptions;
|
||||
};
|
||||
|
||||
export type SmoothStepEdgeProps<T = any> = EdgeComponentProps<T> & {
|
||||
pathOptions?: SmoothStepPathOptions;
|
||||
};
|
||||
|
||||
export type StepEdgeProps<T = any> = EdgeComponentProps<T> & {
|
||||
pathOptions?: StepPathOptions;
|
||||
};
|
||||
|
||||
export type StraightEdgeProps<T = any> = Omit<
|
||||
EdgeComponentProps<T>,
|
||||
'sourcePosition' | 'targetPosition'
|
||||
>;
|
||||
|
||||
export type EdgeTypes = Record<string, ComponentType<SvelteComponent<EdgeProps>>>;
|
||||
|
||||
export type DefaultEdgeOptions = Omit<DefaultEdgeOptionsBase<Edge>, 'focusable'>;
|
||||
|
||||
@@ -4,7 +4,8 @@ import type {
|
||||
HandleType,
|
||||
Position,
|
||||
XYPosition,
|
||||
ConnectingHandle
|
||||
ConnectingHandle,
|
||||
Connection
|
||||
} from '@xyflow/system';
|
||||
|
||||
import type { Node } from './nodes';
|
||||
@@ -30,8 +31,11 @@ export type HandleComponentProps = {
|
||||
isConnectable?: boolean;
|
||||
isConnectableStart?: boolean;
|
||||
isConnectableEnd?: boolean;
|
||||
onconnect?: (connections: Connection[]) => void;
|
||||
ondisconnect?: (connections: Connection[]) => void;
|
||||
};
|
||||
|
||||
export type FitViewOptions = FitViewOptionsBase<Node>;
|
||||
|
||||
export type OnDelete = (params: { nodes: Node[]; edges: Edge[] }) => void;
|
||||
export type OnEdgeCreate = (connection: Connection) => Edge | Connection | void;
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
|
||||
import type { Edge, Node } from '$lib/types';
|
||||
|
||||
export const isNode = isNodeBase<Node, Edge>;
|
||||
export const isEdge = isEdgeBase<Node, Edge>;
|
||||
export const isNode = isNodeBase<Node>;
|
||||
export const isEdge = isEdgeBase<Edge>;
|
||||
export const getOutgoers = getOutgoersBase<Node, Edge>;
|
||||
export const getIncomers = getIncomersBase<Node, Edge>;
|
||||
export const addEdge = addEdgeBase<Edge>;
|
||||
|
||||
Reference in New Issue
Block a user