diff --git a/.eslintrc.js b/.eslintrc.js index bca4331d..54d3f3ec 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -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 -}; +} diff --git a/.prettierrc b/.prettierrc index 418202fe..de63788e 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,7 +1,7 @@ { "singleQuote": true, "trailingComma": "none", - "semi": true, + "semi": false, "bracketSpacing": true, "vueIndentScriptAndStyle": false, "printWidth": 130 diff --git a/examples/App.tsx b/examples/App.tsx index 9287e4be..81c3fbf9 100644 --- a/examples/App.tsx +++ b/examples/App.tsx @@ -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 () => ( <>
- ); + ) } -}); +}) diff --git a/examples/DragNDrop/Sidebar.tsx b/examples/DragNDrop/Sidebar.tsx index 7bc42a3c..1288a373 100644 --- a/examples/DragNDrop/Sidebar.tsx +++ b/examples/DragNDrop/Sidebar.tsx @@ -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 - ); -}; + ) +} -export default Sidebar; +export default Sidebar diff --git a/examples/EdgeTypes/index.tsx b/examples/EdgeTypes/index.tsx index 54db6b84..ed804335 100644 --- a/examples/EdgeTypes/index.tsx +++ b/examples/EdgeTypes/index.tsx @@ -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(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(initialElements) + const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value)) + const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value)) return () => ( - ); + ) } -}); +}) -export default EdgeTypesFlow; +export default EdgeTypesFlow diff --git a/examples/EdgeTypes/utils.ts b/examples/EdgeTypes/utils.ts index 6f5e65b8..12a5ea01 100644 --- a/examples/EdgeTypes/utils.ts +++ b/examples/EdgeTypes/utils.ts @@ -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 } diff --git a/examples/EdgeWithButton/ButtonEdge.tsx b/examples/EdgeWithButton/ButtonEdge.tsx index 2b89663f..5ba0cc20 100644 --- a/examples/EdgeWithButton/ButtonEdge.tsx +++ b/examples/EdgeWithButton/ButtonEdge.tsx @@ -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('store')!; - const hooks = inject('hooks')!; + const store = inject('store')! + const hooks = inject('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({ - ); + ) } -}); +}) diff --git a/examples/Edges/CustomEdge.tsx b/examples/Edges/CustomEdge.tsx index 146a0edb..9010a66e 100644 --- a/examples/Edges/CustomEdge.tsx +++ b/examples/Edges/CustomEdge.tsx @@ -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({ - ); + ) } -}); +}) -export default CustomEdge; +export default CustomEdge diff --git a/examples/Edges/CustomEdge2.tsx b/examples/Edges/CustomEdge2.tsx index 3c1a56c0..6f877733 100644 --- a/examples/Edges/CustomEdge2.tsx +++ b/examples/Edges/CustomEdge2.tsx @@ -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 diff --git a/examples/Edges/index.tsx b/examples/Edges/index.tsx index 8c2c4150..20e70a96 100644 --- a/examples/Edges/index.tsx +++ b/examples/Edges/index.tsx @@ -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 = { custom: CustomEdge, custom2: CustomEdge2 -}; +} const EdgesFlow = () => { - const elements = ref(initialElements as Elements); + const elements = ref(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 ( { - ); -}; + ) +} -export default EdgesFlow; +export default EdgesFlow diff --git a/examples/Empty/index.tsx b/examples/Empty/index.tsx index 1b74e75a..4cf46688 100644 --- a/examples/Empty/index.tsx +++ b/examples/Empty/index.tsx @@ -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([]); - const onElementsRemove = (elementsToRemove: Elements) => - (elements.value = removeElements(elementsToRemove, elements.value)); - const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value)); + const elements = ref([]) + 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 () => ( - ); + ) } } -}); +}) -export default EmptyFlow; +export default EmptyFlow diff --git a/examples/Header.tsx b/examples/Header.tsx index 2bd4669c..2188a635 100644 --- a/examples/Header.tsx +++ b/examples/Header.tsx @@ -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 () => (
@@ -25,8 +25,8 @@ const Header = defineComponent({ )}
- ); + ) } -}); +}) -export default Header; +export default Header diff --git a/examples/Hidden/index.tsx b/examples/Hidden/index.tsx index cbc2d6d3..82616ec4 100644 --- a/examples/Hidden/index.tsx +++ b/examples/Hidden/index.tsx @@ -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(initialElements); - const isHidden = ref(false); - const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value)); + const elements = ref(initialElements) + const isHidden = ref(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 () => ( @@ -44,8 +44,8 @@ const HiddenFlow = defineComponent({ - ); + ) } -}); +}) -export default HiddenFlow; +export default HiddenFlow diff --git a/examples/Interaction/index.tsx b/examples/Interaction/index.tsx index 5c8b3330..17fd377e 100644 --- a/examples/Interaction/index.tsx +++ b/examples/Interaction/index.tsx @@ -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(initialElements); - const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value)); + const elements = ref(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.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.Free) + const zoomOnDoubleClick = ref(false) + const paneMoveable = ref(true) + const captureZoomClick = ref(false) + const captureZoomScroll = ref(false) + const captureElementClick = ref(false) return () => ( - ); + ) } -}); +}) -export default InteractionFlow; +export default InteractionFlow diff --git a/examples/main.ts b/examples/main.ts index a6941400..e4abb9de 100644 --- a/examples/main.ts +++ b/examples/main.ts @@ -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') diff --git a/examples/router.ts b/examples/router.ts index 168dcc43..10d0463e 100644 --- a/examples/router.ts +++ b/examples/router.ts @@ -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 -}); +}) diff --git a/package.json b/package.json index 5348222f..5c428a3d 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/postcss.config.js b/postcss.config.js index 5d63a027..7ec64ba7 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,3 +1,3 @@ module.exports = { plugins: [require('autoprefixer'), require('postcss-nested')] -}; +} diff --git a/rollup.config.js b/rollup.config.js index 69a66b6d..a5ec54bf 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -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') diff --git a/rollup.ts b/rollup.ts index eaba83c6..9c1c3b2f 100644 --- a/rollup.ts +++ b/rollup.ts @@ -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 diff --git a/src/additional-components/Background/index.tsx b/src/additional-components/Background/index.tsx index 654782b6..945010e0 100644 --- a/src/additional-components/Background/index.tsx +++ b/src/additional-components/Background/index.tsx @@ -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('store')!; - const transform = computed(() => store.transform); + const store = inject('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 () => ( - ); + ) } -}); +}) -export default Background; +export default Background diff --git a/src/additional-components/Background/utils.tsx b/src/additional-components/Background/utils.tsx index 31f974a5..11c8c8f6 100644 --- a/src/additional-components/Background/utils.tsx +++ b/src/additional-components/Background/utils.tsx @@ -1,7 +1,7 @@ export const createGridLinesPath = (size: number, strokeWidth: number, stroke: string) => { - return ; -}; + return +} export const createGridDotsPath = (size: number, fill: string) => { - return ; -}; + return +} diff --git a/src/additional-components/Controls/index.tsx b/src/additional-components/Controls/index.tsx index c4de7931..4f46086e 100644 --- a/src/additional-components/Controls/index.tsx +++ b/src/additional-components/Controls/index.tsx @@ -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({ - ); + ) } -}); +}) const Controls = defineComponent({ name: 'Controls', @@ -87,41 +87,41 @@ const Controls = defineComponent({ } }, setup(props, { slots }) { - const store = inject('store')!; - const isVisible = ref(false); - const zoomHelper = ref(); - const { onReady } = useZoomPanHelper(); + const store = inject('store')! + const isVisible = ref(false) + const zoomHelper = ref() + 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 () => (
@@ -147,8 +147,8 @@ const Controls = defineComponent({ )} {slots.default ? slots.default() : ''}
- ); + ) } -}); +}) -export default Controls; +export default Controls diff --git a/src/additional-components/MiniMap/MiniMapNode.tsx b/src/additional-components/MiniMap/MiniMapNode.tsx index bd761267..abb992c5 100644 --- a/src/additional-components/MiniMap/MiniMapNode.tsx +++ b/src/additional-components/MiniMap/MiniMapNode.tsx @@ -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 }) { - 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 () => ( - ); + ) } -}); +}) -export default MiniMapNode; +export default MiniMapNode diff --git a/src/additional-components/MiniMap/index.tsx b/src/additional-components/MiniMap/index.tsx index 4e7c9556..683d50d3 100644 --- a/src/additional-components/MiniMap/index.tsx +++ b/src/additional-components/MiniMap/index.tsx @@ -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 }) { - const store = inject('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('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(() => ({ 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 () => ( - ); + ) } -}); +}) -export default MiniMap; +export default MiniMap diff --git a/src/additional-components/index.ts b/src/additional-components/index.ts index 28b3574b..dc21be20 100644 --- a/src/additional-components/index.ts +++ b/src/additional-components/index.ts @@ -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' diff --git a/src/auto-imports.d.ts b/src/auto-imports.d.ts new file mode 100644 index 00000000..72cd02c0 --- /dev/null +++ b/src/auto-imports.d.ts @@ -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 {} diff --git a/src/components/ConnectionLine/index.tsx b/src/components/ConnectionLine/index.tsx index 242cbbc6..e87b1502 100644 --- a/src/components/ConnectionLine/index.tsx +++ b/src/components/ConnectionLine/index.tsx @@ -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('store')!; - const sourceNode = ref(store.nodes.find((n) => n.id === store.connectionNodeId) || null); - const nodesConnectable = computed(() => store.nodesConnectable); + const store = inject('store')! + const sourceNode = ref(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 })} - ); + ) } return () => @@ -120,6 +120,6 @@ export default defineComponent({ ) : ( '' - ); + ) } -}); +}) diff --git a/src/components/Edges/BezierEdge.tsx b/src/components/Edges/BezierEdge.tsx index fb7efc21..31d04588 100644 --- a/src/components/Edges/BezierEdge.tsx +++ b/src/components/Edges/BezierEdge.tsx @@ -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 diff --git a/src/components/Edges/Edge.tsx b/src/components/Edges/Edge.tsx index 105f9a56..f77df87e 100644 --- a/src/components/Edges/Edge.tsx +++ b/src/components/Edges/Edge.tsx @@ -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('store')!; - const hooks = inject('hooks')!; + const store = inject('store')! + const hooks = inject('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) => { 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(false); - const onEdgeUpdaterMouseEnter = () => (updating.value = true); - const onEdgeUpdaterMouseOut = () => (updating.value = false); + const updating = ref(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 }) ? ( ) : ( '' - ); - }; + ) + } } -}); +}) diff --git a/src/components/Edges/EdgeAnchor.tsx b/src/components/Edges/EdgeAnchor.tsx index d1ad1210..7a4ebfe1 100644 --- a/src/components/Edges/EdgeAnchor.tsx +++ b/src/components/Edges/EdgeAnchor.tsx @@ -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" /> - ); + ) } -}); +}) diff --git a/src/components/Edges/EdgeText.tsx b/src/components/Edges/EdgeText.tsx index b383ea53..538ea0d3 100644 --- a/src/components/Edges/EdgeText.tsx +++ b/src/components/Edges/EdgeText.tsx @@ -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(null); - const edgeTextBox = ref({ x: 0, y: 0, width: 0, height: 0 }); + const edgeRef = ref(null) + const edgeTextBox = ref({ 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 () => ( {slots.default ? slots.default() : ''} - ); + ) } -}); +}) diff --git a/src/components/Edges/SmoothStepEdge.tsx b/src/components/Edges/SmoothStepEdge.tsx index f12a91bb..26916322 100644 --- a/src/components/Edges/SmoothStepEdge.tsx +++ b/src/components/Edges/SmoothStepEdge.tsx @@ -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 diff --git a/src/components/Edges/StepEdge.tsx b/src/components/Edges/StepEdge.tsx index 1235bbf0..c3810200 100644 --- a/src/components/Edges/StepEdge.tsx +++ b/src/components/Edges/StepEdge.tsx @@ -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 () => ; + return () => } -}) as EdgeType; +}) as EdgeType diff --git a/src/components/Edges/StraightEdge.tsx b/src/components/Edges/StraightEdge.tsx index 66c173b6..4c98858c 100644 --- a/src/components/Edges/StraightEdge.tsx +++ b/src/components/Edges/StraightEdge.tsx @@ -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 diff --git a/src/components/Edges/index.ts b/src/components/Edges/index.ts index 65beb017..2430d4ac 100644 --- a/src/components/Edges/index.ts +++ b/src/components/Edges/index.ts @@ -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' diff --git a/src/components/Edges/utils.ts b/src/components/Edges/utils.ts index 9a960dc2..e04c5044 100644 --- a/src/components/Edges/utils.ts +++ b/src/components/Edges/utils.ts @@ -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 } -}; +} diff --git a/src/components/Handle/handler.ts b/src/components/Handle/handler.ts index 207465ab..1d3ac48f 100644 --- a/src/components/Handle/handler.ts +++ b/src/components/Handle/handler.ts @@ -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) } diff --git a/src/components/Handle/index.tsx b/src/components/Handle/index.tsx index 7016880b..efac5c24 100644 --- a/src/components/Handle/index.tsx +++ b/src/components/Handle/index.tsx @@ -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('store')!; - const hooks = inject('hooks')!; - const nodeId = inject('NodeIdContext')!; + const store = inject('store')! + const hooks = inject('hooks')! + const nodeId = inject('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 () => (
{slots.default ? slots.default() : ''}
- ); + ) } -}); +}) diff --git a/src/components/Nodes/DefaultNode.tsx b/src/components/Nodes/DefaultNode.tsx index c1cb4899..e45a85ce 100644 --- a/src/components/Nodes/DefaultNode.tsx +++ b/src/components/Nodes/DefaultNode.tsx @@ -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} - ); + ) } -}) as NodeType; +}) as NodeType diff --git a/src/components/Nodes/InputNode.tsx b/src/components/Nodes/InputNode.tsx index dcf5e1ae..44348791 100644 --- a/src/components/Nodes/InputNode.tsx +++ b/src/components/Nodes/InputNode.tsx @@ -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} - ); + ) } -}) as NodeType; +}) as NodeType diff --git a/src/components/Nodes/Node.tsx b/src/components/Nodes/Node.tsx index 2bffa658..c54359f0 100644 --- a/src/components/Nodes/Node.tsx +++ b/src/components/Nodes/Node.tsx @@ -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; - snapToGrid: boolean; - snapGrid: [number, number]; - selectNodesOnDrag: boolean; + nodeTypes: Record + snapToGrid: boolean + snapGrid: [number, number] + selectNodesOnDrag: boolean } export default defineComponent({ @@ -39,22 +39,22 @@ export default defineComponent({ } }, setup(props) { - const store = inject('store')!; - const hooks = inject('hooks')!; - provide('NodeIdContext', props.node.id); + const store = inject('store')! + const hooks = inject('hooks')! + provide('NodeIdContext', props.node.id) - const nodeElement = templateRef('nodeElement', null); + const nodeElement = templateRef('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({ })} - ); + ) } -}); +}) diff --git a/src/components/Nodes/OutputNode.tsx b/src/components/Nodes/OutputNode.tsx index 14d6a93e..5a596945 100644 --- a/src/components/Nodes/OutputNode.tsx +++ b/src/components/Nodes/OutputNode.tsx @@ -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} - ); + ) } -}) as NodeType; +}) as NodeType diff --git a/src/components/Nodes/utils.ts b/src/components/Nodes/utils.ts index 6bcf2b28..9ecdd5bb 100644 --- a/src/components/Nodes/utils.ts +++ b/src/components/Nodes/utils.ts @@ -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 - }; - }); -}; + } + }) +} diff --git a/src/components/NodesSelection/index.tsx b/src/components/NodesSelection/index.tsx index 40118f0a..a9a5f09c 100644 --- a/src/components/NodesSelection/index.tsx +++ b/src/components/NodesSelection/index.tsx @@ -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('store')!; - const hooks = inject('hooks')!; - const grid = computed(() => (store.snapToGrid ? store.snapGrid : [1, 1])! as [number, number]); - const transform = computed(() => store.transform); + const store = inject('store')! + const hooks = inject('hooks')! + const grid = computed(() => (store.snapToGrid ? store.snapGrid : [1, 1])! as [number, number]) + const transform = computed(() => store.transform) const selectedNodes = computed(() => store.selectedElements ? 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({
- ); - }; + ) + } } -}); +}) -export default NodesSelection; +export default NodesSelection diff --git a/src/components/UserSelection/index.tsx b/src/components/UserSelection/index.tsx index de3db3c7..cb37402e 100644 --- a/src/components/UserSelection/index.tsx +++ b/src/components/UserSelection/index.tsx @@ -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('store')!; - const el = templateRef('user-selection', null); - const shouldRender = computed(() => store.selectionActive || store.elementsSelectable); + const store = inject('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({ ) : ( '' - ); + ) } -}); +}) diff --git a/src/container/EdgeRenderer/Marker.tsx b/src/container/EdgeRenderer/Marker.tsx index 4fead563..1e629eaf 100644 --- a/src/container/EdgeRenderer/Marker.tsx +++ b/src/container/EdgeRenderer/Marker.tsx @@ -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() : ''} - ); + ) } -}); +}) diff --git a/src/container/EdgeRenderer/MarkerDefinitions.tsx b/src/container/EdgeRenderer/MarkerDefinitions.tsx index 84556b01..07ffcfd9 100644 --- a/src/container/EdgeRenderer/MarkerDefinitions.tsx +++ b/src/container/EdgeRenderer/MarkerDefinitions.tsx @@ -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({ /> - ); + ) } -}); +}) diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx index 0a73cb09..08011872 100644 --- a/src/container/EdgeRenderer/index.tsx +++ b/src/container/EdgeRenderer/index.tsx @@ -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('store')!; + const store = inject('store')! return () => ( @@ -94,6 +94,6 @@ export default defineComponent({ )}
- ); + ) } -}); +}) diff --git a/src/container/EdgeRenderer/utils.ts b/src/container/EdgeRenderer/utils.ts index ba0ef9a4..103b1f95 100644 --- a/src/container/EdgeRenderer/utils.ts +++ b/src/container/EdgeRenderer/utils.ts @@ -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): Record { const standardTypes: Record = { @@ -8,75 +8,75 @@ export function createEdgeTypes(edgeTypes: Record): Record; + const wrappedTypes = {} as Record const specialTypes: Record = 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 - ); -}; + ) +} diff --git a/src/container/FlowRenderer/index.tsx b/src/container/FlowRenderer/index.tsx index a9da929c..9178ffed 100644 --- a/src/container/FlowRenderer/index.tsx +++ b/src/container/FlowRenderer/index.tsx @@ -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('store')!; - const hooks = inject('hooks')!; - const keyPressed = useKeyPress(props.selectionKeyCode); + const store = inject('store')! + const hooks = inject('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 () => ( : ''}
- ); + ) } -}); +}) diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index c6d55b44..e4474537 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -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('store')!; - const hooks = inject('hooks')!; + const store = inject('store')! + const hooks = inject('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 () => ( - ); + ) } -}); +}) diff --git a/src/container/NodeRenderer/index.tsx b/src/container/NodeRenderer/index.tsx index 4c7158a6..e7135235 100644 --- a/src/container/NodeRenderer/index.tsx +++ b/src/container/NodeRenderer/index.tsx @@ -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; - selectNodesOnDrag: boolean; - snapToGrid: boolean; - snapGrid: [number, number]; - onlyRenderVisibleElements: boolean; + nodeTypes: Record + selectNodesOnDrag: boolean + snapToGrid: boolean + snapGrid: [number, number] + onlyRenderVisibleElements: boolean } export default defineComponent({ @@ -42,7 +42,7 @@ export default defineComponent({ } }, setup(props) { - const store = inject('store')!; + const store = inject('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 ( - - ); - } - }); + : store.nodes + } return () => (
- {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 ( + + ) + } + })}
- ); + ) } -}); +}) diff --git a/src/container/NodeRenderer/utils.ts b/src/container/NodeRenderer/utils.ts index 454568db..963258d0 100644 --- a/src/container/NodeRenderer/utils.ts +++ b/src/container/NodeRenderer/utils.ts @@ -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): Record { const standardTypes: Record = { 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 - }; + } } diff --git a/src/container/RevueFlow/index.tsx b/src/container/RevueFlow/index.tsx index bca365f0..0553aa34 100644 --- a/src/container/RevueFlow/index.tsx +++ b/src/container/RevueFlow/index.tsx @@ -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('store', store); - const hooks = useRevueFlow().bind(emit); - provide('hooks', hooks); - const elements = useVModel(props, 'modelValue', emit); + const store = configureStore(initialState)() + provide('store', store) + const hooks = useRevueFlow().bind(emit) + provide('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 () => (
@@ -279,6 +278,6 @@ export default defineComponent({ /> {slots.default ? slots.default() : ''}
- ); + ) } -}); +}) diff --git a/src/container/ZoomPane/index.tsx b/src/container/ZoomPane/index.tsx index d81c11ed..5d5c5199 100644 --- a/src/container/ZoomPane/index.tsx +++ b/src/container/ZoomPane/index.tsx @@ -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('store')!; - const hooks = inject('hooks')!; - const zoomPane = templateRef('zoom-pane', null); - const prevTransform = ref({ x: 0, y: 0, zoom: 0 }); + const store = inject('store')! + const hooks = inject('hooks')! + const zoomPane = templateRef('zoom-pane', null) + const prevTransform = ref({ 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) => { d3Zoom.on('start', (event: D3ZoomEvent) => { 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) => { 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) => { - 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) => { - 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 () => (
{slots.default ? slots.default() : ''}
- ); + ) } -}); +}) diff --git a/src/custom.d.ts b/src/custom.d.ts index fe131545..c75864ea 100644 --- a/src/custom.d.ts +++ b/src/custom.d.ts @@ -1,2 +1,2 @@ -declare const __REVUE_FLOW_VERSION__: string; -declare const __ENV__: string; +declare const __REVUE_FLOW_VERSION__: string +declare const __ENV__: string diff --git a/src/hooks/RevueFlowHooks.ts b/src/hooks/RevueFlowHooks.ts index bba26aa6..543ae9eb 100644 --- a/src/hooks/RevueFlowHooks.ts +++ b/src/hooks/RevueFlowHooks.ts @@ -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; - 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; + elementClick: EventHook<{ event: MouseEvent; element: Node | Edge }> + elementsRemove: EventHook + 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 connectStart: EventHook<{ - event: MouseEvent; - params: OnConnectStartParams; - }>; - connectStop: EventHook; - connectEnd: EventHook; - load: EventHook; - move: EventHook; - moveStart: EventHook; - moveEnd: EventHook; - selectionChange: EventHook; - 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; - paneClick: EventHook; - paneContextMenu: EventHook; - 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 + connectEnd: EventHook + load: EventHook + move: EventHook + moveStart: EventHook + moveEnd: EventHook + selectionChange: EventHook + 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 + paneClick: EventHook + paneContextMenu: EventHook + 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(), connectStart: createEventHook<{ - event: MouseEvent; - params: OnConnectStartParams; + event: MouseEvent + params: OnConnectStartParams }>(), connectStop: createEventHook(), connectEnd: createEventHook(), @@ -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 - }; -}; + } +} diff --git a/src/hooks/useGlobalKeyHandler.ts b/src/hooks/useGlobalKeyHandler.ts index f0248a9c..dd66055d 100644 --- a/src/hooks/useGlobalKeyHandler.ts +++ b/src/hooks/useGlobalKeyHandler.ts @@ -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() - ); + ) - 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 + }) +} diff --git a/src/hooks/useKeyPress.ts b/src/hooks/useKeyPress.ts index 92c9d018..bb41a138 100644 --- a/src/hooks/useKeyPress.ts +++ b/src/hooks/useKeyPress.ts @@ -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 => { const keyPressed = controlledRef(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 +} diff --git a/src/hooks/useResizeHandler.ts b/src/hooks/useResizeHandler.ts index a84fd7ba..2f56ae29 100644 --- a/src/hooks/useResizeHandler.ts +++ b/src/hooks/useResizeHandler.ts @@ -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, 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 - }; -}; + } +} diff --git a/src/hooks/useUpdateNodeInternals.ts b/src/hooks/useUpdateNodeInternals.ts index c5296617..7714bc98 100644 --- a/src/hooks/useUpdateNodeInternals.ts +++ b/src/hooks/useUpdateNodeInternals.ts @@ -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('store')!; + const store = inject('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 diff --git a/src/hooks/useZoomPanHelper.ts b/src/hooks/useZoomPanHelper.ts index d64c75fa..47502de1 100644 --- a/src/hooks/useZoomPanHelper.ts +++ b/src/hooks/useZoomPanHelper.ts @@ -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 } => { - const zoomPanHelperEvent = createEventHook(); - const store = inject('store')!; + const zoomPanHelperEvent = createEventHook() + const store = inject('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, @@ -24,18 +24,18 @@ const useZoomPanHelper = (): { onReady: EventHookOn } => 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 } => 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 diff --git a/src/index.ts b/src/index.ts index e45a2492..f0148303 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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' diff --git a/src/shims-tsx.d.ts b/src/shims-tsx.d.ts index bdee7b0a..220884ca 100644 --- a/src/shims-tsx.d.ts +++ b/src/shims-tsx.d.ts @@ -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 } } } diff --git a/src/shims-vue.d.ts b/src/shims-vue.d.ts index 9a8de2df..a191098d 100644 --- a/src/shims-vue.d.ts +++ b/src/shims-vue.d.ts @@ -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 } diff --git a/src/store/configure-store.ts b/src/store/configure-store.ts index 07ddc8d5..aa9725e0 100644 --- a/src/store/configure-store.ts +++ b/src/store/configure-store.ts @@ -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 { - 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 } } - }); + }) } diff --git a/src/store/index.ts b/src/store/index.ts index 8d31fd29..bbb02dbd 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -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__ : '-' -}; +} diff --git a/src/types/index.ts b/src/types/index.ts index 22298a62..b16d7611 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -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 = Node | Edge; +export type FlowElement = Node | Edge -export type Elements = Array>; +export type Elements = Array> -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 { - 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 { - 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; +export type NodeType = DefineComponent -export type EdgeType = DefineComponent; +export type EdgeType = DefineComponent export interface SelectionRect extends Rect { - startX: number; - startY: number; - draw: boolean; + startX: number + startY: number + draw: boolean } export interface EdgeProps { - 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 extends EdgeProps { - 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 { - 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 = { - elements: Elements; - position: [number, number]; - zoom: number; -}; + elements: Elements + position: [number, number] + zoom: number +} -export type FitViewFunc = (fitViewOptions?: FitViewParams) => void; -export type ProjectFunc = (position: XYPosition) => XYPosition; -export type ToObjectFunc = () => FlowExportObject; +export type FitViewFunc = (fitViewOptions?: FitViewParams) => void +export type ProjectFunc = (position: XYPosition) => XYPosition +export type ToObjectFunc = () => FlowExportObject export type OnLoadParams = { - zoomIn: () => void; - zoomOut: () => void; - zoomTo: (zoomLevel: number) => void; - fitView: FitViewFunc; - project: ProjectFunc; - getElements: () => Elements; - setTransform: (transform: FlowTransform) => void; - toObject: ToObjectFunc; -}; + zoomIn: () => void + zoomOut: () => void + zoomTo: (zoomLevel: number) => void + fitView: FitViewFunc + project: ProjectFunc + getElements: () => Elements + setTransform: (transform: FlowTransform) => void + toObject: ToObjectFunc +} -export type OnLoadFunc = (params: OnLoadParams) => void; +export type OnLoadFunc = (params: OnLoadParams) => 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; +export type ConnectionLineComponent = DefineComponent -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 = (oldEdge: Edge, newConnection: Connection) => void; +export type OnEdgeUpdateFunc = (oldEdge: Edge, newConnection: Connection) => void export type NodeDimensionUpdate = { - id: ElementId; - nodeElement: HTMLDivElement; - forceUpdate?: boolean; -}; + id: ElementId + nodeElement: HTMLDivElement + forceUpdate?: boolean +} export type InitD3ZoomPayload = { - d3Zoom: ZoomBehavior; - d3Selection: D3Selection; - d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined; - transform: Transform; -}; + d3Zoom: ZoomBehavior + d3Selection: D3Selection + 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 | null; - d3Selection: D3Selection | null; - d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined; - minZoom: number; - maxZoom: number; - translateExtent: TranslateExtent; - nodeExtent: NodeExtent; + d3Zoom: ZoomBehavior | null + d3Selection: D3Selection | 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; +export type RevueFlowStore = Store -export type UpdateNodeInternals = (nodeId: ElementId) => void; +export type UpdateNodeInternals = (nodeId: ElementId) => void export interface RevueFlowProps extends Omit { - modelValue: Elements; - nodeTypes?: Record; - edgeTypes?: Record; - 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 + edgeTypes?: Record + 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 { - 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 } diff --git a/src/utils/graph.ts b/src/utils/graph.ts index 7e97bea0..23ed1061 100644 --- a/src/utils/graph.ts +++ b/src/utils/graph.ts @@ -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] +} diff --git a/src/utils/index.ts b/src/utils/index.ts index a817fdb7..1075b4b4 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -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 diff --git a/src/utils/symbols.ts b/src/utils/symbols.ts index e96169fe..948d3b86 100644 --- a/src/utils/symbols.ts +++ b/src/utils/symbols.ts @@ -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 = Symbol('store'); -export const HooksKey: InjectionKey = Symbol('hooks'); -export const NodeIdContextKey: InjectionKey = Symbol('NodeIdContext'); +export const StoreKey: InjectionKey = Symbol('store') +export const HooksKey: InjectionKey = Symbol('hooks') +export const NodeIdContextKey: InjectionKey = Symbol('NodeIdContext') diff --git a/tsconfig.json b/tsconfig.json index c8001065..f0a7bc0c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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" + ] } diff --git a/vite.config.ts b/vite.config.ts index f3a26073..ac208d23 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -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' + }) + ] +})