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,6 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
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 ConnectionLine from '~/components/ConnectionLine/ConnectionLine.vue'
|
||||
import {
|
||||
@@ -12,7 +13,6 @@ import {
|
||||
RevueFlowStore,
|
||||
Transform,
|
||||
} from '~/types'
|
||||
import MarkerDefinitions from '~/container/MarkerDefinitions.vue'
|
||||
|
||||
interface EdgeRendererProps {
|
||||
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 '~/theme-default.css'
|
||||
import { CSSProperties, onBeforeUnmount } from 'vue'
|
||||
import ZoomPane from './ZoomPane.vue'
|
||||
import SelectionPane from './SelectionPane.vue'
|
||||
import NodeRenderer from '~/container/NodeRenderer.vue'
|
||||
import EdgeRenderer from '~/container/EdgeRenderer.vue'
|
||||
import ZoomPane from '~/container/ZoomPane/ZoomPane.vue'
|
||||
import SelectionPane from '~/container/SelectionPane/SelectionPane.vue'
|
||||
import NodeRenderer from '~/container/NodeRenderer/NodeRenderer.vue'
|
||||
import EdgeRenderer from '~/container/EdgeRenderer/EdgeRenderer.vue'
|
||||
import {
|
||||
ConnectionLineType,
|
||||
ConnectionMode,
|
||||
@@ -22,10 +22,10 @@ import {
|
||||
import { RevueFlowHooks, useRevueFlow } from '~/hooks/RevueFlowHooks'
|
||||
import configureStore from '~/store/configure-store'
|
||||
import { initialState } from '~/store'
|
||||
import { createNodeTypes } from '~/container/NodeRenderer/utils'
|
||||
import { DefaultNode, InputNode, OutputNode } from '~/components/Nodes'
|
||||
import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge } from '~/components/Edges'
|
||||
import { createEdgeTypes } from '~/container/EdgeRenderer/utils'
|
||||
import { createNodeTypes } from '~/container/NodeRenderer/utils'
|
||||
|
||||
interface RevueFlowProps {
|
||||
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 EdgeRenderer } from './EdgeRenderer.vue'
|
||||
export { default as NodeRenderer } from './NodeRenderer.vue'
|
||||
export { default as Marker } from './Marker.vue'
|
||||
export { default as MarkerDefinitions } from './MarkerDefinitions.vue'
|
||||
export { default as ZoomPane } from './ZoomPane.vue'
|
||||
export { default as SelectionPane } from './SelectionPane.vue'
|
||||
export { default as Flow } from './Flow/Flow.vue'
|
||||
export { default as EdgeRenderer } from './EdgeRenderer/EdgeRenderer.vue'
|
||||
export { default as Marker } from './EdgeRenderer/Marker.vue'
|
||||
export { default as MarkerDefinitions } from './EdgeRenderer/MarkerDefinitions.vue'
|
||||
export { default as NodeRenderer } from './NodeRenderer/NodeRenderer.vue'
|
||||
export { default as ZoomPane } from './ZoomPane/ZoomPane.vue'
|
||||
export { default as SelectionPane } from './SelectionPane/SelectionPane.vue'
|
||||
|
||||
Reference in New Issue
Block a user