update(deps): Add unplugin-components
fix: Excessive rerender of edge texts update(script-setup): Update some examples * Remove more jsx files
This commit is contained in:
@@ -1,64 +0,0 @@
|
|||||||
import { computed, defineComponent, inject } from 'vue'
|
|
||||||
import { getBezierPath, getEdgeCenter, getMarkerEnd, RevueFlowStore } from '../../src'
|
|
||||||
import { DefaultEdgeProps } from '../../src/components/Edges/utils'
|
|
||||||
import { RevueFlowHooks } from '../../src/hooks/RevueFlowHooks'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
props: {
|
|
||||||
...DefaultEdgeProps
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const store = inject<RevueFlowStore>('store')!
|
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
|
||||||
const onEdgeClick = (evt: Event, id: string) => {
|
|
||||||
const edge = store.edges.find((edge) => edge.id === id)
|
|
||||||
if (edge) {
|
|
||||||
hooks.elementsRemove.trigger([edge])
|
|
||||||
}
|
|
||||||
evt.stopPropagation()
|
|
||||||
alert(`remove ${id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const foreignObjectSize = 40
|
|
||||||
|
|
||||||
const edgePath = computed(() =>
|
|
||||||
getBezierPath({
|
|
||||||
sourceX: props.sourceX,
|
|
||||||
sourceY: props.sourceY,
|
|
||||||
sourcePosition: props.sourcePosition,
|
|
||||||
targetX: props.targetX,
|
|
||||||
targetY: props.targetY,
|
|
||||||
targetPosition: props.targetPosition
|
|
||||||
})
|
|
||||||
)
|
|
||||||
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
|
|
||||||
const center = computed(() =>
|
|
||||||
getEdgeCenter({
|
|
||||||
sourceX: props.sourceX,
|
|
||||||
sourceY: props.sourceY,
|
|
||||||
targetX: props.targetX,
|
|
||||||
targetY: props.targetY
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<>
|
|
||||||
<path id={props.id} style={props.style} class="revue-flow__edge-path" d={edgePath.value} marker-end={markerEnd.value} />
|
|
||||||
<foreignObject
|
|
||||||
width={foreignObjectSize}
|
|
||||||
height={foreignObjectSize}
|
|
||||||
x={center.value[0] - foreignObjectSize / 2}
|
|
||||||
y={center.value[1] - foreignObjectSize / 2}
|
|
||||||
class="edgebutton-foreignobject"
|
|
||||||
requiredExtensions="http://www.w3.org/1999/xhtml"
|
|
||||||
>
|
|
||||||
<body>
|
|
||||||
<button class="edgebutton" onClick={(event) => onEdgeClick(event, props.id)}>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</body>
|
|
||||||
</foreignObject>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { RevueFlowHooks } from '~/hooks/RevueFlowHooks'
|
||||||
|
import {
|
||||||
|
getEdgeCenter,
|
||||||
|
getBezierPath,
|
||||||
|
getMarkerEnd,
|
||||||
|
ArrowHeadType,
|
||||||
|
EdgeProps,
|
||||||
|
ElementId,
|
||||||
|
Position,
|
||||||
|
RevueFlowStore,
|
||||||
|
} from '~/index'
|
||||||
|
|
||||||
|
interface CustomEdgeProps<T = any> extends EdgeProps<T> {
|
||||||
|
id: ElementId
|
||||||
|
sourceX: number
|
||||||
|
sourceY: number
|
||||||
|
targetX: number
|
||||||
|
targetY: number
|
||||||
|
sourcePosition: Position
|
||||||
|
targetPosition: Position
|
||||||
|
arrowHeadType?: ArrowHeadType
|
||||||
|
markerEndId?: string
|
||||||
|
data?: T
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<CustomEdgeProps>()
|
||||||
|
const store = inject<RevueFlowStore>('store')!
|
||||||
|
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||||
|
const onEdgeClick = (evt: Event, id: string) => {
|
||||||
|
const edge = store.edges.find((edge) => edge.id === id)
|
||||||
|
if (edge) {
|
||||||
|
hooks.elementsRemove.trigger([edge])
|
||||||
|
}
|
||||||
|
evt.stopPropagation()
|
||||||
|
alert(`remove ${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const foreignObjectSize = 40
|
||||||
|
|
||||||
|
const edgePath = computed(() =>
|
||||||
|
getBezierPath({
|
||||||
|
sourceX: props.sourceX,
|
||||||
|
sourceY: props.sourceY,
|
||||||
|
sourcePosition: props.sourcePosition,
|
||||||
|
targetX: props.targetX,
|
||||||
|
targetY: props.targetY,
|
||||||
|
targetPosition: props.targetPosition,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
|
||||||
|
const center = computed(() =>
|
||||||
|
getEdgeCenter({
|
||||||
|
sourceX: props.sourceX,
|
||||||
|
sourceY: props.sourceY,
|
||||||
|
targetX: props.targetX,
|
||||||
|
targetY: props.targetY,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<path :id="props.id" :style="props.style" class="revue-flow__edge-path" :d="edgePath.value" :marker-end="markerEnd.value" />
|
||||||
|
<foreignObject
|
||||||
|
width="foreignObjectSize"
|
||||||
|
height="foreignObjectSize"
|
||||||
|
:x="center.value[0] - foreignObjectSize / 2"
|
||||||
|
:y="center.value[1] - foreignObjectSize / 2"
|
||||||
|
class="edgebutton-foreignobject"
|
||||||
|
requiredExtensions="http://www.w3.org/1999/xhtml"
|
||||||
|
>
|
||||||
|
<body>
|
||||||
|
<button class="edgebutton" @click="(event) => onEdgeClick(event, props.id)">×</button>
|
||||||
|
</body>
|
||||||
|
</foreignObject>
|
||||||
|
</template>
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import { getBezierPath, getMarkerEnd } from '../../src'
|
|
||||||
import { computed, defineComponent } from 'vue'
|
|
||||||
import { DefaultEdgeProps } from '../../src/components/Edges/utils'
|
|
||||||
|
|
||||||
const CustomEdge = defineComponent({
|
|
||||||
props: {
|
|
||||||
...DefaultEdgeProps
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const edgePath = computed(() =>
|
|
||||||
getBezierPath({
|
|
||||||
sourceX: props.sourceX,
|
|
||||||
sourceY: props.sourceY,
|
|
||||||
sourcePosition: props.sourcePosition,
|
|
||||||
targetX: props.targetX,
|
|
||||||
targetY: props.targetY,
|
|
||||||
targetPosition: props.targetPosition
|
|
||||||
})
|
|
||||||
)
|
|
||||||
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<>
|
|
||||||
<path id={props.id} class="revue-flow__edge-path" d={edgePath.value} marker-end={markerEnd.value} />
|
|
||||||
<text>
|
|
||||||
<textPath href={`#${props.id}`} style={{ fontSize: '12px' }} startOffset="50%" text-anchor="middle">
|
|
||||||
{props.data.text}
|
|
||||||
</textPath>
|
|
||||||
</text>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default CustomEdge
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ArrowHeadType, ElementId, getBezierPath, getMarkerEnd, Position, EdgeProps } from '~/index'
|
||||||
|
|
||||||
|
interface CustomEdgeProps<T = any> extends EdgeProps<T> {
|
||||||
|
source: ElementId
|
||||||
|
target: ElementId
|
||||||
|
sourceHandleId?: ElementId
|
||||||
|
targetHandleId?: ElementId
|
||||||
|
id: ElementId
|
||||||
|
sourceX: number
|
||||||
|
sourceY: number
|
||||||
|
targetX: number
|
||||||
|
targetY: number
|
||||||
|
sourcePosition: Position
|
||||||
|
targetPosition: Position
|
||||||
|
arrowHeadType?: ArrowHeadType
|
||||||
|
markerEndId?: string
|
||||||
|
data?: T
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<CustomEdgeProps>()
|
||||||
|
const edgePath = computed(() =>
|
||||||
|
getBezierPath({
|
||||||
|
sourceX: props.sourceX,
|
||||||
|
sourceY: props.sourceY,
|
||||||
|
sourcePosition: props.sourcePosition,
|
||||||
|
targetX: props.targetX,
|
||||||
|
targetY: props.targetY,
|
||||||
|
targetPosition: props.targetPosition,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<path :id="props.id" class="revue-flow__edge-path" :d="edgePath" :marker-end="markerEnd" />
|
||||||
|
<text>
|
||||||
|
<textPath :href="`#${props.id}`" :style="{ fontSize: '12px' }" startOffset="50%" text-anchor="middle">
|
||||||
|
{{ props.data.text }}
|
||||||
|
</textPath>
|
||||||
|
</text>
|
||||||
|
</template>
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import { computed, defineComponent } from 'vue'
|
|
||||||
import { EdgeText, getBezierPath, getEdgeCenter, getMarkerEnd } from '../../src'
|
|
||||||
import { DefaultEdgeProps } from '../../src/components/Edges/utils'
|
|
||||||
|
|
||||||
const CustomEdge = defineComponent({
|
|
||||||
props: {
|
|
||||||
...DefaultEdgeProps
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const edgePath = computed(() =>
|
|
||||||
getBezierPath({
|
|
||||||
sourceX: props.sourceX,
|
|
||||||
sourceY: props.sourceY,
|
|
||||||
sourcePosition: props.sourcePosition,
|
|
||||||
targetX: props.targetX,
|
|
||||||
targetY: props.targetY,
|
|
||||||
targetPosition: props.targetPosition
|
|
||||||
})
|
|
||||||
)
|
|
||||||
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
|
|
||||||
const center = computed(() =>
|
|
||||||
getEdgeCenter({
|
|
||||||
sourceX: props.sourceX,
|
|
||||||
sourceY: props.sourceY,
|
|
||||||
targetX: props.targetX,
|
|
||||||
targetY: props.targetY
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<>
|
|
||||||
<path id={props.id} class="revue-flow__edge-path" d={edgePath.value} marker-end={markerEnd.value} />
|
|
||||||
<EdgeText
|
|
||||||
x={center.value[0]}
|
|
||||||
y={center.value[1]}
|
|
||||||
label={props.data?.text}
|
|
||||||
labelStyle={{ fill: 'white' }}
|
|
||||||
labelShowBg
|
|
||||||
labelBgStyle={{ fill: 'red' }}
|
|
||||||
labelBgPadding={[2, 4]}
|
|
||||||
labelBgBorderRadius={2}
|
|
||||||
onClick={() => console.log(props.data)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default CustomEdge
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { getBezierPath, getMarkerEnd } from '~/components/Edges/utils'
|
||||||
|
import { ArrowHeadType, EdgeProps, ElementId, getEdgeCenter, Position, EdgeText } from '~/index'
|
||||||
|
|
||||||
|
interface CustomEdgeProps<T = any> extends EdgeProps<T> {
|
||||||
|
source: ElementId
|
||||||
|
target: ElementId
|
||||||
|
sourceHandleId?: ElementId
|
||||||
|
targetHandleId?: ElementId
|
||||||
|
id: ElementId
|
||||||
|
sourceX: number
|
||||||
|
sourceY: number
|
||||||
|
targetX: number
|
||||||
|
targetY: number
|
||||||
|
sourcePosition: Position
|
||||||
|
targetPosition: Position
|
||||||
|
arrowHeadType?: ArrowHeadType
|
||||||
|
markerEndId?: string
|
||||||
|
data?: T
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<CustomEdgeProps>()
|
||||||
|
|
||||||
|
const edgePath = computed(() =>
|
||||||
|
getBezierPath({
|
||||||
|
sourceX: props.sourceX,
|
||||||
|
sourceY: props.sourceY,
|
||||||
|
sourcePosition: props.sourcePosition,
|
||||||
|
targetX: props.targetX,
|
||||||
|
targetY: props.targetY,
|
||||||
|
targetPosition: props.targetPosition,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
|
||||||
|
const center = computed(() =>
|
||||||
|
getEdgeCenter({
|
||||||
|
sourceX: props.sourceX,
|
||||||
|
sourceY: props.sourceY,
|
||||||
|
targetX: props.targetX,
|
||||||
|
targetY: props.targetY,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<path :id="props.id" class="revue-flow__edge-path" :d="edgePath" :marker-end="markerEnd" />
|
||||||
|
<EdgeText
|
||||||
|
:x="center[0]"
|
||||||
|
:y="center[1]"
|
||||||
|
:label="props.data?.text"
|
||||||
|
:label-style="{ fill: 'white' }"
|
||||||
|
:label-show-bg="true"
|
||||||
|
:label-bg-style="{ fill: 'red' }"
|
||||||
|
:label-bg-padding="[2, 4]"
|
||||||
|
:label-bg-border-radius="2"
|
||||||
|
@click="() => console.log(props.data)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<script lang="ts" setup></script>
|
||||||
|
<template>
|
||||||
|
<tspan dy="10" x="0">I am a `tspan`</tspan>
|
||||||
|
</template>
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
import { ref } from 'vue'
|
<script lang="ts" setup>
|
||||||
import CustomEdge from './CustomEdge'
|
import CustomEdge from './CustomEdge.vue'
|
||||||
import CustomEdge2 from './CustomEdge2'
|
import CustomEdge2 from './CustomEdge2.vue'
|
||||||
import RevueFlow, {
|
import Flow, {
|
||||||
Elements,
|
|
||||||
FlowElement,
|
|
||||||
OnLoadParams,
|
|
||||||
ArrowHeadType,
|
|
||||||
Background,
|
|
||||||
Controls,
|
|
||||||
MiniMap,
|
MiniMap,
|
||||||
addEdge,
|
Controls,
|
||||||
|
Background,
|
||||||
|
Elements,
|
||||||
|
OnLoadParams,
|
||||||
|
FlowElement,
|
||||||
|
removeElements,
|
||||||
Connection,
|
Connection,
|
||||||
Edge,
|
Edge,
|
||||||
removeElements
|
addEdge,
|
||||||
} from '../../src'
|
ArrowHeadType,
|
||||||
|
} from '~/index'
|
||||||
|
|
||||||
const initialElements = [
|
const initialElements = [
|
||||||
{ id: '1', type: 'input', data: { label: 'Input 1' }, position: { x: 250, y: 0 } },
|
{ id: '1', type: 'input', data: { label: 'Input 1' }, position: { x: 250, y: 0 } },
|
||||||
@@ -37,16 +37,9 @@ const initialElements = [
|
|||||||
id: 'e5-6',
|
id: 'e5-6',
|
||||||
source: '5',
|
source: '5',
|
||||||
target: '6',
|
target: '6',
|
||||||
label: (
|
label: 'foobar',
|
||||||
<>
|
|
||||||
<tspan>i am using</tspan>
|
|
||||||
<tspan dy={10} x={0}>
|
|
||||||
{'<tspan>'}
|
|
||||||
</tspan>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
labelStyle: { fill: 'red', fontWeight: 700 },
|
labelStyle: { fill: 'red', fontWeight: 700 },
|
||||||
arrowHeadType: ArrowHeadType.Arrow
|
arrowHeadType: ArrowHeadType.Arrow,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'e5-7',
|
id: 'e5-7',
|
||||||
@@ -56,7 +49,7 @@ const initialElements = [
|
|||||||
labelBgPadding: [8, 4],
|
labelBgPadding: [8, 4],
|
||||||
labelBgBorderRadius: 4,
|
labelBgBorderRadius: 4,
|
||||||
labelBgStyle: { fill: '#FFCC00', color: '#fff', fillOpacity: 0.7 },
|
labelBgStyle: { fill: '#FFCC00', color: '#fff', fillOpacity: 0.7 },
|
||||||
arrowHeadType: ArrowHeadType.ArrowClosed
|
arrowHeadType: ArrowHeadType.ArrowClosed,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'e5-8',
|
id: 'e5-8',
|
||||||
@@ -64,47 +57,43 @@ const initialElements = [
|
|||||||
target: '8',
|
target: '8',
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
data: { text: 'custom edge' },
|
data: { text: 'custom edge' },
|
||||||
arrowHeadType: ArrowHeadType.ArrowClosed
|
arrowHeadType: ArrowHeadType.ArrowClosed,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'e5-9',
|
id: 'e5-9',
|
||||||
source: '5',
|
source: '5',
|
||||||
target: '9',
|
target: '9',
|
||||||
type: 'custom2',
|
type: 'custom2',
|
||||||
data: { text: 'custom edge 2' }
|
data: { text: 'custom edge 2' },
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const edgeTypes: Record<string, any> = {
|
const edgeTypes: Record<string, any> = {
|
||||||
custom: CustomEdge,
|
custom: CustomEdge,
|
||||||
custom2: CustomEdge2
|
custom2: CustomEdge2,
|
||||||
}
|
}
|
||||||
|
|
||||||
const EdgesFlow = () => {
|
const elements = ref<Elements>(initialElements as Elements)
|
||||||
const elements = ref<Elements>(initialElements as Elements)
|
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView()
|
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView()
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node)
|
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node)
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element)
|
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element)
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
|
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
|
||||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
|
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
|
||||||
|
</script>
|
||||||
return (
|
<template>
|
||||||
<RevueFlow
|
<Flow
|
||||||
v-model={elements.value}
|
:elements="elements"
|
||||||
onElementClick={onElementClick}
|
:on-element-click="onElementClick"
|
||||||
onElementsRemove={onElementsRemove}
|
:on-elements-remove="onElementsRemove"
|
||||||
onConnect={onConnect}
|
:on-connect="onConnect"
|
||||||
onNodeDragStop={onNodeDragStop}
|
:on-node-drag-stop="onNodeDragStop"
|
||||||
onLoad={onLoad}
|
:on-load="onLoad"
|
||||||
snapToGrid={true}
|
:snap-to-grid="true"
|
||||||
edgeTypes={edgeTypes}
|
:edge-types="edgeTypes"
|
||||||
>
|
>
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
<Controls />
|
<Controls />
|
||||||
<Background />
|
<Background />
|
||||||
</RevueFlow>
|
</Flow>
|
||||||
)
|
</template>
|
||||||
}
|
|
||||||
|
|
||||||
export default EdgesFlow
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import Flowy from '~/container/Flow.vue'
|
import Flowy from '~/container/Flow/Flow.vue'
|
||||||
import { Elements } from '~/types'
|
import { Elements } from '~/types'
|
||||||
|
|
||||||
const elements = ref<Elements>([
|
const elements = ref<Elements>([
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ export const routes: RouterOptions['routes'] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/edges',
|
path: '/edges',
|
||||||
component: () => import('./Edges'),
|
component: () => import('./Edges/EdgesExample.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/button-edge',
|
path: '/button-edge',
|
||||||
|
|||||||
@@ -77,6 +77,7 @@
|
|||||||
"tslib": "^2.3.1",
|
"tslib": "^2.3.1",
|
||||||
"typescript": "^4.4.4",
|
"typescript": "^4.4.4",
|
||||||
"unplugin-auto-import": "^0.4.12",
|
"unplugin-auto-import": "^0.4.12",
|
||||||
|
"unplugin-vue-components": "^0.16.0",
|
||||||
"vite": "^2.6.7",
|
"vite": "^2.6.7",
|
||||||
"vite-svg-loader": "^2.2.0",
|
"vite-svg-loader": "^2.2.0",
|
||||||
"vue": "^3.2.20",
|
"vue": "^3.2.20",
|
||||||
|
|||||||
Generated
+73
@@ -41,6 +41,7 @@ specifiers:
|
|||||||
tslib: ^2.3.1
|
tslib: ^2.3.1
|
||||||
typescript: ^4.4.4
|
typescript: ^4.4.4
|
||||||
unplugin-auto-import: ^0.4.12
|
unplugin-auto-import: ^0.4.12
|
||||||
|
unplugin-vue-components: ^0.16.0
|
||||||
vite: ^2.6.7
|
vite: ^2.6.7
|
||||||
vite-svg-loader: ^2.2.0
|
vite-svg-loader: ^2.2.0
|
||||||
vue: ^3.2.20
|
vue: ^3.2.20
|
||||||
@@ -91,6 +92,7 @@ devDependencies:
|
|||||||
tslib: 2.3.1
|
tslib: 2.3.1
|
||||||
typescript: 4.4.4
|
typescript: 4.4.4
|
||||||
unplugin-auto-import: 0.4.12_5751e524770bdd1a9aebab667ab35e17
|
unplugin-auto-import: 0.4.12_5751e524770bdd1a9aebab667ab35e17
|
||||||
|
unplugin-vue-components: 0.16.0_2684a81276b76b6d63a663e703a5f4ca
|
||||||
vite: 2.6.7
|
vite: 2.6.7
|
||||||
vite-svg-loader: 2.2.0
|
vite-svg-loader: 2.2.0
|
||||||
vue: 3.2.20
|
vue: 3.2.20
|
||||||
@@ -1439,6 +1441,14 @@ packages:
|
|||||||
rxjs: 6.6.7
|
rxjs: 6.6.7
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/anymatch/3.1.2:
|
||||||
|
resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
dependencies:
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
picomatch: 2.3.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/argparse/1.0.10:
|
/argparse/1.0.10:
|
||||||
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
|
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1533,6 +1543,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
|
resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/binary-extensions/2.2.0:
|
||||||
|
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/boolbase/1.0.0:
|
/boolbase/1.0.0:
|
||||||
resolution: {integrity: sha1-aN/1++YMUes3cl6p4+0xDcwed24=}
|
resolution: {integrity: sha1-aN/1++YMUes3cl6p4+0xDcwed24=}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -1712,6 +1727,21 @@ packages:
|
|||||||
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
|
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/chokidar/3.5.2:
|
||||||
|
resolution: {integrity: sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==}
|
||||||
|
engines: {node: '>= 8.10.0'}
|
||||||
|
dependencies:
|
||||||
|
anymatch: 3.1.2
|
||||||
|
braces: 3.0.2
|
||||||
|
glob-parent: 5.1.2
|
||||||
|
is-binary-path: 2.1.0
|
||||||
|
is-glob: 4.0.3
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
readdirp: 3.6.0
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/ci-info/2.0.0:
|
/ci-info/2.0.0:
|
||||||
resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==}
|
resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -3723,6 +3753,13 @@ packages:
|
|||||||
has-bigints: 1.0.1
|
has-bigints: 1.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/is-binary-path/2.1.0:
|
||||||
|
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
binary-extensions: 2.2.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/is-boolean-object/1.1.2:
|
/is-boolean-object/1.1.2:
|
||||||
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
|
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -4494,6 +4531,11 @@ packages:
|
|||||||
validate-npm-package-license: 3.0.4
|
validate-npm-package-license: 3.0.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/normalize-path/3.0.0:
|
||||||
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/normalize-range/0.1.2:
|
/normalize-range/0.1.2:
|
||||||
resolution: {integrity: sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=}
|
resolution: {integrity: sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -5560,6 +5602,13 @@ packages:
|
|||||||
type-fest: 0.6.0
|
type-fest: 0.6.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/readdirp/3.6.0:
|
||||||
|
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||||
|
engines: {node: '>=8.10.0'}
|
||||||
|
dependencies:
|
||||||
|
picomatch: 2.3.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/redent/3.0.0:
|
/redent/3.0.0:
|
||||||
resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
|
resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -6359,6 +6408,30 @@ packages:
|
|||||||
- webpack
|
- webpack
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/unplugin-vue-components/0.16.0_2684a81276b76b6d63a663e703a5f4ca:
|
||||||
|
resolution: {integrity: sha512-BM/5p6/btLgDjUxf290cKbzbaGow95NFibhp+TSQhL0wyb1Q6sP0nmlqOvGnpLANFq0+urXxXDloMLlTJH6Fww==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
peerDependencies:
|
||||||
|
vue: 2 || 3
|
||||||
|
dependencies:
|
||||||
|
'@antfu/utils': 0.3.0
|
||||||
|
'@rollup/pluginutils': 4.1.1
|
||||||
|
chokidar: 3.5.2
|
||||||
|
debug: 4.3.2
|
||||||
|
fast-glob: 3.2.7
|
||||||
|
local-pkg: 0.1.0
|
||||||
|
magic-string: 0.25.7
|
||||||
|
minimatch: 3.0.4
|
||||||
|
resolve: 1.20.0
|
||||||
|
unplugin: 0.2.16_rollup@2.58.0+vite@2.6.7
|
||||||
|
vue: 3.2.20
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- rollup
|
||||||
|
- supports-color
|
||||||
|
- vite
|
||||||
|
- webpack
|
||||||
|
dev: true
|
||||||
|
|
||||||
/unplugin/0.2.16_rollup@2.58.0+vite@2.6.7:
|
/unplugin/0.2.16_rollup@2.58.0+vite@2.6.7:
|
||||||
resolution: {integrity: sha512-KkXatHba0baJszSHW+2e8EQU/5Bz7rYwzYXu8wUeq97tE6K3wvub+7OWSuRv04LttvzNLsJ2jXEyR35gofv74Q==}
|
resolution: {integrity: sha512-KkXatHba0baJszSHW+2e8EQU/5Bz7rYwzYXu8wUeq97tE6K3wvub+7OWSuRv04LttvzNLsJ2jXEyR35gofv74Q==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
export type ControlButtonProps = HTMLButtonElement
|
export interface ControlButtonProps extends HTMLButtonElement {}
|
||||||
|
|
||||||
const props = defineProps<ControlButtonProps>()
|
const props = defineProps<ControlButtonProps>()
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { HTMLAttributes } from 'vue'
|
import { HTMLAttributes } from 'vue'
|
||||||
import MiniMapNodeDepr from './MiniMapNodeDepr'
|
import MiniMapNode from './MiniMapNode.vue'
|
||||||
import { getRectOfNodes, getBoundsofRects } from '~/utils/graph'
|
import { getRectOfNodes, getBoundsofRects } from '~/utils/graph'
|
||||||
import { Node, Rect, RevueFlowStore } from '~/types'
|
import { Node, Rect, RevueFlowStore } from '~/types'
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crisp
|
|||||||
<template>
|
<template>
|
||||||
<svg :width="elementWidth" :height="elementHeight" :viewBox="`${x} ${y} ${width} ${height}`" class="revue-flow__minimap">
|
<svg :width="elementWidth" :height="elementHeight" :viewBox="`${x} ${y} ${width} ${height}`" class="revue-flow__minimap">
|
||||||
<template v-for="(node, i) of store.nodes" :key="`mini-map-node-${i}`">
|
<template v-for="(node, i) of store.nodes" :key="`mini-map-node-${i}`">
|
||||||
<MiniMapNodeDepr
|
<MiniMapNode
|
||||||
v-if="!node.isHidden"
|
v-if="!node.isHidden"
|
||||||
:x="node.__rf.position.x"
|
:x="node.__rf.position.x"
|
||||||
:y="node.__rf.position.y"
|
:y="node.__rf.position.y"
|
||||||
|
|||||||
Vendored
+26
@@ -0,0 +1,26 @@
|
|||||||
|
// generated by unplugin-vue-components
|
||||||
|
// We suggest you to commit this file into source control
|
||||||
|
// Read more: https://github.com/vuejs/vue-next/pull/3399
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
BezierEdge: typeof import('./components/Edges/BezierEdge.vue')['default']
|
||||||
|
ConnectionLine: typeof import('./components/ConnectionLine/ConnectionLine.vue')['default']
|
||||||
|
DefaultNode: typeof import('./components/Nodes/DefaultNode.vue')['default']
|
||||||
|
Edge: typeof import('./components/Edges/Edge.vue')['default']
|
||||||
|
EdgeAnchor: typeof import('./components/Edges/EdgeAnchor.vue')['default']
|
||||||
|
EdgeText: typeof import('./components/Edges/EdgeText.vue')['default']
|
||||||
|
Handle: typeof import('./components/Handle/Handle.vue')['default']
|
||||||
|
InputNode: typeof import('./components/Nodes/InputNode.vue')['default']
|
||||||
|
Node: typeof import('./components/Nodes/Node.vue')['default']
|
||||||
|
NodesSelection: typeof import('./components/NodesSelection/NodesSelection.vue')['default']
|
||||||
|
OutputNode: typeof import('./components/Nodes/OutputNode.vue')['default']
|
||||||
|
SelectionRect: typeof import('./components/UserSelection/SelectionRect.vue')['default']
|
||||||
|
SmoothStepEdge: typeof import('./components/Edges/SmoothStepEdge.vue')['default']
|
||||||
|
StepEdge: typeof import('./components/Edges/StepEdge.vue')['default']
|
||||||
|
StraightEdge: typeof import('./components/Edges/StraightEdge.vue')['default']
|
||||||
|
UserSelection: typeof import('./components/UserSelection/UserSelection.vue')['default']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { }
|
||||||
@@ -180,6 +180,8 @@ const visible = computed(() => !props.edge.isHidden && isVisible(edgePos.value))
|
|||||||
data: props.edge.data,
|
data: props.edge.data,
|
||||||
style: props.edge.style,
|
style: props.edge.style,
|
||||||
arrowHeadType: props.edge.arrowHeadType,
|
arrowHeadType: props.edge.arrowHeadType,
|
||||||
|
sourcePosition,
|
||||||
|
targetPosition,
|
||||||
sourceX: edgePos.sourceX,
|
sourceX: edgePos.sourceX,
|
||||||
sourceY: edgePos.sourceY,
|
sourceY: edgePos.sourceY,
|
||||||
targetX: edgePos.targetX,
|
targetX: edgePos.targetX,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { HTMLAttributes, ref, VNode, watchEffect } from 'vue'
|
import { HTMLAttributes, VNode } from 'vue'
|
||||||
import { Rect } from '~/types'
|
|
||||||
|
|
||||||
interface EdgeTextProps extends HTMLAttributes {
|
interface EdgeTextProps extends HTMLAttributes {
|
||||||
x: number
|
x: number
|
||||||
@@ -22,30 +21,14 @@ const props = withDefaults(defineProps<EdgeTextProps>(), {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const edgeRef = templateRef<SVGTextElement>('edge-text', null)
|
const edgeRef = templateRef<SVGTextElement>('edge-text', null)
|
||||||
const edgeTextBox = ref<Rect>({ x: 0, y: 0, width: 0, height: 0 })
|
const { width = 0, height = 0, x = 0, y = 0 } = useElementBounding(edgeRef)
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
const textBbox = edgeRef.value?.getBBox()
|
|
||||||
|
|
||||||
if (textBbox) {
|
|
||||||
edgeTextBox.value = {
|
|
||||||
x: textBbox.x,
|
|
||||||
y: textBbox.y,
|
|
||||||
width: textBbox.width,
|
|
||||||
height: textBbox.height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<g
|
<g :transform="`translate(${props.x - width / 2} ${props.y - height / 2})`" class="revue-flow__edge-textwrapper">
|
||||||
:transform="`translate(${props.x - edgeTextBox.width / 2} ${props.y - edgeTextBox.height / 2})`"
|
|
||||||
class="revue-flow__edge-textwrapper"
|
|
||||||
>
|
|
||||||
<rect
|
<rect
|
||||||
v-if="props.labelShowBg"
|
v-if="props.labelShowBg"
|
||||||
:width="edgeTextBox.width + 2 * props.labelBgPadding[0] + 'px'"
|
:width="width + 2 * props.labelBgPadding[0] + 'px'"
|
||||||
:height="edgeTextBox.height + 2 * props.labelBgPadding[1] + 'px'"
|
:height="height + 2 * props.labelBgPadding[1] + 'px'"
|
||||||
:x="-props.labelBgPadding[0]"
|
:x="-props.labelBgPadding[0]"
|
||||||
:y="-props.labelBgPadding[1]"
|
:y="-props.labelBgPadding[1]"
|
||||||
class="revue-flow__edge-textbg"
|
class="revue-flow__edge-textbg"
|
||||||
@@ -53,8 +36,11 @@ watchEffect(() => {
|
|||||||
:rx="props.labelBgBorderRadius"
|
:rx="props.labelBgBorderRadius"
|
||||||
:ry="props.labelBgBorderRadius"
|
:ry="props.labelBgBorderRadius"
|
||||||
/>
|
/>
|
||||||
<text ref="edge-text" class="revue-flow__edge-text" :y="edgeTextBox.height / 2" dy="0.3em" :style="props.labelStyle">
|
<text ref="edge-text" class="revue-flow__edge-text" :y="height / 2" dy="0.3em" :style="props.labelStyle">
|
||||||
{{ props.label }}
|
<component :is="props.label" v-if="typeof props.label !== 'string'" />
|
||||||
|
<template v-else>
|
||||||
|
{{ props.label }}
|
||||||
|
</template>
|
||||||
</text>
|
</text>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</g>
|
</g>
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ const props = withDefaults(defineProps<DefaultNodeProps>(), {
|
|||||||
<template>
|
<template>
|
||||||
<div class="revue-flow__node-default">
|
<div class="revue-flow__node-default">
|
||||||
<Handle type="target" :position="props.targetPosition" :is-connectable="props.connectable" />
|
<Handle type="target" :position="props.targetPosition" :is-connectable="props.connectable" />
|
||||||
{{ props.data?.label }}
|
<component :is="props.data?.label" v-if="typeof props.data?.label !== 'string'" />
|
||||||
|
<template v-else>
|
||||||
|
{{ props.data?.label }}
|
||||||
|
</template>
|
||||||
<Handle type="source" :position="props.sourcePosition" :is-connectable="props.connectable" />
|
<Handle type="source" :position="props.sourcePosition" :is-connectable="props.connectable" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ const props = withDefaults(defineProps<InputNodeProps>(), {
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="revue-flow__node-input">
|
<div class="revue-flow__node-input">
|
||||||
{{ props.data?.label }}
|
<component :is="props.data?.label" v-if="typeof props.data?.label !== 'string'" />
|
||||||
|
<template v-else>
|
||||||
|
{{ props.data?.label }}
|
||||||
|
</template>
|
||||||
<Handle type="source" :position="props.sourcePosition" :connectable="props.connectable" />
|
<Handle type="source" :position="props.sourcePosition" :connectable="props.connectable" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ const props = withDefaults(defineProps<OutputNodeProps>(), {
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="revue-flow__node-output">
|
<div class="revue-flow__node-output">
|
||||||
{{ props.data?.label }}
|
<component :is="props.data?.label" v-if="typeof props.data?.label !== 'string'" />
|
||||||
|
<template v-else>
|
||||||
|
{{ props.data?.label }}
|
||||||
|
</template>
|
||||||
<Handle type="source" :position="props.targetPosition" :is-connectable="props.connectable" />
|
<Handle type="source" :position="props.targetPosition" :is-connectable="props.connectable" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { CSSProperties } from 'vue'
|
import { CSSProperties } from 'vue'
|
||||||
import { getSourceTargetNodes } from '~/container/EdgeRenderer/utils'
|
import { getSourceTargetNodes } from './utils'
|
||||||
|
import MarkerDefinitions from './MarkerDefinitions.vue'
|
||||||
import Edge from '~/components/Edges/Edge.vue'
|
import Edge from '~/components/Edges/Edge.vue'
|
||||||
import ConnectionLine from '~/components/ConnectionLine/ConnectionLine.vue'
|
import ConnectionLine from '~/components/ConnectionLine/ConnectionLine.vue'
|
||||||
import {
|
import {
|
||||||
@@ -12,7 +13,6 @@ import {
|
|||||||
RevueFlowStore,
|
RevueFlowStore,
|
||||||
Transform,
|
Transform,
|
||||||
} from '~/types'
|
} from '~/types'
|
||||||
import MarkerDefinitions from '~/container/MarkerDefinitions.vue'
|
|
||||||
|
|
||||||
interface EdgeRendererProps {
|
interface EdgeRendererProps {
|
||||||
edgeTypes: Record<string, EdgeType>
|
edgeTypes: Record<string, EdgeType>
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
interface MarkerProps {
|
|
||||||
id: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
props: {
|
|
||||||
id: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup(props: MarkerProps, { slots }) {
|
|
||||||
return () => (
|
|
||||||
<marker
|
|
||||||
id={props.id}
|
|
||||||
class="revue-flow__arrowhead"
|
|
||||||
markerWidth="12.5"
|
|
||||||
markerHeight="12.5"
|
|
||||||
viewBox="-10 -10 20 20"
|
|
||||||
orient="auto"
|
|
||||||
refX="0"
|
|
||||||
refY="0"
|
|
||||||
>
|
|
||||||
{slots.default ? slots.default() : ''}
|
|
||||||
</marker>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import { defineComponent, PropType } from 'vue'
|
|
||||||
import Marker from './Marker'
|
|
||||||
|
|
||||||
interface MarkerDefinitionsProps {
|
|
||||||
color: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: { Marker },
|
|
||||||
props: {
|
|
||||||
color: {
|
|
||||||
type: String as PropType<MarkerDefinitionsProps['color']>,
|
|
||||||
required: false,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
return () => (
|
|
||||||
<defs>
|
|
||||||
<Marker id="revue-flow__arrowclosed">
|
|
||||||
<polyline
|
|
||||||
stroke={props.color}
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="1"
|
|
||||||
fill={props.color}
|
|
||||||
points="-5,-4 0,0 -5,4 -5,-4"
|
|
||||||
/>
|
|
||||||
</Marker>
|
|
||||||
<Marker id="revue-flow__arrow">
|
|
||||||
<polyline
|
|
||||||
stroke={props.color}
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="1.5"
|
|
||||||
fill="none"
|
|
||||||
points="-5,-4 0,0 -5,4"
|
|
||||||
/>
|
|
||||||
</Marker>
|
|
||||||
</defs>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
import { CSSProperties, defineComponent, inject, PropType } from 'vue'
|
|
||||||
import { ConnectionLineType, ConnectionLineComponent, ConnectionMode, RevueFlowStore } from '../../types'
|
|
||||||
import ConnectionLine from '../../components/ConnectionLine'
|
|
||||||
import Edge from '../../components/Edges/EdgeDepr'
|
|
||||||
import MarkerDefinitions from './MarkerDefinitions'
|
|
||||||
|
|
||||||
interface EdgeRendererProps {
|
|
||||||
edgeTypes: any
|
|
||||||
connectionLineType: ConnectionLineType
|
|
||||||
connectionLineStyle?: CSSProperties
|
|
||||||
connectionLineComponent?: ConnectionLineComponent
|
|
||||||
connectionMode?: ConnectionMode
|
|
||||||
arrowHeadColor: string
|
|
||||||
markerEndId?: string
|
|
||||||
onlyRenderVisibleElements: boolean
|
|
||||||
edgeUpdaterRadius?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'EdgeRenderer',
|
|
||||||
components: {
|
|
||||||
Edge,
|
|
||||||
ConnectionLine,
|
|
||||||
MarkerDefinitions,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
edgeTypes: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
connectionLineType: {
|
|
||||||
type: String as PropType<EdgeRendererProps['connectionLineType']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
connectionLineStyle: {
|
|
||||||
type: Object as PropType<EdgeRendererProps['connectionLineStyle']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
connectionLineComponent: {
|
|
||||||
type: Object as PropType<EdgeRendererProps['connectionLineComponent']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
connectionMode: {
|
|
||||||
type: String as PropType<EdgeRendererProps['connectionMode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
arrowHeadColor: {
|
|
||||||
type: String as PropType<EdgeRendererProps['arrowHeadColor']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
markerEndId: {
|
|
||||||
type: String as PropType<EdgeRendererProps['markerEndId']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
onlyRenderVisibleElements: {
|
|
||||||
type: Boolean as PropType<EdgeRendererProps['onlyRenderVisibleElements']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
edgeUpdaterRadius: {
|
|
||||||
type: Number as PropType<EdgeRendererProps['edgeUpdaterRadius']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const store = inject<RevueFlowStore>('store')!
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<svg width={store.width} height={store.height} className="revue-flow__edges">
|
|
||||||
<MarkerDefinitions color={props.arrowHeadColor} />
|
|
||||||
<g transform={`translate(${store.transform[0]},${store.transform[1]}) scale(${store.transform[2]})`}>
|
|
||||||
{store.edges.map((edge, i) => (
|
|
||||||
<Edge
|
|
||||||
edge={edge}
|
|
||||||
type={props.edgeTypes[edge.type || 'default']}
|
|
||||||
onlyRenderVisibleElements={props.onlyRenderVisibleElements}
|
|
||||||
markerEndId={props.markerEndId}
|
|
||||||
key={edge.id + i}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{store.connectionNodeId && store.connectionHandleType && (
|
|
||||||
<ConnectionLine
|
|
||||||
connectionLineStyle={props.connectionLineStyle}
|
|
||||||
connectionLineType={props.connectionLineType}
|
|
||||||
customConnectionLine={props.connectionLineComponent}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
import '~/style.css'
|
import '~/style.css'
|
||||||
import '~/theme-default.css'
|
import '~/theme-default.css'
|
||||||
import { CSSProperties, onBeforeUnmount } from 'vue'
|
import { CSSProperties, onBeforeUnmount } from 'vue'
|
||||||
import ZoomPane from './ZoomPane.vue'
|
import ZoomPane from '~/container/ZoomPane/ZoomPane.vue'
|
||||||
import SelectionPane from './SelectionPane.vue'
|
import SelectionPane from '~/container/SelectionPane/SelectionPane.vue'
|
||||||
import NodeRenderer from '~/container/NodeRenderer.vue'
|
import NodeRenderer from '~/container/NodeRenderer/NodeRenderer.vue'
|
||||||
import EdgeRenderer from '~/container/EdgeRenderer.vue'
|
import EdgeRenderer from '~/container/EdgeRenderer/EdgeRenderer.vue'
|
||||||
import {
|
import {
|
||||||
ConnectionLineType,
|
ConnectionLineType,
|
||||||
ConnectionMode,
|
ConnectionMode,
|
||||||
@@ -22,10 +22,10 @@ import {
|
|||||||
import { RevueFlowHooks, useRevueFlow } from '~/hooks/RevueFlowHooks'
|
import { RevueFlowHooks, useRevueFlow } from '~/hooks/RevueFlowHooks'
|
||||||
import configureStore from '~/store/configure-store'
|
import configureStore from '~/store/configure-store'
|
||||||
import { initialState } from '~/store'
|
import { initialState } from '~/store'
|
||||||
import { createNodeTypes } from '~/container/NodeRenderer/utils'
|
|
||||||
import { DefaultNode, InputNode, OutputNode } from '~/components/Nodes'
|
import { DefaultNode, InputNode, OutputNode } from '~/components/Nodes'
|
||||||
import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge } from '~/components/Edges'
|
import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge } from '~/components/Edges'
|
||||||
import { createEdgeTypes } from '~/container/EdgeRenderer/utils'
|
import { createEdgeTypes } from '~/container/EdgeRenderer/utils'
|
||||||
|
import { createNodeTypes } from '~/container/NodeRenderer/utils'
|
||||||
|
|
||||||
interface RevueFlowProps {
|
interface RevueFlowProps {
|
||||||
elements: Elements
|
elements: Elements
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
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,
|
|
||||||
| 'elements'
|
|
||||||
| 'snapToGrid'
|
|
||||||
| 'nodeTypes'
|
|
||||||
| 'edgeTypes'
|
|
||||||
| 'snapGrid'
|
|
||||||
| 'connectionLineType'
|
|
||||||
| 'arrowHeadColor'
|
|
||||||
| 'onlyRenderVisibleElements'
|
|
||||||
| 'selectNodesOnDrag'
|
|
||||||
>
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'FlowRenderer',
|
|
||||||
components: { UserSelection, ZoomPane, NodesSelection },
|
|
||||||
props: {
|
|
||||||
deleteKeyCode: {
|
|
||||||
type: [Number, String] as PropType<FlowRendererProps['deleteKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
selectionKeyCode: {
|
|
||||||
type: [Number, String] as PropType<FlowRendererProps['selectionKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
multiSelectionKeyCode: {
|
|
||||||
type: [Number, String] as PropType<FlowRendererProps['multiSelectionKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
zoomActivationKeyCode: {
|
|
||||||
type: [Number, String] as PropType<FlowRendererProps['zoomActivationKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
elementsSelectable: {
|
|
||||||
type: Boolean as PropType<FlowRendererProps['elementsSelectable']>,
|
|
||||||
required: false,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
zoomOnScroll: {
|
|
||||||
type: Boolean as PropType<FlowRendererProps['zoomOnScroll']>,
|
|
||||||
required: false,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
zoomOnPinch: {
|
|
||||||
type: Boolean as PropType<FlowRendererProps['zoomOnPinch']>,
|
|
||||||
required: false,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
panOnScroll: {
|
|
||||||
type: Boolean as PropType<FlowRendererProps['panOnScroll']>,
|
|
||||||
required: false,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
panOnScrollSpeed: {
|
|
||||||
type: Number as PropType<FlowRendererProps['panOnScrollSpeed']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
panOnScrollMode: {
|
|
||||||
type: String as PropType<FlowRendererProps['panOnScrollMode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
zoomOnDoubleClick: {
|
|
||||||
type: Boolean as PropType<FlowRendererProps['zoomOnDoubleClick']>,
|
|
||||||
required: false,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
paneMoveable: {
|
|
||||||
type: Boolean as PropType<FlowRendererProps['paneMoveable']>,
|
|
||||||
required: false,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
defaultPosition: {
|
|
||||||
type: Array as unknown as PropType<FlowRendererProps['defaultPosition']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
defaultZoom: {
|
|
||||||
type: Number as PropType<FlowRendererProps['defaultZoom']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
translateExtent: {
|
|
||||||
type: Array as unknown as PropType<FlowRendererProps['translateExtent']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup(props, { slots }) {
|
|
||||||
const store = inject<RevueFlowStore>('store')!
|
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
|
||||||
const keyPressed = useKeyPress(props.selectionKeyCode)
|
|
||||||
|
|
||||||
useGlobalKeyHandler({
|
|
||||||
store,
|
|
||||||
onElementsRemove: hooks.elementsRemove.trigger,
|
|
||||||
deleteKeyCode: props.deleteKeyCode || '',
|
|
||||||
multiSelectionKeyCode: props.multiSelectionKeyCode || ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const onClick = (event: MouseEvent) => {
|
|
||||||
hooks.paneClick.trigger(event)
|
|
||||||
store.unsetNodesSelection()
|
|
||||||
store.resetSelectedElements()
|
|
||||||
}
|
|
||||||
|
|
||||||
const onContextMenu = (event: MouseEvent) => hooks.paneContextMenu.trigger(event)
|
|
||||||
|
|
||||||
const onWheel = (event: WheelEvent) => hooks.paneScroll.trigger(event)
|
|
||||||
|
|
||||||
const selectionActive = computed(() => store.nodesSelectionActive)
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<ZoomPane
|
|
||||||
elementsSelectable={props.elementsSelectable}
|
|
||||||
zoomOnScroll={props.zoomOnScroll}
|
|
||||||
zoomOnPinch={props.zoomOnPinch}
|
|
||||||
panOnScroll={props.panOnScroll}
|
|
||||||
panOnScrollSpeed={props.panOnScrollSpeed}
|
|
||||||
panOnScrollMode={props.panOnScrollMode}
|
|
||||||
zoomOnDoubleClick={props.zoomOnDoubleClick}
|
|
||||||
paneMoveable={props.paneMoveable}
|
|
||||||
defaultPosition={props.defaultPosition}
|
|
||||||
defaultZoom={props.defaultZoom}
|
|
||||||
translateExtent={props.translateExtent}
|
|
||||||
zoomActivationKeyCode={props.zoomActivationKeyCode}
|
|
||||||
selectionKeyCode={props.selectionKeyCode}
|
|
||||||
>
|
|
||||||
{slots.default ? slots.default() : ''}
|
|
||||||
{keyPressed.value ? <UserSelection /> : ''}
|
|
||||||
{selectionActive.value ? <NodesSelection /> : ''}
|
|
||||||
<div class="revue-flow__pane" onClick={onClick} onContextmenu={onContextMenu} onWheel={onWheel} />
|
|
||||||
</ZoomPane>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
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',
|
|
||||||
components: { NodeRenderer, EdgeRenderer, FlowRenderer },
|
|
||||||
props: {
|
|
||||||
nodeTypes: {
|
|
||||||
type: Object as PropType<GraphViewProps['nodeTypes']>,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
edgeTypes: {
|
|
||||||
type: Object as PropType<GraphViewProps['edgeTypes']>,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
connectionMode: {
|
|
||||||
type: String as PropType<GraphViewProps['connectionMode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
connectionLineType: {
|
|
||||||
type: String as PropType<GraphViewProps['connectionLineType']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
connectionLineStyle: {
|
|
||||||
type: Object as PropType<GraphViewProps['connectionLineStyle']>,
|
|
||||||
required: false,
|
|
||||||
default: () => {}
|
|
||||||
},
|
|
||||||
connectionLineComponent: {
|
|
||||||
type: Object as PropType<GraphViewProps['connectionLineComponent']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
selectionKeyCode: {
|
|
||||||
type: [Number, String] as PropType<GraphViewProps['selectionKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
multiSelectionKeyCode: {
|
|
||||||
type: [Number, String] as PropType<GraphViewProps['multiSelectionKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
zoomActivationKeyCode: {
|
|
||||||
type: [Number, String] as PropType<GraphViewProps['zoomActivationKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
deleteKeyCode: {
|
|
||||||
type: [Number, String] as PropType<GraphViewProps['deleteKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
snapToGrid: {
|
|
||||||
type: Boolean as PropType<GraphViewProps['snapToGrid']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
snapGrid: {
|
|
||||||
type: Array as unknown as PropType<GraphViewProps['snapGrid']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
onlyRenderVisibleElements: {
|
|
||||||
type: Boolean as PropType<GraphViewProps['onlyRenderVisibleElements']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
nodesDraggable: {
|
|
||||||
type: Boolean as PropType<GraphViewProps['nodesDraggable']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
nodesConnectable: {
|
|
||||||
type: Boolean as PropType<GraphViewProps['nodesConnectable']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
elementsSelectable: {
|
|
||||||
type: Boolean as PropType<GraphViewProps['elementsSelectable']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
selectNodesOnDrag: {
|
|
||||||
type: Boolean as PropType<GraphViewProps['selectNodesOnDrag']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
minZoom: {
|
|
||||||
type: Number as PropType<GraphViewProps['minZoom']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
maxZoom: {
|
|
||||||
type: Number as PropType<GraphViewProps['maxZoom']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
defaultZoom: {
|
|
||||||
type: Number as PropType<GraphViewProps['defaultZoom']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
defaultPosition: {
|
|
||||||
type: Array as unknown as PropType<GraphViewProps['defaultPosition']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
translateExtent: {
|
|
||||||
type: Array as unknown as PropType<GraphViewProps['translateExtent']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
nodeExtent: {
|
|
||||||
type: Array as unknown as PropType<GraphViewProps['nodeExtent']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
arrowHeadColor: {
|
|
||||||
type: String as PropType<GraphViewProps['arrowHeadColor']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
markerEndId: {
|
|
||||||
type: String as PropType<GraphViewProps['markerEndId']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
zoomOnScroll: {
|
|
||||||
type: Boolean as PropType<GraphViewProps['zoomOnScroll']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
zoomOnPinch: {
|
|
||||||
type: Boolean as PropType<GraphViewProps['zoomOnPinch']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
panOnScroll: {
|
|
||||||
type: Boolean as PropType<GraphViewProps['panOnScroll']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
panOnScrollSpeed: {
|
|
||||||
type: Number as PropType<GraphViewProps['panOnScrollSpeed']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
panOnScrollMode: {
|
|
||||||
type: String as PropType<GraphViewProps['panOnScrollMode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
zoomOnDoubleClick: {
|
|
||||||
type: Boolean as PropType<GraphViewProps['zoomOnDoubleClick']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
paneMoveable: {
|
|
||||||
type: Boolean as PropType<GraphViewProps['paneMoveable']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
edgeUpdaterRadius: {
|
|
||||||
type: Number as PropType<GraphViewProps['edgeUpdaterRadius']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const store = inject<RevueFlowStore>('store')!
|
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
|
||||||
|
|
||||||
const { onReady } = useZoomPanHelper()
|
|
||||||
onReady(({ zoomIn, zoomOut, zoomTo, transform, fitView }) => {
|
|
||||||
hooks.load.trigger({
|
|
||||||
fitView: (params = { padding: 0.1 }) => fitView(params),
|
|
||||||
zoomIn,
|
|
||||||
zoomOut,
|
|
||||||
zoomTo,
|
|
||||||
setTransform: transform,
|
|
||||||
project: onLoadProject(store),
|
|
||||||
getElements: onLoadGetElements(store),
|
|
||||||
toObject: onLoadToObject(store)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const init = () => {
|
|
||||||
if (typeof props.snapToGrid !== 'undefined') {
|
|
||||||
store.snapToGrid = props.snapToGrid
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof props.snapGrid !== 'undefined') {
|
|
||||||
store.snapGrid = props.snapGrid
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof props.minZoom !== 'undefined') {
|
|
||||||
store.setMinZoom(props.minZoom)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof props.maxZoom !== 'undefined') {
|
|
||||||
store.setMaxZoom(props.maxZoom)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof props.translateExtent !== 'undefined') {
|
|
||||||
store.setTranslateExtent(props.translateExtent)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof props.nodeExtent !== 'undefined') {
|
|
||||||
store.setNodeExtent(props.nodeExtent)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof props.nodesConnectable !== 'undefined') {
|
|
||||||
store.nodesConnectable = !!props.nodesConnectable
|
|
||||||
}
|
|
||||||
if (typeof props.elementsSelectable !== 'undefined') {
|
|
||||||
store.elementsSelectable = !!props.elementsSelectable
|
|
||||||
}
|
|
||||||
if (typeof props.nodesDraggable !== 'undefined') {
|
|
||||||
store.nodesDraggable = !!props.nodesDraggable
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof props.connectionMode !== 'undefined') {
|
|
||||||
store.connectionMode = props.connectionMode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tryOnMounted(() => {
|
|
||||||
init()
|
|
||||||
})
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<FlowRenderer
|
|
||||||
deleteKeyCode={props.deleteKeyCode}
|
|
||||||
selectionKeyCode={props.selectionKeyCode}
|
|
||||||
multiSelectionKeyCode={props.multiSelectionKeyCode}
|
|
||||||
zoomActivationKeyCode={props.zoomActivationKeyCode}
|
|
||||||
elementsSelectable={props.elementsSelectable}
|
|
||||||
zoomOnScroll={props.zoomOnScroll}
|
|
||||||
zoomOnPinch={props.zoomOnPinch}
|
|
||||||
zoomOnDoubleClick={props.zoomOnDoubleClick}
|
|
||||||
panOnScroll={props.panOnScroll}
|
|
||||||
panOnScrollSpeed={props.panOnScrollSpeed}
|
|
||||||
panOnScrollMode={props.panOnScrollMode}
|
|
||||||
paneMoveable={props.paneMoveable}
|
|
||||||
defaultPosition={props.defaultPosition}
|
|
||||||
defaultZoom={props.defaultZoom}
|
|
||||||
translateExtent={props.translateExtent}
|
|
||||||
>
|
|
||||||
<NodeRenderer
|
|
||||||
nodeTypes={props.nodeTypes}
|
|
||||||
selectNodesOnDrag={props.selectNodesOnDrag}
|
|
||||||
snapToGrid={props.snapToGrid}
|
|
||||||
snapGrid={props.snapGrid}
|
|
||||||
onlyRenderVisibleElements={props.onlyRenderVisibleElements}
|
|
||||||
/>
|
|
||||||
<EdgeRenderer
|
|
||||||
edgeTypes={props.edgeTypes}
|
|
||||||
connectionLineType={props.connectionLineType}
|
|
||||||
connectionLineStyle={props.connectionLineStyle}
|
|
||||||
connectionLineComponent={props.connectionLineComponent}
|
|
||||||
connectionMode={props.connectionMode}
|
|
||||||
arrowHeadColor={props.arrowHeadColor}
|
|
||||||
markerEndId={props.markerEndId}
|
|
||||||
onlyRenderVisibleElements={props.onlyRenderVisibleElements}
|
|
||||||
edgeUpdaterRadius={props.edgeUpdaterRadius}
|
|
||||||
/>
|
|
||||||
</FlowRenderer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import { defineComponent, inject, PropType } from 'vue'
|
|
||||||
import Node from '../../components/Nodes/NodeDepr'
|
|
||||||
import { getNodesInside } from '../../utils/graph'
|
|
||||||
import { NodeType, RevueFlowStore } from '../../types'
|
|
||||||
|
|
||||||
interface NodeRendererProps {
|
|
||||||
nodeTypes: Record<string, NodeType>
|
|
||||||
selectNodesOnDrag: boolean
|
|
||||||
snapToGrid: boolean
|
|
||||||
snapGrid: [number, number]
|
|
||||||
onlyRenderVisibleElements: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'NodeRenderer',
|
|
||||||
components: { Node },
|
|
||||||
props: {
|
|
||||||
nodeTypes: {
|
|
||||||
type: Object as PropType<NodeRendererProps['nodeTypes']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
selectNodesOnDrag: {
|
|
||||||
type: Boolean as PropType<NodeRendererProps['selectNodesOnDrag']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
snapToGrid: {
|
|
||||||
type: Boolean as PropType<NodeRendererProps['snapToGrid']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
snapGrid: {
|
|
||||||
type: Array as unknown as PropType<NodeRendererProps['snapGrid']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
onlyRenderVisibleElements: {
|
|
||||||
type: Boolean as PropType<NodeRendererProps['onlyRenderVisibleElements']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const store = inject<RevueFlowStore>('store')!
|
|
||||||
|
|
||||||
const getNodes = () => {
|
|
||||||
return props.onlyRenderVisibleElements
|
|
||||||
? store.nodes &&
|
|
||||||
getNodesInside(
|
|
||||||
store.nodes,
|
|
||||||
{
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: store?.width,
|
|
||||||
height: store?.height,
|
|
||||||
},
|
|
||||||
store.transform,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
: store.nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<div
|
|
||||||
className="revue-flow__nodes"
|
|
||||||
style={{
|
|
||||||
transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{getNodes().map((node, i) => {
|
|
||||||
const nodeType = node.type || 'default'
|
|
||||||
if (props.nodeTypes) {
|
|
||||||
const type = props.nodeTypes[nodeType] || props.nodeTypes.default
|
|
||||||
if (!props.nodeTypes[nodeType]) {
|
|
||||||
console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Node
|
|
||||||
node={node}
|
|
||||||
snapGrid={props.snapGrid}
|
|
||||||
snapToGrid={props.snapToGrid}
|
|
||||||
selectNodesOnDrag={props.selectNodesOnDrag}
|
|
||||||
type={type}
|
|
||||||
key={node.id + i}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,283 +0,0 @@
|
|||||||
import { PropType } from 'vue'
|
|
||||||
import '../../style.css'
|
|
||||||
import '../../theme-default.css'
|
|
||||||
import GraphView from '../GraphView'
|
|
||||||
import DefaultNode from '../../components/Nodes/DefaultNodeDepr'
|
|
||||||
import InputNode from '../../components/Nodes/InputNodeDepr'
|
|
||||||
import OutputNode from '../../components/Nodes/OutputNodeDepr'
|
|
||||||
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',
|
|
||||||
components: { GraphView },
|
|
||||||
props: {
|
|
||||||
modelValue: {
|
|
||||||
type: Array as PropType<RevueFlowProps['modelValue']>,
|
|
||||||
required: false,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
nodeTypesId: {
|
|
||||||
type: String as PropType<RevueFlowProps['nodeTypesId']>,
|
|
||||||
required: false,
|
|
||||||
default: '1',
|
|
||||||
},
|
|
||||||
edgeTypesId: {
|
|
||||||
type: String as PropType<RevueFlowProps['edgeTypesId']>,
|
|
||||||
required: false,
|
|
||||||
default: '1',
|
|
||||||
},
|
|
||||||
nodeTypes: {
|
|
||||||
type: Object as PropType<RevueFlowProps['nodeTypes']>,
|
|
||||||
required: false,
|
|
||||||
default: () => defaultNodeTypes,
|
|
||||||
},
|
|
||||||
edgeTypes: {
|
|
||||||
type: Object as PropType<RevueFlowProps['edgeTypes']>,
|
|
||||||
required: false,
|
|
||||||
default: () => defaultEdgeTypes,
|
|
||||||
},
|
|
||||||
connectionMode: {
|
|
||||||
type: String as PropType<RevueFlowProps['connectionMode']>,
|
|
||||||
required: false,
|
|
||||||
default: ConnectionMode.Strict,
|
|
||||||
},
|
|
||||||
connectionLineType: {
|
|
||||||
type: String as PropType<RevueFlowProps['connectionLineType']>,
|
|
||||||
required: false,
|
|
||||||
default: ConnectionLineType.Bezier,
|
|
||||||
},
|
|
||||||
connectionLineStyle: {
|
|
||||||
type: Object as PropType<RevueFlowProps['connectionLineStyle']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
connectionLineComponent: {
|
|
||||||
type: Object as PropType<RevueFlowProps['connectionLineComponent']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
selectionKeyCode: {
|
|
||||||
type: [Number, String] as PropType<RevueFlowProps['selectionKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: 'Shift',
|
|
||||||
},
|
|
||||||
multiSelectionKeyCode: {
|
|
||||||
type: [Number, String] as PropType<RevueFlowProps['multiSelectionKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: 'Meta',
|
|
||||||
},
|
|
||||||
zoomActivationKeyCode: {
|
|
||||||
type: [Number, String] as PropType<RevueFlowProps['zoomActivationKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: 'Meta',
|
|
||||||
},
|
|
||||||
deleteKeyCode: {
|
|
||||||
type: [Number, String] as PropType<RevueFlowProps['deleteKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: 'Backspace',
|
|
||||||
},
|
|
||||||
snapToGrid: {
|
|
||||||
type: Boolean as PropType<RevueFlowProps['snapToGrid']>,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
snapGrid: {
|
|
||||||
type: Array as unknown as PropType<RevueFlowProps['snapGrid']>,
|
|
||||||
required: false,
|
|
||||||
default: () => [15, 15],
|
|
||||||
},
|
|
||||||
onlyRenderVisibleElements: {
|
|
||||||
type: Boolean as PropType<RevueFlowProps['onlyRenderVisibleElements']>,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
nodesDraggable: {
|
|
||||||
type: Boolean as PropType<RevueFlowProps['nodesDraggable']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
nodesConnectable: {
|
|
||||||
type: Boolean as PropType<RevueFlowProps['nodesConnectable']>,
|
|
||||||
required: false,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
elementsSelectable: {
|
|
||||||
type: Boolean as PropType<RevueFlowProps['elementsSelectable']>,
|
|
||||||
required: false,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
selectNodesOnDrag: {
|
|
||||||
type: Boolean as PropType<RevueFlowProps['selectNodesOnDrag']>,
|
|
||||||
required: false,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
minZoom: {
|
|
||||||
type: Number as PropType<RevueFlowProps['minZoom']>,
|
|
||||||
required: false,
|
|
||||||
default: 0.5,
|
|
||||||
},
|
|
||||||
maxZoom: {
|
|
||||||
type: Number as PropType<RevueFlowProps['maxZoom']>,
|
|
||||||
required: false,
|
|
||||||
default: 2,
|
|
||||||
},
|
|
||||||
defaultZoom: {
|
|
||||||
type: Number as PropType<RevueFlowProps['defaultZoom']>,
|
|
||||||
required: false,
|
|
||||||
default: 1,
|
|
||||||
},
|
|
||||||
defaultPosition: {
|
|
||||||
type: Array as unknown as PropType<RevueFlowProps['defaultPosition']>,
|
|
||||||
required: false,
|
|
||||||
default: () => [0, 0],
|
|
||||||
},
|
|
||||||
translateExtent: {
|
|
||||||
type: Array as unknown as PropType<RevueFlowProps['translateExtent']>,
|
|
||||||
required: false,
|
|
||||||
default: () => [
|
|
||||||
[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
|
|
||||||
[Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
nodeExtent: {
|
|
||||||
type: Array as unknown as PropType<RevueFlowProps['nodeExtent']>,
|
|
||||||
required: false,
|
|
||||||
default: () => [
|
|
||||||
[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
|
|
||||||
[Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
arrowHeadColor: {
|
|
||||||
type: String as PropType<RevueFlowProps['arrowHeadColor']>,
|
|
||||||
required: false,
|
|
||||||
default: '#b1b1b7',
|
|
||||||
},
|
|
||||||
markerEndId: {
|
|
||||||
type: String as PropType<RevueFlowProps['markerEndId']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
zoomOnScroll: {
|
|
||||||
type: Boolean as PropType<RevueFlowProps['zoomOnScroll']>,
|
|
||||||
required: false,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
zoomOnPinch: {
|
|
||||||
type: Boolean as PropType<RevueFlowProps['zoomOnPinch']>,
|
|
||||||
required: false,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
panOnScroll: {
|
|
||||||
type: Boolean as PropType<RevueFlowProps['panOnScroll']>,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
panOnScrollSpeed: {
|
|
||||||
type: Number as PropType<RevueFlowProps['panOnScrollSpeed']>,
|
|
||||||
required: false,
|
|
||||||
default: 0.5,
|
|
||||||
},
|
|
||||||
panOnScrollMode: {
|
|
||||||
type: String as PropType<RevueFlowProps['panOnScrollMode']>,
|
|
||||||
required: false,
|
|
||||||
default: PanOnScrollMode.Free,
|
|
||||||
},
|
|
||||||
zoomOnDoubleClick: {
|
|
||||||
type: Boolean as PropType<RevueFlowProps['zoomOnDoubleClick']>,
|
|
||||||
required: false,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
paneMoveable: {
|
|
||||||
type: Boolean as PropType<RevueFlowProps['paneMoveable']>,
|
|
||||||
required: false,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
edgeUpdaterRadius: {
|
|
||||||
type: Number as PropType<RevueFlowProps['edgeUpdaterRadius']>,
|
|
||||||
required: false,
|
|
||||||
default: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: Object.keys(useRevueFlow()),
|
|
||||||
setup(props, { emit, slots }) {
|
|
||||||
const store = configureStore(initialState)()
|
|
||||||
provide<RevueFlowStore>('store', store)
|
|
||||||
const hooks = useRevueFlow().bind(emit)
|
|
||||||
provide<RevueFlowHooks>('hooks', hooks)
|
|
||||||
const elements = useVModel(props, 'modelValue', emit)
|
|
||||||
|
|
||||||
store.setElements(elements.value)
|
|
||||||
watch(elements.value, () => {
|
|
||||||
store.setElements(elements.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
onUpdated(() => {
|
|
||||||
store.setElements(elements.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
store.$reset()
|
|
||||||
store.setElements([])
|
|
||||||
})
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<div className="revue-flow">
|
|
||||||
<GraphView
|
|
||||||
nodeTypes={createNodeTypes({ ...defaultNodeTypes, ...props.nodeTypes })}
|
|
||||||
edgeTypes={createEdgeTypes({ ...defaultEdgeTypes, ...props.edgeTypes })}
|
|
||||||
connectionMode={props.connectionMode}
|
|
||||||
connectionLineType={props.connectionLineType}
|
|
||||||
connectionLineStyle={props.connectionLineStyle}
|
|
||||||
connectionLineComponent={props.connectionLineComponent}
|
|
||||||
selectionKeyCode={props.selectionKeyCode}
|
|
||||||
deleteKeyCode={props.deleteKeyCode}
|
|
||||||
multiSelectionKeyCode={props.multiSelectionKeyCode}
|
|
||||||
zoomActivationKeyCode={props.zoomActivationKeyCode}
|
|
||||||
snapToGrid={props.snapToGrid}
|
|
||||||
snapGrid={props.snapGrid}
|
|
||||||
onlyRenderVisibleElements={props.onlyRenderVisibleElements}
|
|
||||||
nodesDraggable={props.nodesDraggable}
|
|
||||||
nodesConnectable={props.nodesConnectable}
|
|
||||||
elementsSelectable={props.elementsSelectable}
|
|
||||||
selectNodesOnDrag={props.selectNodesOnDrag}
|
|
||||||
minZoom={props.minZoom}
|
|
||||||
maxZoom={props.maxZoom}
|
|
||||||
defaultZoom={props.defaultZoom}
|
|
||||||
defaultPosition={props.defaultPosition}
|
|
||||||
translateExtent={props.translateExtent}
|
|
||||||
nodeExtent={props.nodeExtent}
|
|
||||||
arrowHeadColor={props.arrowHeadColor}
|
|
||||||
markerEndId={props.markerEndId}
|
|
||||||
zoomOnScroll={props.zoomOnScroll}
|
|
||||||
zoomOnPinch={props.zoomOnPinch}
|
|
||||||
zoomOnDoubleClick={props.zoomOnDoubleClick}
|
|
||||||
panOnScroll={props.panOnScroll}
|
|
||||||
panOnScrollSpeed={props.panOnScrollSpeed}
|
|
||||||
panOnScrollMode={props.panOnScrollMode}
|
|
||||||
paneMoveable={props.paneMoveable}
|
|
||||||
edgeUpdaterRadius={props.edgeUpdaterRadius}
|
|
||||||
/>
|
|
||||||
{slots.default ? slots.default() : ''}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,277 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'ZoomPane',
|
|
||||||
props: {
|
|
||||||
zoomOnScroll: {
|
|
||||||
type: Boolean as PropType<ZoomPaneProps['zoomOnScroll']>,
|
|
||||||
required: false,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
zoomOnPinch: {
|
|
||||||
type: Boolean as PropType<ZoomPaneProps['zoomOnPinch']>,
|
|
||||||
required: false,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
panOnScroll: {
|
|
||||||
type: Boolean as PropType<ZoomPaneProps['panOnScroll']>,
|
|
||||||
required: false,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
panOnScrollSpeed: {
|
|
||||||
type: Number as PropType<ZoomPaneProps['panOnScrollSpeed']>,
|
|
||||||
required: false,
|
|
||||||
default: 0.5
|
|
||||||
},
|
|
||||||
panOnScrollMode: {
|
|
||||||
type: String as PropType<ZoomPaneProps['panOnScrollMode']>,
|
|
||||||
required: false,
|
|
||||||
default: PanOnScrollMode.Free
|
|
||||||
},
|
|
||||||
zoomOnDoubleClick: {
|
|
||||||
type: Boolean as PropType<ZoomPaneProps['zoomOnDoubleClick']>,
|
|
||||||
required: false,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
selectionKeyCode: {
|
|
||||||
type: [Number, String] as PropType<ZoomPaneProps['selectionKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
elementsSelectable: {
|
|
||||||
type: Boolean as PropType<ZoomPaneProps['elementsSelectable']>,
|
|
||||||
required: false,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
paneMoveable: {
|
|
||||||
type: Boolean as PropType<ZoomPaneProps['paneMoveable']>,
|
|
||||||
required: false,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
defaultPosition: {
|
|
||||||
type: Array as unknown as PropType<ZoomPaneProps['defaultPosition']>,
|
|
||||||
required: false,
|
|
||||||
default: () => [0, 0]
|
|
||||||
},
|
|
||||||
defaultZoom: {
|
|
||||||
type: Number as PropType<ZoomPaneProps['defaultZoom']>,
|
|
||||||
required: false,
|
|
||||||
default: 1
|
|
||||||
},
|
|
||||||
translateExtent: {
|
|
||||||
type: Array as unknown as PropType<ZoomPaneProps['translateExtent']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
zoomActivationKeyCode: {
|
|
||||||
type: [Number, String] as PropType<ZoomPaneProps['zoomActivationKeyCode']>,
|
|
||||||
required: false,
|
|
||||||
default: undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup(props, { slots }) {
|
|
||||||
const store = inject<RevueFlowStore>('store')!
|
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
|
||||||
const zoomPane = templateRef<HTMLDivElement>('zoom-pane', null)
|
|
||||||
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 })
|
|
||||||
|
|
||||||
const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean =>
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
store.initD3Zoom({
|
|
||||||
d3Zoom: d3ZoomInstance,
|
|
||||||
d3Selection: selection,
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const applyZoomHandlers = (d3Zoom: ZoomBehavior<Element, unknown>) => {
|
|
||||||
d3Zoom.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
|
||||||
if (viewChanged(prevTransform.value, event.transform)) {
|
|
||||||
const flowTransform = eventToFlowTransform(event.transform)
|
|
||||||
prevTransform.value = flowTransform
|
|
||||||
|
|
||||||
hooks.moveStart.trigger(flowTransform)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
d3Zoom.on('end', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
|
||||||
if (viewChanged(prevTransform.value, event.transform)) {
|
|
||||||
const flowTransform = eventToFlowTransform(event.transform)
|
|
||||||
prevTransform.value = flowTransform
|
|
||||||
|
|
||||||
hooks.moveEnd.trigger(flowTransform)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
useKeyPress(props.selectionKeyCode, (keyPress) => {
|
|
||||||
if (keyPress) {
|
|
||||||
d3Zoom.on('zoom', null)
|
|
||||||
} else {
|
|
||||||
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
|
||||||
store.transform = [event.transform.x, event.transform.y, event.transform.k]
|
|
||||||
hooks.move.trigger(eventToFlowTransform(event.transform))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
useKeyPress(props.zoomActivationKeyCode, (keyPress) => {
|
|
||||||
if (props.panOnScroll && keyPress) {
|
|
||||||
store.d3Selection
|
|
||||||
?.on('wheel', (event: WheelEvent) => {
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopImmediatePropagation()
|
|
||||||
|
|
||||||
const currentZoom = store.d3Selection?.property('__zoom').k || 1
|
|
||||||
|
|
||||||
if (event.ctrlKey && props.zoomOnPinch) {
|
|
||||||
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)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
if (store.d3Selection && props.panOnScrollSpeed)
|
|
||||||
store.d3Zoom?.translateBy(
|
|
||||||
store.d3Selection,
|
|
||||||
-(deltaX / currentZoom) * props.panOnScrollSpeed,
|
|
||||||
-(deltaY / currentZoom) * props.panOnScrollSpeed
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.on('wheel.zoom', null)
|
|
||||||
} else if (typeof store.d3ZoomHandler !== 'undefined') {
|
|
||||||
store.d3Selection?.on('wheel', null).on('wheel.zoom', store.d3ZoomHandler)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
watchPostEffect(() => store.d3Zoom && applyZoomFilter(store.d3Zoom))
|
|
||||||
|
|
||||||
const applyZoomFilter = (d3Zoom: ZoomBehavior<Element, unknown>) => {
|
|
||||||
const keyPress = useKeyPress(props.selectionKeyCode)
|
|
||||||
d3Zoom.filter((event: MouseEvent) => {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// during a selection we prevent all other interactions
|
|
||||||
if (keyPress.value) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// if zoom on double click is disabled, we prevent the double click event
|
|
||||||
if (!props.zoomOnDoubleClick && event.type === 'dblclick') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((event.target as Element).closest('.nowheel') && event.type === 'wheel') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// when the target element is a node, we still allow zooming
|
|
||||||
if (
|
|
||||||
((event.target as Element).closest('.revue-flow__node') || (event.target as Element).closest('.revue-flow__edge')) &&
|
|
||||||
event.type !== 'wheel'
|
|
||||||
) {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!props.zoomOnPinch && event.ctrlKey && event.type === 'wheel') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// when there is no scroll handling enabled, we prevent all wheel events
|
|
||||||
if (!zoomScroll && !props.panOnScroll && !pinchZoom && event.type === 'wheel') {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// default filter for d3-zoom
|
|
||||||
return (!event.ctrlKey || event.type === 'wheel') && !event.button
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init()
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<div ref="zoom-pane" class="revue-flow__renderer revue-flow__zoompane">
|
|
||||||
{slots.default ? slots.default() : ''}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
export { default as Flow } from './Flow.vue'
|
export { default as Flow } from './Flow/Flow.vue'
|
||||||
export { default as EdgeRenderer } from './EdgeRenderer.vue'
|
export { default as EdgeRenderer } from './EdgeRenderer/EdgeRenderer.vue'
|
||||||
export { default as NodeRenderer } from './NodeRenderer.vue'
|
export { default as Marker } from './EdgeRenderer/Marker.vue'
|
||||||
export { default as Marker } from './Marker.vue'
|
export { default as MarkerDefinitions } from './EdgeRenderer/MarkerDefinitions.vue'
|
||||||
export { default as MarkerDefinitions } from './MarkerDefinitions.vue'
|
export { default as NodeRenderer } from './NodeRenderer/NodeRenderer.vue'
|
||||||
export { default as ZoomPane } from './ZoomPane.vue'
|
export { default as ZoomPane } from './ZoomPane/ZoomPane.vue'
|
||||||
export { default as SelectionPane } from './SelectionPane.vue'
|
export { default as SelectionPane } from './SelectionPane/SelectionPane.vue'
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
export { default } from './container/Flow.vue'
|
export { default } from './container/Flow/Flow.vue'
|
||||||
export { default as Handle } from './components/Handle/Handle.vue'
|
export { default as Handle } from './components/Handle/Handle.vue'
|
||||||
export { default as EdgeText } from './components/Edges/EdgeText.vue'
|
export { default as EdgeText } from './components/Edges/EdgeText.vue'
|
||||||
export { getBezierPath, getSmoothStepPath, getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils'
|
export { getBezierPath, getSmoothStepPath, getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils'
|
||||||
|
|||||||
+13
-8
@@ -1,19 +1,21 @@
|
|||||||
import { defineConfig } from 'vite'
|
|
||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import { defineConfig } from 'vite'
|
||||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
import svgLoader from 'vite-svg-loader'
|
import svgLoader from 'vite-svg-loader'
|
||||||
import AutoImport from 'unplugin-auto-import/vite'
|
import AutoImport from 'unplugin-auto-import/vite'
|
||||||
|
import Components from 'unplugin-vue-components/vite'
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'~/': `${resolve(__dirname, 'src')}/`
|
'~/': `${resolve(__dirname, 'src')}/`,
|
||||||
}
|
'@/': `${resolve(__dirname)}/`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir: 'build'
|
outDir: 'build',
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
@@ -21,7 +23,10 @@ export default defineConfig({
|
|||||||
svgLoader(), // https://github.com/antfu/unplugin-auto-import
|
svgLoader(), // https://github.com/antfu/unplugin-auto-import
|
||||||
AutoImport({
|
AutoImport({
|
||||||
imports: ['vue', '@vueuse/core'],
|
imports: ['vue', '@vueuse/core'],
|
||||||
dts: 'src/auto-imports.d.ts'
|
dts: 'src/auto-imports.d.ts',
|
||||||
})
|
}),
|
||||||
]
|
Components({
|
||||||
|
dts: 'src/components.d.ts',
|
||||||
|
}),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user