Merge branch 'xyflow' into feat/ssr

This commit is contained in:
moklick
2023-10-23 12:15:21 +02:00
36 changed files with 534 additions and 204 deletions
@@ -46,6 +46,8 @@ function Background({
? [scaledSize / offset, scaledSize / offset]
: [patternDimensions[0] / offset, patternDimensions[1] / offset];
const _patternId = `${patternId}${id ? id : ''}`;
return (
<svg
className={cc(['react-flow__background', className])}
@@ -61,7 +63,7 @@ function Background({
data-testid="rf__background"
>
<pattern
id={patternId + id}
id={_patternId}
x={transform[0] % scaledGap[0]}
y={transform[1] % scaledGap[1]}
width={scaledGap[0]}
@@ -80,7 +82,7 @@ function Background({
/>
)}
</pattern>
<rect x="0" y="0" width="100%" height="100%" fill={`url(#${patternId + id})`} />
<rect x="0" y="0" width="100%" height="100%" fill={`url(#${_patternId})`} />
</svg>
);
}
@@ -69,8 +69,9 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
const onContextMenuHandler = getMouseHandler(id, store.getState, onContextMenu);
const onDoubleClickHandler = getMouseHandler(id, store.getState, onDoubleClick);
const onSelectNodeHandler = (event: MouseEvent) => {
const { selectNodesOnDrag } = store.getState();
if (isSelectable && (!selectNodesOnDrag || !isDraggable)) {
const { selectNodesOnDrag, nodeDragThreshold } = store.getState();
if (isSelectable && (!selectNodesOnDrag || !isDraggable || nodeDragThreshold > 0)) {
// this handler gets called within the drag start event when selectNodesOnDrag=true
handleNodeClick({
id,
+26
View File
@@ -1,3 +1,29 @@
## 0.0.24
- update node automatically when `type`, `sourcePosition` or `targetPosition` option changes
- prevent dev tool warnings when using built-in node types
- updates `useSvelteFlow` hook:
- add node type "group"
- add `class` prop for BaseEdge
- add `id` prop for Background
- add `selected` prop for MiniMap Node
- rename Controls prop `showInteractive` to `showLock`
## 0.0.23
- updates `useSvelteFlow` hook:
- add `screenToFlowCoordinate` and `flowToScreenCoordinate`
- add `getConnectedEdges`, `getIncomers` and `getOutgoers`
- add `deleteElements`
- add `fitBounds`
- add `getIntersectingNodes` and `isNodeIntersecting`
- add `useConnection` hook
- add `useNodes` hook
- add `useEdges` hook
- add `viewport` prop (writable viewport)
- fix selection style
- fix Background component with lines variant
## 0.0.22
- add `connectionLine` slot for rendering a custom connection line
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@xyflow/svelte",
"version": "0.0.22",
"version": "0.0.24",
"description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.",
"keywords": [
"svelte",
@@ -36,6 +36,7 @@
"sideEffects": [
"*.css"
],
"license": "MIT",
"publishConfig": {
"access": "public"
},
@@ -14,6 +14,8 @@
export let markerEnd: $$Props['markerEnd'] = undefined;
export let style: $$Props['style'] = undefined;
export let interactionWidth: $$Props['interactionWidth'] = 20;
let className: $$Props['class'] = undefined;
export { className as class };
</script>
<path
@@ -10,4 +10,5 @@ export type BaseEdgeProps = Pick<
labelY?: number;
markerStart?: string;
markerEnd?: string;
class?: string;
};
@@ -6,7 +6,7 @@
import { useStore } from '$lib/store';
import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
import type { EdgeLayouted } from '$lib/types';
import type { EdgeLayouted, Edge } from '$lib/types';
type $$Props = EdgeLayouted;
@@ -39,7 +39,10 @@
export { className as class };
const { edges, edgeTypes, flowId, addSelectedEdges } = useStore();
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher<{
edgeclick: { edge: Edge; event: MouseEvent | TouchEvent };
edgecontextmenu: { edge: Edge; event: MouseEvent };
}>();
$: edgeComponent = $edgeTypes[type!] || BezierEdge;
$: markerStartUrl = markerStart ? `url(#${getMarkerId(markerStart, $flowId)})` : undefined;
@@ -51,12 +54,18 @@
}
const edge = $edges.find((e) => e.id === id);
dispatch('edgeclick', { event, edge });
if (edge) {
dispatch('edgeclick', { event, edge });
}
}
function onContextMenu(event: MouseEvent) {
const edge = $edges.find((e) => e.id === id);
dispatch('edgecontextmenu', { event, edge });
if (edge) {
dispatch('edgecontextmenu', { event, edge });
}
}
</script>
@@ -1,7 +1,13 @@
<script lang="ts">
import { getContext, createEventDispatcher } from 'svelte';
import cc from 'classcat';
import { Position, XYHandle, isMouseEvent } from '@xyflow/system';
import {
Position,
XYHandle,
isMouseEvent,
type Connection,
type HandleType
} from '@xyflow/system';
import { useStore } from '$lib/store';
import type { HandleComponentProps } from '$lib/types';
@@ -26,7 +32,18 @@
$: handleConnectable = isConnectable !== undefined ? isConnectable : $connectable;
const handleId = id || null;
const dispatch = createEventDispatcher();
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 {
@@ -17,14 +17,9 @@
class="selection-wrapper nopan"
style="width: {rect.width}px; height: {rect.height}px; transform: translate({rect.x}px, {rect.y}px)"
use:drag={{ disabled: false, store }}
/>
<Selection
isVisible={$selectionRectMode === 'nodes'}
width={rect.width}
height={rect.height}
x={rect.x}
y={rect.y}
/>
>
<Selection width="100%" height="100%" x={0} y={0} />
</div>
{/if}
<style>
@@ -9,13 +9,14 @@
type ComponentType
} from 'svelte';
import cc from 'classcat';
import { errorMessages, type NodeProps } from '@xyflow/system';
import { get, writable } from 'svelte/store';
import { errorMessages, Position, type NodeProps } from '@xyflow/system';
import drag from '$lib/actions/drag';
import { useStore } from '$lib/store';
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
import type { NodeWrapperProps } from './types';
import { writable } from 'svelte/store';
import type { Node } from '$lib/types';
interface $$Props extends NodeWrapperProps {}
@@ -42,25 +43,63 @@
export { className as class };
const store = useStore();
const { nodeTypes, addSelectedNodes } = store;
const { nodeTypes, nodeDragThreshold, addSelectedNodes, updateNodeDimensions } = store;
const nodeType = type || 'default';
let nodeRef: HTMLDivElement;
const nodeTypeValid = !!$nodeTypes[type!];
const nodeTypeValid = !!$nodeTypes[nodeType];
if (!nodeTypeValid) {
console.warn('003', errorMessages['error003'](type!));
type = 'default';
}
const nodeComponent: ComponentType<SvelteComponent<NodeProps>> = $nodeTypes[type!] || DefaultNode;
const nodeComponent: ComponentType<SvelteComponent<NodeProps>> =
$nodeTypes[nodeType] || DefaultNode;
const selectNodesOnDrag = false;
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher<{
nodeclick: { node: Node; event: MouseEvent | TouchEvent };
nodecontextmenu: { node: Node; event: MouseEvent | TouchEvent };
nodedrag: { node: Node; nodes: Node[]; event: MouseEvent | TouchEvent };
nodedragstart: { node: Node; nodes: Node[]; event: MouseEvent | TouchEvent };
nodedragstop: { node: Node; nodes: Node[]; event: MouseEvent | TouchEvent };
nodemouseenter: { node: Node; event: MouseEvent | TouchEvent };
nodemouseleave: { node: Node; event: MouseEvent | TouchEvent };
nodemousemove: { node: Node; event: MouseEvent | TouchEvent };
}>();
const connectableStore = writable(connectable);
let prevType: string | undefined = undefined;
let prevSourcePosition: Position | undefined = undefined;
let prevTargetPosition: Position | undefined = undefined;
$: {
connectableStore.set(!!connectable);
}
$: {
// if type, sourcePosition or targetPosition changes,
// we need to re-calculate the handle positions
const doUpdate =
(prevType && nodeType !== prevType) ||
(prevSourcePosition && sourcePosition !== prevSourcePosition) ||
(prevTargetPosition && targetPosition !== prevTargetPosition);
if (doUpdate) {
requestAnimationFrame(() =>
updateNodeDimensions([
{
id,
nodeElement: nodeRef,
forceUpdate: true
}
])
);
}
prevType = nodeType;
prevSourcePosition = sourcePosition;
prevTargetPosition = targetPosition;
}
setContext('svelteflow__node_id', id);
setContext('svelteflow__node_connectable', connectableStore);
@@ -73,7 +112,7 @@
});
function onSelectNodeHandler(event: MouseEvent | TouchEvent) {
if (selectable && (!selectNodesOnDrag || !draggable)) {
if (selectable && (!selectNodesOnDrag || !draggable || get(nodeDragThreshold) > 0)) {
// this handler gets called within the drag start event when selectNodesOnDrag=true
addSelectedNodes([id]);
}
@@ -108,7 +147,7 @@
}}
bind:this={nodeRef}
data-id={id}
class={cc(['svelte-flow__node', `svelte-flow__node-${type || 'default'}`, className])}
class={cc(['svelte-flow__node', `svelte-flow__node-${nodeType}`, className])}
class:dragging
class:selected
class:draggable
@@ -1,15 +1,17 @@
<script lang="ts">
export let x: number | null = 0;
export let y: number | null = 0;
export let width: number | null = 0;
export let height: number | null = 0;
export let width: number | string | null = 0;
export let height: number | string | null = 0;
export let isVisible: boolean = true;
</script>
{#if isVisible}
<div
class="svelte-flow__selection"
style="width: {width}px; height: {height}px; transform: translate({x}px, {y}px)"
style:width={typeof width === 'string' ? width : `${width}px`}
style:height={typeof height === 'string' ? height : `${height}px`}
style:transform={`translate(${x}px, ${y}px)`}
/>
{/if}
@@ -8,6 +8,28 @@
export let data: $$Props['data'] = { label: 'Node' };
export let targetPosition: $$Props['targetPosition'] = Position.Top;
export let sourcePosition: $$Props['sourcePosition'] = Position.Bottom;
// unused props - we need to list them here in order to prevent warnings
export let id: $$Props['id'] = '';
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 isConnectable: $$Props['isConnectable'] = undefined;
// @todo: there must be a better way to do this
id;
selected;
type;
zIndex;
dragging;
dragHandle;
xPos;
yPos;
isConnectable;
</script>
<Handle type="target" position={targetPosition} on:connectstart on:connect on:connectend />
@@ -0,0 +1,33 @@
<script lang="ts">
import type { NodeProps } from '@xyflow/system';
interface $$Props extends NodeProps<{}> {}
// unused props - we need to list them here in order to prevent warnings
export let id: $$Props['id'] = '';
export let data: $$Props['data'] = {};
export let selected: $$Props['selected'] = undefined;
export let sourcePosition: $$Props['sourcePosition'] = 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 isConnectable: $$Props['isConnectable'] = undefined;
// @todo: there must be a better way to do this
id;
data;
selected;
sourcePosition;
targetPosition;
type;
zIndex;
dragging;
dragHandle;
xPos;
yPos;
isConnectable;
</script>
@@ -7,6 +7,30 @@
export let data: $$Props['data'] = { label: 'Node' };
export let sourcePosition: $$Props['sourcePosition'] = Position.Bottom;
// unused props - we need to list them here in order to prevent warnings
export let id: $$Props['id'] = '';
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 isConnectable: $$Props['isConnectable'] = undefined;
// @todo: there must be a better way to do this
id;
selected;
targetPosition;
type;
zIndex;
dragging;
dragHandle;
xPos;
yPos;
isConnectable;
</script>
{data?.label}
@@ -7,6 +7,30 @@
export let data: $$Props['data'] = { label: 'Node' };
export let targetPosition: $$Props['targetPosition'] = Position.Top;
// unused props - we need to list them here in order to prevent warnings
export let id: $$Props['id'] = '';
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 isConnectable: $$Props['isConnectable'] = undefined;
// @todo: there must be a better way to do this
id;
selected;
sourcePosition;
type;
zIndex;
dragging;
dragHandle;
xPos;
yPos;
isConnectable;
</script>
{data?.label}
@@ -29,7 +29,34 @@
($elementsSelectable && typeof edge.selectable === 'undefined')
)}
<EdgeWrapper {...edge} type={edgeType} {selectable} on:edgeclick on:edgecontextmenu />
<EdgeWrapper
id={edge.id}
source={edge.source}
target={edge.target}
data={edge.data}
style={edge.style}
animated={edge.animated}
selected={edge.selected}
hidden={edge.hidden}
label={edge.label}
labelStyle={edge.labelStyle}
markerStart={edge.markerStart}
markerEnd={edge.markerEnd}
sourceHandle={edge.sourceHandle}
targetHandle={edge.targetHandle}
sourceX={edge.sourceX}
sourceY={edge.sourceY}
targetX={edge.targetX}
targetY={edge.targetY}
sourcePosition={edge.sourcePosition}
targetPosition={edge.targetPosition}
ariaLabel={edge.ariaLabel}
class={edge.class}
type={edgeType}
{selectable}
on:edgeclick
on:edgecontextmenu
/>
{/each}
</g>
</svg>
@@ -41,7 +41,14 @@
export let panOnDrag: $$Props['panOnDrag'] = undefined;
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher<{
paneclick: {
event: MouseEvent | TouchEvent;
};
panecontextmenu: {
event: MouseEvent;
};
}>();
const {
nodes,
edges,
@@ -85,25 +85,4 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
onMove?: OnMove;
onMoveEnd?: OnMoveEnd;
onError?: OnError;
'on:nodeclick'?: CustomEvent<{ event: MouseEvent | TouchEvent; node: Node }>;
'on:nodemouseenter'?: CustomEvent<{ event: MouseEvent; node: Node }>;
'on:nodemousemove'?: CustomEvent<{ event: MouseEvent; node: Node }>;
'on:nodemouseleave'?: CustomEvent<{ event: MouseEvent; node: Node }>;
'on:edgeclick'?: CustomEvent<{ event: MouseEvent; edge: Edge }>;
'on:edgecontextmenu'?: CustomEvent<{ event: MouseEvent; edge: Edge }>;
'on:connectstart'?: CustomEvent<{
event: MouseEvent | TouchEvent;
nodeId?: string;
handleId?: string;
handleType?: HandleType;
}>;
'on:connect'?: CustomEvent<{ connection: Connection }>;
'on:connectend'?: CustomEvent<{ event: MouseEvent | TouchEvent }>;
'on:paneclick'?: CustomEvent<{ event: MouseEvent | TouchEvent }>;
'on:panecontextmenu'?: CustomEvent<{ event: MouseEvent }>;
'on:nodedragstart'?: CustomEvent<{ event: MouseEvent; node: NodeBase; nodes: NodeBase[] }>;
'on:nodedrag'?: CustomEvent<{ event: MouseEvent; node: NodeBase; nodes: NodeBase[] }>;
'on:nodedragstop'?: CustomEvent<{ event: MouseEvent; node: NodeBase; nodes: NodeBase[] }>;
'on:nodecontextmenu'?: CustomEvent<{ event: MouseEvent; node: NodeBase }>;
};
@@ -16,6 +16,7 @@
type $$Props = BackgroundProps;
export let id: $$Props['id'] = undefined;
export let variant: $$Props['variant'] = BackgroundVariant.Dots;
export let gap: $$Props['gap'] = 20;
export let size: $$Props['size'] = 1;
@@ -32,7 +33,7 @@
const isCross = variant === BackgroundVariant.Cross;
const gapXY: number[] = Array.isArray(gap!) ? gap! : [gap!, gap!];
$: patternId = `background-pattern-${$flowId}`;
$: patternId = `background-pattern-${$flowId}-${id ? id : ''}`;
$: scaledGap = [gapXY[0] * $viewport.zoom || 1, gapXY[1] * $viewport.zoom || 1];
$: scaledSize = patternSize * $viewport.zoom;
$: patternDimensions = (isCross ? [scaledSize, scaledSize] : scaledGap) as [number, number];
@@ -5,6 +5,7 @@ export enum BackgroundVariant {
}
export type BackgroundProps = {
id?: string;
bgColor?: string;
patternColor?: string;
patternClass?: string;
@@ -15,7 +15,7 @@
export let position: $$Props['position'] = 'bottom-left';
export let showZoom: $$Props['showZoom'] = true;
export let showFitView: $$Props['showFitView'] = true;
export let showInteractive: $$Props['showInteractive'] = true;
export let showLock: $$Props['showLock'] = true;
export let buttonBgColor: $$Props['buttonBgColor'] = undefined;
export let buttonBgColorHover: $$Props['buttonBgColorHover'] = undefined;
export let buttonColor: $$Props['buttonColor'] = undefined;
@@ -101,7 +101,7 @@
<FitViewIcon />
</ControlButton>
{/if}
{#if showInteractive}
{#if showLock}
<ControlButton
class="svelte-flow__controls-interactive"
on:click={onToggleInteractivity}
@@ -4,7 +4,7 @@ export type ControlsProps = {
position?: PanelPosition;
showZoom?: boolean;
showFitView?: boolean;
showInteractive?: boolean;
showLock?: boolean;
buttonBgColor?: string;
buttonBgColorHover?: string;
buttonColor?: string;
@@ -121,6 +121,7 @@
y={pos.y}
width={node.width}
height={node.height}
selected={node.selected}
color={nodeColorFunc(node)}
borderRadius={nodeBorderRadius}
strokeColor={nodeStrokeColorFunc(node)}
@@ -10,12 +10,14 @@
export let shapeRendering: string;
export let strokeColor: string;
export let strokeWidth: number = 2;
export let selected: boolean = false;
let className: string = '';
export { className as class };
</script>
<rect
class={cc(['svelte-flow__minimap-node', className])}
class:selected
{x}
{y}
rx={borderRadius}
@@ -20,6 +20,7 @@ import {
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
import InputNode from '$lib/components/nodes/InputNode.svelte';
import OutputNode from '$lib/components/nodes/OutputNode.svelte';
import 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';
@@ -31,7 +32,8 @@ import { initConnectionProps, type ConnectionProps } from './derived-connection-
export const initialNodeTypes = {
input: InputNode,
output: OutputNode,
default: DefaultNode
default: DefaultNode,
group: GroupNode
};
export const initialEdgeTypes = {
+4 -4
View File
@@ -10,13 +10,11 @@ import type {
import type { Node } from '$lib/types';
export type DefaultEdge<EdgeData = any> = EdgeBase<EdgeData> & {
export type DefaultEdge<EdgeData = any> = Omit<EdgeBase<EdgeData>, 'focusable'> & {
label?: string;
labelStyle?: string;
style?: string;
class?: string;
sourceNode?: Node;
targetNode?: Node;
};
type SmoothStepEdgeType<T> = DefaultEdge<T> & {
@@ -47,7 +45,7 @@ export type EdgeProps = Omit<Edge, 'sourceHandle' | 'targetHandle'> &
export type EdgeTypes = Record<string, ComponentType<SvelteComponent<EdgeProps>>>;
export type DefaultEdgeOptions = DefaultEdgeOptionsBase<Edge>;
export type DefaultEdgeOptions = Omit<DefaultEdgeOptionsBase<Edge>, 'focusable'>;
export type EdgeLayouted = Pick<
Edge,
@@ -72,6 +70,8 @@ export type EdgeLayouted = Pick<
| 'class'
> &
EdgePosition & {
sourceNode?: Node;
targetNode?: Node;
sourceHandleId?: string | null;
targetHandleId?: string | null;
};
+8
View File
@@ -7,3 +7,11 @@
position: absolute;
font-size: 10px;
}
.svelte-flow__nodes {
z-index: 0;
}
.svelte-flow__edgelabel-renderer {
z-index: 0;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@xyflow/system",
"version": "0.0.7",
"version": "0.0.8",
"description": "xyflow core system that powers React Flow and Svelte Flow.",
"keywords": [
"node-based UI",