Merge branch 'main' into middlewares
This commit is contained in:
@@ -17,9 +17,9 @@
|
||||
"@types/react-dom": "^18.2.8",
|
||||
"@xyflow/react": "workspace:^",
|
||||
"@xyflow/svelte": "workspace:^",
|
||||
"astro": "^5.9.2",
|
||||
"astro": "^5.13.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"svelte": "^5.33.18"
|
||||
"svelte": "^5.38.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ const initialNodes: Node[] = [
|
||||
{
|
||||
id: '3',
|
||||
data: { label: 'Node 3' },
|
||||
position: { x: 400, y: 100 },
|
||||
position: { x: 900, y: 100 },
|
||||
...nodeSize,
|
||||
handles: [
|
||||
{
|
||||
@@ -110,9 +110,10 @@ function Flow() {
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
nodeTypes={nodeTypes}
|
||||
fitView
|
||||
// fitView
|
||||
width={700}
|
||||
height={400}
|
||||
onlyRenderVisibleElements
|
||||
>
|
||||
<Background />
|
||||
<Controls />
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
},
|
||||
{
|
||||
id: 'C',
|
||||
position: { x: 250, y: 300 },
|
||||
position: { x: 950, y: 300 },
|
||||
data: { label: 'C' },
|
||||
sourcePosition: Position.Right,
|
||||
targetPosition: Position.Left,
|
||||
@@ -79,7 +79,7 @@
|
||||
</script>
|
||||
|
||||
<div style="height: 400px; width: 700px;">
|
||||
<SvelteFlow bind:nodes bind:edges fitView {defaultEdgeOptions} width={700} height={400}>
|
||||
<SvelteFlow bind:nodes bind:edges onlyRenderVisibleElements {defaultEdgeOptions} width={700} height={400}>
|
||||
<Controls />
|
||||
<Background variant={BackgroundVariant.Dots} />
|
||||
<ViewportPortal target="front">
|
||||
|
||||
@@ -50,6 +50,7 @@ import CancelConnection from '../examples/CancelConnection';
|
||||
import InteractiveMinimap from '../examples/InteractiveMinimap';
|
||||
import UseOnSelectionChange from '../examples/UseOnSelectionChange';
|
||||
import NodeToolbar from '../examples/NodeToolbar';
|
||||
import EdgeToolbar from '../examples/EdgeToolbar';
|
||||
import UseConnection from '../examples/UseConnection';
|
||||
import UseNodesInitialized from '../examples/UseNodesInit';
|
||||
import UseNodesData from '../examples/UseNodesData';
|
||||
@@ -193,6 +194,11 @@ const routes: IRoute[] = [
|
||||
path: 'edge-routing',
|
||||
component: EdgeRouting,
|
||||
},
|
||||
{
|
||||
name: 'Edge Toolbar',
|
||||
path: 'edge-toolbar',
|
||||
component: EdgeToolbar,
|
||||
},
|
||||
{
|
||||
name: 'Empty',
|
||||
path: 'empty',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MouseEvent, useCallback } from 'react';
|
||||
import { MouseEvent, useCallback, useState } from 'react';
|
||||
import {
|
||||
ReactFlow,
|
||||
MiniMap,
|
||||
@@ -134,47 +134,57 @@ const BasicFlow = () => {
|
||||
});
|
||||
fitView();
|
||||
};
|
||||
const [isHidden, setIsHidden] = useState(false);
|
||||
|
||||
const toggleVisibility = () => {
|
||||
setIsHidden(!isHidden);
|
||||
};
|
||||
return (
|
||||
<ReactFlow
|
||||
defaultNodes={initialNodes}
|
||||
defaultEdges={initialEdges}
|
||||
onNodesChange={console.log}
|
||||
onNodeClick={onNodeClick}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
onNodeDragStart={onNodeDragStart}
|
||||
onNodeDrag={onNodeDrag}
|
||||
onSelectionDragStart={printSelectionEvent('selection drag start')}
|
||||
onSelectionDrag={printSelectionEvent('selection drag')}
|
||||
onSelectionDragStop={printSelectionEvent('selection drag stop')}
|
||||
className="react-flow-basic-example"
|
||||
minZoom={0.2}
|
||||
maxZoom={4}
|
||||
fitView
|
||||
fitViewOptions={fitViewOptions}
|
||||
defaultEdgeOptions={defaultEdgeOptions}
|
||||
selectNodesOnDrag={false}
|
||||
elevateEdgesOnSelect
|
||||
elevateNodesOnSelect={false}
|
||||
nodeDragThreshold={0}
|
||||
>
|
||||
<Background variant={BackgroundVariant.Dots} />
|
||||
<MiniMap />
|
||||
<Controls />
|
||||
<>
|
||||
<ReactFlow
|
||||
defaultNodes={initialNodes}
|
||||
defaultEdges={initialEdges}
|
||||
onNodesChange={console.log}
|
||||
onNodeClick={onNodeClick}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
onNodeDragStart={onNodeDragStart}
|
||||
onNodeDrag={onNodeDrag}
|
||||
onSelectionDragStart={printSelectionEvent('selection drag start')}
|
||||
onSelectionDrag={printSelectionEvent('selection drag')}
|
||||
onSelectionDragStop={printSelectionEvent('selection drag stop')}
|
||||
className="react-flow-basic-example"
|
||||
style={{ display: isHidden ? 'none' : 'block' }}
|
||||
minZoom={0.2}
|
||||
maxZoom={4}
|
||||
fitView
|
||||
fitViewOptions={fitViewOptions}
|
||||
defaultEdgeOptions={defaultEdgeOptions}
|
||||
selectNodesOnDrag={false}
|
||||
elevateEdgesOnSelect
|
||||
elevateNodesOnSelect={false}
|
||||
nodeDragThreshold={0}
|
||||
>
|
||||
<Background variant={BackgroundVariant.Dots} />
|
||||
<MiniMap />
|
||||
<Controls />
|
||||
|
||||
<Panel position="top-right">
|
||||
<button onClick={resetTransform}>reset transform</button>
|
||||
<button onClick={updatePos}>change pos</button>
|
||||
<button onClick={toggleClassnames}>toggle classnames</button>
|
||||
<button onClick={logToObject}>toObject</button>
|
||||
<Panel position="top-right">
|
||||
<button onClick={resetTransform}>reset transform</button>
|
||||
<button onClick={updatePos}>change pos</button>
|
||||
<button onClick={toggleClassnames}>toggle classnames</button>
|
||||
<button onClick={logToObject}>toObject</button>
|
||||
|
||||
<button onClick={deleteSelectedElements}>deleteSelectedElements</button>
|
||||
<button onClick={deleteSomeElements}>deleteSomeElements</button>
|
||||
<button onClick={onSetNodes}>setNodes</button>
|
||||
<button onClick={onUpdateNode}>updateNode</button>
|
||||
<button onClick={addNode}>addNode</button>
|
||||
</Panel>
|
||||
</ReactFlow>
|
||||
<button onClick={deleteSelectedElements}>deleteSelectedElements</button>
|
||||
<button onClick={deleteSomeElements}>deleteSomeElements</button>
|
||||
<button onClick={onSetNodes}>setNodes</button>
|
||||
<button onClick={onUpdateNode}>updateNode</button>
|
||||
<button onClick={addNode}>addNode</button>
|
||||
</Panel>
|
||||
</ReactFlow>
|
||||
<button onClick={toggleVisibility} style={{ position: 'absolute', zIndex: 10, right: 10, top: 100 }}>
|
||||
{isHidden ? 'Show' : 'Hide'} Flow
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ConnectionLineComponentProps } from '@xyflow/react';
|
||||
|
||||
function ConnectionLine({ fromX, fromY, toX, toY }: ConnectionLineComponentProps) {
|
||||
function ConnectionLine({ fromX, fromY, toX, toY, pointer }: ConnectionLineComponentProps) {
|
||||
console.log('pointer', pointer);
|
||||
return (
|
||||
<>
|
||||
<path
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MouseEvent, CSSProperties, useCallback } from 'react';
|
||||
import { MouseEvent, CSSProperties, useCallback, useState } from 'react';
|
||||
|
||||
import {
|
||||
ReactFlow,
|
||||
@@ -14,19 +14,13 @@ import {
|
||||
ReactFlowInstance,
|
||||
useEdgesState,
|
||||
useNodesState,
|
||||
Panel,
|
||||
} from '@xyflow/react';
|
||||
|
||||
const onInit = (reactFlowInstance: ReactFlowInstance) => console.log('flow loaded:', reactFlowInstance);
|
||||
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
|
||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||
|
||||
const buttonStyle: CSSProperties = {
|
||||
position: 'absolute',
|
||||
left: 10,
|
||||
top: 10,
|
||||
zIndex: 4,
|
||||
};
|
||||
|
||||
const CustomMiniMapNode = ({ x, y, width, height }: MiniMapNodeProps) => {
|
||||
return <circle cx={x} cy={y} r={Math.max(width, height) / 2} fill="#ffcc00" />;
|
||||
};
|
||||
@@ -34,6 +28,7 @@ const CustomMiniMapNode = ({ x, y, width, height }: MiniMapNodeProps) => {
|
||||
const CustomMiniMapNodeFlow = () => {
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState<Node>([]);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
|
||||
const [hideAllNodes, setHideAllNodes] = useState(false);
|
||||
|
||||
const onConnect = useCallback((params: Connection | Edge) => setEdges((els) => addEdge(params, els)), [setEdges]);
|
||||
const addRandomNode = () => {
|
||||
@@ -45,10 +40,20 @@ const CustomMiniMapNodeFlow = () => {
|
||||
x: Math.random() * window.innerWidth,
|
||||
y: Math.random() * window.innerHeight,
|
||||
},
|
||||
hidden: hideAllNodes,
|
||||
};
|
||||
setNodes((nds) => nds.concat(newNode));
|
||||
};
|
||||
|
||||
const toggleHideAllNodes = () => {
|
||||
setHideAllNodes(prev => {
|
||||
const next = !prev;
|
||||
setNodes(nds => nds.map(n => ({ ...n, hidden: next })));
|
||||
setEdges(eds => eds.map(e => ({ ...e, hidden: next })));
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
@@ -65,9 +70,14 @@ const CustomMiniMapNodeFlow = () => {
|
||||
<Background variant={BackgroundVariant.Lines} />
|
||||
<MiniMap nodeComponent={CustomMiniMapNode} />
|
||||
|
||||
<button type="button" onClick={addRandomNode} style={buttonStyle}>
|
||||
add node
|
||||
</button>
|
||||
<Panel position="top-left">
|
||||
<button type="button" onClick={addRandomNode}>
|
||||
add node
|
||||
</button>
|
||||
<button type="button" onClick={toggleHideAllNodes}>
|
||||
{hideAllNodes ? 'show all nodes' : 'hide all nodes'}
|
||||
</button>
|
||||
</Panel>
|
||||
</ReactFlow>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ function ColorSelectorNode({ data, isConnectable }: NodeProps<ColorSelectorNode>
|
||||
<div>
|
||||
Custom Color Picker Node: <strong>{data.color}</strong>
|
||||
</div>
|
||||
<input className="nodrag" type="color" onChange={data.onChange} defaultValue={data.color} />
|
||||
<input className="nodrag nokey" type="color" onChange={data.onChange} defaultValue={data.color} />
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Right}
|
||||
|
||||
40
examples/react/src/examples/EdgeToolbar/CustomEdge.tsx
Normal file
40
examples/react/src/examples/EdgeToolbar/CustomEdge.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { getBezierPath, BaseEdge, EdgeProps, useReactFlow, getStraightPath, getSmoothStepPath } from '@xyflow/react';
|
||||
import { EdgeToolbar } from '@xyflow/react';
|
||||
|
||||
const getPath = (props: EdgeProps) => {
|
||||
switch (props.data!.type) {
|
||||
case 'smoothstep':
|
||||
return getSmoothStepPath(props);
|
||||
case 'straight':
|
||||
return getStraightPath(props);
|
||||
default:
|
||||
return getBezierPath(props);
|
||||
}
|
||||
};
|
||||
|
||||
export function CustomEdge(props: EdgeProps) {
|
||||
const [edgePath, centerX, centerY] = getPath(props);
|
||||
const { setEdges } = useReactFlow();
|
||||
|
||||
const deleteEdge = () => {
|
||||
setEdges((edges) => edges.filter((edge) => edge.id !== props.id));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<BaseEdge id={props.id} path={edgePath} />
|
||||
<EdgeToolbar
|
||||
edgeId={props.id}
|
||||
x={centerX + 0}
|
||||
y={centerY + 0}
|
||||
alignX={props.data?.align?.[0] ?? 'center'}
|
||||
alignY={props.data?.align?.[1] ?? 'center'}
|
||||
isVisible
|
||||
>
|
||||
<button style={{}} onClick={deleteEdge}>
|
||||
Delete
|
||||
</button>
|
||||
</EdgeToolbar>
|
||||
</>
|
||||
);
|
||||
}
|
||||
80
examples/react/src/examples/EdgeToolbar/index.tsx
Normal file
80
examples/react/src/examples/EdgeToolbar/index.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
Background,
|
||||
BackgroundVariant,
|
||||
Controls,
|
||||
Edge,
|
||||
EdgeTypes,
|
||||
MiniMap,
|
||||
Node,
|
||||
Position,
|
||||
ReactFlow,
|
||||
} from '@xyflow/react';
|
||||
|
||||
import { CustomEdge } from './CustomEdge';
|
||||
|
||||
const edgeTypes: EdgeTypes = {
|
||||
custom: CustomEdge,
|
||||
};
|
||||
|
||||
const initialNodes: Node[] = [
|
||||
{
|
||||
id: '1',
|
||||
data: { label: 'Node 1', toolbarPosition: Position.Top },
|
||||
position: { x: 0, y: 0 },
|
||||
className: 'react-flow__node-default',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
data: { label: 'Node 2', toolbarPosition: Position.Top },
|
||||
position: { x: 100, y: 150 },
|
||||
className: 'react-flow__node-default',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
data: { label: 'Node 3', toolbarPosition: Position.Top },
|
||||
position: { x: 200, y: 0 },
|
||||
className: 'react-flow__node-default',
|
||||
},
|
||||
];
|
||||
|
||||
const initialEdges: Edge[] = [
|
||||
{
|
||||
id: 'e1-2',
|
||||
source: '1',
|
||||
target: '2',
|
||||
type: 'custom',
|
||||
data: { type: 'smoothstep', align: ['left', 'bottom'] },
|
||||
},
|
||||
{
|
||||
id: 'e3-2',
|
||||
source: '3',
|
||||
target: '2',
|
||||
type: 'custom',
|
||||
data: { type: 'bezier', align: ['right', 'bottom'] },
|
||||
},
|
||||
{
|
||||
id: 'e1-3',
|
||||
source: '1',
|
||||
target: '3',
|
||||
type: 'custom',
|
||||
data: { type: 'straight', align: ['center', 'center'] },
|
||||
},
|
||||
];
|
||||
|
||||
export default function EdgeToolbarExample() {
|
||||
return (
|
||||
<ReactFlow
|
||||
defaultNodes={initialNodes}
|
||||
defaultEdges={initialEdges}
|
||||
className="react-flow-edge-toolbar-example"
|
||||
minZoom={0.2}
|
||||
maxZoom={4}
|
||||
fitView
|
||||
edgeTypes={edgeTypes}
|
||||
>
|
||||
<Background variant={BackgroundVariant.Dots} />
|
||||
<MiniMap />
|
||||
<Controls />
|
||||
</ReactFlow>
|
||||
);
|
||||
}
|
||||
@@ -30,10 +30,11 @@ const BasicFlow = () => {
|
||||
<ReactFlow
|
||||
defaultNodes={initialNodes}
|
||||
defaultEdges={initialEdges}
|
||||
selectionOnDrag
|
||||
selectionOnDrag={true}
|
||||
selectionMode={SelectionMode.Partial}
|
||||
panOnDrag={panOnDrag}
|
||||
panOnScroll
|
||||
paneClickDistance={100}
|
||||
zoomActivationKeyCode="Meta"
|
||||
multiSelectionKeyCode={MULTI_SELECT_KEY}
|
||||
onPaneContextMenu={onPaneContextMenu}
|
||||
@@ -43,6 +44,12 @@ const BasicFlow = () => {
|
||||
onMoveStart={onMoveStart}
|
||||
onMove={onMove}
|
||||
onMoveEnd={onMoveEnd}
|
||||
onPaneClick={(e) => console.log('pane click', e)}
|
||||
onSelectionStart={(e) => console.log('on selection start', e)}
|
||||
onSelectionEnd={(e) => console.log('on selection end', e)}
|
||||
onPointerDown={(e) => console.log('pointer down', e)}
|
||||
onPointerUp={(e) => console.log('pointer up', e)}
|
||||
onClick={(e) => console.log('click', e)}
|
||||
>
|
||||
<Background variant={BackgroundVariant.Cross} />
|
||||
<Controls />
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { memo, FC } from 'react';
|
||||
import { Handle, Position, NodeProps, NodeResizer } from '@xyflow/react';
|
||||
import { Handle, Position, NodeProps, NodeResizer, useKeyPress } from '@xyflow/react';
|
||||
|
||||
const DefaultResizerNode: FC<NodeProps> = ({ data, selected }) => {
|
||||
const keepAspectRatio = useKeyPress('k');
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeResizer
|
||||
@@ -14,7 +16,7 @@ const DefaultResizerNode: FC<NodeProps> = ({ data, selected }) => {
|
||||
onResizeStart={data.onResizeStart ?? undefined}
|
||||
onResize={data.onResize ?? undefined}
|
||||
onResizeEnd={data.onResizeEnd ?? undefined}
|
||||
keepAspectRatio={data.keepAspectRatio ?? undefined}
|
||||
keepAspectRatio={keepAspectRatio || (data.keepAspectRatio ?? undefined)}
|
||||
/>
|
||||
<Handle type="target" position={Position.Left} />
|
||||
<div>{data.label}</div>
|
||||
|
||||
@@ -239,6 +239,16 @@ const StressFlow = () => {
|
||||
<button onClick={remount}>re-mount</button>
|
||||
<button onClick={updatePos}>change pos</button>
|
||||
<button onClick={updateElements}>update elements</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setNodes((nds) => [
|
||||
...nds,
|
||||
{ id: (nds.length + 1).toString(), position: { x: 0, y: 0 }, data: { label: `Node ${nds.length + 1}` } },
|
||||
]);
|
||||
}}
|
||||
>
|
||||
Add element
|
||||
</button>
|
||||
</Panel>
|
||||
</ReactFlow>
|
||||
);
|
||||
|
||||
@@ -27,15 +27,15 @@ const onEdgeClick = (_: MouseEvent, edge: Edge) => console.log('click', edge);
|
||||
|
||||
const defaultViewport = { x: 0, y: 0, zoom: 1.5 };
|
||||
const initialNodes: Node[] = [
|
||||
{
|
||||
id: 'extent',
|
||||
position: { x: 0, y: 0 },
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
data: { label: 'Extent' },
|
||||
origin: [0, 0],
|
||||
zIndex: -1,
|
||||
},
|
||||
// {
|
||||
// id: 'extent',
|
||||
// position: { x: 0, y: 0 },
|
||||
// width: 1000,
|
||||
// height: 1000,
|
||||
// data: { label: 'Extent' },
|
||||
// origin: [0, 0],
|
||||
// zIndex: -1,
|
||||
// },
|
||||
{
|
||||
id: '1',
|
||||
type: 'input',
|
||||
@@ -96,12 +96,12 @@ const initialNodes: Node[] = [
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
type: 'group',
|
||||
// type: 'group',
|
||||
data: { label: 'Node 5' },
|
||||
position: { x: 650, y: 250 },
|
||||
className: 'light',
|
||||
style: { width: 100, height: 100 },
|
||||
zIndex: 1000,
|
||||
// zIndex: 1000,
|
||||
},
|
||||
{
|
||||
id: '5a',
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
'add-node-on-drop',
|
||||
'color-mode',
|
||||
'custom-connection-line',
|
||||
'custom-minimap',
|
||||
'customnode',
|
||||
'dagre',
|
||||
'detached-handle',
|
||||
'drag-n-drop',
|
||||
'edges',
|
||||
'edge-toolbar',
|
||||
'figma',
|
||||
'handle-connect',
|
||||
'interaction',
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
$inspect(connection.current.pointer);
|
||||
</script>
|
||||
|
||||
{#if connection.current.inProgress}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
SvelteFlow,
|
||||
Background,
|
||||
Controls,
|
||||
MiniMap,
|
||||
Panel,
|
||||
type Node,
|
||||
type Edge
|
||||
} from '@xyflow/svelte';
|
||||
|
||||
import '@xyflow/svelte/dist/style.css';
|
||||
import CustomMiniMapNode from './CustomMiniMapNode.svelte';
|
||||
|
||||
let nodes = $state.raw<Node[]>([]);
|
||||
let edges = $state.raw<Edge[]>([]);
|
||||
let hideAllNodes = $state(false);
|
||||
|
||||
function addRandomNode() {
|
||||
const nodeId = (nodes.length + 1).toString();
|
||||
const newNode: Node = {
|
||||
id: nodeId,
|
||||
data: { label: `Node: ${nodeId}` },
|
||||
position: {
|
||||
x: Math.random() * (typeof window !== 'undefined' ? window.innerWidth : 800),
|
||||
y: Math.random() * (typeof window !== 'undefined' ? window.innerHeight : 600)
|
||||
},
|
||||
type: 'default',
|
||||
hidden: hideAllNodes
|
||||
};
|
||||
nodes = [...nodes, newNode];
|
||||
}
|
||||
|
||||
function toggleHideAllNodes() {
|
||||
hideAllNodes = !hideAllNodes;
|
||||
nodes = nodes.map((node) => ({
|
||||
...node,
|
||||
hidden: hideAllNodes
|
||||
}));
|
||||
}
|
||||
</script>
|
||||
|
||||
<SvelteFlow bind:nodes bind:edges onlyRenderVisibleElements={true}>
|
||||
<Controls />
|
||||
<Background />
|
||||
<MiniMap nodeComponent={CustomMiniMapNode} />
|
||||
|
||||
<Panel position="top-left">
|
||||
<button type="button" onclick={addRandomNode}> add node </button>
|
||||
|
||||
<button type="button" onclick={toggleHideAllNodes}>
|
||||
{hideAllNodes ? 'show all nodes' : 'hide all nodes'}
|
||||
</button>
|
||||
</Panel>
|
||||
</SvelteFlow>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import type { MiniMapNodeProps } from '@xyflow/svelte';
|
||||
|
||||
let { id, x, y, width, height, selected }: MiniMapNodeProps = $props();
|
||||
</script>
|
||||
|
||||
<circle
|
||||
cx={x + width / 2}
|
||||
cy={y + height / 2}
|
||||
r={Math.min(width, height) - 2}
|
||||
fill={selected ? '#ff6b6b' : '#ffcc00'}
|
||||
/>
|
||||
<text
|
||||
x={x + width / 2}
|
||||
y={y + height / 2}
|
||||
text-anchor="middle"
|
||||
dominant-baseline="middle"
|
||||
fill="blue"
|
||||
stroke-width="0.5"
|
||||
font-size="100"
|
||||
font-family="Arial, sans-serif"
|
||||
font-weight="bold"
|
||||
>
|
||||
{id}
|
||||
</text>
|
||||
@@ -13,7 +13,7 @@
|
||||
Custom Color Picker Node: <strong>{bg.color}</strong>
|
||||
</div>
|
||||
<input
|
||||
class="nodrag"
|
||||
class="nodrag nokey"
|
||||
type="color"
|
||||
oninput={(evt) => (bg.color = evt.currentTarget.value)}
|
||||
value={bg.color}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<script lang="ts">
|
||||
import { Background, Position, SvelteFlow, type Edge, type Node } from '@xyflow/svelte';
|
||||
|
||||
import '@xyflow/svelte/dist/style.css';
|
||||
|
||||
import CustomEdge from './CustomEdge.svelte';
|
||||
|
||||
const edgeTypes = {
|
||||
custom: CustomEdge
|
||||
};
|
||||
|
||||
const initialNodes: Node[] = [
|
||||
{
|
||||
id: '1',
|
||||
data: { label: 'Node 1', toolbarPosition: Position.Top },
|
||||
position: { x: 0, y: 0 },
|
||||
class: 'svelte-flow__node-default'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
data: { label: 'Node 2', toolbarPosition: Position.Top },
|
||||
position: { x: 100, y: 150 },
|
||||
class: 'svelte-flow__node-default'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
data: { label: 'Node 3', toolbarPosition: Position.Top },
|
||||
position: { x: 200, y: 0 },
|
||||
class: 'svelte-flow__node-default'
|
||||
}
|
||||
];
|
||||
|
||||
const initialEdges: Edge[] = [
|
||||
{
|
||||
id: 'e1-2',
|
||||
source: '1',
|
||||
target: '2',
|
||||
type: 'custom'
|
||||
},
|
||||
{
|
||||
id: 'e3-2',
|
||||
source: '3',
|
||||
target: '2',
|
||||
type: 'custom'
|
||||
},
|
||||
{
|
||||
id: 'e1-3',
|
||||
source: '1',
|
||||
target: '3',
|
||||
type: 'custom'
|
||||
}
|
||||
];
|
||||
|
||||
let nodes = $state.raw<Node[]>(initialNodes);
|
||||
let edges = $state.raw<Edge[]>(initialEdges);
|
||||
</script>
|
||||
|
||||
<SvelteFlow bind:nodes bind:edges {edgeTypes} fitView>
|
||||
<Background />
|
||||
</SvelteFlow>
|
||||
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { getBezierPath, BaseEdge, type EdgeProps, useSvelteFlow } from '@xyflow/svelte';
|
||||
import { EdgeToolbar } from '@xyflow/svelte';
|
||||
|
||||
const { deleteElements } = useSvelteFlow();
|
||||
|
||||
let { id, sourceX, sourceY, targetX, targetY }: EdgeProps = $props();
|
||||
|
||||
let [edgePath, labelX, labelY] = $derived(
|
||||
getBezierPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY
|
||||
})
|
||||
);
|
||||
|
||||
const deleteEdge = () => {
|
||||
deleteElements({ edges: [{ id }] });
|
||||
};
|
||||
</script>
|
||||
|
||||
<BaseEdge {id} path={edgePath} />
|
||||
<EdgeToolbar edgeId={id} x={labelX} y={labelY} isVisible>
|
||||
<button onclick={deleteEdge}>Delete</button>
|
||||
</EdgeToolbar>
|
||||
@@ -9,11 +9,6 @@
|
||||
|
||||
import '@xyflow/svelte/dist/style.css';
|
||||
|
||||
const onPaneContextMenu = (e: any) => {
|
||||
e.preventDefault();
|
||||
console.log('context menu');
|
||||
};
|
||||
|
||||
const panOnDrag = [1, 2];
|
||||
|
||||
const onmovestart = (e: any) => console.log('move start', e);
|
||||
@@ -44,12 +39,17 @@
|
||||
bind:edges
|
||||
fitView
|
||||
selectionMode={SelectionMode.Partial}
|
||||
multiSelectionKey={['Meta', 'Shift']}
|
||||
selectionOnDrag
|
||||
panOnScroll
|
||||
paneClickDistance={100}
|
||||
{panOnDrag}
|
||||
{onmovestart}
|
||||
{onmove}
|
||||
{onmoveend}
|
||||
onpaneclick={(e) => console.log('on pane click', e)}
|
||||
onselectionend={(e) => console.log('on selection end', e)}
|
||||
onselectionstart={(e) => console.log('on selection start', e)}
|
||||
>
|
||||
<Controls />
|
||||
<Background variant={BackgroundVariant.Dots} />
|
||||
|
||||
@@ -5,15 +5,18 @@
|
||||
import CustomResizer from './CustomResizer.svelte';
|
||||
import VerticalResizer from './VerticalResizer.svelte';
|
||||
import HorizontalResizer from './HorizontalResizer.svelte';
|
||||
import BottomRightResizer from './BottomRightResizer.svelte';
|
||||
|
||||
import '@xyflow/svelte/dist/style.css';
|
||||
import type { ResizeNode } from './types';
|
||||
import { store } from './store.svelte';
|
||||
|
||||
const nodeTypes = {
|
||||
defaultResizer: DefaultResizer,
|
||||
customResizer: CustomResizer,
|
||||
verticalResizer: VerticalResizer,
|
||||
horizontalResizer: HorizontalResizer
|
||||
horizontalResizer: HorizontalResizer,
|
||||
bottomRightResizer: BottomRightResizer
|
||||
};
|
||||
|
||||
const nodeStyle = 'border: 1px solid #222; font-size: 10px; background-color: #ddd;';
|
||||
@@ -124,12 +127,28 @@
|
||||
position: { x: 100, y: 100 },
|
||||
parentId: '5',
|
||||
style: nodeStyle
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
type: 'bottomRightResizer',
|
||||
data: { label: 'bottom-right horizontal resizer' },
|
||||
position: { x: 500, y: 0 },
|
||||
style: nodeStyle
|
||||
}
|
||||
]);
|
||||
|
||||
let snapToGrid = $state(false);
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'k') store.keepAspectRatio = true;
|
||||
}}
|
||||
on:keyup={(e) => {
|
||||
if (e.key === 'k') store.keepAspectRatio = false;
|
||||
}}
|
||||
/>
|
||||
|
||||
<SvelteFlow
|
||||
bind:nodes
|
||||
bind:edges
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
Handle,
|
||||
Position,
|
||||
type NodeProps,
|
||||
NodeResizeControl,
|
||||
ResizeControlVariant
|
||||
} from '@xyflow/svelte';
|
||||
import type { ResizeNode } from './types';
|
||||
|
||||
let { data }: NodeProps<ResizeNode> = $props();
|
||||
</script>
|
||||
|
||||
<NodeResizeControl
|
||||
variant={ResizeControlVariant.Handle}
|
||||
position="bottom-right"
|
||||
resizeDirection="horizontal"
|
||||
minWidth={100}
|
||||
maxWidth={500}
|
||||
color="orange"
|
||||
autoScale={false}
|
||||
/>
|
||||
<Handle type="target" position={Position.Left} />
|
||||
<div>{data.label}</div>
|
||||
<Handle type="source" position={Position.Right} />
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Handle, Position, type NodeProps, NodeResizeControl, type Node } from '@xyflow/svelte';
|
||||
import type { ResizeNode } from './types';
|
||||
import { store } from './store.svelte';
|
||||
|
||||
let { data }: NodeProps<ResizeNode> = $props();
|
||||
</script>
|
||||
@@ -14,7 +15,7 @@
|
||||
onResizeStart={data.onResizeStart ?? undefined}
|
||||
onResize={data.onResize ?? undefined}
|
||||
onResizeEnd={data.onResizeEnd ?? undefined}
|
||||
keepAspectRatio={data.keepAspectRatio ?? undefined}
|
||||
keepAspectRatio={store.keepAspectRatio || (data.keepAspectRatio ?? undefined)}
|
||||
style="background: transparent; border: none;"
|
||||
>
|
||||
<svg
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import { Handle, NodeResizer, Position, type NodeProps } from '@xyflow/svelte';
|
||||
import type { ResizeNode } from './types';
|
||||
|
||||
import { store } from './store.svelte';
|
||||
|
||||
let { data, selected }: NodeProps<ResizeNode> = $props();
|
||||
</script>
|
||||
|
||||
@@ -15,7 +17,7 @@
|
||||
onResizeStart={data.onResizeStart ?? undefined}
|
||||
onResize={data.onResize ?? undefined}
|
||||
onResizeEnd={data.onResizeEnd ?? undefined}
|
||||
keepAspectRatio={data.keepAspectRatio ?? undefined}
|
||||
keepAspectRatio={store.keepAspectRatio || (data.keepAspectRatio ?? undefined)}
|
||||
/>
|
||||
<Handle type="target" position={Position.Left} />
|
||||
<div>{data.label}</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Handle, NodeResizeControl, Position, type NodeProps } from '@xyflow/svelte';
|
||||
import type { ResizeNode } from './types';
|
||||
import { store } from './store.svelte';
|
||||
|
||||
let { data }: NodeProps<ResizeNode> = $props();
|
||||
</script>
|
||||
@@ -14,7 +15,7 @@
|
||||
onResizeStart={data.onResizeStart ?? undefined}
|
||||
onResize={data.onResize ?? undefined}
|
||||
onResizeEnd={data.onResizeEnd ?? undefined}
|
||||
keepAspectRatio={data.keepAspectRatio ?? undefined}
|
||||
keepAspectRatio={store.keepAspectRatio || (data.keepAspectRatio ?? undefined)}
|
||||
color="red"
|
||||
position={Position.Left}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Handle, NodeResizeControl, Position, type NodeProps } from '@xyflow/svelte';
|
||||
import type { ResizeNode } from './types';
|
||||
import { store } from './store.svelte';
|
||||
|
||||
let { data }: NodeProps<ResizeNode> = $props();
|
||||
</script>
|
||||
@@ -14,7 +15,7 @@
|
||||
onResizeStart={data.onResizeStart ?? undefined}
|
||||
onResize={data.onResize ?? undefined}
|
||||
onResizeEnd={data.onResizeEnd ?? undefined}
|
||||
keepAspectRatio={data.keepAspectRatio ?? undefined}
|
||||
keepAspectRatio={store.keepAspectRatio || (data.keepAspectRatio ?? undefined)}
|
||||
color="red"
|
||||
position={Position.Top}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
function createStore() {
|
||||
let keepAspectRatio = $state(false);
|
||||
|
||||
return {
|
||||
get keepAspectRatio() {
|
||||
return keepAspectRatio;
|
||||
},
|
||||
set keepAspectRatio(value: boolean) {
|
||||
keepAspectRatio = value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const store = createStore();
|
||||
@@ -155,6 +155,8 @@
|
||||
// console.log(edges.map((edge) => ({ id: edge.id, selected: edge.selected })));
|
||||
edges.forEach((edge) => console.log({ id: edge.id, selected: edge.selected }));
|
||||
});
|
||||
|
||||
let hidden = $state(false);
|
||||
</script>
|
||||
|
||||
<!-- oninit={() => console.log('on init')}
|
||||
@@ -197,6 +199,7 @@
|
||||
}} -->
|
||||
|
||||
<SvelteFlow
|
||||
style="display:{hidden ? 'none' : 'block'}"
|
||||
bind:nodes
|
||||
bind:edges
|
||||
{nodeTypes}
|
||||
@@ -247,7 +250,20 @@
|
||||
<InitTracker />
|
||||
</SvelteFlow>
|
||||
|
||||
<button
|
||||
class="hide"
|
||||
onclick={() => {
|
||||
hidden = !hidden;
|
||||
}}>Hide/Unhide</button
|
||||
>
|
||||
|
||||
<style>
|
||||
.hide {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
:root {
|
||||
/* --background-color: #ffffdd; */
|
||||
--background-pattern-color: #5050ff;
|
||||
|
||||
Reference in New Issue
Block a user