chore(lint): Update eslint rules

* Remove semi
This commit is contained in:
Braks
2021-10-20 22:39:54 +02:00
parent 5ed1094dfe
commit 77eac0c12a
74 changed files with 2007 additions and 1827 deletions
+14 -15
View File
@@ -3,12 +3,12 @@ const baseRules = {
'@typescript-eslint/ban-ts-ignore': 0,
'@typescript-eslint/ban-ts-comment': 0,
'@typescript-eslint/no-empty-function': 0,
'@typescript-eslint/explicit-module-boundary-types': 0,
'no-use-before-define': 0,
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['warn'],
indent: ['warn', 2, { SwitchCase: 1, flatTernaryExpressions: true }],
quotes: ['error', 'single', { avoidEscape: true }],
semi: ['error', 'always'],
'import/no-mutable-exports': 0,
'no-cond-assign': [2],
'no-console': 'off',
@@ -16,28 +16,27 @@ const baseRules = {
'@typescript/camelcase': 0,
'vue/no-v-html': 'off',
'vue/attribute-hyphenation': 0,
'prettier/prettier': [
'warn',
{
singleQuote: true,
trailingComma: 'none',
semi: true,
bracketSpacing: true,
vueIndentScriptAndStyle: false,
printWidth: 130
}
]
};
'no-undef': 'off',
'prettier/prettier': ['error', {}, { usePrettierrc: true }]
}
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
env: {
node: true,
commonjs: true,
es6: true,
browser: true
},
extends: ['plugin:vue/vue3-recommended', 'eslint:recommended', '@vue/typescript/recommended', 'plugin:prettier/recommended'],
extends: ['eslint:recommended', 'plugin:vue/vue3-recommended', '@vue/typescript/recommended', 'plugin:prettier/recommended'],
plugins: [],
rules: baseRules
};
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"singleQuote": true,
"trailingComma": "none",
"semi": true,
"semi": false,
"bracketSpacing": true,
"vueIndentScriptAndStyle": false,
"printWidth": 130
+7 -7
View File
@@ -1,6 +1,6 @@
import { computed, defineComponent } from 'vue';
import Header from './Header';
import { useRoute } from 'vue-router';
import { computed, defineComponent } from 'vue'
import Header from './Header'
import { useRoute } from 'vue-router'
export default defineComponent({
name: 'App',
@@ -8,13 +8,13 @@ export default defineComponent({
Header
},
setup() {
const route = useRoute();
const key = computed(() => route.fullPath);
const route = useRoute()
const key = computed(() => route.fullPath)
return () => (
<>
<Header />
<router-view ref="view" key={key.value} />
</>
);
)
}
});
})
+6 -6
View File
@@ -1,9 +1,9 @@
const onDragStart = (event: DragEvent, nodeType: string) => {
if (event.dataTransfer) {
event.dataTransfer.setData('application/revueflow', nodeType);
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('application/revueflow', nodeType)
event.dataTransfer.effectAllowed = 'move'
}
};
}
const Sidebar = () => {
return (
@@ -19,7 +19,7 @@ const Sidebar = () => {
Output Node
</div>
</aside>
);
};
)
}
export default Sidebar;
export default Sidebar
+13 -13
View File
@@ -11,22 +11,22 @@ import RevueFlow, {
Connection,
Edge,
Elements
} from '../../src';
import { getElements } from './utils';
import { defineComponent, ref } from 'vue';
} from '../../src'
import { getElements } from './utils'
import { defineComponent, ref } from 'vue'
const onLoad = (reactFlowInstance: OnLoadParams) => {
reactFlowInstance.fitView();
console.log(reactFlowInstance.getElements());
};
reactFlowInstance.fitView()
console.log(reactFlowInstance.getElements())
}
const initialElements = getElements();
const initialElements = getElements()
const EdgeTypesFlow = defineComponent({
setup() {
const elements = ref<Elements>(initialElements);
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value));
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
const elements = ref<Elements>(initialElements)
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
return () => (
<RevueFlow
@@ -36,8 +36,8 @@ const EdgeTypesFlow = defineComponent({
onConnect={onConnect}
minZoom={0.2}
></RevueFlow>
);
)
}
});
})
export default EdgeTypesFlow;
export default EdgeTypesFlow
+27 -27
View File
@@ -1,18 +1,18 @@
import { ElementId, Elements, Position } from '../../src';
import { ElementId, Elements, Position } from '../../src'
const nodeWidth = 80;
const nodeGapWidth = nodeWidth * 2;
const nodeStyle = { width: nodeWidth + 'px', fontSize: '11px', color: 'white' };
const nodeWidth = 80
const nodeGapWidth = nodeWidth * 2
const nodeStyle = { width: nodeWidth + 'px', fontSize: '11px', color: 'white' }
const sourceTargetPositions = [
{ source: Position.Bottom, target: Position.Top },
{ source: Position.Right, target: Position.Left }
];
]
const nodeColors = [
['#1e9e99', '#4cb3ac', '#6ec9c0', '#8ddfd4'],
['#0f4c75', '#1b5d8b', '#276fa1', '#3282b8']
];
const edgeTypes = ['default', 'step', 'smoothstep', 'straight'];
]
const edgeTypes = ['default', 'step', 'smoothstep', 'straight']
const offsets = [
{
x: 0,
@@ -46,30 +46,30 @@ const offsets = [
x: -nodeGapWidth,
y: -nodeGapWidth
}
];
]
let id = 0;
const getNodeId = (): ElementId => (id++).toString();
let id = 0
const getNodeId = (): ElementId => (id++).toString()
export function getElements(): Elements {
const initialElements = [];
const initialElements = []
for (let sourceTargetIndex = 0; sourceTargetIndex < sourceTargetPositions.length; sourceTargetIndex++) {
const currSourceTargetPos = sourceTargetPositions[sourceTargetIndex];
const currSourceTargetPos = sourceTargetPositions[sourceTargetIndex]
for (let edgeTypeIndex = 0; edgeTypeIndex < edgeTypes.length; edgeTypeIndex++) {
const currEdgeType = edgeTypes[edgeTypeIndex];
const currEdgeType = edgeTypes[edgeTypeIndex]
for (let offsetIndex = 0; offsetIndex < offsets.length; offsetIndex++) {
const currOffset = offsets[offsetIndex];
const currOffset = offsets[offsetIndex]
const style = { ...nodeStyle, background: nodeColors[sourceTargetIndex][edgeTypeIndex] };
const style = { ...nodeStyle, background: nodeColors[sourceTargetIndex][edgeTypeIndex] }
const sourcePosition = {
x: offsetIndex * nodeWidth * 4,
y: edgeTypeIndex * 300 + sourceTargetIndex * edgeTypes.length * 300
};
const sourceId = getNodeId();
const sourceData = { label: `Source ${sourceId}` };
}
const sourceId = getNodeId()
const sourceData = { label: `Source ${sourceId}` }
const sourceNode = {
id: sourceId,
style,
@@ -77,14 +77,14 @@ export function getElements(): Elements {
position: sourcePosition,
sourcePosition: currSourceTargetPos.source,
targetPosition: currSourceTargetPos.target
};
}
const targetId = getNodeId();
const targetData = { label: `Target ${targetId}` };
const targetId = getNodeId()
const targetData = { label: `Target ${targetId}` }
const targetPosition = {
x: sourcePosition.x + currOffset.x,
y: sourcePosition.y + currOffset.y
};
}
const targetNode = {
id: targetId,
style,
@@ -92,15 +92,15 @@ export function getElements(): Elements {
position: targetPosition,
sourcePosition: currSourceTargetPos.source,
targetPosition: currSourceTargetPos.target
};
}
initialElements.push(sourceNode);
initialElements.push(targetNode);
initialElements.push(sourceNode)
initialElements.push(targetNode)
initialElements.push({ id: `${sourceId}-${targetId}`, source: sourceId, target: targetId, type: currEdgeType });
initialElements.push({ id: `${sourceId}-${targetId}`, source: sourceId, target: targetId, type: currEdgeType })
}
}
}
return initialElements as Elements;
return initialElements as Elements
}
+17 -17
View File
@@ -1,25 +1,25 @@
import { computed, defineComponent, inject } from 'vue';
import { getBezierPath, getEdgeCenter, getMarkerEnd, RevueFlowStore } from '../../src';
import { DefaultEdgeProps } from '../../src/components/Edges/utils';
import { RevueFlowHooks } from '../../src/hooks/RevueFlowHooks';
import { computed, defineComponent, inject } from 'vue'
import { getBezierPath, getEdgeCenter, getMarkerEnd, RevueFlowStore } from '../../src'
import { DefaultEdgeProps } from '../../src/components/Edges/utils'
import { RevueFlowHooks } from '../../src/hooks/RevueFlowHooks'
export default defineComponent({
props: {
...DefaultEdgeProps
},
setup(props) {
const store = inject<RevueFlowStore>('store')!;
const hooks = inject<RevueFlowHooks>('hooks')!;
const store = inject<RevueFlowStore>('store')!
const hooks = inject<RevueFlowHooks>('hooks')!
const onEdgeClick = (evt: Event, id: string) => {
const edge = store.edges.find((edge) => edge.id === id);
const edge = store.edges.find((edge) => edge.id === id)
if (edge) {
hooks.elementsRemove.trigger([edge]);
hooks.elementsRemove.trigger([edge])
}
evt.stopPropagation();
alert(`remove ${id}`);
};
evt.stopPropagation()
alert(`remove ${id}`)
}
const foreignObjectSize = 40;
const foreignObjectSize = 40
const edgePath = computed(() =>
getBezierPath({
@@ -30,8 +30,8 @@ export default defineComponent({
targetY: props.targetY,
targetPosition: props.targetPosition
})
);
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId));
)
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
const center = computed(() =>
getEdgeCenter({
sourceX: props.sourceX,
@@ -39,7 +39,7 @@ export default defineComponent({
targetX: props.targetX,
targetY: props.targetY
})
);
)
return () => (
<>
@@ -59,6 +59,6 @@ export default defineComponent({
</body>
</foreignObject>
</>
);
)
}
});
})
+8 -8
View File
@@ -1,6 +1,6 @@
import { getBezierPath, getMarkerEnd } from '../../src';
import { computed, defineComponent } from 'vue';
import { DefaultEdgeProps } from '../../src/components/Edges/utils';
import { getBezierPath, getMarkerEnd } from '../../src'
import { computed, defineComponent } from 'vue'
import { DefaultEdgeProps } from '../../src/components/Edges/utils'
const CustomEdge = defineComponent({
props: {
@@ -16,8 +16,8 @@ const CustomEdge = defineComponent({
targetY: props.targetY,
targetPosition: props.targetPosition
})
);
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId));
)
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
return () => (
<>
@@ -28,8 +28,8 @@ const CustomEdge = defineComponent({
</textPath>
</text>
</>
);
)
}
});
})
export default CustomEdge;
export default CustomEdge
+9 -9
View File
@@ -1,6 +1,6 @@
import { computed, defineComponent } from 'vue';
import { EdgeText, getBezierPath, getEdgeCenter, getMarkerEnd } from '../../src';
import { DefaultEdgeProps } from '../../src/components/Edges/utils';
import { computed, defineComponent } from 'vue'
import { EdgeText, getBezierPath, getEdgeCenter, getMarkerEnd } from '../../src'
import { DefaultEdgeProps } from '../../src/components/Edges/utils'
const CustomEdge = defineComponent({
props: {
@@ -16,8 +16,8 @@ const CustomEdge = defineComponent({
targetY: props.targetY,
targetPosition: props.targetPosition
})
);
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId));
)
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
const center = computed(() =>
getEdgeCenter({
sourceX: props.sourceX,
@@ -25,7 +25,7 @@ const CustomEdge = defineComponent({
targetX: props.targetX,
targetY: props.targetY
})
);
)
return () => (
<>
@@ -42,8 +42,8 @@ const CustomEdge = defineComponent({
onClick={() => console.log(props.data)}
/>
</>
);
)
}
});
})
export default CustomEdge;
export default CustomEdge
+15 -15
View File
@@ -1,6 +1,6 @@
import { ref } from 'vue';
import CustomEdge from './CustomEdge';
import CustomEdge2 from './CustomEdge2';
import { ref } from 'vue'
import CustomEdge from './CustomEdge'
import CustomEdge2 from './CustomEdge2'
import RevueFlow, {
Elements,
FlowElement,
@@ -13,7 +13,7 @@ import RevueFlow, {
Connection,
Edge,
removeElements
} from '../../src';
} from '../../src'
const initialElements = [
{ id: '1', type: 'input', data: { label: 'Input 1' }, position: { x: 250, y: 0 } },
@@ -73,21 +73,21 @@ const initialElements = [
type: 'custom2',
data: { text: 'custom edge 2' }
}
];
]
const edgeTypes: Record<string, any> = {
custom: CustomEdge,
custom2: CustomEdge2
};
}
const EdgesFlow = () => {
const elements = ref<Elements>(initialElements as Elements);
const elements = ref<Elements>(initialElements as Elements)
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 onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value));
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
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 onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
return (
<RevueFlow
@@ -104,7 +104,7 @@ const EdgesFlow = () => {
<Controls />
<Background />
</RevueFlow>
);
};
)
}
export default EdgesFlow;
export default EdgesFlow
+16 -17
View File
@@ -12,32 +12,31 @@ import RevueFlow, {
BackgroundVariant,
Connection,
Edge
} from '../../src';
import { CSSProperties, defineComponent, ref } from 'vue';
} from '../../src'
import { CSSProperties, defineComponent, ref } from 'vue'
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance);
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance)
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element)
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node)
const buttonStyle: CSSProperties = { position: 'absolute', left: '10px', top: '10px', zIndex: 4 };
const buttonStyle: CSSProperties = { position: 'absolute', left: '10px', top: '10px', zIndex: 4 }
const EmptyFlow = defineComponent({
setup() {
{
const elements = ref<Elements>([]);
const onElementsRemove = (elementsToRemove: Elements) =>
(elements.value = removeElements(elementsToRemove, elements.value));
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
const elements = ref<Elements>([])
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
const addRandomNode = () => {
const nodeId: ElementId = (elements.value.length + 1).toString();
const nodeId: ElementId = (elements.value.length + 1).toString()
const newNode: Node = {
id: nodeId,
data: { label: `Node: ${nodeId}` },
position: { x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight }
} as Node;
elements.value = [...elements.value, newNode];
};
} as Node
elements.value = [...elements.value, newNode]
}
return () => (
<RevueFlow
@@ -57,9 +56,9 @@ const EmptyFlow = defineComponent({
add node
</button>
</RevueFlow>
);
)
}
}
});
})
export default EmptyFlow;
export default EmptyFlow
+10 -10
View File
@@ -1,14 +1,14 @@
import { defineComponent } from 'vue';
import { routes } from './router';
import { useRoute, useRouter } from 'vue-router';
import { defineComponent } from 'vue'
import { routes } from './router'
import { useRoute, useRouter } from 'vue-router'
const Header = defineComponent({
setup() {
const router = useRouter();
const route = useRoute();
const router = useRouter()
const route = useRoute()
const onChange = async (event: any) => {
await router.push(event.target.value);
};
await router.push(event.target.value)
}
return () => (
<header>
@@ -25,8 +25,8 @@ const Header = defineComponent({
)}
</select>
</header>
);
)
}
});
})
export default Header;
export default Header
+17 -17
View File
@@ -1,5 +1,5 @@
import RevueFlow, { addEdge, MiniMap, Controls, Connection, Edge, Elements } from '../../src';
import { defineComponent, ref, watchEffect } from 'vue';
import RevueFlow, { addEdge, MiniMap, Controls, Connection, Edge, Elements } from '../../src'
import { defineComponent, ref, watchEffect } from 'vue'
const initialElements: Elements = [
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
@@ -9,26 +9,26 @@ const initialElements: Elements = [
{ id: 'e1-2', source: '1', target: '2' },
{ id: 'e1-3', source: '1', target: '3' },
{ id: 'e3-4', source: '3', target: '4' }
] as Elements;
] as Elements
const HiddenFlow = defineComponent({
setup() {
const elements = ref<Elements>(initialElements);
const isHidden = ref<boolean>(false);
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
const elements = ref<Elements>(initialElements)
const isHidden = ref<boolean>(false)
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
watchEffect(() => {
console.log(isHidden.value);
console.log(isHidden.value)
elements.value = elements.value.map((e) => {
e.isHidden = isHidden.value;
return e;
});
});
e.isHidden = isHidden.value
return e
})
})
const toggle = (event) => {
console.log(event.target);
isHidden.value = event.target?.checked;
};
console.log(event.target)
isHidden.value = event.target?.checked
}
return () => (
<RevueFlow v-model={elements.value} onConnect={onConnect}>
@@ -44,8 +44,8 @@ const HiddenFlow = defineComponent({
</div>
</div>
</RevueFlow>
);
)
}
});
})
export default HiddenFlow;
export default HiddenFlow
+27 -27
View File
@@ -9,8 +9,8 @@ import RevueFlow, {
Edge,
PanOnScrollMode,
FlowTransform
} from '../../src';
import { defineComponent, ref } from 'vue';
} from '../../src'
import { defineComponent, ref } from 'vue'
const initialElements: Elements = [
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
@@ -19,33 +19,33 @@ const initialElements: Elements = [
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
{ id: 'e1-2', source: '1', target: '2', animated: true },
{ id: 'e1-3', source: '1', target: '3' }
] as Elements;
] as Elements
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
const onPaneClick = (event: MouseEvent) => console.log('onPaneClick', event);
const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event);
const onPaneContextMenu = (event: MouseEvent) => console.log('onPaneContextMenu', event);
const onMoveEnd = (flowTranasform?: FlowTransform) => console.log('onMoveEnd', flowTranasform);
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node)
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node)
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element)
const onPaneClick = (event: MouseEvent) => console.log('onPaneClick', event)
const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event)
const onPaneContextMenu = (event: MouseEvent) => console.log('onPaneContextMenu', event)
const onMoveEnd = (flowTranasform?: FlowTransform) => console.log('onMoveEnd', flowTranasform)
const InteractionFlow = defineComponent({
setup() {
const elements = ref<Elements>(initialElements);
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
const elements = ref<Elements>(initialElements)
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
const isSelectable = ref(false);
const isDraggable = ref(false);
const isConnectable = ref(false);
const zoomOnScroll = ref(false);
const zoomOnPinch = ref(false);
const panOnScroll = ref(false);
const panOnScrollMode = ref<PanOnScrollMode>(PanOnScrollMode.Free);
const zoomOnDoubleClick = ref(false);
const paneMoveable = ref(true);
const captureZoomClick = ref(false);
const captureZoomScroll = ref(false);
const captureElementClick = ref(false);
const isSelectable = ref(false)
const isDraggable = ref(false)
const isConnectable = ref(false)
const zoomOnScroll = ref(false)
const zoomOnPinch = ref(false)
const panOnScroll = ref(false)
const panOnScrollMode = ref<PanOnScrollMode>(PanOnScrollMode.Free)
const zoomOnDoubleClick = ref(false)
const paneMoveable = ref(true)
const captureZoomClick = ref(false)
const captureZoomScroll = ref(false)
const captureElementClick = ref(false)
return () => (
<RevueFlow
@@ -165,8 +165,8 @@ const InteractionFlow = defineComponent({
</div>
</div>
</RevueFlow>
);
)
}
});
})
export default InteractionFlow;
export default InteractionFlow
+12 -12
View File
@@ -1,14 +1,14 @@
import { createApp } from 'vue';
import './index.css';
import App from './App';
import { router } from './router';
import { createPinia } from 'pinia';
import { DraggablePlugin } from '@braks/revue-draggable';
import { createApp } from 'vue'
import './index.css'
import App from './App'
import { router } from './router'
import { createPinia } from 'pinia'
import { DraggablePlugin } from '@braks/revue-draggable'
const app = createApp(App);
const app = createApp(App)
app.config.performance = true;
app.use(router);
app.use(DraggablePlugin);
app.use(createPinia());
app.mount('#root');
app.config.performance = true
app.use(router)
app.use(DraggablePlugin)
app.use(createPinia())
app.mount('#root')
+3 -3
View File
@@ -1,4 +1,4 @@
import { createRouter, createWebHashHistory, RouterOptions } from 'vue-router';
import { createRouter, createWebHashHistory, RouterOptions } from 'vue-router'
export const routes: RouterOptions['routes'] = [
{
@@ -45,9 +45,9 @@ export const routes: RouterOptions['routes'] = [
path: '/interaction',
component: () => import('./Interaction')
}
];
]
export const router = createRouter({
history: createWebHashHistory(),
routes
});
})
+1
View File
@@ -76,6 +76,7 @@
"rollup-plugin-typescript2": "^0.30.0",
"tslib": "^2.3.0",
"typescript": "^4.3.5",
"unplugin-auto-import": "^0.4.12",
"vite": "^2.3.8",
"vite-svg-loader": "^2.1.0",
"vue": "^3.2.0-beta.8",
+1 -1
View File
@@ -1,3 +1,3 @@
module.exports = {
plugins: [require('autoprefixer'), require('postcss-nested')]
};
}
+2 -2
View File
@@ -1,3 +1,3 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('esbuild-register');
module.exports = require('./rollup.ts');
require('esbuild-register')
module.exports = require('./rollup.ts')
+21 -22
View File
@@ -1,39 +1,38 @@
import typescript from 'rollup-plugin-typescript2';
import { terser } from 'rollup-plugin-terser';
import dts from 'rollup-plugin-dts';
import { OutputOptions, RollupOptions } from 'rollup';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import babel from '@rollup/plugin-babel';
import typescript from 'rollup-plugin-typescript2'
import { terser } from 'rollup-plugin-terser'
import dts from 'rollup-plugin-dts'
import commonjs from '@rollup/plugin-commonjs'
import resolve from '@rollup/plugin-node-resolve'
import babel from '@rollup/plugin-babel'
// @ts-ignore
import svg from 'rollup-plugin-svg';
import postcss from 'rollup-plugin-postcss';
import svg from 'rollup-plugin-svg'
import postcss from 'rollup-plugin-postcss'
// @ts-ignore
import { DEFAULT_EXTENSIONS as DEFAULT_BABEL_EXTENSIONS } from '@babel/core';
import { DEFAULT_EXTENSIONS as DEFAULT_BABEL_EXTENSIONS } from '@babel/core'
const configs: RollupOptions[] = [];
const configs: any[] = []
const activePackages = [
{
display: 'Revue-Flow',
external: ['@vueuse/core']
}
];
]
// @ts-ignore
for (const { external, iife } of activePackages) {
const iifeGlobals = {
vue: 'Vue',
'@vueuse/core': 'VueUse'
};
}
const iifeName = 'RevueFlow';
const functionNames = ['revue-flow'];
const iifeName = 'RevueFlow'
const functionNames = ['revue-flow']
for (const fn of functionNames) {
const input = 'src/index.ts';
const input = 'src/index.ts'
const output: OutputOptions[] = [
const output: any[] = [
{
file: `dist/${fn}.cjs.js`,
format: 'cjs'
@@ -42,7 +41,7 @@ for (const { external, iife } of activePackages) {
file: `dist/${fn}.esm.js`,
format: 'es'
}
];
]
if (iife !== false) {
output.push(
@@ -67,7 +66,7 @@ for (const { external, iife } of activePackages) {
})
]
}
);
)
}
configs.push({
@@ -95,7 +94,7 @@ for (const { external, iife } of activePackages) {
})
],
external: ['vue', ...(external || [])]
});
})
configs.push({
input,
@@ -105,8 +104,8 @@ for (const { external, iife } of activePackages) {
},
plugins: [dts()],
external: ['vue', ...(external || []), /\.css$/]
});
})
}
}
export default configs;
export default configs
+21 -21
View File
@@ -1,18 +1,18 @@
import { BackgroundVariant, RevueFlowStore } from '../../types';
import { createGridDotsPath, createGridLinesPath } from './utils';
import { computed, defineComponent, HTMLAttributes, inject, PropType } from 'vue';
import { BackgroundVariant, RevueFlowStore } from '../../types'
import { createGridDotsPath, createGridLinesPath } from './utils'
import { computed, defineComponent, HTMLAttributes, inject, PropType } from 'vue'
export interface BackgroundProps extends HTMLAttributes {
variant?: BackgroundVariant;
gap?: number;
color?: string;
size?: number;
variant?: BackgroundVariant
gap?: number
color?: string
size?: number
}
const defaultColors = {
[BackgroundVariant.Dots]: '#81818a',
[BackgroundVariant.Lines]: '#eee'
};
}
const Background = defineComponent({
name: 'Background',
@@ -39,23 +39,23 @@ const Background = defineComponent({
}
},
setup(props) {
const store = inject<RevueFlowStore>('store')!;
const transform = computed(() => store.transform);
const store = inject<RevueFlowStore>('store')!
const transform = computed(() => store.transform)
// when there are multiple flows on a page we need to make sure that every background gets its own pattern.
const patternId = `pattern-${Math.floor(Math.random() * 100000)}`;
const patternId = `pattern-${Math.floor(Math.random() * 100000)}`
const bgClasses = ['revue-flow__background'];
const scaledGap = computed(() => props.gap && props.gap * transform.value[2]);
const xOffset = computed(() => scaledGap.value && transform.value[0] % scaledGap.value);
const yOffset = computed(() => scaledGap.value && transform.value[1] % scaledGap.value);
const bgClasses = ['revue-flow__background']
const scaledGap = computed(() => props.gap && props.gap * transform.value[2])
const xOffset = computed(() => scaledGap.value && transform.value[0] % scaledGap.value)
const yOffset = computed(() => scaledGap.value && transform.value[1] % scaledGap.value)
const isLines = computed(() => props.variant === BackgroundVariant.Lines);
const bgColor = computed(() => (props.color ? props.color : defaultColors[props.variant || BackgroundVariant.Dots]));
const isLines = computed(() => props.variant === BackgroundVariant.Lines)
const bgColor = computed(() => (props.color ? props.color : defaultColors[props.variant || BackgroundVariant.Dots]))
const path = computed(() =>
isLines.value
? scaledGap.value && props.size && createGridLinesPath(scaledGap.value, props.size, bgColor.value)
: createGridDotsPath(props.size || 0.4 * transform.value[2], bgColor.value)
);
)
return () => (
<svg
@@ -77,8 +77,8 @@ const Background = defineComponent({
</pattern>
<rect x="0" y="0" width="100%" height="100%" fill={`url(#${patternId})`} />
</svg>
);
)
}
});
})
export default Background;
export default Background
@@ -1,7 +1,7 @@
export const createGridLinesPath = (size: number, strokeWidth: number, stroke: string) => {
return <path stroke={stroke} stroke-width={strokeWidth} d={`M${size / 2} 0 V${size} M0 ${size / 2} H${size}`} />;
};
return <path stroke={stroke} stroke-width={strokeWidth} d={`M${size / 2} 0 V${size} M0 ${size / 2} H${size}`} />
}
export const createGridDotsPath = (size: number, fill: string) => {
return <circle cx={size} cy={size} r={size} fill={fill} />;
};
return <circle cx={size} cy={size} r={size} fill={fill} />
}
+44 -44
View File
@@ -1,24 +1,24 @@
import { defineComponent, HTMLAttributes, inject, onMounted, PropType, ref } from 'vue';
import useZoomPanHelper from '../../hooks/useZoomPanHelper';
import { FitViewParams, RevueFlowStore, ZoomPanHelperFunctions } from '../../types';
import PlusIcon from '../../../assets/icons/plus.svg';
import MinusIcon from '../../../assets/icons/minus.svg';
import Fitview from '../../../assets/icons/fitview.svg';
import Lock from '../../../assets/icons/lock.svg';
import Unlock from '../../../assets/icons/unlock.svg';
import { defineComponent, HTMLAttributes, inject, onMounted, PropType, ref } from 'vue'
import useZoomPanHelper from '../../hooks/useZoomPanHelper'
import { FitViewParams, RevueFlowStore, ZoomPanHelperFunctions } from '../../types'
import PlusIcon from '../../../assets/icons/plus.svg'
import MinusIcon from '../../../assets/icons/minus.svg'
import Fitview from '../../../assets/icons/fitview.svg'
import Lock from '../../../assets/icons/lock.svg'
import Unlock from '../../../assets/icons/unlock.svg'
export interface ControlProps extends HTMLAttributes {
showZoom?: boolean;
showFitView?: boolean;
showInteractive?: boolean;
fitViewParams?: FitViewParams;
onZoomIn?: () => void;
onZoomOut?: () => void;
onFitView?: () => void;
onInteractiveChange?: (interactiveStatus: boolean) => void;
showZoom?: boolean
showFitView?: boolean
showInteractive?: boolean
fitViewParams?: FitViewParams
onZoomIn?: () => void
onZoomOut?: () => void
onFitView?: () => void
onInteractiveChange?: (interactiveStatus: boolean) => void
}
export type ControlButtonProps = HTMLButtonElement;
export type ControlButtonProps = HTMLButtonElement
export const ControlButton = defineComponent({
props: {
@@ -38,9 +38,9 @@ export const ControlButton = defineComponent({
<button class={['revue-flow__controls-button']} {...props}>
{slots.default ? slots.default() : ''}
</button>
);
)
}
});
})
const Controls = defineComponent({
name: 'Controls',
@@ -87,41 +87,41 @@ const Controls = defineComponent({
}
},
setup(props, { slots }) {
const store = inject<RevueFlowStore>('store')!;
const isVisible = ref<boolean>(false);
const zoomHelper = ref<ZoomPanHelperFunctions>();
const { onReady } = useZoomPanHelper();
const store = inject<RevueFlowStore>('store')!
const isVisible = ref<boolean>(false)
const zoomHelper = ref<ZoomPanHelperFunctions>()
const { onReady } = useZoomPanHelper()
onReady((helper) => {
zoomHelper.value = helper;
});
zoomHelper.value = helper
})
const isInteractive = store.nodesDraggable && store.nodesConnectable && store.elementsSelectable;
const mapClasses = ['revue-flow__controls'];
const isInteractive = store.nodesDraggable && store.nodesConnectable && store.elementsSelectable
const mapClasses = ['revue-flow__controls']
const onZoomInHandler = () => {
zoomHelper.value?.zoomIn?.();
props.onZoomIn?.();
};
zoomHelper.value?.zoomIn?.()
props.onZoomIn?.()
}
const onZoomOutHandler = () => {
zoomHelper.value?.zoomOut?.();
props.onZoomOut?.();
};
zoomHelper.value?.zoomOut?.()
props.onZoomOut?.()
}
const onFitViewHandler = () => {
zoomHelper.value?.fitView?.(props.fitViewParams);
props.onFitView?.();
};
zoomHelper.value?.fitView?.(props.fitViewParams)
props.onFitView?.()
}
const onInteractiveChangeHandler = () => {
store.setInteractive?.(!isInteractive);
props.onInteractiveChange?.(!isInteractive);
};
store.setInteractive?.(!isInteractive)
props.onInteractiveChange?.(!isInteractive)
}
onMounted(() => {
isVisible.value = true;
});
isVisible.value = true
})
return () => (
<div class={mapClasses}>
@@ -147,8 +147,8 @@ const Controls = defineComponent({
)}
{slots.default ? slots.default() : ''}
</div>
);
)
}
});
})
export default Controls;
export default Controls
@@ -1,15 +1,15 @@
import { computed, defineComponent, PropType } from 'vue';
import { computed, defineComponent, PropType } from 'vue'
interface MiniMapNodeProps {
x: number;
y: number;
width: number;
height: number;
borderRadius: number;
color: string;
shapeRendering: string;
strokeColor: string;
strokeWidth: number;
x: number
y: number
width: number
height: number
borderRadius: number
color: string
shapeRendering: string
strokeColor: string
strokeWidth: number
}
const MiniMapNode = defineComponent({
@@ -62,8 +62,8 @@ const MiniMapNode = defineComponent({
}
},
setup(props, { attrs }: { attrs: Record<string, any> }) {
const styles = attrs.style || {};
const fill = computed(() => (props.color || styles.value.background || styles.value.backgroundColor) as string);
const styles = attrs.style || {}
const fill = computed(() => (props.color || styles.value.background || styles.value.backgroundColor) as string)
return () => (
<rect
@@ -79,8 +79,8 @@ const MiniMapNode = defineComponent({
stroke-width={props.strokeWidth}
shape-rendering={props.shapeRendering}
/>
);
)
}
});
})
export default MiniMapNode;
export default MiniMapNode
+41 -39
View File
@@ -1,23 +1,23 @@
import { getRectOfNodes, getBoundsofRects } from '../../utils/graph';
import { Node, Rect, RevueFlowStore } from '../../types';
import MiniMapNode from './MiniMapNode';
import { computed, defineComponent, HTMLAttributes, inject, PropType } from 'vue';
import { getRectOfNodes, getBoundsofRects } from '../../utils/graph'
import { Node, Rect, RevueFlowStore } from '../../types'
import MiniMapNode from './MiniMapNode'
import { computed, defineComponent, HTMLAttributes, inject, PropType } from 'vue'
type StringFunc = (node: Node) => string;
type StringFunc = (node: Node) => string
export interface MiniMapProps extends HTMLAttributes {
nodeColor?: string | StringFunc;
nodeStrokeColor?: string | StringFunc;
nodeClassName?: string | StringFunc;
nodeBorderRadius?: number;
nodeStrokeWidth?: number;
maskColor?: string;
nodeColor?: string | StringFunc
nodeStrokeColor?: string | StringFunc
nodeClassName?: string | StringFunc
nodeBorderRadius?: number
nodeStrokeWidth?: number
maskColor?: string
}
declare const window: any;
declare const window: any
const defaultWidth = 200;
const defaultHeight = 150;
const defaultWidth = 200
const defaultHeight = 150
const MiniMap = defineComponent({
name: 'MiniMap',
@@ -54,37 +54,39 @@ const MiniMap = defineComponent({
}
},
setup(props, { attrs }: { attrs: Record<string, any> }) {
const store = inject<RevueFlowStore>('store')!;
const transform = computed(() => store.transform);
const elementWidth = computed(() => (attrs.style?.width || defaultWidth)! as number);
const elementHeight = computed(() => (attrs.style?.height || defaultHeight)! as number);
const nodeColorFunc = computed(() => (props.nodeColor instanceof Function ? props.nodeColor : () => props.nodeColor) as StringFunc);
const store = inject<RevueFlowStore>('store')!
const transform = computed(() => store.transform)
const elementWidth = computed(() => (attrs.style?.width || defaultWidth)! as number)
const elementHeight = computed(() => (attrs.style?.height || defaultHeight)! as number)
const nodeColorFunc = computed(
() => (props.nodeColor instanceof Function ? props.nodeColor : () => props.nodeColor) as StringFunc
)
const nodeStrokeColorFunc = computed(
() => (props.nodeStrokeColor instanceof Function ? props.nodeStrokeColor : () => props.nodeStrokeColor) as StringFunc
);
)
const nodeClassNameFunc = computed(
() => (props.nodeClassName instanceof Function ? props.nodeClassName : () => props.nodeClassName) as StringFunc
);
const hasNodes = computed(() => store.nodes && store.nodes.length);
const bb = computed(() => getRectOfNodes(store.nodes));
)
const hasNodes = computed(() => store.nodes && store.nodes.length)
const bb = computed(() => getRectOfNodes(store.nodes))
const viewBB = computed<Rect>(() => ({
x: -transform.value[0] / transform.value[2],
y: -transform.value[1] / transform.value[2],
width: store.width / transform.value[2],
height: store.height / transform.value[2]
}));
const boundingRect = computed(() => (hasNodes.value ? getBoundsofRects(bb.value, viewBB.value) : viewBB.value));
const scaledWidth = computed(() => boundingRect.value.width / elementWidth.value);
const scaledHeight = computed(() => boundingRect.value.height / elementHeight.value);
const viewScale = computed(() => Math.max(scaledWidth.value, scaledHeight.value));
const viewWidth = computed(() => viewScale.value * elementWidth.value);
const viewHeight = computed(() => viewScale.value * elementHeight.value);
const offset = computed(() => 5 * viewScale.value);
const x = computed(() => boundingRect.value.x - (viewWidth.value - boundingRect.value.width) / 2 - offset.value);
const y = computed(() => boundingRect.value.y - (viewHeight.value - boundingRect.value.height) / 2 - offset.value);
const width = computed(() => viewWidth.value + offset.value * 2);
const height = computed(() => viewHeight.value + offset.value * 2);
const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision';
}))
const boundingRect = computed(() => (hasNodes.value ? getBoundsofRects(bb.value, viewBB.value) : viewBB.value))
const scaledWidth = computed(() => boundingRect.value.width / elementWidth.value)
const scaledHeight = computed(() => boundingRect.value.height / elementHeight.value)
const viewScale = computed(() => Math.max(scaledWidth.value, scaledHeight.value))
const viewWidth = computed(() => viewScale.value * elementWidth.value)
const viewHeight = computed(() => viewScale.value * elementHeight.value)
const offset = computed(() => 5 * viewScale.value)
const x = computed(() => boundingRect.value.x - (viewWidth.value - boundingRect.value.width) / 2 - offset.value)
const y = computed(() => boundingRect.value.y - (viewHeight.value - boundingRect.value.height) / 2 - offset.value)
const width = computed(() => viewWidth.value + offset.value * 2)
const height = computed(() => viewHeight.value + offset.value * 2)
const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision'
return () => (
<svg
@@ -121,8 +123,8 @@ const MiniMap = defineComponent({
fill-rule="evenodd"
/>
</svg>
);
)
}
});
})
export default MiniMap;
export default MiniMap
+3 -3
View File
@@ -1,6 +1,6 @@
// These components are not used by revue Flow directly
// but the user can add them as children of a revue Flow component
export { default as MiniMap } from './MiniMap';
export { default as Controls, ControlButton } from './Controls';
export { default as Background } from './Background';
export { default as MiniMap } from './MiniMap'
export { default as Controls, ControlButton } from './Controls'
export { default as Background } from './Background'
+156
View File
@@ -0,0 +1,156 @@
// Generated by 'unplugin-auto-import'
// We suggest you to commit this file into source control
declare global {
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const biSyncRef: typeof import('@vueuse/core')['biSyncRef']
const computed: typeof import('vue')['computed']
const computedInject: typeof import('@vueuse/core')['computedInject']
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
const controlledRef: typeof import('@vueuse/core')['controlledRef']
const createApp: typeof import('vue')['createApp']
const createEventHook: typeof import('@vueuse/core')['createEventHook']
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
const customRef: typeof import('vue')['customRef']
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const extendRef: typeof import('@vueuse/core')['extendRef']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const h: typeof import('vue')['h']
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
const inject: typeof import('vue')['inject']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
const provide: typeof import('vue')['provide']
const reactify: typeof import('@vueuse/core')['reactify']
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
const reactive: typeof import('vue')['reactive']
const reactivePick: typeof import('@vueuse/core')['reactivePick']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const syncRef: typeof import('@vueuse/core')['syncRef']
const templateRef: typeof import('@vueuse/core')['templateRef']
const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
const toRaw: typeof import('vue')['toRaw']
const toReactive: typeof import('@vueuse/core')['toReactive']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const triggerRef: typeof import('vue')['triggerRef']
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
const unref: typeof import('vue')['unref']
const unrefElement: typeof import('@vueuse/core')['unrefElement']
const until: typeof import('@vueuse/core')['until']
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
const useAttrs: typeof import('vue')['useAttrs']
const useBattery: typeof import('@vueuse/core')['useBattery']
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
const useClipboard: typeof import('@vueuse/core')['useClipboard']
const useCounter: typeof import('@vueuse/core')['useCounter']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVar: typeof import('@vueuse/core')['useCssVar']
const useDark: typeof import('@vueuse/core')['useDark']
const useDebounce: typeof import('@vueuse/core')['useDebounce']
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
const useElementSize: typeof import('@vueuse/core')['useElementSize']
const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
const useEventBus: typeof import('@vueuse/core')['useEventBus']
const useEventListener: typeof import('@vueuse/core')['useEventListener']
const useEventSource: typeof import('@vueuse/core')['useEventSource']
const useFavicon: typeof import('@vueuse/core')['useFavicon']
const useFetch: typeof import('@vueuse/core')['useFetch']
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useIdle: typeof import('@vueuse/core')['useIdle']
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
const useInterval: typeof import('@vueuse/core')['useInterval']
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
const useMouse: typeof import('@vueuse/core')['useMouse']
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
const useNetwork: typeof import('@vueuse/core')['useNetwork']
const useNow: typeof import('@vueuse/core')['useNow']
const useOnline: typeof import('@vueuse/core')['useOnline']
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
const useParallax: typeof import('@vueuse/core')['useParallax']
const usePermission: typeof import('@vueuse/core')['usePermission']
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
const useRafFn: typeof import('@vueuse/core')['useRafFn']
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
const useShare: typeof import('@vueuse/core')['useShare']
const useSlots: typeof import('vue')['useSlots']
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
const useStorage: typeof import('@vueuse/core')['useStorage']
const useSwipe: typeof import('@vueuse/core')['useSwipe']
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
const useThrottle: typeof import('@vueuse/core')['useThrottle']
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
const useTimeout: typeof import('@vueuse/core')['useTimeout']
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
const useTitle: typeof import('@vueuse/core')['useTitle']
const useToggle: typeof import('@vueuse/core')['useToggle']
const useTransition: typeof import('@vueuse/core')['useTransition']
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
const useVModel: typeof import('@vueuse/core')['useVModel']
const useVModels: typeof import('@vueuse/core')['useVModels']
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: typeof import('@vueuse/core')['whenever']
}
export {}
+28 -28
View File
@@ -1,13 +1,13 @@
import { ref, defineComponent, CSSProperties, PropType, computed, inject, h } from 'vue';
import { ref, defineComponent, CSSProperties, PropType, computed, inject, h } from 'vue'
import { getBezierPath } from '../Edges/BezierEdge';
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
import { Node, HandleElement, Position, ConnectionLineType, ConnectionLineComponent, RevueFlowStore } from '../../types';
import { getBezierPath } from '../Edges/BezierEdge'
import { getSmoothStepPath } from '../Edges/SmoothStepEdge'
import { Node, HandleElement, Position, ConnectionLineType, ConnectionLineComponent, RevueFlowStore } from '../../types'
interface ConnectionLineProps {
connectionLineType: ConnectionLineType;
connectionLineStyle?: CSSProperties;
customConnectionLine?: ConnectionLineComponent;
connectionLineType: ConnectionLineType
connectionLineStyle?: CSSProperties
customConnectionLine?: ConnectionLineComponent
}
export default defineComponent({
@@ -29,35 +29,35 @@ export default defineComponent({
}
},
setup(props) {
const store = inject<RevueFlowStore>('store')!;
const sourceNode = ref<Node | null>(store.nodes.find((n) => n.id === store.connectionNodeId) || null);
const nodesConnectable = computed(() => store.nodesConnectable);
const store = inject<RevueFlowStore>('store')!
const sourceNode = ref<Node | null>(store.nodes.find((n) => n.id === store.connectionNodeId) || null)
const nodesConnectable = computed(() => store.nodesConnectable)
const sourceHandle = computed(() =>
store.connectionHandleId && store.connectionHandleType
? sourceNode.value?.__rf.handleBounds[store.connectionHandleType].find(
(d: HandleElement) => d.id === store.connectionHandleId
)
(d: HandleElement) => d.id === store.connectionHandleId
)
: store.connectionHandleType && sourceNode.value?.__rf.handleBounds[store.connectionHandleType][0]
);
)
const sourceHandleX = computed(() =>
sourceHandle.value ? sourceHandle.value.x + sourceHandle.value.width / 2 : (sourceNode.value?.__rf.width as number) / 2
);
)
const sourceHandleY = computed(() =>
sourceHandle.value ? sourceHandle.value.y + sourceHandle.value.height / 2 : sourceNode.value?.__rf.height
);
const sourceX = computed(() => sourceNode.value?.__rf.position.x + sourceHandleX.value);
const sourceY = computed(() => sourceNode.value?.__rf.position.y + sourceHandleY.value);
)
const sourceX = computed(() => sourceNode.value?.__rf.position.x + sourceHandleX.value)
const sourceY = computed(() => sourceNode.value?.__rf.position.y + sourceHandleY.value)
const targetX = computed(() => (store.connectionPosition.x - store.transform[0]) / store.transform[2]);
const targetY = computed(() => (store.connectionPosition.y - store.transform[1]) / store.transform[2]);
const targetX = computed(() => (store.connectionPosition.x - store.transform[0]) / store.transform[2])
const targetY = computed(() => (store.connectionPosition.y - store.transform[1]) / store.transform[2])
const isRightOrLeft = computed(
() => sourceHandle.value?.position === Position.Left || sourceHandle.value?.position === Position.Right
);
const targetPosition = computed(() => (isRightOrLeft.value ? Position.Left : Position.Top));
)
const targetPosition = computed(() => (isRightOrLeft.value ? Position.Left : Position.Top))
let dAttr = computed(() => `M${sourceX.value},${sourceY.value} ${targetX.value},${targetY.value}`);
let dAttr = computed(() => `M${sourceX.value},${sourceY.value} ${targetX.value},${targetY.value}`)
if (props.connectionLineType === ConnectionLineType.Bezier) {
dAttr = computed(() =>
@@ -69,7 +69,7 @@ export default defineComponent({
targetY: targetY.value,
targetPosition: targetPosition.value
})
);
)
} else if (props.connectionLineType === ConnectionLineType.Step) {
dAttr = computed(() =>
getSmoothStepPath({
@@ -81,7 +81,7 @@ export default defineComponent({
targetPosition: targetPosition.value,
borderRadius: 0
})
);
)
} else if (props.connectionLineType === ConnectionLineType.SmoothStep) {
dAttr = computed(() =>
getSmoothStepPath({
@@ -92,7 +92,7 @@ export default defineComponent({
targetY: targetY.value,
targetPosition: targetPosition.value
})
);
)
}
if (props.customConnectionLine) {
@@ -110,7 +110,7 @@ export default defineComponent({
connectionLineStyle: props.connectionLineStyle
})}
</g>
);
)
}
return () =>
@@ -120,6 +120,6 @@ export default defineComponent({
</g>
) : (
''
);
)
}
});
})
+29 -29
View File
@@ -1,18 +1,18 @@
import { defineComponent } from 'vue';
import EdgeText from './EdgeText';
import { getMarkerEnd, getCenter, GetCenterParams, DefaultEdgeProps } from './utils';
import { ArrowHeadType, EdgeType, Position } from '../../types';
import { reactify } from '@vueuse/core';
import { defineComponent } from 'vue'
import EdgeText from './EdgeText'
import { getMarkerEnd, getCenter, GetCenterParams, DefaultEdgeProps } from './utils'
import { ArrowHeadType, EdgeType, Position } from '../../types'
import { reactify } from '@vueuse/core'
interface GetBezierPathParams {
sourceX: number;
sourceY: number;
sourcePosition?: Position;
targetX: number;
targetY: number;
targetPosition?: Position;
centerX?: number;
centerY?: number;
sourceX: number
sourceY: number
sourcePosition?: Position
targetX: number
targetY: number
targetPosition?: Position
centerX?: number
centerY?: number
}
export function getBezierPath({
@@ -25,23 +25,23 @@ export function getBezierPath({
centerX,
centerY
}: GetBezierPathParams): string {
const [_centerX, _centerY] = getCenter({ sourceX, sourceY, targetX, targetY });
const leftAndRight = [Position.Left, Position.Right];
const [_centerX, _centerY] = getCenter({ sourceX, sourceY, targetX, targetY })
const leftAndRight = [Position.Left, Position.Right]
const cX = typeof centerX !== 'undefined' ? centerX : _centerX;
const cY = typeof centerY !== 'undefined' ? centerY : _centerY;
const cX = typeof centerX !== 'undefined' ? centerX : _centerX
const cY = typeof centerY !== 'undefined' ? centerY : _centerY
let path = `M${sourceX},${sourceY} C${sourceX},${cY} ${targetX},${cY} ${targetX},${targetY}`;
let path = `M${sourceX},${sourceY} C${sourceX},${cY} ${targetX},${cY} ${targetX},${targetY}`
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
path = `M${sourceX},${sourceY} C${cX},${sourceY} ${cX},${targetY} ${targetX},${targetY}`;
path = `M${sourceX},${sourceY} C${cX},${sourceY} ${cX},${targetY} ${targetX},${targetY}`
} else if (leftAndRight.includes(targetPosition)) {
path = `M${sourceX},${sourceY} C${sourceX},${targetY} ${sourceX},${targetY} ${targetX},${targetY}`;
path = `M${sourceX},${sourceY} C${sourceX},${targetY} ${sourceX},${targetY} ${targetX},${targetY}`
} else if (leftAndRight.includes(sourcePosition)) {
path = `M${sourceX},${sourceY} C${targetX},${sourceY} ${targetX},${sourceY} ${targetX},${targetY}`;
path = `M${sourceX},${sourceY} C${targetX},${sourceY} ${targetX},${sourceY} ${targetX},${targetY}`
}
return path;
return path
}
export default defineComponent({
@@ -58,8 +58,8 @@ export default defineComponent({
targetY: targetY,
sourcePosition: sourcePosition,
targetPosition: targetPosition
});
});
})
})
const path = reactify(({ sourceX, sourceY, targetX, targetY, targetPosition, sourcePosition }: GetBezierPathParams) => {
return getBezierPath({
sourceX: sourceX,
@@ -68,10 +68,10 @@ export default defineComponent({
targetY: targetY,
targetPosition: targetPosition,
sourcePosition: sourcePosition
});
});
})
})
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId));
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId))
return () => (
<>
@@ -94,6 +94,6 @@ export default defineComponent({
/>
) : null}
</>
);
)
}
}) as EdgeType;
}) as EdgeType
+69 -69
View File
@@ -1,24 +1,24 @@
import { computed, defineComponent, h, inject, PropType, ref, watchPostEffect } from 'vue';
import EdgeAnchor from './EdgeAnchor';
import { ConnectionMode, Edge, Elements, Node, Position, RevueFlowStore, Transform } from '../../types';
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '../../container/EdgeRenderer/utils';
import { isEdge } from '../../utils/graph';
import { onMouseDown } from '../Handle/handler';
import { controlledRef } from '@vueuse/core';
import { computed, defineComponent, h, inject, PropType, ref, watchPostEffect } from 'vue'
import EdgeAnchor from './EdgeAnchor'
import { ConnectionMode, Edge, Elements, Node, Position, RevueFlowStore, Transform } from '../../types'
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '../../container/EdgeRenderer/utils'
import { isEdge } from '../../utils/graph'
import { onMouseDown } from '../Handle/handler'
import { controlledRef } from '@vueuse/core'
interface EdgeProps {
edge: Edge;
nodes: Node[];
selectedElements: Elements | null;
elementsSelectable: boolean;
transform: Transform;
width: number;
height: number;
onlyRenderVisibleElements: boolean;
connectionMode?: ConnectionMode;
markerEndId?: string;
edgeUpdaterRadius?: number;
edge: Edge
nodes: Node[]
selectedElements: Elements | null
elementsSelectable: boolean
transform: Transform
width: number
height: number
onlyRenderVisibleElements: boolean
connectionMode?: ConnectionMode
markerEndId?: string
edgeUpdaterRadius?: number
}
export default defineComponent({
@@ -54,41 +54,41 @@ export default defineComponent({
}
},
setup(props) {
const store = inject<RevueFlowStore>('store')!;
const hooks = inject<RevueFlowHooks>('hooks')!;
const store = inject<RevueFlowStore>('store')!
const hooks = inject<RevueFlowHooks>('hooks')!
const nodes = controlledRef(getSourceTargetNodes(props.edge, store.nodes), {
onBeforeChange: (value, oldValue) => {
if (JSON.stringify(value) === JSON.stringify(oldValue)) return false;
if (JSON.stringify(value) === JSON.stringify(oldValue)) return false
}
});
})
watchPostEffect(() => {
nodes.value = getSourceTargetNodes(props.edge, store.nodes);
});
nodes.value = getSourceTargetNodes(props.edge, store.nodes)
})
hooks.connect.on((connection) => {
hooks.edgeUpdate.trigger({ edge: props.edge, connection });
});
hooks.edgeUpdate.trigger({ edge: props.edge, connection })
})
if (!nodes.value.sourceNode) {
console.warn(`couldn't create edge for source id: ${props.edge.source}; edge id: ${props.edge.id}`);
console.warn(`couldn't create edge for source id: ${props.edge.source}; edge id: ${props.edge.id}`)
}
if (!nodes.value.targetNode) {
console.warn(`couldn't create edge for target id: ${props.edge.target}; edge id: ${props.edge.id}`);
console.warn(`couldn't create edge for target id: ${props.edge.target}; edge id: ${props.edge.id}`)
}
// when connection type is loose we can define all handles as sources
const targetNodeHandles =
props.connectionMode === ConnectionMode.Strict
? nodes.value.targetNode?.__rf.handleBounds.target
: nodes.value.targetNode?.__rf.handleBounds.target || nodes.value.targetNode?.__rf.handleBounds.source;
: nodes.value.targetNode?.__rf.handleBounds.target || nodes.value.targetNode?.__rf.handleBounds.source
const sourceHandle =
nodes.value.sourceNode && getHandle(nodes.value.sourceNode.__rf.handleBounds.source, props.edge.sourceHandle || null);
const targetHandle = getHandle(targetNodeHandles, props.edge.targetHandle || null);
const sourcePosition = sourceHandle ? sourceHandle.position : Position.Bottom;
const targetPosition = targetHandle ? targetHandle.position : Position.Top;
nodes.value.sourceNode && getHandle(nodes.value.sourceNode.__rf.handleBounds.source, props.edge.sourceHandle || null)
const targetHandle = getHandle(targetNodeHandles, props.edge.targetHandle || null)
const sourcePosition = sourceHandle ? sourceHandle.position : Position.Bottom
const targetPosition = targetHandle ? targetHandle.position : Position.Top
const isVisible = ({ sourceX, sourceY, targetX, targetY }: Record<string, number>) => {
return props.onlyRenderVisibleElements
@@ -99,10 +99,10 @@ export default defineComponent({
height: store.height || 0,
transform: store.transform
})
: true;
};
: true
}
const isSelected = computed(() => store.selectedElements?.some((elm) => isEdge(elm) && elm.id === props.edge.id) || false);
const isSelected = computed(() => store.selectedElements?.some((elm) => isEdge(elm) && elm.id === props.edge.id) || false)
const edgeElement = () => {
const el: Edge = {
@@ -110,51 +110,51 @@ export default defineComponent({
source: props.edge.source,
target: props.edge.target,
type: props.edge.type
};
}
if (props.edge.sourceHandle) {
el.sourceHandle = props.edge.sourceHandle;
el.sourceHandle = props.edge.sourceHandle
}
if (props.edge.targetHandle) {
el.targetHandle = props.edge.targetHandle;
el.targetHandle = props.edge.targetHandle
}
if (typeof props.edge.data !== 'undefined') {
el.data = props.edge.data;
el.data = props.edge.data
}
return el;
};
return el
}
const onEdgeClick = (event: MouseEvent) => {
if (store.elementsSelectable) {
store.unsetNodesSelection();
store.addSelectedElements(edgeElement() as any);
store.unsetNodesSelection()
store.addSelectedElements(edgeElement() as any)
}
hooks.edgeClick.trigger({ event, edge: edgeElement() });
};
hooks.edgeClick.trigger({ event, edge: edgeElement() })
}
const onEdgeContextMenu = (event: MouseEvent) =>
hooks.edgeContextMenu.trigger({
event,
edge: edgeElement()
});
})
const onEdgeMouseEnter = (event: MouseEvent) => hooks.edgeMouseEnter.trigger({ event, edge: edgeElement() });
const onEdgeMouseEnter = (event: MouseEvent) => hooks.edgeMouseEnter.trigger({ event, edge: edgeElement() })
const onEdgeMouseMove = (event: MouseEvent) => hooks.edgeMouseMove.trigger({ event, edge: edgeElement() });
const onEdgeMouseMove = (event: MouseEvent) => hooks.edgeMouseMove.trigger({ event, edge: edgeElement() })
const onEdgeMouseLeave = (event: MouseEvent) => hooks.edgeMouseLeave.trigger({ event, edge: edgeElement() });
const onEdgeMouseLeave = (event: MouseEvent) => hooks.edgeMouseLeave.trigger({ event, edge: edgeElement() })
const handleEdgeUpdater = (event: MouseEvent, isSourceHandle: boolean) => {
const nodeId = isSourceHandle ? props.edge.target : props.edge.source;
const handleId = isSourceHandle ? props.edge.targetHandle : props.edge.sourceHandle;
const isValidConnection = () => true;
const isTarget = isSourceHandle;
const nodeId = isSourceHandle ? props.edge.target : props.edge.source
const handleId = isSourceHandle ? props.edge.targetHandle : props.edge.sourceHandle
const isValidConnection = () => true
const isTarget = isSourceHandle
hooks.edgeUpdateStart.trigger({ event, edge: edgeElement() });
hooks.edgeUpdateStart.trigger({ event, edge: edgeElement() })
onMouseDown(
event,
store,
@@ -164,20 +164,20 @@ export default defineComponent({
isTarget,
isValidConnection,
isSourceHandle ? 'target' : 'source'
);
};
)
}
const onEdgeUpdaterSourceMouseDown = (event: MouseEvent) => {
handleEdgeUpdater(event, true);
};
handleEdgeUpdater(event, true)
}
const onEdgeUpdaterTargetMouseDown = (event: MouseEvent) => {
handleEdgeUpdater(event, false);
};
handleEdgeUpdater(event, false)
}
const updating = ref<boolean>(false);
const onEdgeUpdaterMouseEnter = () => (updating.value = true);
const onEdgeUpdaterMouseOut = () => (updating.value = false);
const updating = ref<boolean>(false)
const onEdgeUpdaterMouseEnter = () => (updating.value = true)
const onEdgeUpdaterMouseOut = () => (updating.value = false)
return () => {
const { targetX, targetY, sourceX, sourceY } = getEdgePositions(
@@ -187,7 +187,7 @@ export default defineComponent({
nodes.value.targetNode as Node,
targetHandle,
targetPosition
);
)
return !props.edge.isHidden && isVisible({ targetX, targetY, sourceX, sourceY }) ? (
<g
class={[
@@ -242,7 +242,7 @@ export default defineComponent({
</g>
) : (
''
);
};
)
}
}
});
})
+16 -16
View File
@@ -1,23 +1,23 @@
import { Position } from '../../types';
import { defineComponent, HTMLAttributes, PropType } from 'vue';
import { Position } from '../../types'
import { defineComponent, HTMLAttributes, PropType } from 'vue'
const shiftX = (x: number, shift: number, position: Position): number => {
if (position === Position.Left) return x - shift;
if (position === Position.Right) return x + shift;
return x;
};
if (position === Position.Left) return x - shift
if (position === Position.Right) return x + shift
return x
}
const shiftY = (y: number, shift: number, position: Position): number => {
if (position === Position.Top) return y - shift;
if (position === Position.Bottom) return y + shift;
return y;
};
if (position === Position.Top) return y - shift
if (position === Position.Bottom) return y + shift
return y
}
export interface EdgeAnchorProps extends HTMLAttributes {
position: Position;
centerX: number;
centerY: number;
radius?: number;
position: Position
centerX: number
centerY: number
radius?: number
}
export default defineComponent({
@@ -50,6 +50,6 @@ export default defineComponent({
stroke="transparent"
fill="transparent"
/>
);
)
}
});
})
+9 -9
View File
@@ -1,5 +1,5 @@
import { EdgeTextProps, Rect } from '../../types';
import { defineComponent, PropType, ref, watchEffect } from 'vue';
import { EdgeTextProps, Rect } from '../../types'
import { defineComponent, PropType, ref, watchEffect } from 'vue'
export default defineComponent({
props: {
@@ -37,11 +37,11 @@ export default defineComponent({
}
},
setup(props, { slots }) {
const edgeRef = ref<SVGTextElement | null>(null);
const edgeTextBox = ref<Rect>({ x: 0, y: 0, width: 0, height: 0 });
const edgeRef = ref<SVGTextElement | null>(null)
const edgeTextBox = ref<Rect>({ x: 0, y: 0, width: 0, height: 0 })
watchEffect(() => {
const textBbox = edgeRef.value?.getBBox();
const textBbox = edgeRef.value?.getBBox()
if (textBbox) {
edgeTextBox.value = {
@@ -49,9 +49,9 @@ export default defineComponent({
y: textBbox.y,
width: textBbox.width,
height: textBbox.height
};
}
}
});
})
return () => (
<g
@@ -75,6 +75,6 @@ export default defineComponent({
</text>
{slots.default ? slots.default() : ''}
</g>
);
)
}
});
})
+54 -55
View File
@@ -1,33 +1,33 @@
import { defineComponent } from 'vue';
import EdgeText from './EdgeText';
import { getMarkerEnd, getCenter, EdgeSmoothProps, GetCenterParams } from './utils';
import { ArrowHeadType, EdgeType, Position } from '../../types';
import { reactify } from '@vueuse/core';
import { getBezierPath } from './BezierEdge';
import { defineComponent } from 'vue'
import EdgeText from './EdgeText'
import { getMarkerEnd, getCenter, EdgeSmoothProps, GetCenterParams } from './utils'
import { ArrowHeadType, EdgeType, Position } from '../../types'
import { reactify } from '@vueuse/core'
import { getBezierPath } from './BezierEdge'
// These are some helper methods for drawing the round corners
// The name indicates the direction of the path. "bottomLeftCorner" goes
// from bottom to the left and "leftBottomCorner" goes from left to the bottom.
// We have to consider the direction of the paths because of the animated lines.
const bottomLeftCorner = (x: number, y: number, size: number): string => `L ${x},${y - size}Q ${x},${y} ${x + size},${y}`;
const leftBottomCorner = (x: number, y: number, size: number): string => `L ${x + size},${y}Q ${x},${y} ${x},${y - size}`;
const bottomRightCorner = (x: number, y: number, size: number): string => `L ${x},${y - size}Q ${x},${y} ${x - size},${y}`;
const rightBottomCorner = (x: number, y: number, size: number): string => `L ${x - size},${y}Q ${x},${y} ${x},${y - size}`;
const leftTopCorner = (x: number, y: number, size: number): string => `L ${x + size},${y}Q ${x},${y} ${x},${y + size}`;
const topLeftCorner = (x: number, y: number, size: number): string => `L ${x},${y + size}Q ${x},${y} ${x + size},${y}`;
const topRightCorner = (x: number, y: number, size: number): string => `L ${x},${y + size}Q ${x},${y} ${x - size},${y}`;
const rightTopCorner = (x: number, y: number, size: number): string => `L ${x - size},${y}Q ${x},${y} ${x},${y + size}`;
const bottomLeftCorner = (x: number, y: number, size: number): string => `L ${x},${y - size}Q ${x},${y} ${x + size},${y}`
const leftBottomCorner = (x: number, y: number, size: number): string => `L ${x + size},${y}Q ${x},${y} ${x},${y - size}`
const bottomRightCorner = (x: number, y: number, size: number): string => `L ${x},${y - size}Q ${x},${y} ${x - size},${y}`
const rightBottomCorner = (x: number, y: number, size: number): string => `L ${x - size},${y}Q ${x},${y} ${x},${y - size}`
const leftTopCorner = (x: number, y: number, size: number): string => `L ${x + size},${y}Q ${x},${y} ${x},${y + size}`
const topLeftCorner = (x: number, y: number, size: number): string => `L ${x},${y + size}Q ${x},${y} ${x + size},${y}`
const topRightCorner = (x: number, y: number, size: number): string => `L ${x},${y + size}Q ${x},${y} ${x - size},${y}`
const rightTopCorner = (x: number, y: number, size: number): string => `L ${x - size},${y}Q ${x},${y} ${x},${y + size}`
interface GetSmoothStepPathParams {
sourceX: number;
sourceY: number;
sourcePosition?: Position;
targetX: number;
targetY: number;
targetPosition?: Position;
borderRadius?: number;
centerX?: number;
centerY?: number;
sourceX: number
sourceY: number
sourcePosition?: Position
targetX: number
targetY: number
targetPosition?: Position
borderRadius?: number
centerX?: number
centerY?: number
}
export function getSmoothStepPath({
@@ -41,56 +41,55 @@ export function getSmoothStepPath({
centerX,
centerY
}: GetSmoothStepPathParams): string {
const [_centerX, _centerY, offsetX, offsetY] = getCenter({ sourceX, sourceY, targetX, targetY });
const cornerWidth = Math.min(borderRadius, Math.abs(targetX - sourceX));
const cornerHeight = Math.min(borderRadius, Math.abs(targetY - sourceY));
const cornerSize = Math.min(cornerWidth, cornerHeight, offsetX, offsetY);
const leftAndRight = [Position.Left, Position.Right];
const cX = typeof centerX !== 'undefined' ? centerX : _centerX;
const cY = typeof centerY !== 'undefined' ? centerY : _centerY;
const [_centerX, _centerY, offsetX, offsetY] = getCenter({ sourceX, sourceY, targetX, targetY })
const cornerWidth = Math.min(borderRadius, Math.abs(targetX - sourceX))
const cornerHeight = Math.min(borderRadius, Math.abs(targetY - sourceY))
const cornerSize = Math.min(cornerWidth, cornerHeight, offsetX, offsetY)
const leftAndRight = [Position.Left, Position.Right]
const cX = typeof centerX !== 'undefined' ? centerX : _centerX
const cY = typeof centerY !== 'undefined' ? centerY : _centerY
let firstCornerPath;
let secondCornerPath;
let firstCornerPath
let secondCornerPath
if (sourceX <= targetX) {
firstCornerPath = sourceY <= targetY ? bottomLeftCorner(sourceX, cY, cornerSize) : topLeftCorner(sourceX, cY, cornerSize);
secondCornerPath = sourceY <= targetY ? rightTopCorner(targetX, cY, cornerSize) : rightBottomCorner(targetX, cY, cornerSize);
firstCornerPath = sourceY <= targetY ? bottomLeftCorner(sourceX, cY, cornerSize) : topLeftCorner(sourceX, cY, cornerSize)
secondCornerPath = sourceY <= targetY ? rightTopCorner(targetX, cY, cornerSize) : rightBottomCorner(targetX, cY, cornerSize)
} else {
firstCornerPath = sourceY < targetY ? bottomRightCorner(sourceX, cY, cornerSize) : topRightCorner(sourceX, cY, cornerSize);
secondCornerPath = sourceY < targetY ? leftTopCorner(targetX, cY, cornerSize) : leftBottomCorner(targetX, cY, cornerSize);
firstCornerPath = sourceY < targetY ? bottomRightCorner(sourceX, cY, cornerSize) : topRightCorner(sourceX, cY, cornerSize)
secondCornerPath = sourceY < targetY ? leftTopCorner(targetX, cY, cornerSize) : leftBottomCorner(targetX, cY, cornerSize)
}
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
if (sourceX <= targetX) {
firstCornerPath = sourceY <= targetY ? rightTopCorner(cX, sourceY, cornerSize) : rightBottomCorner(cX, sourceY, cornerSize);
secondCornerPath = sourceY <= targetY ? bottomLeftCorner(cX, targetY, cornerSize) : topLeftCorner(cX, targetY, cornerSize);
firstCornerPath = sourceY <= targetY ? rightTopCorner(cX, sourceY, cornerSize) : rightBottomCorner(cX, sourceY, cornerSize)
secondCornerPath = sourceY <= targetY ? bottomLeftCorner(cX, targetY, cornerSize) : topLeftCorner(cX, targetY, cornerSize)
} else if (sourcePosition === Position.Right && targetPosition === Position.Left) {
// and sourceX > targetX
firstCornerPath = sourceY <= targetY ? leftTopCorner(cX, sourceY, cornerSize) : leftBottomCorner(cX, sourceY, cornerSize);
secondCornerPath =
sourceY <= targetY ? bottomRightCorner(cX, targetY, cornerSize) : topRightCorner(cX, targetY, cornerSize);
firstCornerPath = sourceY <= targetY ? leftTopCorner(cX, sourceY, cornerSize) : leftBottomCorner(cX, sourceY, cornerSize)
secondCornerPath = sourceY <= targetY ? bottomRightCorner(cX, targetY, cornerSize) : topRightCorner(cX, targetY, cornerSize)
}
} else if (leftAndRight.includes(sourcePosition) && !leftAndRight.includes(targetPosition)) {
if (sourceX <= targetX) {
firstCornerPath =
sourceY <= targetY ? rightTopCorner(targetX, sourceY, cornerSize) : rightBottomCorner(targetX, sourceY, cornerSize);
sourceY <= targetY ? rightTopCorner(targetX, sourceY, cornerSize) : rightBottomCorner(targetX, sourceY, cornerSize)
} else {
firstCornerPath =
sourceY <= targetY ? leftTopCorner(targetX, sourceY, cornerSize) : leftBottomCorner(targetX, sourceY, cornerSize);
sourceY <= targetY ? leftTopCorner(targetX, sourceY, cornerSize) : leftBottomCorner(targetX, sourceY, cornerSize)
}
secondCornerPath = '';
secondCornerPath = ''
} else if (!leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
if (sourceX <= targetX) {
firstCornerPath =
sourceY <= targetY ? bottomLeftCorner(sourceX, targetY, cornerSize) : topLeftCorner(sourceX, targetY, cornerSize);
sourceY <= targetY ? bottomLeftCorner(sourceX, targetY, cornerSize) : topLeftCorner(sourceX, targetY, cornerSize)
} else {
firstCornerPath =
sourceY <= targetY ? bottomRightCorner(sourceX, targetY, cornerSize) : topRightCorner(sourceX, targetY, cornerSize);
sourceY <= targetY ? bottomRightCorner(sourceX, targetY, cornerSize) : topRightCorner(sourceX, targetY, cornerSize)
}
secondCornerPath = '';
secondCornerPath = ''
}
return `M ${sourceX},${sourceY}${firstCornerPath}${secondCornerPath}L ${targetX},${targetY}`;
return `M ${sourceX},${sourceY}${firstCornerPath}${secondCornerPath}L ${targetX},${targetY}`
}
export default defineComponent({
@@ -107,8 +106,8 @@ export default defineComponent({
targetY: targetY,
sourcePosition: sourcePosition,
targetPosition: targetPosition
});
});
})
})
const path = reactify(({ sourceX, sourceY, targetX, targetY, targetPosition, sourcePosition }: GetSmoothStepPathParams) => {
return getBezierPath({
sourceX: sourceX,
@@ -117,10 +116,10 @@ export default defineComponent({
targetY: targetY,
targetPosition: targetPosition,
sourcePosition: sourcePosition
});
});
})
})
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId));
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId))
return () => (
<>
@@ -143,6 +142,6 @@ export default defineComponent({
/>
) : null}
</>
);
)
}
}) as unknown as EdgeType;
}) as unknown as EdgeType
+6 -6
View File
@@ -1,7 +1,7 @@
import { defineComponent } from 'vue';
import SmoothStepEdge from './SmoothStepEdge';
import { EdgeSmoothProps } from './utils';
import { EdgeType } from '../../types';
import { defineComponent } from 'vue'
import SmoothStepEdge from './SmoothStepEdge'
import { EdgeSmoothProps } from './utils'
import { EdgeType } from '../../types'
export default defineComponent({
components: { SmoothStepEdge },
@@ -10,6 +10,6 @@ export default defineComponent({
...EdgeSmoothProps
},
setup(props) {
return () => <SmoothStepEdge {...props} borderRadius={0} />;
return () => <SmoothStepEdge {...props} borderRadius={0} />
}
}) as EdgeType;
}) as EdgeType
+14 -14
View File
@@ -1,8 +1,8 @@
import { defineComponent } from 'vue';
import EdgeText from './EdgeText';
import { getMarkerEnd, DefaultEdgeProps } from './utils';
import { reactify } from '@vueuse/core';
import { ArrowHeadType, EdgeType } from '../../types';
import { defineComponent } from 'vue'
import EdgeText from './EdgeText'
import { getMarkerEnd, DefaultEdgeProps } from './utils'
import { reactify } from '@vueuse/core'
import { ArrowHeadType, EdgeType } from '../../types'
export default defineComponent({
inheritAttrs: false,
@@ -11,15 +11,15 @@ export default defineComponent({
},
setup(props) {
const centerY = reactify((targetY: number, sourceY: number) => {
const yOffset = Math.abs(targetY - sourceY) / 2;
return targetY < sourceY ? targetY + yOffset : targetY - yOffset;
});
const yOffset = Math.abs(targetY - sourceY) / 2
return targetY < sourceY ? targetY + yOffset : targetY - yOffset
})
const centerX = reactify((targetX: number, sourceX: number) => {
const xOffset = Math.abs(targetX - sourceX) / 2;
return targetX < sourceX ? targetX + xOffset : targetX - xOffset;
});
const xOffset = Math.abs(targetX - sourceX) / 2
return targetX < sourceX ? targetX + xOffset : targetX - xOffset
})
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId));
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId))
return () => (
<>
@@ -42,6 +42,6 @@ export default defineComponent({
/>
) : null}
</>
);
)
}
}) as unknown as EdgeType;
}) as unknown as EdgeType
+4 -4
View File
@@ -1,4 +1,4 @@
export { default as BezierEdge } from './BezierEdge';
export { default as StepEdge } from './StepEdge';
export { default as SmoothStepEdge } from './SmoothStepEdge';
export { default as StraightEdge } from './StraightEdge';
export { default as BezierEdge } from './BezierEdge'
export { default as StepEdge } from './StepEdge'
export { default as SmoothStepEdge } from './SmoothStepEdge'
export { default as StraightEdge } from './StraightEdge'
+30 -30
View File
@@ -1,24 +1,24 @@
import { ArrowHeadType, Position, EdgeProps, EdgeSmoothStepProps } from '../../types';
import { PropType } from 'vue';
import { ArrowHeadType, Position, EdgeProps, EdgeSmoothStepProps } from '../../types'
import { PropType } from 'vue'
export const getMarkerEnd = (arrowHeadType?: ArrowHeadType, markerEndId?: string): string => {
if (typeof markerEndId !== 'undefined' && markerEndId) {
return `url(#${markerEndId})`;
return `url(#${markerEndId})`
}
return typeof arrowHeadType !== 'undefined' ? `url(#revue-flow__${arrowHeadType})` : 'none';
};
export interface GetCenterParams {
sourceX: number;
sourceY: number;
targetX: number;
targetY: number;
sourcePosition?: Position;
targetPosition?: Position;
return typeof arrowHeadType !== 'undefined' ? `url(#revue-flow__${arrowHeadType})` : 'none'
}
const LeftOrRight = [Position.Left, Position.Right];
export interface GetCenterParams {
sourceX: number
sourceY: number
targetX: number
targetY: number
sourcePosition?: Position
targetPosition?: Position
}
const LeftOrRight = [Position.Left, Position.Right]
export const getCenter = ({
sourceX,
@@ -28,31 +28,31 @@ export const getCenter = ({
sourcePosition = Position.Bottom,
targetPosition = Position.Top
}: GetCenterParams): [number, number, number, number] => {
const sourceIsLeftOrRight = LeftOrRight.includes(sourcePosition);
const targetIsLeftOrRight = LeftOrRight.includes(targetPosition);
const sourceIsLeftOrRight = LeftOrRight.includes(sourcePosition)
const targetIsLeftOrRight = LeftOrRight.includes(targetPosition)
// we expect flows to be horizontal or vertical (all handles left or right respectively top or bottom)
// a mixed edge is when one the source is on the left and the target is on the top for example.
const mixedEdge = (sourceIsLeftOrRight && !targetIsLeftOrRight) || (targetIsLeftOrRight && !sourceIsLeftOrRight);
const mixedEdge = (sourceIsLeftOrRight && !targetIsLeftOrRight) || (targetIsLeftOrRight && !sourceIsLeftOrRight)
if (mixedEdge) {
const xOffset = sourceIsLeftOrRight ? Math.abs(targetX - sourceX) : 0;
const centerX = sourceX > targetX ? sourceX - xOffset : sourceX + xOffset;
const xOffset = sourceIsLeftOrRight ? Math.abs(targetX - sourceX) : 0
const centerX = sourceX > targetX ? sourceX - xOffset : sourceX + xOffset
const yOffset = sourceIsLeftOrRight ? 0 : Math.abs(targetY - sourceY);
const centerY = sourceY < targetY ? sourceY + yOffset : sourceY - yOffset;
const yOffset = sourceIsLeftOrRight ? 0 : Math.abs(targetY - sourceY)
const centerY = sourceY < targetY ? sourceY + yOffset : sourceY - yOffset
return [centerX, centerY, xOffset, yOffset];
return [centerX, centerY, xOffset, yOffset]
}
const xOffset = Math.abs(targetX - sourceX) / 2;
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
const xOffset = Math.abs(targetX - sourceX) / 2
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset
const yOffset = Math.abs(targetY - sourceY) / 2;
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
const yOffset = Math.abs(targetY - sourceY) / 2
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset
return [centerX, centerY, xOffset, yOffset];
};
return [centerX, centerY, xOffset, yOffset]
}
export const DefaultEdgeProps = {
id: {
@@ -165,7 +165,7 @@ export const DefaultEdgeProps = {
required: false,
default: undefined
}
};
}
export const EdgeSmoothProps = {
...DefaultEdgeProps,
@@ -174,4 +174,4 @@ export const EdgeSmoothProps = {
required: false,
default: 5
}
};
}
+61 -61
View File
@@ -1,15 +1,15 @@
import { getHostForElement } from '../../utils';
import { ElementId, ConnectionMode, Connection, HandleType, RevueFlowStore } from '../../types';
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
import { getHostForElement } from '../../utils'
import { ElementId, ConnectionMode, Connection, HandleType, RevueFlowStore } from '../../types'
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
export type ValidConnectionFunc = (connection: Connection) => boolean;
export type ValidConnectionFunc = (connection: Connection) => boolean
type Result = {
elementBelow: Element | null;
isValid: boolean;
connection: Connection;
isHoveringHandle: boolean;
};
elementBelow: Element | null
isValid: boolean
connection: Connection
isHoveringHandle: boolean
}
// checks if element below mouse is a handle and returns connection in form of an object { source: 123, target: 312 }
function checkElementBelowIsValid(
@@ -21,27 +21,27 @@ function checkElementBelowIsValid(
isValidConnection: ValidConnectionFunc,
doc: Document | ShadowRoot
) {
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY);
const elementBelowIsTarget = elementBelow?.classList.contains('target') || false;
const elementBelowIsSource = elementBelow?.classList.contains('source') || false;
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY)
const elementBelowIsTarget = elementBelow?.classList.contains('target') || false
const elementBelowIsSource = elementBelow?.classList.contains('source') || false
const result: Result = {
elementBelow,
isValid: false,
connection: { source: null, target: null, sourceHandle: null, targetHandle: null },
isHoveringHandle: false
};
}
if (elementBelow && (elementBelowIsTarget || elementBelowIsSource)) {
result.isHoveringHandle = true;
result.isHoveringHandle = true
// in strict mode we don't allow target to target or source to source connections
const isValid =
connectionMode === ConnectionMode.Strict ? (isTarget && elementBelowIsSource) || (!isTarget && elementBelowIsTarget) : true;
connectionMode === ConnectionMode.Strict ? (isTarget && elementBelowIsSource) || (!isTarget && elementBelowIsTarget) : true
if (isValid) {
const elementBelowNodeId = elementBelow.getAttribute('data-nodeid');
const elementBelowHandleId = elementBelow.getAttribute('data-handleid');
const elementBelowNodeId = elementBelow.getAttribute('data-nodeid')
const elementBelowHandleId = elementBelow.getAttribute('data-handleid')
const connection: Connection = isTarget
? {
source: elementBelowNodeId,
@@ -54,30 +54,30 @@ function checkElementBelowIsValid(
sourceHandle: handleId,
target: elementBelowNodeId,
targetHandle: elementBelowHandleId
};
}
result.connection = connection;
result.isValid = isValidConnection(connection);
result.connection = connection
result.isValid = isValidConnection(connection)
}
}
return result;
return result
}
function resetRecentHandle(hoveredHandle: Element): void {
hoveredHandle?.classList.remove('revue-flow__handle-valid');
hoveredHandle?.classList.remove('revue-flow__handle-connecting');
hoveredHandle?.classList.remove('revue-flow__handle-valid')
hoveredHandle?.classList.remove('revue-flow__handle-connecting')
}
export function onMouseDown(
event: MouseEvent,
store: RevueFlowStore,
hooks: {
connectStart: RevueFlowHooks['connectStart'];
connectStop: RevueFlowHooks['connectStop'];
connectEnd: RevueFlowHooks['connectEnd'];
connect: RevueFlowHooks['connect'];
edgeUpdateEnd: RevueFlowHooks['edgeUpdateEnd'];
connectStart: RevueFlowHooks['connectStart']
connectStop: RevueFlowHooks['connectStop']
connectEnd: RevueFlowHooks['connectEnd']
connect: RevueFlowHooks['connect']
edgeUpdateEnd: RevueFlowHooks['edgeUpdateEnd']
},
handleId: ElementId | null,
nodeId: ElementId,
@@ -85,39 +85,39 @@ export function onMouseDown(
isValidConnection: ValidConnectionFunc,
elementEdgeUpdaterType?: HandleType
): void {
const revueFlowNode = (event.target as Element).closest('.revue-flow');
const revueFlowNode = (event.target as Element).closest('.revue-flow')
// when revue-flow is used inside a shadow root we can't use document
const doc = getHostForElement(event.target as HTMLElement);
const doc = getHostForElement(event.target as HTMLElement)
if (!doc) {
return;
return
}
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY);
const elementBelowIsTarget = elementBelow?.classList.contains('target');
const elementBelowIsSource = elementBelow?.classList.contains('source');
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY)
const elementBelowIsTarget = elementBelow?.classList.contains('target')
const elementBelowIsSource = elementBelow?.classList.contains('source')
if (!revueFlowNode || (!elementBelowIsTarget && !elementBelowIsSource && !elementEdgeUpdaterType)) {
return;
return
}
const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementBelowIsTarget ? 'target' : 'source';
const containerBounds = revueFlowNode.getBoundingClientRect();
let recentHoveredHandle: Element;
const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementBelowIsTarget ? 'target' : 'source'
const containerBounds = revueFlowNode.getBoundingClientRect()
let recentHoveredHandle: Element
store.connectionPosition.x = event.clientX - containerBounds.left;
store.connectionPosition.y = event.clientY - containerBounds.top;
store.connectionPosition.x = event.clientX - containerBounds.left
store.connectionPosition.y = event.clientY - containerBounds.top
store.setConnectionNodeId({
connectionNodeId: nodeId,
connectionHandleId: handleId,
connectionHandleType: handleType
});
hooks.connectStart.trigger({ event, params: { nodeId, handleId, handleType } });
})
hooks.connectStart.trigger({ event, params: { nodeId, handleId, handleType } })
function onMouseMove(event: MouseEvent) {
store.connectionPosition.x = event.clientX - containerBounds.left;
store.connectionPosition.y = event.clientY - containerBounds.top;
store.connectionPosition.x = event.clientX - containerBounds.left
store.connectionPosition.y = event.clientY - containerBounds.top
const { connection, elementBelow, isValid, isHoveringHandle } = checkElementBelowIsValid(
event,
@@ -127,18 +127,18 @@ export function onMouseDown(
handleId,
isValidConnection,
doc
);
)
if (!isHoveringHandle) {
return resetRecentHandle(recentHoveredHandle);
return resetRecentHandle(recentHoveredHandle)
}
const isOwnHandle = connection.source === connection.target;
const isOwnHandle = connection.source === connection.target
if (!isOwnHandle && elementBelow) {
recentHoveredHandle = elementBelow;
elementBelow.classList.add('revue-flow__handle-connecting');
elementBelow.classList.toggle('revue-flow__handle-valid', isValid);
recentHoveredHandle = elementBelow
elementBelow.classList.add('revue-flow__handle-connecting')
elementBelow.classList.toggle('revue-flow__handle-valid', isValid)
}
}
@@ -151,27 +151,27 @@ export function onMouseDown(
handleId,
isValidConnection,
doc
);
)
hooks.connectStop.trigger(event);
hooks.connectStop.trigger(event)
if (isValid) {
hooks.connect.trigger(connection);
hooks.connect.trigger(connection)
}
hooks.connectEnd.trigger(event);
hooks.connectEnd.trigger(event)
if (elementEdgeUpdaterType) {
hooks.edgeUpdateEnd.trigger({ event } as any);
hooks.edgeUpdateEnd.trigger({ event } as any)
}
resetRecentHandle(recentHoveredHandle);
store.setConnectionNodeId({ connectionNodeId: null, connectionHandleId: null, connectionHandleType: null });
resetRecentHandle(recentHoveredHandle)
store.setConnectionNodeId({ connectionNodeId: null, connectionHandleId: null, connectionHandleType: null })
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject);
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject);
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
}
doc.addEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject);
doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject);
doc.addEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
}
+11 -11
View File
@@ -1,9 +1,9 @@
import { ElementId, Position, RevueFlowStore } from '../../types';
import { onMouseDown, ValidConnectionFunc } from './handler';
import { defineComponent, inject, PropType } from 'vue';
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
import { ElementId, Position, RevueFlowStore } from '../../types'
import { onMouseDown, ValidConnectionFunc } from './handler'
import { defineComponent, inject, PropType } from 'vue'
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
const alwaysValid = () => true;
const alwaysValid = () => true
export default defineComponent({
props: {
@@ -34,12 +34,12 @@ export default defineComponent({
}
},
setup(props, { slots }) {
const store = inject<RevueFlowStore>('store')!;
const hooks = inject<RevueFlowHooks>('hooks')!;
const nodeId = inject<ElementId>('NodeIdContext')!;
const store = inject<RevueFlowStore>('store')!
const hooks = inject<RevueFlowHooks>('hooks')!
const nodeId = inject<ElementId>('NodeIdContext')!
const onMouseDownHandler = (event: MouseEvent) =>
onMouseDown(event, store, hooks, props.id as string, nodeId, props.type === 'target', props.isValidConnection);
onMouseDown(event, store, hooks, props.id as string, nodeId, props.type === 'target', props.isValidConnection)
return () => (
<div
@@ -60,6 +60,6 @@ export default defineComponent({
>
{slots.default ? slots.default() : ''}
</div>
);
)
}
});
})
+5 -5
View File
@@ -1,6 +1,6 @@
import { defineComponent, PropType } from 'vue';
import Handle from '../../components/Handle';
import { NodeProps, NodeType, Position } from '../../types';
import { defineComponent, PropType } from 'vue'
import Handle from '../../components/Handle'
import { NodeProps, NodeType, Position } from '../../types'
export default defineComponent({
name: 'DefaultNode',
@@ -35,6 +35,6 @@ export default defineComponent({
{props.data?.label}
<Handle type="source" position={props.sourcePosition} isConnectable={props.isConnectable} />
</>
);
)
}
}) as NodeType;
}) as NodeType
+5 -5
View File
@@ -1,6 +1,6 @@
import Handle from '../../components/Handle';
import { NodeProps, NodeType, Position } from '../../types';
import { defineComponent, PropType } from 'vue';
import Handle from '../../components/Handle'
import { NodeProps, NodeType, Position } from '../../types'
import { defineComponent, PropType } from 'vue'
export default defineComponent({
name: 'InputNode',
@@ -29,6 +29,6 @@ export default defineComponent({
{props.data?.label}
<Handle type="source" position={props.sourcePosition} isConnectable={props.isConnectable} />
</>
);
)
}
}) as NodeType;
}) as NodeType
+61 -61
View File
@@ -1,14 +1,14 @@
import { computed, defineComponent, h, inject, onMounted, PropType, provide } from 'vue';
import { templateRef, useResizeObserver } from '@vueuse/core';
import { DraggableCore, DraggableEventListener } from '@braks/revue-draggable';
import { Node, NodeDimensionUpdate, NodeType, RevueFlowStore } from '../../types';
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
import { computed, defineComponent, h, inject, onMounted, PropType, provide } from 'vue'
import { templateRef, useResizeObserver } from '@vueuse/core'
import { DraggableCore, DraggableEventListener } from '@braks/revue-draggable'
import { Node, NodeDimensionUpdate, NodeType, RevueFlowStore } from '../../types'
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
interface NodeProps {
nodeTypes: Record<string, NodeType>;
snapToGrid: boolean;
snapGrid: [number, number];
selectNodesOnDrag: boolean;
nodeTypes: Record<string, NodeType>
snapToGrid: boolean
snapGrid: [number, number]
selectNodesOnDrag: boolean
}
export default defineComponent({
@@ -39,22 +39,22 @@ export default defineComponent({
}
},
setup(props) {
const store = inject<RevueFlowStore>('store')!;
const hooks = inject<RevueFlowHooks>('hooks')!;
provide('NodeIdContext', props.node.id);
const store = inject<RevueFlowStore>('store')!
const hooks = inject<RevueFlowHooks>('hooks')!
provide('NodeIdContext', props.node.id)
const nodeElement = templateRef<HTMLDivElement>('nodeElement', null);
const nodeElement = templateRef<HTMLDivElement>('nodeElement', null)
const selected = computed(() => store.selectedElements?.some(({ id }) => id === props.node.id) || false);
const selected = computed(() => store.selectedElements?.some(({ id }) => id === props.node.id) || false)
const isDraggable = computed(
() => props.node.draggable || (store.nodesDraggable && typeof props.node.draggable === 'undefined')
);
)
const isSelectable = computed(
() => props.node.selectable || (store.elementsSelectable && typeof props.node.selectable === 'undefined')
);
)
const isConnectable = computed(
() => props.node.connectable || (store.nodesConnectable && typeof props.node.connectable === 'undefined')
);
)
const node = () =>
({
@@ -62,72 +62,72 @@ export default defineComponent({
type: props.node.type,
position: { x: props.node.__rf.position.x, y: props.node.__rf.position.y },
data: props.node.data
} as Node);
} as Node)
const onMouseEnterHandler = () => {
if (props.node.__rf.isDragging) {
return;
return
}
return (event: MouseEvent) => hooks.nodeMouseEnter.trigger({ event, node: node() });
};
return (event: MouseEvent) => hooks.nodeMouseEnter.trigger({ event, node: node() })
}
const onMouseMoveHandler = () => {
if (props.node.__rf.isDragging) {
return;
return
}
return (event: MouseEvent) => hooks.nodeMouseMove.trigger({ event, node: node() });
};
return (event: MouseEvent) => hooks.nodeMouseMove.trigger({ event, node: node() })
}
const onMouseLeaveHandler = () => {
if (props.node.__rf.isDragging) {
return;
return
}
return (event: MouseEvent) => hooks.nodeMouseLeave.trigger({ event, node: node() });
};
return (event: MouseEvent) => hooks.nodeMouseLeave.trigger({ event, node: node() })
}
const onContextMenuHandler = () => {
return (event: MouseEvent) => hooks.nodeContextMenu.trigger({ event, node: node() });
};
return (event: MouseEvent) => hooks.nodeContextMenu.trigger({ event, node: node() })
}
const onSelectNodeHandler = (event: MouseEvent) => {
if (!isDraggable.value) {
const n = node();
const n = node()
if (isSelectable.value) {
store.unsetNodesSelection();
store.unsetNodesSelection()
if (!selected.value) {
store.addSelectedElements([n]);
store.addSelectedElements([n])
}
}
hooks.nodeClick.trigger({ event, node: n });
hooks.nodeClick.trigger({ event, node: n })
}
};
}
const onDragStart: DraggableEventListener = ({ event }) => {
const n = node();
hooks.nodeDragStart.trigger({ event, node: n });
const n = node()
hooks.nodeDragStart.trigger({ event, node: n })
if (props.selectNodesOnDrag && isSelectable.value) {
store.unsetNodesSelection();
store.unsetNodesSelection()
if (!selected.value) {
store.addSelectedElements([n]);
store.addSelectedElements([n])
}
} else if (!props.selectNodesOnDrag && !selected.value && isSelectable.value) {
store.unsetNodesSelection();
store.addSelectedElements([]);
store.unsetNodesSelection()
store.addSelectedElements([])
}
};
}
const onDrag: DraggableEventListener = ({ event, data }) => {
const n = node();
n.position.x += data.deltaX;
n.position.y += data.deltaY;
hooks.nodeDrag.trigger({ event, node: n });
const n = node()
n.position.x += data.deltaX
n.position.y += data.deltaY
hooks.nodeDrag.trigger({ event, node: n })
store?.updateNodePosDiff({
id: props.node.id as string,
@@ -136,39 +136,39 @@ export default defineComponent({
y: data.deltaY
},
isDragging: true
});
};
})
}
const onDragStop: DraggableEventListener = ({ event }) => {
const n = node();
const n = node()
// onDragStop also gets called when user just clicks on a node.
// Because of that we set dragging to true inside the onDrag handler and handle the click here
if (!props.node.__rf.isDragging) {
if (isSelectable.value && !props.selectNodesOnDrag && !selected.value) {
store.addSelectedElements([n]);
store.addSelectedElements([n])
}
hooks.nodeClick.trigger({ event, node: n });
hooks.nodeClick.trigger({ event, node: n })
return;
return
}
store.updateNodePosDiff({
id: n.id || '',
isDragging: false
});
})
hooks.nodeDragStop.trigger({ event, node: n });
};
hooks.nodeDragStop.trigger({ event, node: n })
}
useResizeObserver(nodeElement, (entries) => {
const updates: NodeDimensionUpdate[] = entries.map((entry) => ({
id: entry.target.getAttribute('data-id') || '',
nodeElement: entry.target as HTMLDivElement
}));
}))
store.updateNodeDimensions(updates);
});
store.updateNodeDimensions(updates)
})
onMounted(() => {
store.updateNodeDimensions([
@@ -177,8 +177,8 @@ export default defineComponent({
nodeElement: nodeElement.value,
forceUpdate: true
}
]);
});
])
})
return () =>
props.node.isHidden ? (
@@ -231,6 +231,6 @@ export default defineComponent({
})}
</div>
</DraggableCore>
);
)
}
});
})
+5 -5
View File
@@ -1,6 +1,6 @@
import Handle from '../../components/Handle';
import { NodeProps, NodeType, Position } from '../../types';
import { defineComponent, PropType } from 'vue';
import Handle from '../../components/Handle'
import { NodeProps, NodeType, Position } from '../../types'
import { defineComponent, PropType } from 'vue'
export default defineComponent({
name: 'OutputNode',
@@ -29,6 +29,6 @@ export default defineComponent({
{props.data?.label}
<Handle type="source" position={props.targetPosition} isConnectable={props.isConnectable} />
</>
);
)
}
}) as NodeType;
}) as NodeType
+15 -15
View File
@@ -1,14 +1,14 @@
import { HandleElement, Position } from '../../types';
import { getDimensions } from '../../utils';
import { HandleElement, Position } from '../../types'
import { getDimensions } from '../../utils'
export const getHandleBounds = (nodeElement: HTMLDivElement, scale: number) => {
const bounds = nodeElement.getBoundingClientRect();
const bounds = nodeElement.getBoundingClientRect()
return {
source: getHandleBoundsByHandleType('.source', nodeElement, bounds, scale),
target: getHandleBoundsByHandleType('.target', nodeElement, bounds, scale)
};
};
}
}
export const getHandleBoundsByHandleType = (
selector: string,
@@ -16,19 +16,19 @@ export const getHandleBoundsByHandleType = (
parentBounds: ClientRect | DOMRect,
k: number
): HandleElement[] | null => {
const handles = nodeElement.querySelectorAll(selector);
const handles = nodeElement.querySelectorAll(selector)
if (!handles || !handles.length) {
return null;
return null
}
const handlesArray = Array.from(handles) as HTMLDivElement[];
const handlesArray = Array.from(handles) as HTMLDivElement[]
return handlesArray.map((handle): HandleElement => {
const bounds = handle.getBoundingClientRect();
const dimensions = getDimensions(handle);
const handleId = handle.getAttribute('data-handleid');
const handlePosition = handle.getAttribute('data-handlepos') as unknown as Position;
const bounds = handle.getBoundingClientRect()
const dimensions = getDimensions(handle)
const handleId = handle.getAttribute('data-handleid')
const handlePosition = handle.getAttribute('data-handlepos') as unknown as Position
return {
id: handleId,
@@ -36,6 +36,6 @@ export const getHandleBoundsByHandleType = (
x: (bounds.left - parentBounds.left) / k,
y: (bounds.top - parentBounds.top) / k,
...dimensions
};
});
};
}
})
}
+33 -33
View File
@@ -3,56 +3,56 @@
* The nodes selection rectangle gets displayed when a user
* made a selectio with on or several nodes
*/
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';
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;
onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void;
onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void;
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void;
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void
onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void
onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void
}
const NodesSelection = defineComponent({
setup() {
const store = inject<RevueFlowStore>('store')!;
const hooks = inject<RevueFlowHooks>('hooks')!;
const grid = computed(() => (store.snapToGrid ? store.snapGrid : [1, 1])! as [number, number]);
const transform = computed(() => store.transform);
const store = inject<RevueFlowStore>('store')!
const hooks = inject<RevueFlowHooks>('hooks')!
const grid = computed(() => (store.snapToGrid ? store.snapGrid : [1, 1])! as [number, number])
const transform = computed(() => store.transform)
const selectedNodes = computed(() =>
store.selectedElements
? store.selectedElements.filter(isNode).map((selectedNode) => {
const matchingNode = store.nodes.find((node) => node.id === selectedNode.id);
const matchingNode = store.nodes.find((node) => node.id === selectedNode.id)
return {
...matchingNode,
position: matchingNode?.__rf.position
} as Node;
} as Node
})
: []
);
)
const style = computed(() => ({
transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`
}));
}))
const innerStyle = computed(() => ({
width: `${store.selectedNodesBbox.width}px`,
height: `${store.selectedNodesBbox.height}px`,
top: `${store.selectedNodesBbox.y}px`,
left: `${store.selectedNodesBbox.x}px`
}));
}))
const onStart: DraggableEventListener = ({ event }) => {
hooks.selectionDragStart.trigger({ event, nodes: selectedNodes.value });
};
hooks.selectionDragStart.trigger({ event, nodes: selectedNodes.value })
}
const onDrag: DraggableEventListener = ({ event, data }) => {
hooks.selectionDrag.trigger({ event, nodes: selectedNodes.value });
hooks.selectionDrag.trigger({ event, nodes: selectedNodes.value })
store.updateNodePosDiff({
diff: {
@@ -60,24 +60,24 @@ const NodesSelection = defineComponent({
y: data.deltaY
},
isDragging: true
});
};
})
}
const onStop: DraggableEventListener = ({ event }) => {
store.updateNodePosDiff({
isDragging: false
});
})
hooks.selectionDragStop.trigger({ event, nodes: selectedNodes.value });
};
hooks.selectionDragStop.trigger({ event, nodes: selectedNodes.value })
}
const onContextMenu = (event: MouseEvent) => {
const selectedNodes: Node[] = store.selectedElements
? store.selectedElements.filter(isNode).map((selectedNode) => store.nodes.find((node) => node.id === selectedNode.id))
: [];
: []
hooks.selectionContextMenu.trigger({ event, nodes: selectedNodes });
};
hooks.selectionContextMenu.trigger({ event, nodes: selectedNodes })
}
return () => {
return !store.selectedElements || store.selectionActive ? (
@@ -95,9 +95,9 @@ const NodesSelection = defineComponent({
<div class="revue-flow__nodesselection-rect" onContextmenu={onContextMenu} style={innerStyle.value} />
</Draggable>
</div>
);
};
)
}
}
});
})
export default NodesSelection;
export default NodesSelection
+33 -33
View File
@@ -1,22 +1,22 @@
/**
* The user selection rectangle gets displayed when a user drags the mouse while pressing shift
*/
import { RevueFlowStore, XYPosition } from '../../types';
import { computed, defineComponent, inject } from 'vue';
import { templateRef, useEventListener } from '@vueuse/core';
import { RevueFlowStore, XYPosition } from '../../types'
import { computed, defineComponent, inject } from 'vue'
import { templateRef, useEventListener } from '@vueuse/core'
function getMousePosition(event: MouseEvent): XYPosition | void {
const revueFlowNode = (event.target as Element).closest('.revue-flow');
const revueFlowNode = (event.target as Element).closest('.revue-flow')
if (!revueFlowNode) {
return;
return
}
const containerBounds = revueFlowNode.getBoundingClientRect();
const containerBounds = revueFlowNode.getBoundingClientRect()
return {
x: event.clientX - containerBounds.left,
y: event.clientY - containerBounds.top
};
}
}
const SelectionRect = (props: { width: number; height: number; x: number; y: number }) => {
@@ -29,51 +29,51 @@ const SelectionRect = (props: { width: number; height: number; x: number; y: num
transform: `translate(${props.x}px, ${props.y}px)`
}}
/>
);
};
)
}
export default defineComponent({
components: { SelectionRect },
setup() {
const store = inject<RevueFlowStore>('store')!;
const el = templateRef('user-selection', null);
const shouldRender = computed(() => store.selectionActive || store.elementsSelectable);
const store = inject<RevueFlowStore>('store')!
const el = templateRef('user-selection', null)
const shouldRender = computed(() => store.selectionActive || store.elementsSelectable)
const onMouseDown = (event: MouseEvent) => {
const mousePos = getMousePosition(event);
const mousePos = getMousePosition(event)
if (!mousePos) {
return;
return
}
store.setUserSelection(mousePos);
};
store.setUserSelection(mousePos)
}
const onMouseMove = (event: MouseEvent) => {
if (!store.selectionActive) {
return;
return
}
const mousePos = getMousePosition(event);
const mousePos = getMousePosition(event)
if (!mousePos) {
return;
return
}
store.updateUserSelection(mousePos);
};
store.updateUserSelection(mousePos)
}
const onMouseUp = () => {
store.unsetUserSelection();
};
store.unsetUserSelection()
}
const onMouseLeave = () => {
store.unsetUserSelection();
store.unsetNodesSelection();
};
store.unsetUserSelection()
store.unsetNodesSelection()
}
useEventListener(el, 'mousedown', onMouseDown);
useEventListener(el, 'mousemove', onMouseMove);
useEventListener(el, 'click', onMouseUp);
useEventListener(el, 'mouseup', onMouseUp);
useEventListener(el, 'mouseleave', onMouseLeave);
useEventListener(el, 'mousedown', onMouseDown)
useEventListener(el, 'mousemove', onMouseMove)
useEventListener(el, 'click', onMouseUp)
useEventListener(el, 'mouseup', onMouseUp)
useEventListener(el, 'mouseleave', onMouseLeave)
return () =>
shouldRender.value ? (
@@ -91,6 +91,6 @@ export default defineComponent({
</div>
) : (
''
);
)
}
});
})
+4 -4
View File
@@ -1,7 +1,7 @@
import { defineComponent } from 'vue';
import { defineComponent } from 'vue'
interface MarkerProps {
id: string;
id: string
}
export default defineComponent({
@@ -25,6 +25,6 @@ export default defineComponent({
>
{slots.default ? slots.default() : ''}
</marker>
);
)
}
});
})
@@ -1,8 +1,8 @@
import { defineComponent, PropType } from 'vue';
import Marker from './Marker';
import { defineComponent, PropType } from 'vue'
import Marker from './Marker'
interface MarkerDefinitionsProps {
color: string;
color: string
}
export default defineComponent({
@@ -38,6 +38,6 @@ export default defineComponent({
/>
</Marker>
</defs>
);
)
}
});
})
+17 -17
View File
@@ -1,19 +1,19 @@
import { CSSProperties, defineComponent, inject, PropType } from 'vue';
import { ConnectionLineType, ConnectionLineComponent, ConnectionMode, RevueFlowStore } from '../../types';
import ConnectionLine from '../../components/ConnectionLine';
import MarkerDefinitions from './MarkerDefinitions';
import Edge from '../../components/Edges/Edge';
import { CSSProperties, defineComponent, inject, PropType } from 'vue'
import { ConnectionLineType, ConnectionLineComponent, ConnectionMode, RevueFlowStore } from '../../types'
import ConnectionLine from '../../components/ConnectionLine'
import MarkerDefinitions from './MarkerDefinitions'
import Edge from '../../components/Edges/Edge'
interface EdgeRendererProps {
edgeTypes: any;
connectionLineType: ConnectionLineType;
connectionLineStyle?: CSSProperties;
connectionLineComponent?: ConnectionLineComponent;
connectionMode?: ConnectionMode;
arrowHeadColor: string;
markerEndId?: string;
onlyRenderVisibleElements: boolean;
edgeUpdaterRadius?: number;
edgeTypes: any
connectionLineType: ConnectionLineType
connectionLineStyle?: CSSProperties
connectionLineComponent?: ConnectionLineComponent
connectionMode?: ConnectionMode
arrowHeadColor: string
markerEndId?: string
onlyRenderVisibleElements: boolean
edgeUpdaterRadius?: number
}
export default defineComponent({
@@ -70,7 +70,7 @@ export default defineComponent({
}
},
setup(props) {
const store = inject<RevueFlowStore>('store')!;
const store = inject<RevueFlowStore>('store')!
return () => (
<svg width={store.width} height={store.height} class="revue-flow__edges">
@@ -94,6 +94,6 @@ export default defineComponent({
)}
</g>
</svg>
);
)
}
});
})
+51 -51
View File
@@ -1,6 +1,6 @@
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges';
import { rectToBox } from '../../utils/graph';
import { Position, Node, XYPosition, ElementId, HandleElement, Transform, Edge, EdgeType } from '../../types';
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges'
import { rectToBox } from '../../utils/graph'
import { Position, Node, XYPosition, ElementId, HandleElement, Transform, Edge, EdgeType } from '../../types'
export function createEdgeTypes(edgeTypes: Record<string, EdgeType>): Record<string, EdgeType> {
const standardTypes: Record<string, EdgeType> = {
@@ -8,75 +8,75 @@ export function createEdgeTypes(edgeTypes: Record<string, EdgeType>): Record<str
straight: edgeTypes.bezier || StraightEdge,
step: edgeTypes.step || StepEdge,
smoothstep: edgeTypes.step || SmoothStepEdge
};
}
const wrappedTypes = {} as Record<string, EdgeType>;
const wrappedTypes = {} as Record<string, EdgeType>
const specialTypes: Record<string, EdgeType> = Object.keys(edgeTypes)
.filter((k) => !['default', 'bezier'].includes(k))
.reduce((res, key) => {
res[key] = edgeTypes[key] || BezierEdge;
res[key] = edgeTypes[key] || BezierEdge
return res;
}, wrappedTypes);
return res
}, wrappedTypes)
return {
...standardTypes,
...specialTypes
};
}
}
export function getHandlePosition(position: Position, node: Node, handle: any | null = null): XYPosition {
const x = (handle?.x || 0) + node.__rf.position.x;
const y = (handle?.y || 0) + node.__rf.position.y;
const width = handle?.width || node.__rf.width;
const height = handle?.height || node.__rf.height;
const x = (handle?.x || 0) + node.__rf.position.x
const y = (handle?.y || 0) + node.__rf.position.y
const width = handle?.width || node.__rf.width
const height = handle?.height || node.__rf.height
switch (position) {
case Position.Top:
return {
x: x + width / 2,
y
};
}
case Position.Right:
return {
x: x + width,
y: y + height / 2
};
}
case Position.Bottom:
return {
x: x + width / 2,
y: y + height
};
}
case Position.Left:
return {
x,
y: y + height / 2
};
}
}
}
export function getHandle(bounds: HandleElement[], handleId: ElementId | null): HandleElement | null {
if (!bounds) {
return null;
return null
}
// there is no handleId when there are no multiple handles/ handles with ids
// so we just pick the first one
let handle = null;
let handle = null
if (bounds.length === 1 || !handleId) {
handle = bounds[0];
handle = bounds[0]
} else if (handleId) {
handle = bounds.find((d) => d.id === handleId);
handle = bounds.find((d) => d.id === handleId)
}
return typeof handle === 'undefined' ? null : handle;
return typeof handle === 'undefined' ? null : handle
}
interface EdgePositions {
sourceX: number;
sourceY: number;
targetX: number;
targetY: number;
sourceX: number
sourceY: number
targetX: number
targetY: number
}
export const getEdgePositions = (
@@ -87,23 +87,23 @@ export const getEdgePositions = (
targetHandle: HandleElement | unknown,
targetPosition: Position
): EdgePositions => {
const sourceHandlePos = getHandlePosition(sourcePosition, sourceNode, sourceHandle);
const targetHandlePos = getHandlePosition(targetPosition, targetNode, targetHandle);
const sourceHandlePos = getHandlePosition(sourcePosition, sourceNode, sourceHandle)
const targetHandlePos = getHandlePosition(targetPosition, targetNode, targetHandle)
return {
sourceX: sourceHandlePos.x,
sourceY: sourceHandlePos.y,
targetX: targetHandlePos.x,
targetY: targetHandlePos.y
};
};
}
}
interface IsEdgeVisibleParams {
sourcePos: XYPosition;
targetPos: XYPosition;
width: number;
height: number;
transform: Transform;
sourcePos: XYPosition
targetPos: XYPosition
width: number
height: number
transform: Transform
}
export function isEdgeVisible({ sourcePos, targetPos, width, height, transform }: IsEdgeVisibleParams): boolean {
@@ -112,14 +112,14 @@ export function isEdgeVisible({ sourcePos, targetPos, width, height, transform }
y: Math.min(sourcePos.y, targetPos.y),
x2: Math.max(sourcePos.x, targetPos.x),
y2: Math.max(sourcePos.y, targetPos.y)
};
}
if (edgeBox.x === edgeBox.x2) {
edgeBox.x2 += 1;
edgeBox.x2 += 1
}
if (edgeBox.y === edgeBox.y2) {
edgeBox.y2 += 1;
edgeBox.y2 += 1
}
const viewBox = rectToBox({
@@ -127,31 +127,31 @@ export function isEdgeVisible({ sourcePos, targetPos, width, height, transform }
y: (0 - transform[1]) / transform[2],
width: width / transform[2],
height: height / transform[2]
});
})
const xOverlap = Math.max(0, Math.min(viewBox.x2, edgeBox.x2) - Math.max(viewBox.x, edgeBox.x));
const yOverlap = Math.max(0, Math.min(viewBox.y2, edgeBox.y2) - Math.max(viewBox.y, edgeBox.y));
const overlappingArea = Math.ceil(xOverlap * yOverlap);
const xOverlap = Math.max(0, Math.min(viewBox.x2, edgeBox.x2) - Math.max(viewBox.x, edgeBox.x))
const yOverlap = Math.max(0, Math.min(viewBox.y2, edgeBox.y2) - Math.max(viewBox.y, edgeBox.y))
const overlappingArea = Math.ceil(xOverlap * yOverlap)
return overlappingArea > 0;
return overlappingArea > 0
}
type SourceTargetNode = {
sourceNode: Node | null;
targetNode: Node | null;
};
sourceNode: Node | null
targetNode: Node | null
}
export const getSourceTargetNodes = (edge: Edge, nodes: Node[]): SourceTargetNode => {
return nodes.reduce(
(res, node) => {
if (node.id === edge.source) {
res.sourceNode = node;
res.sourceNode = node
}
if (node.id === edge.target) {
res.targetNode = node;
res.targetNode = node
}
return res;
return res
},
{ sourceNode: null, targetNode: null } as SourceTargetNode
);
};
)
}
+22 -22
View File
@@ -1,11 +1,11 @@
import { computed, defineComponent, inject, PropType } from 'vue';
import ZoomPane from '../ZoomPane';
import UserSelection from '../../components/UserSelection';
import NodesSelection from '../../components/NodesSelection';
import { RevueFlowStore, GraphViewProps } from '../../types';
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
import useKeyPress from '../../hooks/useKeyPress';
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
import { computed, defineComponent, inject, PropType } from 'vue'
import ZoomPane from '../ZoomPane'
import UserSelection from '../../components/UserSelection'
import NodesSelection from '../../components/NodesSelection'
import { RevueFlowStore, GraphViewProps } from '../../types'
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler'
import useKeyPress from '../../hooks/useKeyPress'
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
type FlowRendererProps = Omit<
GraphViewProps,
@@ -18,7 +18,7 @@ type FlowRendererProps = Omit<
| 'arrowHeadColor'
| 'onlyRenderVisibleElements'
| 'selectNodesOnDrag'
>;
>
export default defineComponent({
name: 'FlowRenderer',
@@ -101,28 +101,28 @@ export default defineComponent({
}
},
setup(props, { slots }) {
const store = inject<RevueFlowStore>('store')!;
const hooks = inject<RevueFlowHooks>('hooks')!;
const keyPressed = useKeyPress(props.selectionKeyCode);
const store = inject<RevueFlowStore>('store')!
const hooks = inject<RevueFlowHooks>('hooks')!
const keyPressed = useKeyPress(props.selectionKeyCode)
useGlobalKeyHandler({
store,
onElementsRemove: hooks.elementsRemove.trigger,
deleteKeyCode: props.deleteKeyCode || '',
multiSelectionKeyCode: props.multiSelectionKeyCode || ''
});
})
const onClick = (event: MouseEvent) => {
hooks.paneClick.trigger(event);
store.unsetNodesSelection();
store.resetSelectedElements();
};
hooks.paneClick.trigger(event)
store.unsetNodesSelection()
store.resetSelectedElements()
}
const onContextMenu = (event: MouseEvent) => hooks.paneContextMenu.trigger(event);
const onContextMenu = (event: MouseEvent) => hooks.paneContextMenu.trigger(event)
const onWheel = (event: WheelEvent) => hooks.paneScroll.trigger(event);
const onWheel = (event: WheelEvent) => hooks.paneScroll.trigger(event)
const selectionActive = computed(() => store.nodesSelectionActive);
const selectionActive = computed(() => store.nodesSelectionActive)
return () => (
<ZoomPane
@@ -145,6 +145,6 @@ export default defineComponent({
{selectionActive.value ? <NodesSelection /> : ''}
<div class="revue-flow__pane" onClick={onClick} onContextmenu={onContextMenu} onWheel={onWheel} />
</ZoomPane>
);
)
}
});
})
+29 -29
View File
@@ -1,12 +1,12 @@
import { defineComponent, inject, PropType } from 'vue';
import FlowRenderer from '../FlowRenderer';
import NodeRenderer from '../NodeRenderer';
import EdgeRenderer from '../EdgeRenderer';
import { onLoadProject, onLoadGetElements, onLoadToObject } from '../../utils/graph';
import { GraphViewProps, RevueFlowStore } from '../../types';
import useZoomPanHelper from '../../hooks/useZoomPanHelper';
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
import { tryOnMounted } from '@vueuse/core';
import { defineComponent, inject, PropType } from 'vue'
import FlowRenderer from '../FlowRenderer'
import NodeRenderer from '../NodeRenderer'
import EdgeRenderer from '../EdgeRenderer'
import { onLoadProject, onLoadGetElements, onLoadToObject } from '../../utils/graph'
import { GraphViewProps, RevueFlowStore } from '../../types'
import useZoomPanHelper from '../../hooks/useZoomPanHelper'
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
import { tryOnMounted } from '@vueuse/core'
export default defineComponent({
name: 'GraphView',
@@ -177,10 +177,10 @@ export default defineComponent({
}
},
setup(props) {
const store = inject<RevueFlowStore>('store')!;
const hooks = inject<RevueFlowHooks>('hooks')!;
const store = inject<RevueFlowStore>('store')!
const hooks = inject<RevueFlowHooks>('hooks')!
const { onReady } = useZoomPanHelper();
const { onReady } = useZoomPanHelper()
onReady(({ zoomIn, zoomOut, zoomTo, transform, fitView }) => {
hooks.load.trigger({
fitView: (params = { padding: 0.1 }) => fitView(params),
@@ -191,52 +191,52 @@ export default defineComponent({
project: onLoadProject(store),
getElements: onLoadGetElements(store),
toObject: onLoadToObject(store)
});
});
})
})
const init = () => {
if (typeof props.snapToGrid !== 'undefined') {
store.snapToGrid = props.snapToGrid;
store.snapToGrid = props.snapToGrid
}
if (typeof props.snapGrid !== 'undefined') {
store.snapGrid = props.snapGrid;
store.snapGrid = props.snapGrid
}
if (typeof props.minZoom !== 'undefined') {
store.setMinZoom(props.minZoom);
store.setMinZoom(props.minZoom)
}
if (typeof props.maxZoom !== 'undefined') {
store.setMaxZoom(props.maxZoom);
store.setMaxZoom(props.maxZoom)
}
if (typeof props.translateExtent !== 'undefined') {
store.setTranslateExtent(props.translateExtent);
store.setTranslateExtent(props.translateExtent)
}
if (typeof props.nodeExtent !== 'undefined') {
store.setNodeExtent(props.nodeExtent);
store.setNodeExtent(props.nodeExtent)
}
if (typeof props.nodesConnectable !== 'undefined') {
store.nodesConnectable = !!props.nodesConnectable;
store.nodesConnectable = !!props.nodesConnectable
}
if (typeof props.elementsSelectable !== 'undefined') {
store.elementsSelectable = !!props.elementsSelectable;
store.elementsSelectable = !!props.elementsSelectable
}
if (typeof props.nodesDraggable !== 'undefined') {
store.nodesDraggable = !!props.nodesDraggable;
store.nodesDraggable = !!props.nodesDraggable
}
if (typeof props.connectionMode !== 'undefined') {
store.connectionMode = props.connectionMode;
store.connectionMode = props.connectionMode
}
};
}
tryOnMounted(() => {
init();
});
init()
})
return () => (
<FlowRenderer
@@ -275,6 +275,6 @@ export default defineComponent({
edgeUpdaterRadius={props.edgeUpdaterRadius}
/>
</FlowRenderer>
);
)
}
});
})
+34 -37
View File
@@ -1,14 +1,14 @@
import { defineComponent, inject, PropType } from 'vue';
import Node from '../../components/Nodes/Node';
import { getNodesInside } from '../../utils/graph';
import { NodeType, RevueFlowStore } from '../../types';
import { defineComponent, inject, PropType } from 'vue'
import Node from '../../components/Nodes/Node'
import { getNodesInside } from '../../utils/graph'
import { NodeType, RevueFlowStore } from '../../types'
interface NodeRendererProps {
nodeTypes: Record<string, NodeType>;
selectNodesOnDrag: boolean;
snapToGrid: boolean;
snapGrid: [number, number];
onlyRenderVisibleElements: boolean;
nodeTypes: Record<string, NodeType>
selectNodesOnDrag: boolean
snapToGrid: boolean
snapGrid: [number, number]
onlyRenderVisibleElements: boolean
}
export default defineComponent({
@@ -42,7 +42,7 @@ export default defineComponent({
}
},
setup(props) {
const store = inject<RevueFlowStore>('store')!;
const store = inject<RevueFlowStore>('store')!
const getNodes = () => {
return props.onlyRenderVisibleElements
@@ -58,30 +58,8 @@ export default defineComponent({
store.transform,
true
)
: store.nodes;
};
const visibleNodes = () =>
getNodes().map((node, i) => {
const nodeType = node.type || 'default';
if (props.nodeTypes) {
const type = props.nodeTypes[nodeType] || props.nodeTypes.default;
if (!props.nodeTypes[nodeType]) {
console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`);
}
return (
<Node
node={node}
snapGrid={props.snapGrid}
snapToGrid={props.snapToGrid}
selectNodesOnDrag={props.selectNodesOnDrag}
type={type}
key={node.id + i}
/>
);
}
});
: store.nodes
}
return () => (
<div
@@ -90,8 +68,27 @@ export default defineComponent({
transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`
}}
>
{visibleNodes()}
{getNodes().map((node, i) => {
const nodeType = node.type || 'default'
if (props.nodeTypes) {
const type = props.nodeTypes[nodeType] || props.nodeTypes.default
if (!props.nodeTypes[nodeType]) {
console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`)
}
return (
<Node
node={node}
snapGrid={props.snapGrid}
snapToGrid={props.snapToGrid}
selectNodesOnDrag={props.selectNodesOnDrag}
type={type}
key={node.id + i}
/>
)
}
})}
</div>
);
)
}
});
})
+10 -10
View File
@@ -1,26 +1,26 @@
import DefaultNode from '../../components/Nodes/DefaultNode';
import InputNode from '../../components/Nodes/InputNode';
import OutputNode from '../../components/Nodes/OutputNode';
import { NodeType } from '../../types';
import DefaultNode from '../../components/Nodes/DefaultNode'
import InputNode from '../../components/Nodes/InputNode'
import OutputNode from '../../components/Nodes/OutputNode'
import { NodeType } from '../../types'
export function createNodeTypes(nodeTypes: Record<string, NodeType>): Record<string, NodeType> {
const standardTypes: Record<string, NodeType> = {
input: nodeTypes.input || InputNode,
default: nodeTypes.default || DefaultNode,
output: nodeTypes.output || OutputNode
};
}
const wrappedTypes = {} as NodeType;
const wrappedTypes = {} as NodeType
const specialTypes: NodeType = Object.keys(nodeTypes)
.filter((k) => !['input', 'default', 'output'].includes(k))
.reduce((res, key) => {
res[key] = nodeTypes[key] || DefaultNode;
res[key] = nodeTypes[key] || DefaultNode
return res;
}, wrappedTypes);
return res
}, wrappedTypes)
return {
...standardTypes,
...specialTypes
};
}
}
+31 -32
View File
@@ -1,31 +1,30 @@
import { defineComponent, onBeforeUnmount, onUpdated, PropType, provide, watch } from 'vue';
import { useVModel } from '@vueuse/core';
import '../../style.css';
import '../../theme-default.css';
import GraphView from '../GraphView';
import DefaultNode from '../../components/Nodes/DefaultNode';
import InputNode from '../../components/Nodes/InputNode';
import OutputNode from '../../components/Nodes/OutputNode';
import { createNodeTypes } from '../NodeRenderer/utils';
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges';
import { createEdgeTypes } from '../EdgeRenderer/utils';
import { ConnectionMode, ConnectionLineType, PanOnScrollMode, RevueFlowStore, RevueFlowProps } from '../../types';
import { initialState } from '../../store';
import configureStore from '../../store/configure-store';
import { RevueFlowHooks, useRevueFlow } from '../../hooks/RevueFlowHooks';
import { PropType } from 'vue'
import '../../style.css'
import '../../theme-default.css'
import GraphView from '../GraphView'
import DefaultNode from '../../components/Nodes/DefaultNode'
import InputNode from '../../components/Nodes/InputNode'
import OutputNode from '../../components/Nodes/OutputNode'
import { createNodeTypes } from '../NodeRenderer/utils'
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges'
import { createEdgeTypes } from '../EdgeRenderer/utils'
import { ConnectionMode, ConnectionLineType, PanOnScrollMode, RevueFlowStore, RevueFlowProps } from '../../types'
import { initialState } from '../../store'
import configureStore from '../../store/configure-store'
import { RevueFlowHooks, useRevueFlow } from '../../hooks/RevueFlowHooks'
const defaultNodeTypes = {
input: InputNode,
default: DefaultNode,
output: OutputNode
};
}
const defaultEdgeTypes = {
default: BezierEdge,
straight: StraightEdge,
step: StepEdge,
smoothstep: SmoothStepEdge
};
}
export default defineComponent({
name: 'RevueFlow',
@@ -220,25 +219,25 @@ export default defineComponent({
},
emits: Object.keys(useRevueFlow()),
setup(props, { emit, slots }) {
const store = configureStore(initialState)();
provide<RevueFlowStore>('store', store);
const hooks = useRevueFlow().bind(emit);
provide<RevueFlowHooks>('hooks', hooks);
const elements = useVModel(props, 'modelValue', emit);
const store = configureStore(initialState)()
provide<RevueFlowStore>('store', store)
const hooks = useRevueFlow().bind(emit)
provide<RevueFlowHooks>('hooks', hooks)
const elements = useVModel(props, 'modelValue', emit)
store.setElements(elements.value);
store.setElements(elements.value)
watch(elements.value, () => {
store.setElements(elements.value);
});
store.setElements(elements.value)
})
onUpdated(() => {
store.setElements(elements.value);
});
store.setElements(elements.value)
})
onBeforeUnmount(() => {
store.$reset();
store.setElements([]);
});
store.$reset()
store.setElements([])
})
return () => (
<div class="revue-flow">
@@ -279,6 +278,6 @@ export default defineComponent({
/>
{slots.default ? slots.default() : ''}
</div>
);
)
}
});
})
+91 -91
View File
@@ -1,28 +1,28 @@
import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom';
import { select, pointer } from 'd3-selection';
import { clamp } from '../../utils';
import { FlowTransform, TranslateExtent, PanOnScrollMode, KeyCode, RevueFlowStore } from '../../types';
import { defineComponent, inject, PropType, ref, watchPostEffect } from 'vue';
import useKeyPress from '../../hooks/useKeyPress';
import useResizeHandler from '../../hooks/useResizeHandler';
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
import { templateRef } from '@vueuse/core';
import { ZoomBehavior } from 'd3';
import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom'
import { select, pointer } from 'd3-selection'
import { clamp } from '../../utils'
import { FlowTransform, TranslateExtent, PanOnScrollMode, KeyCode, RevueFlowStore } from '../../types'
import { defineComponent, inject, PropType, ref, watchPostEffect } from 'vue'
import useKeyPress from '../../hooks/useKeyPress'
import useResizeHandler from '../../hooks/useResizeHandler'
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
import { templateRef } from '@vueuse/core'
import { ZoomBehavior } from 'd3'
interface ZoomPaneProps {
elementsSelectable?: boolean;
zoomOnScroll?: boolean;
zoomOnPinch?: boolean;
panOnScroll?: boolean;
panOnScrollSpeed?: number;
panOnScrollMode?: PanOnScrollMode;
zoomOnDoubleClick?: boolean;
paneMoveable?: boolean;
defaultPosition?: [number, number];
defaultZoom?: number;
translateExtent?: TranslateExtent;
zoomActivationKeyCode?: KeyCode;
selectionKeyCode?: KeyCode;
elementsSelectable?: boolean
zoomOnScroll?: boolean
zoomOnPinch?: boolean
panOnScroll?: boolean
panOnScrollSpeed?: number
panOnScrollMode?: PanOnScrollMode
zoomOnDoubleClick?: boolean
paneMoveable?: boolean
defaultPosition?: [number, number]
defaultZoom?: number
translateExtent?: TranslateExtent
zoomActivationKeyCode?: KeyCode
selectionKeyCode?: KeyCode
}
export default defineComponent({
@@ -95,35 +95,35 @@ export default defineComponent({
}
},
setup(props, { slots }) {
const store = inject<RevueFlowStore>('store')!;
const hooks = inject<RevueFlowHooks>('hooks')!;
const zoomPane = templateRef<HTMLDivElement>('zoom-pane', null);
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 });
const store = inject<RevueFlowStore>('store')!
const hooks = inject<RevueFlowHooks>('hooks')!
const zoomPane = templateRef<HTMLDivElement>('zoom-pane', null)
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 })
const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean =>
prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k;
prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k
const eventToFlowTransform = (eventTransform: ZoomTransform): FlowTransform => ({
x: eventTransform.x,
y: eventTransform.y,
zoom: eventTransform.k
});
})
const init = () => {
const { onReady } = useResizeHandler(zoomPane, store.updateSize);
const { onReady } = useResizeHandler(zoomPane, store.updateSize)
onReady(() => {
if (props.defaultPosition && props.defaultZoom) {
const currentTranslateExtent =
typeof props.translateExtent !== 'undefined' ? props.translateExtent : store.translateExtent;
const d3ZoomInstance = zoom().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(currentTranslateExtent);
const selection = select(zoomPane.value as Element).call(d3ZoomInstance);
typeof props.translateExtent !== 'undefined' ? props.translateExtent : store.translateExtent
const d3ZoomInstance = zoom().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(currentTranslateExtent)
const selection = select(zoomPane.value as Element).call(d3ZoomInstance)
const clampedX = clamp(props.defaultPosition[0], currentTranslateExtent[0][0], currentTranslateExtent[1][0]);
const clampedY = clamp(props.defaultPosition[1], currentTranslateExtent[0][1], currentTranslateExtent[1][1]);
const clampedZoom = clamp(props.defaultZoom, store.minZoom, store.maxZoom);
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom);
const clampedX = clamp(props.defaultPosition[0], currentTranslateExtent[0][0], currentTranslateExtent[1][0])
const clampedY = clamp(props.defaultPosition[1], currentTranslateExtent[0][1], currentTranslateExtent[1][1])
const clampedZoom = clamp(props.defaultZoom, store.minZoom, store.maxZoom)
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom)
d3ZoomInstance.transform(selection, updatedTransform);
d3ZoomInstance.transform(selection, updatedTransform)
store.initD3Zoom({
d3Zoom: d3ZoomInstance,
@@ -131,106 +131,106 @@ export default defineComponent({
d3ZoomHandler: selection.on('wheel.zoom'),
// we need to pass transform because zoom handler is not registered when we set the initial transform
transform: [clampedX, clampedY, clampedZoom]
});
applyZoomHandlers(d3ZoomInstance);
applyZoomFilter(d3ZoomInstance);
})
applyZoomHandlers(d3ZoomInstance)
applyZoomFilter(d3ZoomInstance)
}
});
})
const applyZoomHandlers = (d3Zoom: ZoomBehavior<Element, unknown>) => {
d3Zoom.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
if (viewChanged(prevTransform.value, event.transform)) {
const flowTransform = eventToFlowTransform(event.transform);
prevTransform.value = flowTransform;
const flowTransform = eventToFlowTransform(event.transform)
prevTransform.value = flowTransform
hooks.moveStart.trigger(flowTransform);
hooks.moveStart.trigger(flowTransform)
}
});
})
d3Zoom.on('end', (event: D3ZoomEvent<HTMLDivElement, any>) => {
if (viewChanged(prevTransform.value, event.transform)) {
const flowTransform = eventToFlowTransform(event.transform);
prevTransform.value = flowTransform;
const flowTransform = eventToFlowTransform(event.transform)
prevTransform.value = flowTransform
hooks.moveEnd.trigger(flowTransform);
hooks.moveEnd.trigger(flowTransform)
}
});
})
useKeyPress(props.selectionKeyCode, (keyPress) => {
if (keyPress) {
d3Zoom.on('zoom', null);
d3Zoom.on('zoom', null)
} else {
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
store.transform = [event.transform.x, event.transform.y, event.transform.k];
hooks.move.trigger(eventToFlowTransform(event.transform));
});
store.transform = [event.transform.x, event.transform.y, event.transform.k]
hooks.move.trigger(eventToFlowTransform(event.transform))
})
}
});
})
useKeyPress(props.zoomActivationKeyCode, (keyPress) => {
if (props.panOnScroll && keyPress) {
store.d3Selection
?.on('wheel', (event: WheelEvent) => {
event.preventDefault();
event.stopImmediatePropagation();
event.preventDefault()
event.stopImmediatePropagation()
const currentZoom = store.d3Selection?.property('__zoom').k || 1;
const currentZoom = store.d3Selection?.property('__zoom').k || 1
if (event.ctrlKey && props.zoomOnPinch) {
const point = pointer(event);
const point = pointer(event)
// taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10;
const zoom = currentZoom * Math.pow(2, pinchDelta);
if (store.d3Selection) store.d3Zoom?.scaleTo(store.d3Selection, zoom, point);
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10
const zoom = currentZoom * Math.pow(2, pinchDelta)
if (store.d3Selection) store.d3Zoom?.scaleTo(store.d3Selection, zoom, point)
return;
return
}
// increase scroll speed in firefox
// firefox: deltaMode === 1; chrome: deltaMode === 0
const deltaNormalize = event.deltaMode === 1 ? 20 : 1;
const deltaX = props.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize;
const deltaY = props.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize;
const deltaNormalize = event.deltaMode === 1 ? 20 : 1
const deltaX = props.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize
const deltaY = props.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize
if (store.d3Selection && props.panOnScrollSpeed)
store.d3Zoom?.translateBy(
store.d3Selection,
-(deltaX / currentZoom) * props.panOnScrollSpeed,
-(deltaY / currentZoom) * props.panOnScrollSpeed
);
)
})
.on('wheel.zoom', null);
.on('wheel.zoom', null)
} else if (typeof store.d3ZoomHandler !== 'undefined') {
store.d3Selection?.on('wheel', null).on('wheel.zoom', store.d3ZoomHandler);
store.d3Selection?.on('wheel', null).on('wheel.zoom', store.d3ZoomHandler)
}
});
};
})
}
watchPostEffect(() => store.d3Zoom && applyZoomFilter(store.d3Zoom));
watchPostEffect(() => store.d3Zoom && applyZoomFilter(store.d3Zoom))
const applyZoomFilter = (d3Zoom: ZoomBehavior<Element, unknown>) => {
const keyPress = useKeyPress(props.selectionKeyCode);
const keyPress = useKeyPress(props.selectionKeyCode)
d3Zoom.filter((event: MouseEvent) => {
const zoomScroll = props.zoomOnScroll;
const pinchZoom = props.zoomOnPinch && event.ctrlKey;
const zoomScroll = props.zoomOnScroll
const pinchZoom = props.zoomOnPinch && event.ctrlKey
// if all interactions are disabled, we prevent all zoom events
if (!props.paneMoveable && !zoomScroll && !props.panOnScroll && !props.zoomOnDoubleClick && !props.zoomOnPinch) {
return false;
return false
}
// during a selection we prevent all other interactions
if (keyPress.value) {
return false;
return false
}
// if zoom on double click is disabled, we prevent the double click event
if (!props.zoomOnDoubleClick && event.type === 'dblclick') {
return false;
return false
}
if ((event.target as Element).closest('.nowheel') && event.type === 'wheel') {
return false;
return false
}
// when the target element is a node, we still allow zooming
@@ -238,40 +238,40 @@ export default defineComponent({
((event.target as Element).closest('.revue-flow__node') || (event.target as Element).closest('.revue-flow__edge')) &&
event.type !== 'wheel'
) {
return false;
return false
}
// when the target element is a node selection, we still allow zooming
if ((event.target as Element).closest('.revue-flow__nodesselection') && event.type !== 'wheel') {
return false;
return false
}
if (!props.zoomOnPinch && event.ctrlKey && event.type === 'wheel') {
return false;
return false
}
// when there is no scroll handling enabled, we prevent all wheel events
if (!zoomScroll && !props.panOnScroll && !pinchZoom && event.type === 'wheel') {
return false;
return false
}
// if the pane is not movable, we prevent dragging it with mousestart or touchstart
if (!props.paneMoveable && (event.type === 'mousedown' || event.type === 'touchstart')) {
return false;
return false
}
// default filter for d3-zoom
return (!event.ctrlKey || event.type === 'wheel') && !event.button;
});
};
};
return (!event.ctrlKey || event.type === 'wheel') && !event.button
})
}
}
init();
init()
return () => (
<div ref="zoom-pane" class="revue-flow__renderer revue-flow__zoompane">
{slots.default ? slots.default() : ''}
</div>
);
)
}
});
})
+2 -2
View File
@@ -1,2 +1,2 @@
declare const __REVUE_FLOW_VERSION__: string;
declare const __ENV__: string;
declare const __REVUE_FLOW_VERSION__: string
declare const __ENV__: string
+49 -49
View File
@@ -1,47 +1,47 @@
// global hooks
import { Connection, Edge, Elements, FlowTransform, Node, OnConnectStartParams, OnLoadParams } from '../types';
import { createEventHook, EventHook } from '@vueuse/core';
import { Connection, Edge, Elements, FlowTransform, Node, OnConnectStartParams, OnLoadParams } from '../types'
import { createEventHook, EventHook } from '@vueuse/core'
export interface RevueFlowHooks {
elementClick: EventHook<{ event: MouseEvent; element: Node | Edge }>;
elementsRemove: EventHook<Elements>;
nodeDoubleClick: EventHook<{ event: MouseEvent; node: Node }>;
nodeClick: EventHook<{ event: MouseEvent; node: Node }>;
nodeMouseEnter: EventHook<{ event: MouseEvent; node: Node }>;
nodeMouseMove: EventHook<{ event: MouseEvent; node: Node }>;
nodeMouseLeave: EventHook<{ event: MouseEvent; node: Node }>;
nodeContextMenu: EventHook<{ event: MouseEvent; node: Node }>;
nodeDragStart: EventHook<{ event: MouseEvent; node: Node }>;
nodeDrag: EventHook<{ event: MouseEvent; node: Node }>;
nodeDragStop: EventHook<{ event: MouseEvent; node: Node }>;
connect: EventHook<Connection>;
elementClick: EventHook<{ event: MouseEvent; element: Node | Edge }>
elementsRemove: EventHook<Elements>
nodeDoubleClick: EventHook<{ event: MouseEvent; node: Node }>
nodeClick: EventHook<{ event: MouseEvent; node: Node }>
nodeMouseEnter: EventHook<{ event: MouseEvent; node: Node }>
nodeMouseMove: EventHook<{ event: MouseEvent; node: Node }>
nodeMouseLeave: EventHook<{ event: MouseEvent; node: Node }>
nodeContextMenu: EventHook<{ event: MouseEvent; node: Node }>
nodeDragStart: EventHook<{ event: MouseEvent; node: Node }>
nodeDrag: EventHook<{ event: MouseEvent; node: Node }>
nodeDragStop: EventHook<{ event: MouseEvent; node: Node }>
connect: EventHook<Connection>
connectStart: EventHook<{
event: MouseEvent;
params: OnConnectStartParams;
}>;
connectStop: EventHook<MouseEvent>;
connectEnd: EventHook<MouseEvent>;
load: EventHook<OnLoadParams>;
move: EventHook<FlowTransform | undefined>;
moveStart: EventHook<FlowTransform | undefined>;
moveEnd: EventHook<FlowTransform | undefined>;
selectionChange: EventHook<Elements | null>;
selectionDragStart: EventHook<{ event: MouseEvent; nodes: Node[] }>;
selectionDrag: EventHook<{ event: MouseEvent; nodes: Node[] }>;
selectionDragStop: EventHook<{ event: MouseEvent; nodes: Node[] }>;
selectionContextMenu: EventHook<{ event: MouseEvent; nodes: Node[] }>;
paneScroll: EventHook<WheelEvent | undefined>;
paneClick: EventHook<MouseEvent>;
paneContextMenu: EventHook<MouseEvent>;
edgeUpdate: EventHook<{ edge: Edge; connection: Connection }>;
edgeContextMenu: EventHook<{ event: MouseEvent; edge: Edge }>;
edgeMouseEnter: EventHook<{ event: MouseEvent; edge: Edge }>;
edgeMouseMove: EventHook<{ event: MouseEvent; edge: Edge }>;
edgeMouseLeave: EventHook<{ event: MouseEvent; edge: Edge }>;
edgeDoubleClick: EventHook<{ event: MouseEvent; edge: Edge }>;
edgeClick: EventHook<{ event: MouseEvent; edge: Edge }>;
edgeUpdateStart: EventHook<{ event: MouseEvent; edge: Edge }>;
edgeUpdateEnd: EventHook<{ event: MouseEvent; edge: Edge }>;
event: MouseEvent
params: OnConnectStartParams
}>
connectStop: EventHook<MouseEvent>
connectEnd: EventHook<MouseEvent>
load: EventHook<OnLoadParams>
move: EventHook<FlowTransform | undefined>
moveStart: EventHook<FlowTransform | undefined>
moveEnd: EventHook<FlowTransform | undefined>
selectionChange: EventHook<Elements | null>
selectionDragStart: EventHook<{ event: MouseEvent; nodes: Node[] }>
selectionDrag: EventHook<{ event: MouseEvent; nodes: Node[] }>
selectionDragStop: EventHook<{ event: MouseEvent; nodes: Node[] }>
selectionContextMenu: EventHook<{ event: MouseEvent; nodes: Node[] }>
paneScroll: EventHook<WheelEvent | undefined>
paneClick: EventHook<MouseEvent>
paneContextMenu: EventHook<MouseEvent>
edgeUpdate: EventHook<{ edge: Edge; connection: Connection }>
edgeContextMenu: EventHook<{ event: MouseEvent; edge: Edge }>
edgeMouseEnter: EventHook<{ event: MouseEvent; edge: Edge }>
edgeMouseMove: EventHook<{ event: MouseEvent; edge: Edge }>
edgeMouseLeave: EventHook<{ event: MouseEvent; edge: Edge }>
edgeDoubleClick: EventHook<{ event: MouseEvent; edge: Edge }>
edgeClick: EventHook<{ event: MouseEvent; edge: Edge }>
edgeUpdateStart: EventHook<{ event: MouseEvent; edge: Edge }>
edgeUpdateEnd: EventHook<{ event: MouseEvent; edge: Edge }>
}
export const useRevueFlow = (): RevueFlowHooks & { bind: (emit: (event: string, ...args: any[]) => void) => RevueFlowHooks } => {
@@ -59,8 +59,8 @@ export const useRevueFlow = (): RevueFlowHooks & { bind: (emit: (event: string,
nodeDragStop: createEventHook<{ event: MouseEvent; node: Node }>(),
connect: createEventHook<Connection>(),
connectStart: createEventHook<{
event: MouseEvent;
params: OnConnectStartParams;
event: MouseEvent
params: OnConnectStartParams
}>(),
connectStop: createEventHook<MouseEvent>(),
connectEnd: createEventHook<MouseEvent>(),
@@ -85,20 +85,20 @@ export const useRevueFlow = (): RevueFlowHooks & { bind: (emit: (event: string,
edgeClick: createEventHook<{ event: MouseEvent; edge: Edge }>(),
edgeUpdateStart: createEventHook<{ event: MouseEvent; edge: Edge }>(),
edgeUpdateEnd: createEventHook<{ event: MouseEvent; edge: Edge }>()
};
}
const bind = (emit: (event: string, args: any) => void) => {
for (const [key, value] of Object.entries(eventHooks)) {
value.on((data: any) => {
emit(key, data);
});
emit(key, data)
})
}
return eventHooks;
};
return eventHooks
}
return {
...eventHooks,
bind
};
};
}
}
+22 -22
View File
@@ -1,37 +1,37 @@
import useKeyPress from './useKeyPress';
import { isNode, getConnectedEdges } from '../utils/graph';
import { Elements, KeyCode, ElementId, FlowElement, RevueFlowStore, Edge } from '../types';
import { computed, watch } from 'vue';
import useKeyPress from './useKeyPress'
import { isNode, getConnectedEdges } from '../utils/graph'
import { Elements, KeyCode, ElementId, FlowElement, RevueFlowStore, Edge } from '../types'
import { computed, watch } from 'vue'
interface HookParams {
store: RevueFlowStore;
deleteKeyCode: KeyCode;
multiSelectionKeyCode: KeyCode;
onElementsRemove?: (elements: Elements) => void;
store: RevueFlowStore
deleteKeyCode: KeyCode
multiSelectionKeyCode: KeyCode
onElementsRemove?: (elements: Elements) => void
}
export default ({ store, deleteKeyCode, multiSelectionKeyCode, onElementsRemove }: HookParams): void => {
const deleteKeyPressed = useKeyPress(deleteKeyCode);
const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode);
const selectedElements = computed(() => store.selectedElements || []);
const edges = computed(() => store.edges);
const deleteKeyPressed = useKeyPress(deleteKeyCode)
const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode)
const selectedElements = computed(() => store.selectedElements || [])
const edges = computed(() => store.edges)
watch(selectedElements, () => {
if (onElementsRemove && deleteKeyPressed.value && selectedElements.value.length > 0) {
const selectedNodes = selectedElements.value.filter(isNode);
const connectedEdges = getConnectedEdges(selectedNodes, edges.value as Edge[]);
const selectedNodes = selectedElements.value.filter(isNode)
const connectedEdges = getConnectedEdges(selectedNodes, edges.value as Edge[])
const elementsToRemove = [...selectedElements.value, ...connectedEdges].reduce(
(res, item) => res.set(item.id, item),
new Map<ElementId, FlowElement>()
);
)
onElementsRemove(Array.from(elementsToRemove.values()));
store.unsetNodesSelection();
store.resetSelectedElements();
onElementsRemove(Array.from(elementsToRemove.values()))
store.unsetNodesSelection()
store.resetSelectedElements()
}
});
})
watch(multiSelectionKeyPressed, () => {
store.multiSelectionActive = multiSelectionKeyPressed.value;
});
};
store.multiSelectionActive = multiSelectionKeyPressed.value
})
}
+20 -20
View File
@@ -1,47 +1,47 @@
import { Ref } from 'vue';
import { KeyCode } from '../types';
import { controlledRef, onKeyStroke, useEventListener } from '@vueuse/core';
import { Ref } from 'vue'
import { KeyCode } from '../types'
import { controlledRef, onKeyStroke, useEventListener } from '@vueuse/core'
export default (keyCode?: KeyCode, cb?: (keyPress: boolean) => void): Ref<boolean> => {
const keyPressed = controlledRef<boolean>(false, {
onBeforeChange(val, oldVal) {
if (val === oldVal) return false;
if (val === oldVal) return false
},
onChanged() {
if (cb && typeof cb === 'function') cb(keyPressed.value);
if (cb && typeof cb === 'function') cb(keyPressed.value)
}
});
})
onKeyStroke(
(event) => event.key === keyCode || event.keyCode === keyCode,
(e) => {
e.preventDefault();
keyPressed.value = true;
e.preventDefault()
keyPressed.value = true
},
{ eventName: 'keypress' }
);
)
onKeyStroke(
(event) => event.key === keyCode || event.keyCode === keyCode,
(e) => {
e.preventDefault();
keyPressed.value = true;
e.preventDefault()
keyPressed.value = true
},
{ eventName: 'keydown' }
);
)
onKeyStroke(
(event) => event.key === keyCode || event.keyCode === keyCode,
(e) => {
e.preventDefault();
keyPressed.value = false;
e.preventDefault()
keyPressed.value = false
},
{ eventName: 'keyup' }
);
)
useEventListener(window, 'blur', () => {
keyPressed.value = false;
});
keyPressed.value = false
})
if (cb && typeof cb === 'function') cb(keyPressed.value);
if (cb && typeof cb === 'function') cb(keyPressed.value)
return keyPressed;
};
return keyPressed
}
+16 -16
View File
@@ -1,33 +1,33 @@
import { createEventHook, MaybeRef, unrefElement, until, useEventListener, useResizeObserver } from '@vueuse/core';
import { getDimensions } from '../utils';
import { Dimensions } from '../types';
import { createEventHook, MaybeRef, unrefElement, until, useEventListener, useResizeObserver } from '@vueuse/core'
import { getDimensions } from '../utils'
import { Dimensions } from '../types'
export default (rendererNode: MaybeRef<HTMLDivElement>, updateSize: (size: Dimensions) => any) => {
const resizeHandlerHook = createEventHook();
const resizeHandlerHook = createEventHook()
const updateDimensions = () => {
if (!unrefElement(rendererNode)) {
return;
return
}
const size = getDimensions(unrefElement(rendererNode));
const size = getDimensions(unrefElement(rendererNode))
if (size.height === 0 || size.width === 0) {
console.log('The revue Flow parent container needs a width and a height to render the graph.');
console.log('The revue Flow parent container needs a width and a height to render the graph.')
}
updateSize(size);
};
updateSize(size)
}
until(rendererNode)
.toBeTruthy()
.then(() => {
updateDimensions();
useEventListener(window, 'resize', updateDimensions);
useResizeObserver(unrefElement(rendererNode), () => updateDimensions());
resizeHandlerHook.trigger(true);
});
updateDimensions()
useEventListener(window, 'resize', updateDimensions)
useResizeObserver(unrefElement(rendererNode), () => updateDimensions())
resizeHandlerHook.trigger(true)
})
return {
onReady: resizeHandlerHook.on
};
};
}
}
+7 -7
View File
@@ -1,16 +1,16 @@
import { ElementId, RevueFlowStore, UpdateNodeInternals } from '../types';
import { inject } from 'vue';
import { ElementId, RevueFlowStore, UpdateNodeInternals } from '../types'
import { inject } from 'vue'
function useUpdateNodeInternals(): UpdateNodeInternals {
const store = inject<RevueFlowStore>('store')!;
const store = inject<RevueFlowStore>('store')!
return (id: ElementId) => {
const nodeElement: HTMLDivElement | null = document.querySelector(`.revue-flow__node[data-id="${id}"]`);
const nodeElement: HTMLDivElement | null = document.querySelector(`.revue-flow__node[data-id="${id}"]`)
if (nodeElement) {
store.updateNodeDimensions([{ id, nodeElement, forceUpdate: true }]);
store.updateNodeDimensions([{ id, nodeElement, forceUpdate: true }])
}
};
}
}
export default useUpdateNodeInternals;
export default useUpdateNodeInternals
+41 -41
View File
@@ -1,20 +1,20 @@
import { zoomIdentity } from 'd3-zoom';
import { computed, effectScope, inject } from 'vue';
import { createEventHook, EventHookOn, until } from '@vueuse/core';
import { Selection, ZoomBehavior } from 'd3';
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '../utils/graph';
import { FitViewParams, FlowTransform, ZoomPanHelperFunctions, Rect, XYPosition, RevueFlowStore } from '../types';
import { zoomIdentity } from 'd3-zoom'
import { computed, effectScope, inject } from 'vue'
import { createEventHook, EventHookOn, until } from '@vueuse/core'
import { Selection, ZoomBehavior } from 'd3'
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '../utils/graph'
import { FitViewParams, FlowTransform, ZoomPanHelperFunctions, Rect, XYPosition, RevueFlowStore } from '../types'
const DEFAULT_PADDING = 0.1;
const DEFAULT_PADDING = 0.1
const useZoomPanHelper = (): { onReady: EventHookOn<ZoomPanHelperFunctions> } => {
const zoomPanHelperEvent = createEventHook<ZoomPanHelperFunctions>();
const store = inject<RevueFlowStore>('store')!;
const zoomPanHelperEvent = createEventHook<ZoomPanHelperFunctions>()
const store = inject<RevueFlowStore>('store')!
const scope = effectScope();
const scope = effectScope()
scope.run(() => {
const d3Zoom = computed(() => store.d3Zoom);
const d3Selection = computed(() => store.d3Selection);
const d3Zoom = computed(() => store.d3Zoom)
const d3Selection = computed(() => store.d3Selection)
const createZoomPanHelper = (
d3Zoom: ZoomBehavior<Element, unknown>,
@@ -24,18 +24,18 @@ const useZoomPanHelper = (): { onReady: EventHookOn<ZoomPanHelperFunctions> } =>
zoomOut: () => d3Zoom?.scaleBy(d3Selection, 1 / 1.2),
zoomTo: (zoomLevel: number) => d3Zoom.scaleTo(d3Selection, zoomLevel),
transform: (transform: FlowTransform) => {
const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom);
const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom)
d3Zoom.transform(d3Selection, nextTransform);
d3Zoom.transform(d3Selection, nextTransform)
},
fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {
const { nodes, width, height, minZoom, maxZoom } = store;
const { nodes, width, height, minZoom, maxZoom } = store
if (!nodes.length) {
return;
return
}
const bounds = getRectOfNodes(options.includeHiddenNodes ? nodes : nodes.filter((node) => !node.isHidden));
const bounds = getRectOfNodes(options.includeHiddenNodes ? nodes : nodes.filter((node) => !node.isHidden))
const [x, y, zoom] = getTransformForBounds(
bounds,
width,
@@ -43,49 +43,49 @@ const useZoomPanHelper = (): { onReady: EventHookOn<ZoomPanHelperFunctions> } =>
options.minZoom || minZoom,
options.maxZoom || maxZoom,
options.padding || DEFAULT_PADDING
);
const transform = zoomIdentity.translate(x, y).scale(zoom);
)
const transform = zoomIdentity.translate(x, y).scale(zoom)
d3Zoom?.transform(d3Selection, transform);
d3Zoom?.transform(d3Selection, transform)
},
setCenter: (x: number, y: number, zoom?: number) => {
const { width, height, maxZoom } = store;
const { width, height, maxZoom } = store
const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom;
const centerX = width / 2 - x * nextZoom;
const centerY = height / 2 - y * nextZoom;
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom);
const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom
const centerX = width / 2 - x * nextZoom
const centerY = height / 2 - y * nextZoom
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom)
d3Zoom.transform(d3Selection, transform);
d3Zoom.transform(d3Selection, transform)
},
fitBounds: (bounds: Rect, padding = DEFAULT_PADDING) => {
const { width, height, minZoom, maxZoom } = store;
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding);
const transform = zoomIdentity.translate(x, y).scale(zoom);
const { width, height, minZoom, maxZoom } = store
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding)
const transform = zoomIdentity.translate(x, y).scale(zoom)
d3Zoom.transform(d3Selection, transform);
d3Zoom.transform(d3Selection, transform)
},
project: (position: XYPosition) => {
const { transform, snapToGrid, snapGrid } = store;
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
const { transform, snapToGrid, snapGrid } = store
return pointToRendererPoint(position, transform, snapToGrid, snapGrid)
}
});
})
until(d3Zoom)
.toBeTruthy()
.then(() => {
if (d3Zoom.value && d3Selection.value) {
zoomPanHelperEvent.trigger(createZoomPanHelper(d3Zoom.value, d3Selection.value));
zoomPanHelperEvent.trigger(createZoomPanHelper(d3Zoom.value, d3Selection.value))
} else {
throw Error('D3 could not be initialized.');
throw Error('D3 could not be initialized.')
}
scope.stop();
});
});
scope.stop()
})
})
return {
onReady: zoomPanHelperEvent.on
};
};
}
}
export default useZoomPanHelper;
export default useZoomPanHelper
+11 -11
View File
@@ -1,9 +1,9 @@
export { default as default } from './container/RevueFlow';
export { default as Handle } from './components/Handle';
export { default as EdgeText } from './components/Edges/EdgeText';
export { getBezierPath } from './components/Edges/BezierEdge';
export { getSmoothStepPath } from './components/Edges/SmoothStepEdge';
export { getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils';
export { default as default } from './container/RevueFlow'
export { default as Handle } from './components/Handle'
export { default as EdgeText } from './components/Edges/EdgeText'
export { getBezierPath } from './components/Edges/BezierEdge'
export { getSmoothStepPath } from './components/Edges/SmoothStepEdge'
export { getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils'
export {
isNode,
@@ -16,9 +16,9 @@ export {
updateEdge,
getTransformForBounds,
getRectOfNodes
} from './utils/graph';
export { default as useZoomPanHelper } from './hooks/useZoomPanHelper';
export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals';
} from './utils/graph'
export { default as useZoomPanHelper } from './hooks/useZoomPanHelper'
export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals'
export * from './additional-components';
export * from './types';
export * from './additional-components'
export * from './types'
+2 -2
View File
@@ -1,4 +1,4 @@
import Vue, { VNode } from 'vue';
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
@@ -9,7 +9,7 @@ declare global {
interface ElementClass extends Vue {}
interface IntrinsicAttributes {
[prop: string]: any;
[prop: string]: any
}
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
declare module '*.vue' {
import { DefineComponent } from 'vue';
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>;
export default component;
const component: DefineComponent<{}, {}, any>
export default component
}
+91 -91
View File
@@ -1,20 +1,20 @@
import { setActivePinia, createPinia, defineStore, StoreDefinition } from 'pinia';
import isEqual from 'fast-deep-equal';
import { Edge, Node, NodeDiffUpdate, RevueFlowState, RevueFlowActions, XYPosition } from '../types';
import { getConnectedEdges, getNodesInside, getRectOfNodes, isEdge, isNode, parseEdge, parseNode } from '../utils/graph';
import { clampPosition, getDimensions } from '../utils';
import { getHandleBounds } from '../components/Nodes/utils';
import { setActivePinia, createPinia, defineStore, StoreDefinition } from 'pinia'
import isEqual from 'fast-deep-equal'
import { Edge, Node, NodeDiffUpdate, RevueFlowState, RevueFlowActions, XYPosition } from '../types'
import { getConnectedEdges, getNodesInside, getRectOfNodes, isEdge, isNode, parseEdge, parseNode } from '../utils/graph'
import { clampPosition, getDimensions } from '../utils'
import { getHandleBounds } from '../components/Nodes/utils'
type NextElements = {
nextNodes: Node[];
nextEdges: Edge[];
};
const pinia = createPinia();
nextNodes: Node[]
nextEdges: Edge[]
}
const pinia = createPinia()
export default function configureStore(
preloadedState: RevueFlowState
): StoreDefinition<string, RevueFlowState, any, RevueFlowActions> {
setActivePinia(pinia);
setActivePinia(pinia)
return defineStore({
id: 'revue-flow-' + Math.random(),
@@ -24,66 +24,66 @@ export default function configureStore(
getters: {},
actions: {
setElements(elements) {
const propElements = elements;
const propElements = elements
const nextElements: NextElements = {
nextNodes: [],
nextEdges: []
};
}
const { nextNodes, nextEdges } = propElements.reduce((res, propElement): NextElements => {
if (isNode(propElement)) {
const storeNode = this.nodes.find((node) => node.id === propElement.id);
const storeNode = this.nodes.find((node) => node.id === propElement.id)
if (storeNode) {
const updatedNode: Node = {
...storeNode,
...propElement
};
}
if (storeNode.position.x !== propElement.position.x || storeNode.position.y !== propElement.position.y) {
updatedNode.__rf.position = propElement.position;
updatedNode.__rf.position = propElement.position
}
if (typeof propElement.type !== 'undefined' && propElement.type !== storeNode.type) {
// we reset the elements dimensions here in order to force a re-calculation of the bounds.
// When the type of a node changes it is possible that the number or positions of handles changes too.
updatedNode.__rf.width = null;
updatedNode.__rf.width = null
}
res.nextNodes.push(updatedNode);
res.nextNodes.push(updatedNode)
} else {
res.nextNodes.push(parseNode(propElement, this.nodeExtent));
res.nextNodes.push(parseNode(propElement, this.nodeExtent))
}
} else if (isEdge(propElement)) {
const storeEdge = this.edges.find((se) => se.id === propElement.id);
const storeEdge = this.edges.find((se) => se.id === propElement.id)
if (storeEdge) {
res.nextEdges.push({
...storeEdge,
...propElement
});
})
} else {
res.nextEdges.push(parseEdge(propElement));
res.nextEdges.push(parseEdge(propElement))
}
}
return res;
}, nextElements);
return res
}, nextElements)
this.nodes = nextNodes;
this.edges = nextEdges;
this.nodes = nextNodes
this.edges = nextEdges
},
updateNodeDimensions(updates) {
this.nodes = this.nodes.map((node) => {
const update = updates.find((u) => u.id === node.id);
const update = updates.find((u) => u.id === node.id)
if (update) {
const dimensions = getDimensions(update.nodeElement);
const dimensions = getDimensions(update.nodeElement)
const doUpdate =
dimensions.width &&
dimensions.height &&
(node.__rf.width !== dimensions.width || node.__rf.height !== dimensions.height || update.forceUpdate);
(node.__rf.width !== dimensions.width || node.__rf.height !== dimensions.height || update.forceUpdate)
if (doUpdate) {
const handleBounds = getHandleBounds(update.nodeElement, this.transform[2]);
const handleBounds = getHandleBounds(update.nodeElement, this.transform[2])
return {
...node,
@@ -92,23 +92,23 @@ export default function configureStore(
...dimensions,
handleBounds
}
};
}
}
}
return node;
});
return node
})
},
updateNodePos(payload) {
const { id, pos } = payload;
let position: XYPosition = pos;
const { id, pos } = payload
let position: XYPosition = pos
if (this.snapToGrid) {
const [gridSizeX, gridSizeY] = this.snapGrid;
const [gridSizeX, gridSizeY] = this.snapGrid
position = {
x: gridSizeX * Math.round(pos.x / gridSizeX),
y: gridSizeY * Math.round(pos.y / gridSizeY)
};
}
}
this.nodes = this.nodes.map((node) => {
@@ -119,14 +119,14 @@ export default function configureStore(
...node.__rf,
position
}
};
}
}
return node;
});
return node
})
},
updateNodePosDiff(payload: NodeDiffUpdate) {
const { id, diff, isDragging } = payload;
const { id, diff, isDragging } = payload
this.nodes = this.nodes.map((node) => {
if (id === node.id || this.selectedElements?.find((sNode) => sNode.id === node.id)) {
@@ -136,23 +136,23 @@ export default function configureStore(
...node.__rf,
isDragging
}
};
}
if (diff) {
updatedNode.__rf.position = {
x: node.__rf.position.x + diff.x,
y: node.__rf.position.y + diff.y
};
}
}
return updatedNode;
return updatedNode
}
return node;
});
return node
})
},
setUserSelection(mousePos) {
this.selectionActive = true;
this.selectionActive = true
this.userSelectionRect = {
width: 0,
height: 0,
@@ -161,11 +161,11 @@ export default function configureStore(
x: mousePos.x,
y: mousePos.y,
draw: true
};
}
},
updateUserSelection(mousePos) {
const startX = this.userSelectionRect.startX || 0;
const startY = this.userSelectionRect.startY || 0;
const startX = this.userSelectionRect.startX || 0
const startY = this.userSelectionRect.startY || 0
const nextUserSelectRect: RevueFlowState['userSelectionRect'] = {
...this.userSelectionRect,
@@ -173,59 +173,59 @@ export default function configureStore(
y: mousePos.y < startY ? mousePos.y : this.userSelectionRect.y,
width: Math.abs(mousePos.x - startX),
height: Math.abs(mousePos.y - startY)
};
}
const selectedNodes = getNodesInside(this.nodes, nextUserSelectRect, this.transform);
const selectedEdges = getConnectedEdges(selectedNodes, this.edges);
const selectedNodes = getNodesInside(this.nodes, nextUserSelectRect, this.transform)
const selectedEdges = getConnectedEdges(selectedNodes, this.edges)
const nextSelectedElements = [...selectedNodes, ...selectedEdges];
this.userSelectionRect = nextUserSelectRect;
this.selectedElements = nextSelectedElements;
const nextSelectedElements = [...selectedNodes, ...selectedEdges]
this.userSelectionRect = nextUserSelectRect
this.selectedElements = nextSelectedElements
},
unsetUserSelection() {
const selectedNodes = this.selectedElements?.filter((node) => isNode(node) && node.__rf) as Node[];
const selectedNodes = this.selectedElements?.filter((node) => isNode(node) && node.__rf) as Node[]
this.selectionActive = false;
this.userSelectionRect.draw = false;
this.selectionActive = false
this.userSelectionRect.draw = false
if (!selectedNodes || selectedNodes.length === 0) {
this.selectedElements = null;
this.nodesSelectionActive = false;
this.selectedElements = null
this.nodesSelectionActive = false
} else {
this.selectedNodesBbox = getRectOfNodes(selectedNodes);
this.nodesSelectionActive = true;
this.selectedNodesBbox = getRectOfNodes(selectedNodes)
this.nodesSelectionActive = true
}
},
addSelectedElements(elements) {
const selectedElementsArr = Array.isArray(elements) ? elements : [elements];
const selectedElementsUpdated = !isEqual(selectedElementsArr, this.selectedElements);
this.selectedElements = selectedElementsUpdated ? selectedElementsArr : this.selectedElements;
const selectedElementsArr = Array.isArray(elements) ? elements : [elements]
const selectedElementsUpdated = !isEqual(selectedElementsArr, this.selectedElements)
this.selectedElements = selectedElementsUpdated ? selectedElementsArr : this.selectedElements
},
initD3Zoom(payload) {
const { d3Zoom, d3Selection, d3ZoomHandler, transform } = payload;
const { d3Zoom, d3Selection, d3ZoomHandler, transform } = payload
this.d3Zoom = d3Zoom;
this.d3Selection = d3Selection;
this.d3ZoomHandler = d3ZoomHandler;
this.transform = transform;
this.d3Zoom = d3Zoom
this.d3Selection = d3Selection
this.d3ZoomHandler = d3ZoomHandler
this.transform = transform
},
setMinZoom(minZoom) {
this.d3Zoom?.scaleExtent([minZoom, this.maxZoom]);
this.d3Zoom?.scaleExtent([minZoom, this.maxZoom])
this.minZoom = minZoom;
this.minZoom = minZoom
},
setMaxZoom(maxZoom) {
this.d3Zoom?.scaleExtent([this.minZoom, maxZoom]);
this.d3Zoom?.scaleExtent([this.minZoom, maxZoom])
this.maxZoom = maxZoom;
this.maxZoom = maxZoom
},
setTranslateExtent(translateExtent) {
this.d3Zoom?.translateExtent(translateExtent);
this.d3Zoom?.translateExtent(translateExtent)
this.translateExtent = translateExtent;
this.translateExtent = translateExtent
},
setNodeExtent(nodeExtent) {
this.nodeExtent = nodeExtent;
this.nodeExtent = nodeExtent
this.nodes = this.nodes.map((node) => {
return {
...node,
@@ -233,29 +233,29 @@ export default function configureStore(
...node.__rf,
position: clampPosition(node.__rf.position, nodeExtent)
}
};
});
}
})
},
resetSelectedElements() {
this.selectedElements = null;
this.selectedElements = null
},
unsetNodesSelection() {
this.nodesSelectionActive = false;
this.nodesSelectionActive = false
},
updateSize(size) {
this.height = size.height;
this.width = size.width;
this.height = size.height
this.width = size.width
},
setConnectionNodeId(payload) {
this.connectionNodeId = payload.connectionNodeId;
this.connectionHandleId = payload.connectionHandleId;
this.connectionHandleType = payload.connectionHandleType;
this.connectionNodeId = payload.connectionNodeId
this.connectionHandleId = payload.connectionHandleId
this.connectionHandleType = payload.connectionHandleType
},
setInteractive(isInteractive) {
this.nodesDraggable = isInteractive;
this.nodesConnectable = isInteractive;
this.elementsSelectable = isInteractive;
this.nodesDraggable = isInteractive
this.nodesConnectable = isInteractive
this.elementsSelectable = isInteractive
}
}
});
})
}
+2 -2
View File
@@ -1,4 +1,4 @@
import { RevueFlowState, ConnectionMode } from '../types';
import { RevueFlowState, ConnectionMode } from '../types'
export const initialState: RevueFlowState = {
width: 0,
@@ -52,4 +52,4 @@ export const initialState: RevueFlowState = {
multiSelectionActive: false,
revueFlowVersion: typeof __REVUE_FLOW_VERSION__ !== 'undefined' ? __REVUE_FLOW_VERSION__ : '-'
};
}
+287 -287
View File
@@ -1,14 +1,14 @@
import { Selection as D3Selection, ZoomBehavior } from 'd3';
import { CSSProperties, DefineComponent, HTMLAttributes, VNode } from 'vue';
import { Store } from 'pinia';
import { Selection as D3Selection, ZoomBehavior } from 'd3'
import { CSSProperties, DefineComponent, HTMLAttributes, VNode } from 'vue'
import { Store } from 'pinia'
export type ElementId = string;
export type ElementId = string
export type FlowElement<T = any> = Node<T> | Edge<T>;
export type FlowElement<T = any> = Node<T> | Edge<T>
export type Elements<T = any> = Array<FlowElement<T>>;
export type Elements<T = any> = Array<FlowElement<T>>
export type Transform = [number, number, number];
export type Transform = [number, number, number]
export enum Position {
Left = 'left',
@@ -18,44 +18,44 @@ export enum Position {
}
export interface XYPosition {
x: number;
y: number;
x: number
y: number
}
export interface Dimensions {
width: number;
height: number;
width: number
height: number
}
export interface Rect extends Dimensions, XYPosition {}
export interface Box extends XYPosition {
x2: number;
y2: number;
x2: number
y2: number
}
export type SnapGrid = [number, number];
export type SnapGrid = [number, number]
export interface Node<T = any> {
id: ElementId;
position: XYPosition;
type?: string;
id: ElementId
position: XYPosition
type?: string
__rf: {
position: XYPosition;
isDragging?: boolean;
width: number | null;
height: number | null;
handleBounds?: any;
};
data?: T;
style?: any;
className?: string;
targetPosition?: Position;
sourcePosition?: Position;
isHidden?: boolean;
draggable?: boolean;
selectable?: boolean;
connectable?: boolean;
position: XYPosition
isDragging?: boolean
width: number | null
height: number | null
handleBounds?: any
}
data?: T
style?: any
className?: string
targetPosition?: Position
sourcePosition?: Position
isHidden?: boolean
draggable?: boolean
selectable?: boolean
connectable?: boolean
}
export enum ArrowHeadType {
@@ -64,24 +64,24 @@ export enum ArrowHeadType {
}
export interface Edge<T = any> {
id: ElementId;
type?: string;
source: ElementId;
target: ElementId;
sourceHandle?: ElementId | null;
targetHandle?: ElementId | null;
label?: string | VNode;
labelStyle?: any;
labelShowBg?: boolean;
labelBgStyle?: any;
labelBgPadding?: [number, number];
labelBgBorderRadius?: number;
style?: any;
animated?: boolean;
arrowHeadType?: ArrowHeadType;
isHidden?: boolean;
data?: T;
className?: string;
id: ElementId
type?: string
source: ElementId
target: ElementId
sourceHandle?: ElementId | null
targetHandle?: ElementId | null
label?: string | VNode
labelStyle?: any
labelShowBg?: boolean
labelBgStyle?: any
labelBgPadding?: [number, number]
labelBgBorderRadius?: number
style?: any
animated?: boolean
arrowHeadType?: ArrowHeadType
isHidden?: boolean
data?: T
className?: string
}
export enum BackgroundVariant {
@@ -89,107 +89,107 @@ export enum BackgroundVariant {
Dots = 'dots'
}
export type HandleType = 'source' | 'target';
export type HandleType = 'source' | 'target'
export type NodeType = DefineComponent<NodeProps>;
export type NodeType = DefineComponent<NodeProps>
export type EdgeType = DefineComponent<EdgeSmoothStepProps>;
export type EdgeType = DefineComponent<EdgeSmoothStepProps>
export interface SelectionRect extends Rect {
startX: number;
startY: number;
draw: boolean;
startX: number
startY: number
draw: boolean
}
export interface EdgeProps<T = any> {
id: ElementId;
source: ElementId;
target: ElementId;
sourceX: number;
sourceY: number;
targetX: number;
targetY: number;
selected?: boolean;
animated?: boolean;
sourcePosition: Position;
targetPosition: Position;
label?: string | VNode;
labelStyle?: any;
labelShowBg?: boolean;
labelBgStyle?: any;
labelBgPadding?: [number, number];
labelBgBorderRadius?: number;
style?: any;
arrowHeadType?: ArrowHeadType;
markerEndId?: string;
data?: T;
sourceHandleId?: ElementId | null;
targetHandleId?: ElementId | null;
id: ElementId
source: ElementId
target: ElementId
sourceX: number
sourceY: number
targetX: number
targetY: number
selected?: boolean
animated?: boolean
sourcePosition: Position
targetPosition: Position
label?: string | VNode
labelStyle?: any
labelShowBg?: boolean
labelBgStyle?: any
labelBgPadding?: [number, number]
labelBgBorderRadius?: number
style?: any
arrowHeadType?: ArrowHeadType
markerEndId?: string
data?: T
sourceHandleId?: ElementId | null
targetHandleId?: ElementId | null
}
export interface EdgeSmoothStepProps<T = any> extends EdgeProps<T> {
borderRadius?: number;
borderRadius?: number
}
export interface EdgeTextProps extends HTMLAttributes {
x: number;
y: number;
label?: string | VNode;
labelStyle?: any;
labelShowBg?: boolean;
labelBgStyle?: any;
labelBgPadding?: [number, number];
labelBgBorderRadius?: number;
x: number
y: number
label?: string | VNode
labelStyle?: any
labelShowBg?: boolean
labelBgStyle?: any
labelBgPadding?: [number, number]
labelBgBorderRadius?: number
}
export interface NodeProps<T = any> {
id: ElementId;
type: string;
data: T;
selected: boolean;
isConnectable: boolean;
xPos?: number;
yPos?: number;
targetPosition?: Position;
sourcePosition?: Position;
isDragging?: boolean;
id: ElementId
type: string
data: T
selected: boolean
isConnectable: boolean
xPos?: number
yPos?: number
targetPosition?: Position
sourcePosition?: Position
isDragging?: boolean
}
export type FitViewParams = {
padding?: number;
includeHiddenNodes?: boolean;
minZoom?: number;
maxZoom?: number;
};
padding?: number
includeHiddenNodes?: boolean
minZoom?: number
maxZoom?: number
}
export type FlowExportObject<T = any> = {
elements: Elements<T>;
position: [number, number];
zoom: number;
};
elements: Elements<T>
position: [number, number]
zoom: number
}
export type FitViewFunc = (fitViewOptions?: FitViewParams) => void;
export type ProjectFunc = (position: XYPosition) => XYPosition;
export type ToObjectFunc<T = any> = () => FlowExportObject<T>;
export type FitViewFunc = (fitViewOptions?: FitViewParams) => void
export type ProjectFunc = (position: XYPosition) => XYPosition
export type ToObjectFunc<T = any> = () => FlowExportObject<T>
export type OnLoadParams<T = any> = {
zoomIn: () => void;
zoomOut: () => void;
zoomTo: (zoomLevel: number) => void;
fitView: FitViewFunc;
project: ProjectFunc;
getElements: () => Elements<T>;
setTransform: (transform: FlowTransform) => void;
toObject: ToObjectFunc<T>;
};
zoomIn: () => void
zoomOut: () => void
zoomTo: (zoomLevel: number) => void
fitView: FitViewFunc
project: ProjectFunc
getElements: () => Elements<T>
setTransform: (transform: FlowTransform) => void
toObject: ToObjectFunc<T>
}
export type OnLoadFunc<T = any> = (params: OnLoadParams<T>) => void;
export type OnLoadFunc<T = any> = (params: OnLoadParams<T>) => void
export interface Connection {
source: ElementId | null;
target: ElementId | null;
sourceHandle: ElementId | null;
targetHandle: ElementId | null;
source: ElementId | null
target: ElementId | null
sourceHandle: ElementId | null
targetHandle: ElementId | null
}
export enum ConnectionMode {
@@ -205,69 +205,69 @@ export enum ConnectionLineType {
}
export type ConnectionLineComponentProps = {
sourceX: number;
sourceY: number;
sourcePosition?: Position;
targetX: number;
targetY: number;
targetPosition?: Position;
connectionLineStyle?: any;
connectionLineType: ConnectionLineType;
};
sourceX: number
sourceY: number
sourcePosition?: Position
targetX: number
targetY: number
targetPosition?: Position
connectionLineStyle?: any
connectionLineType: ConnectionLineType
}
export type ConnectionLineComponent = DefineComponent<ConnectionLineComponentProps>;
export type ConnectionLineComponent = DefineComponent<ConnectionLineComponentProps>
export type OnConnectFunc = (connection: Connection) => void;
export type OnConnectFunc = (connection: Connection) => void
export type OnConnectStartParams = {
nodeId: ElementId | null;
handleId: ElementId | null;
handleType: HandleType | null;
};
export type OnConnectStartFunc = (event: MouseEvent, params: OnConnectStartParams) => void;
export type OnConnectStopFunc = (event: MouseEvent) => void;
export type OnConnectEndFunc = (event: MouseEvent) => void;
nodeId: ElementId | null
handleId: ElementId | null
handleType: HandleType | null
}
export type OnConnectStartFunc = (event: MouseEvent, params: OnConnectStartParams) => void
export type OnConnectStopFunc = (event: MouseEvent) => void
export type OnConnectEndFunc = (event: MouseEvent) => void
export type SetConnectionId = {
connectionNodeId: ElementId | null;
connectionHandleId: ElementId | null;
connectionHandleType: HandleType | null;
};
connectionNodeId: ElementId | null
connectionHandleId: ElementId | null
connectionHandleType: HandleType | null
}
export interface HandleElement extends XYPosition, Dimensions {
id?: ElementId | null;
position: Position;
id?: ElementId | null
position: Position
}
export interface HandleProps {
type: HandleType;
position: Position;
isConnectable?: boolean;
onConnect?: OnConnectFunc;
isValidConnection?: (connection: Connection) => boolean;
id?: ElementId;
type: HandleType
position: Position
isConnectable?: boolean
onConnect?: OnConnectFunc
isValidConnection?: (connection: Connection) => boolean
id?: ElementId
}
export type NodePosUpdate = {
id: ElementId;
pos: XYPosition;
};
id: ElementId
pos: XYPosition
}
export type NodeDiffUpdate = {
id?: ElementId;
diff?: XYPosition;
isDragging?: boolean;
};
id?: ElementId
diff?: XYPosition
isDragging?: boolean
}
export type FlowTransform = {
x: number;
y: number;
zoom: number;
};
x: number
y: number
zoom: number
}
export type TranslateExtent = [[number, number], [number, number]];
export type NodeExtent = TranslateExtent;
export type TranslateExtent = [[number, number], [number, number]]
export type NodeExtent = TranslateExtent
export type KeyCode = number | string;
export type KeyCode = number | string
export enum PanOnScrollMode {
Free = 'free',
@@ -276,150 +276,150 @@ export enum PanOnScrollMode {
}
export interface ZoomPanHelperFunctions {
zoomIn: () => void;
zoomOut: () => void;
zoomTo: (zoomLevel: number) => void;
transform: (transform: FlowTransform) => void;
fitView: FitViewFunc;
setCenter: (x: number, y: number, zoom?: number) => void;
fitBounds: (bounds: Rect, padding?: number) => void;
project: (position: XYPosition) => XYPosition;
zoomIn: () => void
zoomOut: () => void
zoomTo: (zoomLevel: number) => void
transform: (transform: FlowTransform) => void
fitView: FitViewFunc
setCenter: (x: number, y: number, zoom?: number) => void
fitBounds: (bounds: Rect, padding?: number) => void
project: (position: XYPosition) => XYPosition
}
export type OnEdgeUpdateFunc<T = any> = (oldEdge: Edge<T>, newConnection: Connection) => void;
export type OnEdgeUpdateFunc<T = any> = (oldEdge: Edge<T>, newConnection: Connection) => void
export type NodeDimensionUpdate = {
id: ElementId;
nodeElement: HTMLDivElement;
forceUpdate?: boolean;
};
id: ElementId
nodeElement: HTMLDivElement
forceUpdate?: boolean
}
export type InitD3ZoomPayload = {
d3Zoom: ZoomBehavior<Element, unknown>;
d3Selection: D3Selection<Element, unknown, null, undefined>;
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
transform: Transform;
};
d3Zoom: ZoomBehavior<Element, unknown>
d3Selection: D3Selection<Element, unknown, null, undefined>
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined
transform: Transform
}
export interface RevueFlowState {
width: number;
height: number;
transform: Transform;
nodes: Node[];
edges: Edge[];
selectedElements: Elements | null;
selectedNodesBbox: Rect;
width: number
height: number
transform: Transform
nodes: Node[]
edges: Edge[]
selectedElements: Elements | null
selectedNodesBbox: Rect
d3Zoom: ZoomBehavior<Element, unknown> | null;
d3Selection: D3Selection<Element, unknown, null, undefined> | null;
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
minZoom: number;
maxZoom: number;
translateExtent: TranslateExtent;
nodeExtent: NodeExtent;
d3Zoom: ZoomBehavior<Element, unknown> | null
d3Selection: D3Selection<Element, unknown, null, undefined> | null
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined
minZoom: number
maxZoom: number
translateExtent: TranslateExtent
nodeExtent: NodeExtent
nodesSelectionActive: boolean;
selectionActive: boolean;
nodesSelectionActive: boolean
selectionActive: boolean
userSelectionRect: SelectionRect;
userSelectionRect: SelectionRect
connectionNodeId: ElementId | null;
connectionHandleId: ElementId | null;
connectionHandleType: HandleType | null;
connectionPosition: XYPosition;
connectionMode: ConnectionMode;
connectionNodeId: ElementId | null
connectionHandleId: ElementId | null
connectionHandleType: HandleType | null
connectionPosition: XYPosition
connectionMode: ConnectionMode
snapToGrid: boolean;
snapGrid: SnapGrid;
snapToGrid: boolean
snapGrid: SnapGrid
nodesDraggable: boolean;
nodesConnectable: boolean;
elementsSelectable: boolean;
nodesDraggable: boolean
nodesConnectable: boolean
elementsSelectable: boolean
multiSelectionActive: boolean;
multiSelectionActive: boolean
revueFlowVersion: string;
revueFlowVersion: string
onConnect?: OnConnectFunc;
onConnectStart?: OnConnectStartFunc;
onConnectStop?: OnConnectStopFunc;
onConnectEnd?: OnConnectEndFunc;
onConnect?: OnConnectFunc
onConnectStart?: OnConnectStartFunc
onConnectStop?: OnConnectStopFunc
onConnectEnd?: OnConnectEndFunc
}
export type RevueFlowActions = {
setElements: (elements: Elements) => void;
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
updateNodePos: (payload: NodePosUpdate) => void;
updateNodePosDiff: (payload: NodeDiffUpdate) => void;
setUserSelection: (mousePos: XYPosition) => void;
updateUserSelection: (mousePos: XYPosition) => void;
unsetUserSelection: () => void;
addSelectedElements: (elements: Elements) => void;
initD3Zoom: (payload: InitD3ZoomPayload) => void;
setMinZoom: (zoom: number) => void;
setMaxZoom: (zoom: number) => void;
setTranslateExtent: (translateExtent: TranslateExtent) => void;
setNodeExtent: (nodeExtent: NodeExtent) => void;
resetSelectedElements: () => void;
unsetNodesSelection: () => void;
updateSize: (size: Dimensions) => void;
setConnectionNodeId: (payload: SetConnectionId) => void;
setInteractive: (isInteractive: boolean) => void;
};
setElements: (elements: Elements) => void
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void
updateNodePos: (payload: NodePosUpdate) => void
updateNodePosDiff: (payload: NodeDiffUpdate) => void
setUserSelection: (mousePos: XYPosition) => void
updateUserSelection: (mousePos: XYPosition) => void
unsetUserSelection: () => void
addSelectedElements: (elements: Elements) => void
initD3Zoom: (payload: InitD3ZoomPayload) => void
setMinZoom: (zoom: number) => void
setMaxZoom: (zoom: number) => void
setTranslateExtent: (translateExtent: TranslateExtent) => void
setNodeExtent: (nodeExtent: NodeExtent) => void
resetSelectedElements: () => void
unsetNodesSelection: () => void
updateSize: (size: Dimensions) => void
setConnectionNodeId: (payload: SetConnectionId) => void
setInteractive: (isInteractive: boolean) => void
}
export type RevueFlowStore = Store<string, RevueFlowState, any, RevueFlowActions>;
export type RevueFlowStore = Store<string, RevueFlowState, any, RevueFlowActions>
export type UpdateNodeInternals = (nodeId: ElementId) => void;
export type UpdateNodeInternals = (nodeId: ElementId) => void
export interface RevueFlowProps extends Omit<HTMLAttributes, 'onLoad'> {
modelValue: Elements;
nodeTypes?: Record<string, NodeType>;
edgeTypes?: Record<string, EdgeType>;
connectionMode?: ConnectionMode;
connectionLineType?: ConnectionLineType;
connectionLineStyle?: CSSProperties;
connectionLineComponent?: ConnectionLineComponent;
deleteKeyCode?: KeyCode;
selectionKeyCode?: KeyCode;
multiSelectionKeyCode?: KeyCode;
zoomActivationKeyCode?: KeyCode;
snapToGrid?: boolean;
snapGrid?: [number, number];
onlyRenderVisibleElements?: boolean;
nodesDraggable?: boolean;
nodesConnectable?: boolean;
elementsSelectable?: boolean;
selectNodesOnDrag?: boolean;
paneMoveable?: boolean;
minZoom?: number;
maxZoom?: number;
defaultZoom?: number;
defaultPosition?: [number, number];
translateExtent?: TranslateExtent;
nodeExtent?: NodeExtent;
arrowHeadColor?: string;
markerEndId?: string;
zoomOnScroll?: boolean;
zoomOnPinch?: boolean;
panOnScroll?: boolean;
panOnScrollSpeed?: number;
panOnScrollMode?: PanOnScrollMode;
zoomOnDoubleClick?: boolean;
edgeUpdaterRadius?: number;
nodeTypesId?: string;
edgeTypesId?: string;
modelValue: Elements
nodeTypes?: Record<string, NodeType>
edgeTypes?: Record<string, EdgeType>
connectionMode?: ConnectionMode
connectionLineType?: ConnectionLineType
connectionLineStyle?: CSSProperties
connectionLineComponent?: ConnectionLineComponent
deleteKeyCode?: KeyCode
selectionKeyCode?: KeyCode
multiSelectionKeyCode?: KeyCode
zoomActivationKeyCode?: KeyCode
snapToGrid?: boolean
snapGrid?: [number, number]
onlyRenderVisibleElements?: boolean
nodesDraggable?: boolean
nodesConnectable?: boolean
elementsSelectable?: boolean
selectNodesOnDrag?: boolean
paneMoveable?: boolean
minZoom?: number
maxZoom?: number
defaultZoom?: number
defaultPosition?: [number, number]
translateExtent?: TranslateExtent
nodeExtent?: NodeExtent
arrowHeadColor?: string
markerEndId?: string
zoomOnScroll?: boolean
zoomOnPinch?: boolean
panOnScroll?: boolean
panOnScrollSpeed?: number
panOnScrollMode?: PanOnScrollMode
zoomOnDoubleClick?: boolean
edgeUpdaterRadius?: number
nodeTypesId?: string
edgeTypesId?: string
}
export interface GraphViewProps extends Required<RevueFlowProps> {
selectionKeyCode: KeyCode;
deleteKeyCode: KeyCode;
multiSelectionKeyCode: KeyCode;
connectionLineType: ConnectionLineType;
snapToGrid: boolean;
snapGrid: [number, number];
onlyRenderVisibleElements: boolean;
defaultZoom: number;
defaultPosition: [number, number];
arrowHeadColor: string;
selectNodesOnDrag: boolean;
selectionKeyCode: KeyCode
deleteKeyCode: KeyCode
multiSelectionKeyCode: KeyCode
connectionLineType: ConnectionLineType
snapToGrid: boolean
snapGrid: [number, number]
onlyRenderVisibleElements: boolean
defaultZoom: number
defaultPosition: [number, number]
arrowHeadColor: string
selectNodesOnDrag: boolean
}
+91 -91
View File
@@ -1,4 +1,4 @@
import { clampPosition, clamp } from './index';
import { clampPosition, clamp } from './index'
import {
ElementId,
@@ -13,47 +13,47 @@ import {
FlowExportObject,
NodeExtent,
RevueFlowStore
} from '../types';
} from '../types'
export const isEdge = (element: Node | Connection | Edge): element is Edge =>
'id' in element && 'source' in element && 'target' in element;
'id' in element && 'source' in element && 'target' in element
export const isNode = (element: Node | Connection | Edge): element is Node =>
'id' in element && !('source' in element) && !('target' in element);
'id' in element && !('source' in element) && !('target' in element)
export const getOutgoers = (node: Node, elements: Elements): Node[] => {
if (!isNode(node)) {
return [];
return []
}
const outgoerIds = elements.filter((e) => isEdge(e) && e.source === node.id).map((e) => (e as Edge).target);
return elements.filter((e) => outgoerIds.includes(e.id)) as Node[];
};
const outgoerIds = elements.filter((e) => isEdge(e) && e.source === node.id).map((e) => (e as Edge).target)
return elements.filter((e) => outgoerIds.includes(e.id)) as Node[]
}
export const getIncomers = (node: Node, elements: Elements): Node[] => {
if (!isNode(node)) {
return [];
return []
}
const incomersIds = elements.filter((e) => isEdge(e) && e.target === node.id).map((e) => (e as Edge).source);
return elements.filter((e) => incomersIds.includes(e.id)) as Node[];
};
const incomersIds = elements.filter((e) => isEdge(e) && e.target === node.id).map((e) => (e as Edge).source)
return elements.filter((e) => incomersIds.includes(e.id)) as Node[]
}
export const removeElements = (elementsToRemove: Elements, elements: Elements): Elements => {
const nodeIdsToRemove = elementsToRemove.map((n) => n.id);
const nodeIdsToRemove = elementsToRemove.map((n) => n.id)
return elements.filter((element) => {
const edgeElement = element as Edge;
const edgeElement = element as Edge
return !(
nodeIdsToRemove.includes(element.id) ||
nodeIdsToRemove.includes(edgeElement.target) ||
nodeIdsToRemove.includes(edgeElement.source)
);
});
};
)
})
}
const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection): ElementId =>
`revueflow__edge-${source}${sourceHandle}-${target}${targetHandle}`;
`revueflow__edge-${source}${sourceHandle}-${target}${targetHandle}`
const connectionExists = (edge: Edge, elements: Elements) => {
return elements.some(
@@ -63,43 +63,43 @@ const connectionExists = (edge: Edge, elements: Elements) => {
el.target === edge.target &&
(el.sourceHandle === edge.sourceHandle || (!el.sourceHandle && !edge.sourceHandle)) &&
(el.targetHandle === edge.targetHandle || (!el.targetHandle && !edge.targetHandle))
);
};
)
}
export const addEdge = (edgeParams: Edge | Connection, elements: Elements): Elements => {
if (!edgeParams.source || !edgeParams.target) {
console.log("Can't create edge. An edge needs a source and a target.");
return elements;
console.log("Can't create edge. An edge needs a source and a target.")
return elements
}
let edge: Edge;
let edge: Edge
if (isEdge(edgeParams)) {
edge = { ...edgeParams };
edge = { ...edgeParams }
} else {
edge = {
...edgeParams,
id: getEdgeId(edgeParams)
} as Edge;
} as Edge
}
if (connectionExists(edge, elements)) {
return elements;
return elements
}
return elements.concat(edge);
};
return elements.concat(edge)
}
export const updateEdge = (oldEdge: Edge, newConnection: Connection, elements: Elements): Elements => {
if (!newConnection.source || !newConnection.target) {
console.warn("Can't create new edge. An edge needs a source and a target.");
return elements;
console.warn("Can't create new edge. An edge needs a source and a target.")
return elements
}
const foundEdge = elements.find((e) => isEdge(e) && e.id === oldEdge.id) as Edge;
const foundEdge = elements.find((e) => isEdge(e) && e.id === oldEdge.id) as Edge
if (!foundEdge) {
console.warn(`The old edge with id=${oldEdge.id} does not exist.`);
return elements;
console.warn(`The old edge with id=${oldEdge.id} does not exist.`)
return elements
}
// Remove old edge and create the new edge with parameters of old edge.
@@ -110,10 +110,10 @@ export const updateEdge = (oldEdge: Edge, newConnection: Connection, elements: E
target: newConnection.target,
sourceHandle: newConnection.sourceHandle,
targetHandle: newConnection.targetHandle
} as Edge;
} as Edge
return elements.filter((e) => e.id !== oldEdge.id).concat(edge);
};
return elements.filter((e) => e.id !== oldEdge.id).concat(edge)
}
export const pointToRendererPoint = (
{ x, y }: XYPosition,
@@ -124,23 +124,23 @@ export const pointToRendererPoint = (
const position: XYPosition = {
x: (x - tx) / tScale,
y: (y - ty) / tScale
};
}
if (snapToGrid) {
return {
x: snapX * Math.round(position.x / snapX),
y: snapY * Math.round(position.y / snapY)
};
}
}
return position;
};
return position
}
export const onLoadProject = (currentStore: RevueFlowStore) => {
return (position: XYPosition): XYPosition => {
return pointToRendererPoint(position, currentStore.transform, currentStore.snapToGrid, currentStore.snapGrid);
};
};
return pointToRendererPoint(position, currentStore.transform, currentStore.snapToGrid, currentStore.snapGrid)
}
}
export const parseNode = (node: Node, nodeExtent: NodeExtent): Node => {
return {
@@ -154,8 +154,8 @@ export const parseNode = (node: Node, nodeExtent: NodeExtent): Node => {
handleBounds: {},
isDragging: false
}
};
};
}
}
export const parseEdge = (edge: Edge): Edge => {
return {
@@ -166,32 +166,32 @@ export const parseEdge = (edge: Edge): Edge => {
targetHandle: edge.targetHandle ? edge.targetHandle.toString() : null,
id: edge.id.toString(),
type: edge.type || 'default'
};
};
}
}
const getBoundsOfBoxes = (box1: Box, box2: Box): Box => ({
x: Math.min(box1.x, box2.x),
y: Math.min(box1.y, box2.y),
x2: Math.max(box1.x2, box2.x2),
y2: Math.max(box1.y2, box2.y2)
});
})
export const rectToBox = ({ x, y, width, height }: Rect): Box => ({
x,
y,
x2: x + width,
y2: y + height
});
})
export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({
x,
y,
width: x2 - x,
height: y2 - y
});
})
export const getBoundsofRects = (rect1: Rect, rect2: Rect): Rect =>
boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2)));
boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2)))
export const getRectOfNodes = (nodes: Node[]): Rect => {
const box = nodes.reduce(
@@ -205,15 +205,15 @@ export const getRectOfNodes = (nodes: Node[]): Rect => {
} as any)
),
{ x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity }
);
)
return boxToRect(box);
};
return boxToRect(box)
}
export const graphPosToZoomedPos = ({ x, y }: XYPosition, [tx, ty, tScale]: Transform): XYPosition => ({
x: x * tScale + tx,
y: y * tScale + ty
});
})
export const getNodesInside = (nodes: Node[], rect: Rect, [tx, ty, tScale]: Transform = [0, 0, 1], partially = false): Node[] => {
const rBox = rectToBox({
@@ -221,54 +221,54 @@ export const getNodesInside = (nodes: Node[], rect: Rect, [tx, ty, tScale]: Tran
y: (rect.y - ty) / tScale,
width: rect.width / tScale,
height: rect.height / tScale
});
})
return nodes.filter(({ __rf: { position, width, height, isDragging } }) => {
const nBox = rectToBox({ ...position, width, height } as any);
const xOverlap = Math.max(0, Math.min(rBox.x2, nBox.x2) - Math.max(rBox.x, nBox.x));
const yOverlap = Math.max(0, Math.min(rBox.y2, nBox.y2) - Math.max(rBox.y, nBox.y));
const overlappingArea = Math.ceil(xOverlap * yOverlap);
const nBox = rectToBox({ ...position, width, height } as any)
const xOverlap = Math.max(0, Math.min(rBox.x2, nBox.x2) - Math.max(rBox.x, nBox.x))
const yOverlap = Math.max(0, Math.min(rBox.y2, nBox.y2) - Math.max(rBox.y, nBox.y))
const overlappingArea = Math.ceil(xOverlap * yOverlap)
if (width === null || height === null || isDragging) {
// nodes are initialized with width and height = null
return true;
return true
}
if (partially) {
return overlappingArea > 0;
return overlappingArea > 0
}
const area = width * height;
const area = width * height
return overlappingArea >= area;
});
};
return overlappingArea >= area
})
}
export const getConnectedEdges = (nodes: Node[], edges: Edge[]): Edge[] => {
const nodeIds = nodes.map((node) => node.id);
const nodeIds = nodes.map((node) => node.id)
return edges.filter((edge) => nodeIds.includes(edge.source) || nodeIds.includes(edge.target));
};
return edges.filter((edge) => nodeIds.includes(edge.source) || nodeIds.includes(edge.target))
}
const parseElements = (nodes: Node[], edges: Edge[]): Elements => {
return [
...nodes.map((node) => {
const n = { ...node };
const n = { ...node }
n.position = n.__rf.position;
n.position = n.__rf.position
n.__rf = {} as any;
return n;
n.__rf = {} as any
return n
}),
...edges.map((e) => ({ ...e }))
];
};
]
}
export const onLoadGetElements = (currentStore: RevueFlowStore) => {
return (): Elements => {
return parseElements(currentStore.nodes || [], currentStore.edges || []);
};
};
return parseElements(currentStore.nodes || [], currentStore.edges || [])
}
}
export const onLoadToObject = (currentStore: RevueFlowStore) => {
return (): FlowExportObject => {
@@ -276,9 +276,9 @@ export const onLoadToObject = (currentStore: RevueFlowStore) => {
elements: parseElements(currentStore.nodes || [], currentStore.edges || []),
position: [currentStore.transform[0], currentStore.transform[1]],
zoom: currentStore.transform[2]
};
};
};
}
}
}
export const getTransformForBounds = (
bounds: Rect,
@@ -288,14 +288,14 @@ export const getTransformForBounds = (
maxZoom: number,
padding = 0.1
): Transform => {
const xZoom = width / (bounds.width * (1 + padding));
const yZoom = height / (bounds.height * (1 + padding));
const zoom = Math.min(xZoom, yZoom);
const clampedZoom = clamp(zoom, minZoom, maxZoom);
const boundsCenterX = bounds.x + bounds.width / 2;
const boundsCenterY = bounds.y + bounds.height / 2;
const x = width / 2 - boundsCenterX * clampedZoom;
const y = height / 2 - boundsCenterY * clampedZoom;
const xZoom = width / (bounds.width * (1 + padding))
const yZoom = height / (bounds.height * (1 + padding))
const zoom = Math.min(xZoom, yZoom)
const clampedZoom = clamp(zoom, minZoom, maxZoom)
const boundsCenterX = bounds.x + bounds.width / 2
const boundsCenterY = bounds.y + bounds.height / 2
const x = width / 2 - boundsCenterX * clampedZoom
const y = height / 2 - boundsCenterY * clampedZoom
return [x, y, clampedZoom];
};
return [x, y, clampedZoom]
}
+8 -8
View File
@@ -1,21 +1,21 @@
import { Dimensions, XYPosition, NodeExtent } from '../types';
import { Dimensions, XYPosition, NodeExtent } from '../types'
export const isInputDOMNode = (e: KeyboardEvent | MouseEvent): boolean => {
const target = e.target as HTMLElement;
return ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(target.nodeName) || target.hasAttribute('contentEditable');
};
const target = e.target as HTMLElement
return ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(target.nodeName) || target.hasAttribute('contentEditable')
}
export const getDimensions = (node: HTMLDivElement): Dimensions => ({
width: node.offsetWidth,
height: node.offsetHeight
});
})
export const clamp = (val: number, min = 0, max = 1): number => Math.min(Math.max(val, min), max);
export const clamp = (val: number, min = 0, max = 1): number => Math.min(Math.max(val, min), max)
export const clampPosition = (position: XYPosition, extent: NodeExtent): XYPosition => ({
x: clamp(position.x, extent[0][0], extent[1][0]),
y: clamp(position.y, extent[0][1], extent[1][1])
});
})
export const getHostForElement = (element: HTMLElement): Document | ShadowRoot =>
(element.getRootNode() as Document | ShadowRoot) || window.document;
(element.getRootNode() as Document | ShadowRoot) || window.document
+6 -6
View File
@@ -1,7 +1,7 @@
import { InjectionKey } from 'vue';
import { RevueFlowStore } from '../types';
import { RevueFlowHooks } from '../hooks/RevueFlowHooks';
import { InjectionKey } from 'vue'
import { RevueFlowStore } from '../types'
import { RevueFlowHooks } from '../hooks/RevueFlowHooks'
export const StoreKey: InjectionKey<RevueFlowStore> = Symbol('store');
export const HooksKey: InjectionKey<RevueFlowHooks> = Symbol('hooks');
export const NodeIdContextKey: InjectionKey<string> = Symbol('NodeIdContext');
export const StoreKey: InjectionKey<RevueFlowStore> = Symbol('store')
export const HooksKey: InjectionKey<RevueFlowHooks> = Symbol('hooks')
export const NodeIdContextKey: InjectionKey<string> = Symbol('NodeIdContext')
+35 -21
View File
@@ -1,28 +1,42 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"baseUrl": ".",
"module": "ESNext",
"target": "es2016",
"lib": [
"DOM",
"ESNext"
],
"strict": true,
"jsx": "preserve",
"jsxFactory": "",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["es6", "dom", "es2016", "es2017", "es2020"],
"types": ["@types/node"],
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"incremental": false,
"skipLibCheck": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": false,
"noFallthroughCasesInSwitch": true,
"allowSyntheticDefaultImports": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"jsx": "preserve",
"types": [
"vite/client",
"@types/node"
],
"paths": {
"~/*": [
"src/*"
]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules", "build", "dist", "example"]
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue"
],
"exclude": [
"node_modules",
"build",
"dist",
"example"
]
}
+21 -6
View File
@@ -1,12 +1,27 @@
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import svgLoader from 'vite-svg-loader';
import { defineConfig } from 'vite'
import { resolve } from 'path'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import svgLoader from 'vite-svg-loader'
import AutoImport from 'unplugin-auto-import/vite'
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
'~/': `${resolve(__dirname, 'src')}/`
}
},
build: {
outDir: 'build'
},
plugins: [vue(), vueJsx(), svgLoader()]
});
plugins: [
vue(),
vueJsx(),
svgLoader(), // https://github.com/antfu/unplugin-auto-import
AutoImport({
imports: ['vue', '@vueuse/core'],
dts: 'src/auto-imports.d.ts'
})
]
})