diff --git a/examples/CustomNode/CustomNode.vue b/examples/CustomNode/CustomNode.vue index 6c7cd42b..b163e888 100644 --- a/examples/CustomNode/CustomNode.vue +++ b/examples/CustomNode/CustomNode.vue @@ -45,7 +45,10 @@ const CustomNodeFlow = defineComponent({ selectorNode: ColorSelectorNode }; - const onLoad = (revueFlowInstance: OnLoadParams) => console.log('flow loaded:', revueFlowInstance); + const onLoad = (revueFlowInstance: OnLoadParams) => { + revueFlowInstance.fitView(); + console.log('flow loaded:', revueFlowInstance); + }; const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element); const nodeStroke = (n: Node): string => { diff --git a/examples/Edges/CustomEdge.tsx b/examples/Edges/CustomEdge.tsx new file mode 100644 index 00000000..1e04c67f --- /dev/null +++ b/examples/Edges/CustomEdge.tsx @@ -0,0 +1,31 @@ +import { EdgeProps, getBezierPath, getMarkerEnd } from '../../src'; +import { FunctionalComponent } from 'vue'; + +const CustomEdge: FunctionalComponent = ({ + id, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + data, + arrowHeadType, + markerEndId +}) => { + const edgePath = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition }); + const markerEnd = getMarkerEnd(arrowHeadType, markerEndId); + + return () => ( + <> + + + + {data.text} + + + + ); +}; + +export default CustomEdge; diff --git a/examples/Edges/CustomEdge2.tsx b/examples/Edges/CustomEdge2.tsx new file mode 100644 index 00000000..e84f32be --- /dev/null +++ b/examples/Edges/CustomEdge2.tsx @@ -0,0 +1,44 @@ +import { FunctionalComponent } from 'vue'; +import { EdgeProps, EdgeText, getBezierPath, getEdgeCenter, getMarkerEnd } from '../../src'; + +const CustomEdge: FunctionalComponent = ({ + id, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + data, + arrowHeadType, + markerEndId +}) => { + const edgePath = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition }); + const markerEnd = getMarkerEnd(arrowHeadType, markerEndId); + const [centerX, centerY] = getEdgeCenter({ + sourceX, + sourceY, + targetX, + targetY + }); + + return () => ( + <> + + console.log(data)} + /> + ; + + ); +}; + +export default CustomEdge; diff --git a/examples/Edges/index.tsx b/examples/Edges/index.tsx new file mode 100644 index 00000000..93878747 --- /dev/null +++ b/examples/Edges/index.tsx @@ -0,0 +1,112 @@ +import { FunctionalComponent, ref } from 'vue'; +import CustomEdge from './CustomEdge'; +import CustomEdge2 from './CustomEdge2'; +import RevueFlow, { + Elements, + FlowElement, + OnLoadParams, + ArrowHeadType, + Background, + Controls, + MiniMap, + addEdge, + Connection, + Edge, + EdgeProps, + removeElements +} from '../../src'; + +const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView(); +const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); +const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element); + +const initialElements = [ + { id: '1', type: 'input', data: { label: 'Input 1' }, position: { x: 250, y: 0 } }, + { id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } }, + { id: '2a', data: { label: 'Node 2a' }, position: { x: 0, y: 180 } }, + { id: '3', data: { label: 'Node 3' }, position: { x: 250, y: 200 } }, + { id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 300 } }, + { id: '3a', data: { label: 'Node 3a' }, position: { x: 150, y: 300 } }, + { id: '5', data: { label: 'Node 5' }, position: { x: 250, y: 400 } }, + { id: '6', type: 'output', data: { label: 'Output 6' }, position: { x: 50, y: 550 } }, + { id: '7', type: 'output', data: { label: 'Output 7' }, position: { x: 250, y: 550 } }, + { id: '8', type: 'output', data: { label: 'Output 8' }, position: { x: 525, y: 600 } }, + { id: '9', type: 'output', data: { label: 'Output 9' }, position: { x: 675, y: 500 } }, + { id: 'e1-2', source: '1', target: '2', label: 'bezier edge (default)', className: 'normal-edge' }, + { id: 'e2-2a', source: '2', target: '2a', type: 'smoothstep', label: 'smoothstep edge' }, + { id: 'e2-3', source: '2', target: '3', type: 'step', label: 'step edge' }, + { id: 'e3-4', source: '3', target: '4', type: 'straight', label: 'straight edge' }, + { id: 'e3-3a', source: '3', target: '3a', type: 'straight', label: 'label only edge', style: { stroke: 'none' } }, + { id: 'e3-5', source: '4', target: '5', animated: true, label: 'animated styled edge', style: { stroke: 'red' } }, + { + id: 'e5-6', + source: '5', + target: '6', + label: ( + <> + i am using + + {''} + + + ), + labelStyle: { fill: 'red', fontWeight: 700 }, + arrowHeadType: ArrowHeadType.Arrow + }, + { + id: 'e5-7', + source: '5', + target: '7', + label: 'label with styled bg', + labelBgPadding: [8, 4], + labelBgBorderRadius: 4, + labelBgStyle: { fill: '#FFCC00', color: '#fff', fillOpacity: 0.7 }, + arrowHeadType: ArrowHeadType.ArrowClosed + }, + { + id: 'e5-8', + source: '5', + target: '8', + type: 'custom', + data: { text: 'custom edge' }, + arrowHeadType: ArrowHeadType.ArrowClosed + }, + { + id: 'e5-9', + source: '5', + target: '9', + type: 'custom2', + data: { text: 'custom edge 2' } + } +]; + +const edgeTypes: Record> = { + custom: CustomEdge, + custom2: CustomEdge2 +}; + +const EdgesFlow = () => { + const elements = ref(initialElements as Elements); + + const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value)); + const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value)); + + return ( + + + + + + ); +}; + +export default EdgesFlow; diff --git a/examples/index.css b/examples/index.css index 8a67a756..b69664e6 100644 --- a/examples/index.css +++ b/examples/index.css @@ -1,10 +1,10 @@ body { - font-family: sans-serif; color: #111; } #root { - font-family: Avenir, Helvetica, Arial, sans-serif; + text-transform: uppercase; + font-family: 'JetBrains Mono', monospace; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; diff --git a/examples/router.ts b/examples/router.ts index efbe90b0..7f3a8c94 100644 --- a/examples/router.ts +++ b/examples/router.ts @@ -16,6 +16,10 @@ export const routes = [ { path: '/drag-n-drop', component: () => import('./DragNDrop/DnD.vue') + }, + { + path: '/edges', + component: () => import('./Edges') } ]; diff --git a/package.json b/package.json index 6e5afe4d..3ea7266d 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "dist" ], "scripts": { - "dev": "vite", + "dev": "vue-tsc --noEmit && vite", "build": "vue-tsc --noEmit && vite build", "build:dist": "rollup -c --environment NODE_ENV:production", "serve": "vite preview", diff --git a/src/components/NodesSelection/index.tsx b/src/components/NodesSelection/index.tsx index a8436c4e..40118f0a 100644 --- a/src/components/NodesSelection/index.tsx +++ b/src/components/NodesSelection/index.tsx @@ -3,10 +3,11 @@ * The nodes selection rectangle gets displayed when a user * made a selectio with on or several nodes */ -import { computed, defineComponent, inject, PropType } from 'vue'; +import { computed, defineComponent, inject } from 'vue'; import { Draggable, DraggableEventListener } from '@braks/revue-draggable'; import { isNode } from '../../utils/graph'; import { Node, RevueFlowStore } from '../../types'; +import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'; export interface NodesSelectionProps { onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void; @@ -16,31 +17,11 @@ export interface NodesSelectionProps { } const NodesSelection = defineComponent({ - props: { - onSelectionDragStart: { - type: Function as unknown as PropType, - required: false, - default: undefined - }, - onSelectionDrag: { - type: Function as unknown as PropType, - required: false, - default: undefined - }, - onSelectionDragStop: { - type: Function as unknown as PropType, - required: false, - default: undefined - }, - onSelectionContextMenu: { - type: Function as unknown as PropType, - required: false, - default: undefined - } - }, - setup(props) { + setup() { const store = inject('store')!; + const hooks = inject('hooks')!; const grid = computed(() => (store.snapToGrid ? store.snapGrid : [1, 1])! as [number, number]); + const transform = computed(() => store.transform); const selectedNodes = computed(() => store.selectedElements @@ -67,11 +48,11 @@ const NodesSelection = defineComponent({ })); const onStart: DraggableEventListener = ({ event }) => { - props.onSelectionDragStart?.(event, selectedNodes.value); + hooks.selectionDragStart.trigger({ event, nodes: selectedNodes.value }); }; const onDrag: DraggableEventListener = ({ event, data }) => { - props.onSelectionDrag?.(event, selectedNodes.value); + hooks.selectionDrag.trigger({ event, nodes: selectedNodes.value }); store.updateNodePosDiff({ diff: { @@ -87,15 +68,15 @@ const NodesSelection = defineComponent({ isDragging: false }); - props.onSelectionDragStop?.(event, selectedNodes.value); + hooks.selectionDragStop.trigger({ event, nodes: selectedNodes.value }); }; const onContextMenu = (event: MouseEvent) => { - const selectedNodes = store.selectedElements + const selectedNodes: Node[] = store.selectedElements ? store.selectedElements.filter(isNode).map((selectedNode) => store.nodes.find((node) => node.id === selectedNode.id)) : []; - props.onSelectionContextMenu?.(event, selectedNodes as any); + hooks.selectionContextMenu.trigger({ event, nodes: selectedNodes }); }; return () => { @@ -107,7 +88,7 @@ const NodesSelection = defineComponent({ onStart={onStart} onMove={onDrag} onStop={onStop} - scale={store.transform[2]} + scale={transform.value[2]} grid={grid.value} enableUserSelectHack={false} >