feat(svelte): add isValidConnection prop, cleanup handle classes
This commit is contained in:
@@ -4,7 +4,8 @@
|
|||||||
const routes = [
|
const routes = [
|
||||||
'overview',
|
'overview',
|
||||||
'stress',
|
'stress',
|
||||||
'usesvelteflow'
|
'usesvelteflow',
|
||||||
|
'validation'
|
||||||
];
|
];
|
||||||
|
|
||||||
const onChange = (event: Event) => {
|
const onChange = (event: Event) => {
|
||||||
|
|||||||
@@ -13,14 +13,14 @@
|
|||||||
export let id: $$Props['id'] = undefined;
|
export let id: $$Props['id'] = undefined;
|
||||||
export let type: $$Props['type'] = 'source';
|
export let type: $$Props['type'] = 'source';
|
||||||
export let position: $$Props['position'] = Position.Top;
|
export let position: $$Props['position'] = Position.Top;
|
||||||
export let isConnectable: $$Props['isConnectable'] = true;
|
|
||||||
export let style: $$Props['style'] = undefined;
|
export let style: $$Props['style'] = undefined;
|
||||||
export let isValidConnection: $$Props['isValidConnection'] = (_: Connection) => true;
|
|
||||||
let className: $$Props['class'] = undefined;
|
let className: $$Props['class'] = undefined;
|
||||||
export { className as class };
|
export { className as class };
|
||||||
|
|
||||||
const isTarget = type === 'target';
|
const isTarget = type === 'target';
|
||||||
const nodeId = getContext<string>('rf_nodeid');
|
const nodeId = getContext<string>('svelteflow__node_id');
|
||||||
|
const connectable = getContext<string>('svelteflow__node_connectable');
|
||||||
|
|
||||||
const handleId = id || null;
|
const handleId = id || null;
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
@@ -30,10 +30,11 @@
|
|||||||
nodes,
|
nodes,
|
||||||
connectionRadius,
|
connectionRadius,
|
||||||
transform,
|
transform,
|
||||||
|
isValidConnection,
|
||||||
addEdge,
|
addEdge,
|
||||||
panBy,
|
panBy,
|
||||||
cancelConnection,
|
cancelConnection,
|
||||||
updateConnection
|
updateConnection,
|
||||||
} = useStore();
|
} = useStore();
|
||||||
|
|
||||||
function dispatchEvent(eventName: string, params?: Connection) {
|
function dispatchEvent(eventName: string, params?: Connection) {
|
||||||
@@ -56,10 +57,10 @@
|
|||||||
isTarget,
|
isTarget,
|
||||||
connectionRadius: $connectionRadius,
|
connectionRadius: $connectionRadius,
|
||||||
domNode: $domNode,
|
domNode: $domNode,
|
||||||
nodes: $nodes,
|
nodes,
|
||||||
connectionMode: $connectionMode,
|
connectionMode: $connectionMode,
|
||||||
transform,
|
transform,
|
||||||
isValidConnection: isValidConnection!,
|
isValidConnection: $isValidConnection,
|
||||||
onConnect: onConnectExtended,
|
onConnect: onConnectExtended,
|
||||||
updateConnection,
|
updateConnection,
|
||||||
cancelConnection,
|
cancelConnection,
|
||||||
@@ -86,7 +87,7 @@
|
|||||||
])}
|
])}
|
||||||
class:source={!isTarget}
|
class:source={!isTarget}
|
||||||
class:target={isTarget}
|
class:target={isTarget}
|
||||||
class:connectable={isConnectable}
|
class:connectable
|
||||||
on:mousedown={onPointerDown}
|
on:mousedown={onPointerDown}
|
||||||
on:touchstart={onPointerDown}
|
on:touchstart={onPointerDown}
|
||||||
{style}
|
{style}
|
||||||
|
|||||||
@@ -22,10 +22,9 @@ import {
|
|||||||
getHandleType,
|
getHandleType,
|
||||||
isValidHandle,
|
isValidHandle,
|
||||||
resetRecentHandle,
|
resetRecentHandle,
|
||||||
type ConnectionHandle,
|
type ConnectionHandle
|
||||||
type ValidConnectionFunc
|
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import type { ConnectionData, Node } from '$lib/types';
|
import type { ConnectionData, IsValidConnection, Node } from '$lib/types';
|
||||||
|
|
||||||
export function handlePointerDown({
|
export function handlePointerDown({
|
||||||
event,
|
event,
|
||||||
@@ -54,9 +53,9 @@ export function handlePointerDown({
|
|||||||
isTarget: boolean;
|
isTarget: boolean;
|
||||||
connectionMode: ConnectionMode;
|
connectionMode: ConnectionMode;
|
||||||
domNode: HTMLDivElement | null;
|
domNode: HTMLDivElement | null;
|
||||||
nodes: Node[];
|
nodes: Writable<Node[]>;
|
||||||
connectionRadius: number;
|
connectionRadius: number;
|
||||||
isValidConnection: ValidConnectionFunc;
|
isValidConnection: IsValidConnection;
|
||||||
transform: Writable<Transform>;
|
transform: Writable<Transform>;
|
||||||
updateConnection: (connection: Partial<ConnectionData>) => void;
|
updateConnection: (connection: Partial<ConnectionData>) => void;
|
||||||
cancelConnection: () => void;
|
cancelConnection: () => void;
|
||||||
@@ -90,7 +89,7 @@ export function handlePointerDown({
|
|||||||
const autoPanOnConnect = true;
|
const autoPanOnConnect = true;
|
||||||
|
|
||||||
const handleLookup = getHandleLookup({
|
const handleLookup = getHandleLookup({
|
||||||
nodes,
|
nodes: get(nodes),
|
||||||
nodeId,
|
nodeId,
|
||||||
handleId,
|
handleId,
|
||||||
handleType
|
handleType
|
||||||
@@ -116,8 +115,6 @@ export function handlePointerDown({
|
|||||||
status: null
|
status: null
|
||||||
});
|
});
|
||||||
|
|
||||||
// @todo add prop
|
|
||||||
|
|
||||||
// onConnectStart?.(event, { nodeId, handleId, handleType });
|
// onConnectStart?.(event, { nodeId, handleId, handleType });
|
||||||
onConnectStart();
|
onConnectStart();
|
||||||
|
|
||||||
@@ -144,7 +141,8 @@ export function handlePointerDown({
|
|||||||
handleId,
|
handleId,
|
||||||
isTarget ? 'target' : 'source',
|
isTarget ? 'target' : 'source',
|
||||||
isValidConnection,
|
isValidConnection,
|
||||||
doc
|
doc,
|
||||||
|
get(nodes)
|
||||||
);
|
);
|
||||||
|
|
||||||
handleDomNode = result.handleDomNode;
|
handleDomNode = result.handleDomNode;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { internalsSymbol, ConnectionMode, type ConnectionStatus } from '@reactfl
|
|||||||
import type { Connection, HandleType, XYPosition, NodeHandleBounds } from '@reactflow/system';
|
import type { Connection, HandleType, XYPosition, NodeHandleBounds } from '@reactflow/system';
|
||||||
import { getEventPosition } from '@reactflow/utils';
|
import { getEventPosition } from '@reactflow/utils';
|
||||||
|
|
||||||
import type { Node } from '$lib/types';
|
import type { IsValidConnection, Node } from '$lib/types';
|
||||||
|
|
||||||
export type ConnectionHandle = {
|
export type ConnectionHandle = {
|
||||||
id: string | null;
|
id: string | null;
|
||||||
@@ -76,8 +76,9 @@ export function isValidHandle(
|
|||||||
fromNodeId: string,
|
fromNodeId: string,
|
||||||
fromHandleId: string | null,
|
fromHandleId: string | null,
|
||||||
fromType: string,
|
fromType: string,
|
||||||
isValidConnection: ValidConnectionFunc,
|
isValidConnection: IsValidConnection,
|
||||||
doc: Document | ShadowRoot
|
doc: Document | ShadowRoot,
|
||||||
|
nodes: Node[]
|
||||||
) {
|
) {
|
||||||
const isTarget = fromType === 'target';
|
const isTarget = fromType === 'target';
|
||||||
const handleDomNode = doc.querySelector(
|
const handleDomNode = doc.querySelector(
|
||||||
@@ -116,7 +117,10 @@ export function isValidHandle(
|
|||||||
: handleNodeId !== fromNodeId || handleId !== fromHandleId;
|
: handleNodeId !== fromNodeId || handleId !== fromHandleId;
|
||||||
|
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
result.isValid = isValidConnection(connection);
|
const fromNode: Node | undefined = nodes.find((n) => n.id === connection.source);
|
||||||
|
const toNode: Node | undefined = nodes.find((n) => n.id === connection.target);
|
||||||
|
|
||||||
|
if (fromNode && toNode) result.isValid = isValidConnection(connection, { fromNode, toNode });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
export let data: NodeWrapperProps['data'] = {};
|
export let data: NodeWrapperProps['data'] = {};
|
||||||
export let selected: NodeWrapperProps['selected'] = false;
|
export let selected: NodeWrapperProps['selected'] = false;
|
||||||
export let draggable: NodeWrapperProps['draggable'] = undefined;
|
export let draggable: NodeWrapperProps['draggable'] = undefined;
|
||||||
|
export let connectable: NodeWrapperProps['connectable'] = true;
|
||||||
export let dragging: boolean = false;
|
export let dragging: boolean = false;
|
||||||
export let resizeObserver: NodeWrapperProps['resizeObserver'] = null;
|
export let resizeObserver: NodeWrapperProps['resizeObserver'] = null;
|
||||||
export let style: NodeWrapperProps['style'] = undefined;
|
export let style: NodeWrapperProps['style'] = undefined;
|
||||||
@@ -45,7 +46,8 @@
|
|||||||
const selectNodesOnDrag = false;
|
const selectNodesOnDrag = false;
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
setContext('rf_nodeid', id);
|
setContext('svelteflow__node_id', id);
|
||||||
|
setContext('svelteflow__node_connectable', connectable);
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
resizeObserver?.observe(nodeRef);
|
resizeObserver?.observe(nodeRef);
|
||||||
@@ -79,6 +81,7 @@
|
|||||||
class:dragging
|
class:dragging
|
||||||
class:selected
|
class:selected
|
||||||
class:draggable
|
class:draggable
|
||||||
|
class:connectable
|
||||||
style:transform={`translate(${positionOrigin?.x ?? 0}px, ${positionOrigin?.y ?? 0}px)`}
|
style:transform={`translate(${positionOrigin?.x ?? 0}px, ${positionOrigin?.y ?? 0}px)`}
|
||||||
{style}
|
{style}
|
||||||
on:click={onSelectNodeHandler}
|
on:click={onSelectNodeHandler}
|
||||||
@@ -93,7 +96,6 @@
|
|||||||
{selected}
|
{selected}
|
||||||
{sourcePosition}
|
{sourcePosition}
|
||||||
{targetPosition}
|
{targetPosition}
|
||||||
isConnectable={true}
|
|
||||||
xPos={positionAbsolute?.x ?? 0}
|
xPos={positionAbsolute?.x ?? 0}
|
||||||
yPos={positionAbsolute?.y ?? 0}
|
yPos={positionAbsolute?.y ?? 0}
|
||||||
on:connect:start
|
on:connect:start
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export type NodeWrapperProps = Pick<
|
|||||||
Node,
|
Node,
|
||||||
| 'id'
|
| 'id'
|
||||||
| 'class'
|
| 'class'
|
||||||
|
| 'connectable'
|
||||||
| 'data'
|
| 'data'
|
||||||
| 'draggable'
|
| 'draggable'
|
||||||
| 'dragging'
|
| 'dragging'
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
data={node.data}
|
data={node.data}
|
||||||
selected={node.selected}
|
selected={node.selected}
|
||||||
draggable={node.draggable || node.draggable === undefined}
|
draggable={node.draggable || node.draggable === undefined}
|
||||||
|
connectable={node.connectable || node.connectable === undefined}
|
||||||
positionAbsolute={node.positionAbsolute}
|
positionAbsolute={node.positionAbsolute}
|
||||||
positionOrigin={posOrigin}
|
positionOrigin={posOrigin}
|
||||||
width={node.width}
|
width={node.width}
|
||||||
|
|||||||
@@ -27,7 +27,9 @@
|
|||||||
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 connectionRadius: $$Props['connectionRadius'] = undefined;
|
export let connectionRadius: $$Props['connectionRadius'] = undefined;
|
||||||
export let connectionLineType: $$Props['connectionLineType'] = undefined
|
export let connectionLineType: $$Props['connectionLineType'] = undefined;
|
||||||
|
export let isValidConnection: $$Props['isValidConnection'] = undefined;
|
||||||
|
|
||||||
export let style: $$Props['style'] = undefined;
|
export let style: $$Props['style'] = undefined;
|
||||||
let className: $$Props['class'] = undefined;
|
let className: $$Props['class'] = undefined;
|
||||||
export { className as class };
|
export { className as class };
|
||||||
@@ -52,6 +54,7 @@
|
|||||||
id,
|
id,
|
||||||
connectionLineType,
|
connectionLineType,
|
||||||
connectionRadius,
|
connectionRadius,
|
||||||
|
isValidConnection
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.keys(updatableProps).forEach(prop => {
|
Object.keys(updatableProps).forEach(prop => {
|
||||||
|
|||||||
@@ -6,9 +6,14 @@ import type {
|
|||||||
Viewport
|
Viewport
|
||||||
} from '@reactflow/system';
|
} from '@reactflow/system';
|
||||||
|
|
||||||
import type { Edge, Node, NodeTypes, KeyDefinition, EdgeTypes } from '$lib/types';
|
import type {
|
||||||
import type { Writable } from 'svelte/store';
|
Edge,
|
||||||
import type { createNodes } from '$lib/utils';
|
Node,
|
||||||
|
NodeTypes,
|
||||||
|
KeyDefinition,
|
||||||
|
EdgeTypes,
|
||||||
|
IsValidConnection
|
||||||
|
} from '$lib/types';
|
||||||
|
|
||||||
export type SvelteFlowProps = {
|
export type SvelteFlowProps = {
|
||||||
id?: string;
|
id?: string;
|
||||||
@@ -27,6 +32,7 @@ export type SvelteFlowProps = {
|
|||||||
style?: string;
|
style?: string;
|
||||||
|
|
||||||
connectionLineType?: ConnectionLineType;
|
connectionLineType?: ConnectionLineType;
|
||||||
|
isValidConnection?: IsValidConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SvelteFlowEvents = {
|
export type SvelteFlowEvents = {
|
||||||
|
|||||||
@@ -15,7 +15,15 @@ import OutputNode from '$lib/components/nodes/OutputNode.svelte';
|
|||||||
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 { ConnectionData, NodeTypes, EdgeTypes, EdgeLayouted, Edge, Node } from '$lib/types';
|
import type {
|
||||||
|
ConnectionData,
|
||||||
|
NodeTypes,
|
||||||
|
EdgeTypes,
|
||||||
|
EdgeLayouted,
|
||||||
|
Edge,
|
||||||
|
Node,
|
||||||
|
IsValidConnection
|
||||||
|
} from '$lib/types';
|
||||||
|
|
||||||
export const initConnectionData = {
|
export const initConnectionData = {
|
||||||
nodeId: null,
|
nodeId: null,
|
||||||
@@ -67,5 +75,6 @@ export const initialStoreState = {
|
|||||||
connectionPath: readable<string | null>(null),
|
connectionPath: readable<string | null>(null),
|
||||||
connection: writable<ConnectionData>(initConnectionData),
|
connection: writable<ConnectionData>(initConnectionData),
|
||||||
connectionRadius: writable<number>(25),
|
connectionRadius: writable<number>(25),
|
||||||
connectionLineType: writable<ConnectionLineType>(ConnectionLineType.Bezier)
|
connectionLineType: writable<ConnectionLineType>(ConnectionLineType.Bezier),
|
||||||
|
isValidConnection: writable<IsValidConnection>(() => true)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import type { ShortcutModifierDefinition } from '@svelte-put/shortcut';
|
import type { ShortcutModifierDefinition } from '@svelte-put/shortcut';
|
||||||
import type { FitViewOptionsBase, HandleProps, HandleType, XYPosition } from '@reactflow/system';
|
import type {
|
||||||
|
Connection,
|
||||||
|
FitViewOptionsBase,
|
||||||
|
HandleProps,
|
||||||
|
HandleType,
|
||||||
|
XYPosition
|
||||||
|
} from '@reactflow/system';
|
||||||
|
|
||||||
import type { Node } from './nodes';
|
import type { Node } from './nodes';
|
||||||
|
|
||||||
@@ -15,9 +21,14 @@ export type ConnectionData = {
|
|||||||
status: string | null;
|
status: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HandleComponentProps = HandleProps & {
|
export type HandleComponentProps = Omit<HandleProps, 'isConnectable'> & {
|
||||||
class?: string;
|
class?: string;
|
||||||
style?: string;
|
style?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FitViewOptions = FitViewOptionsBase<Node>;
|
export type FitViewOptions = FitViewOptionsBase<Node>;
|
||||||
|
|
||||||
|
export type IsValidConnection = (
|
||||||
|
connection: Connection,
|
||||||
|
{ fromNode, toNode }: { fromNode: Node; toNode: Node }
|
||||||
|
) => boolean;
|
||||||
|
|||||||
@@ -26,4 +26,4 @@ export type NodeProps<NodeData = any> = Pick<
|
|||||||
|
|
||||||
export type NodeTypes = Record<string, typeof SvelteComponentTyped<Partial<NodeProps>>>;
|
export type NodeTypes = Record<string, typeof SvelteComponentTyped<Partial<NodeProps>>>;
|
||||||
|
|
||||||
export type DefaultNodeOptions = Omit<Node, 'id'>;
|
export type DefaultNodeOptions = Partial<Omit<Node, 'id'>>;
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import SvelteFlow, {
|
||||||
|
SvelteFlowProvider,
|
||||||
|
Controls,
|
||||||
|
Background,
|
||||||
|
BackgroundVariant,
|
||||||
|
createNodes,
|
||||||
|
createEdges,
|
||||||
|
type IsValidConnection
|
||||||
|
} from '../../lib/index';
|
||||||
|
import { Position } from '@reactflow/system';
|
||||||
|
|
||||||
|
const nodes = createNodes([
|
||||||
|
{ id: '0', type: 'default', position: { x: 0, y: 150 }, data: { label: 'only connecatbale with B' } },
|
||||||
|
{ id: 'A', type: 'default', position: { x: 250, y: 0 }, data: { label: 'A' } },
|
||||||
|
{ id: 'B', type: 'default', position: { x: 250, y: 150 }, data: { label: 'B' } },
|
||||||
|
{ id: 'C', type: 'default', position: { x: 250, y: 300 }, data: { label: 'C' } }
|
||||||
|
], {
|
||||||
|
sourcePosition: Position.Right,
|
||||||
|
targetPosition: Position.Left
|
||||||
|
});
|
||||||
|
|
||||||
|
const edges = createEdges([]);
|
||||||
|
|
||||||
|
const isValidConnection: IsValidConnection = (connection) => connection.target === 'B'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SvelteFlowProvider
|
||||||
|
{nodes}
|
||||||
|
{edges}
|
||||||
|
>
|
||||||
|
<SvelteFlow
|
||||||
|
fitView
|
||||||
|
minZoom={0.1}
|
||||||
|
maxZoom={2.5}
|
||||||
|
isValidConnection={isValidConnection}
|
||||||
|
>
|
||||||
|
<Controls />
|
||||||
|
<Background variant={BackgroundVariant.Dots} />
|
||||||
|
</SvelteFlow>
|
||||||
|
</SvelteFlowProvider>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:global(.svelte-flow__handle.connecting) {
|
||||||
|
background: #ff6060;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.svelte-flow__handle.valid) {
|
||||||
|
background: #55dd99;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user