refactor: back to jsx 🥇
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { FunctionalComponent } from 'vue';
|
||||
|
||||
interface MarkerProps {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export default ((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>
|
||||
);
|
||||
}) as FunctionalComponent<MarkerProps>;
|
||||
@@ -1,30 +0,0 @@
|
||||
<template>
|
||||
<marker
|
||||
:id="id"
|
||||
class="revue-flow__arrowhead"
|
||||
markerWidth="12.5"
|
||||
markerHeight="12.5"
|
||||
viewBox="-10 -10 20 20"
|
||||
orient="auto"
|
||||
refX="0"
|
||||
refY="0"
|
||||
>
|
||||
<slot></slot>
|
||||
</marker>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
|
||||
interface MarkerProps {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
id: {
|
||||
type: String as PropType<MarkerProps['id']>,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,42 @@
|
||||
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: true
|
||||
}
|
||||
},
|
||||
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,45 +0,0 @@
|
||||
<template>
|
||||
<defs>
|
||||
<Marker id="revue-flow__arrowclosed">
|
||||
<polyline
|
||||
:stroke="color"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
:fill="color"
|
||||
points="-5,-4 0,0 -5,4 -5,-4"
|
||||
/>
|
||||
</Marker>
|
||||
<Marker id="revue-flow__arrow">
|
||||
<polyline
|
||||
:stroke="color"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
fill="none"
|
||||
points="-5,-4 0,0 -5,4"
|
||||
/>
|
||||
</Marker>
|
||||
</defs>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import Marker from './Marker.vue';
|
||||
|
||||
interface MarkerDefinitionsProps {
|
||||
color: string;
|
||||
}
|
||||
|
||||
const MarkerDefinitions = defineComponent({
|
||||
name: 'MarkerDefinitions',
|
||||
components: { Marker },
|
||||
props: {
|
||||
color: {
|
||||
type: String as PropType<MarkerDefinitionsProps['color']>,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default MarkerDefinitions;
|
||||
</script>
|
||||
+32
-46
@@ -1,30 +1,8 @@
|
||||
<template>
|
||||
<svg :width="width" :height="height" class="revue-flow__edges">
|
||||
<MarkerDefinitions :color="arrowHeadColor" />
|
||||
<g :transform="transformStyle">
|
||||
<template v-for="edge of edges" :key="edge.id">
|
||||
<Edge
|
||||
:edge="edge"
|
||||
:marker-end-id="markerEndId"
|
||||
:onlyRenderVisibleElements="onlyRenderVisibleElements"
|
||||
:type="edgeTypes[edge.type]"
|
||||
/>
|
||||
<ConnectionLine
|
||||
v-if="renderConnectionLine"
|
||||
:connectionLineStyle="connectionLineStyle"
|
||||
:connectionLineType="connectionLineType"
|
||||
:CustomConnectionLineComponent="connectionLineComponent"
|
||||
/>
|
||||
</template>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, CSSProperties, defineComponent, inject, PropType } from 'vue';
|
||||
import Edge from '../../components/Edges/Edge.vue';
|
||||
import ConnectionLine from '../../components/ConnectionLine/ConnectionLine.vue';
|
||||
import { ConnectionLineType, ConnectionLineComponent, ConnectionMode, RevueFlowStore } from '../../types';
|
||||
import MarkerDefinitions from './MarkerDefinitions.vue';
|
||||
import ConnectionLine from '../../components/ConnectionLine';
|
||||
import MarkerDefinitions from './MarkerDefinitions';
|
||||
import Edge from '../../components/Edges/Edge';
|
||||
|
||||
interface EdgeRendererProps {
|
||||
edgeTypes: any;
|
||||
@@ -38,7 +16,7 @@ interface EdgeRendererProps {
|
||||
edgeUpdaterRadius?: number;
|
||||
}
|
||||
|
||||
const EdgeRenderer = defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'EdgeRenderer',
|
||||
components: {
|
||||
Edge,
|
||||
@@ -48,8 +26,7 @@ const EdgeRenderer = defineComponent({
|
||||
props: {
|
||||
edgeTypes: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: undefined
|
||||
required: true
|
||||
},
|
||||
connectionLineType: {
|
||||
type: String as PropType<EdgeRendererProps['connectionLineType']>,
|
||||
@@ -92,27 +69,36 @@ const EdgeRenderer = defineComponent({
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const edges = computed(() => store.edges);
|
||||
const transform = computed(() => store.transform);
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store');
|
||||
const transform = computed(() => store?.transform);
|
||||
const transformStyle = computed(() => {
|
||||
return `translate(${transform.value?.[0]},${transform.value?.[1]}) scale(${transform.value?.[2]})`;
|
||||
});
|
||||
const renderConnectionLine = computed(() => store.connectionNodeId && store.connectionHandleType);
|
||||
const width = computed(() => store.width);
|
||||
const height = computed(() => store.height);
|
||||
const renderConnectionLine = computed(() => store?.connectionNodeId && store?.connectionHandleType);
|
||||
|
||||
return {
|
||||
transform,
|
||||
transformStyle,
|
||||
renderConnectionLine,
|
||||
edges,
|
||||
width,
|
||||
height
|
||||
};
|
||||
return () => (
|
||||
<svg width={store?.width} height={store?.height} class="revue-flow__edges">
|
||||
<MarkerDefinitions color={props.arrowHeadColor as string} />
|
||||
<g transform={transformStyle.value}>
|
||||
{store?.edges.map((edge) => (
|
||||
<Edge
|
||||
key={edge.id}
|
||||
edge={edge}
|
||||
type={props.edgeTypes[edge.type || 'default']}
|
||||
onlyRenderVisibleElements={props.onlyRenderVisibleElements}
|
||||
markerEndId={props.markerEndId}
|
||||
/>
|
||||
))}
|
||||
{renderConnectionLine.value && (
|
||||
<ConnectionLine
|
||||
connectionLineStyle={props.connectionLineStyle}
|
||||
connectionLineType={props.connectionLineType}
|
||||
customConnectionLine={props.connectionLineComponent}
|
||||
/>
|
||||
)}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default EdgeRenderer;
|
||||
</script>
|
||||
+24
-35
@@ -1,26 +1,3 @@
|
||||
<template>
|
||||
<ZoomPane
|
||||
:selectionKeyPressed="keyPressed"
|
||||
:elementsSelectable="elementsSelectable"
|
||||
:zoomOnScroll="zoomOnScroll"
|
||||
:zoomOnPinch="zoomOnPinch"
|
||||
:panOnScroll="panOnScroll"
|
||||
:panOnScrollSpeed="panOnScrollSpeed"
|
||||
:panOnScrollMode="panOnScrollMode"
|
||||
:zoomOnDoubleClick="zoomOnDoubleClick"
|
||||
:paneMoveable="paneMoveable"
|
||||
:defaultPosition="defaultPosition"
|
||||
:defaultZoom="defaultZoom"
|
||||
:translateExtent="translateExtent"
|
||||
:zoomActivationKeyCode="zoomActivationKeyCode"
|
||||
>
|
||||
<slot></slot>
|
||||
<UserSelection v-if="keyPressed" />
|
||||
<NodesSelection v-if="selectionActive" />
|
||||
<div class="revue-flow__pane" @click="onClick" @onContextmenu="onContextMenu" @wheel="onWheel" />
|
||||
</ZoomPane>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, inject, PropType } from 'vue';
|
||||
import ZoomPane from '../ZoomPane';
|
||||
import UserSelection from '../../components/UserSelection';
|
||||
@@ -43,7 +20,7 @@ type FlowRendererProps = Omit<
|
||||
| 'selectNodesOnDrag'
|
||||
>;
|
||||
|
||||
const FlowRenderer = defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'FlowRenderer',
|
||||
components: { UserSelection, ZoomPane, NodesSelection },
|
||||
props: {
|
||||
@@ -123,7 +100,7 @@ const FlowRenderer = defineComponent({
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
setup(props, { slots }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const keyPressed = useKeyPress(props.selectionKeyCode);
|
||||
@@ -147,15 +124,27 @@ const FlowRenderer = defineComponent({
|
||||
|
||||
const selectionActive = computed(() => store.nodesSelectionActive);
|
||||
|
||||
return {
|
||||
onClick,
|
||||
onContextMenu,
|
||||
onWheel,
|
||||
keyPressed,
|
||||
selectionActive
|
||||
};
|
||||
return () => (
|
||||
<ZoomPane
|
||||
selectionKeyPressed={keyPressed.value}
|
||||
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}
|
||||
>
|
||||
{slots.default ? slots.default() : ''}
|
||||
{keyPressed.value ? <UserSelection /> : ''}
|
||||
{selectionActive.value ? <NodesSelection /> : ''}
|
||||
<div class="revue-flow__pane" onClick={onClick} onContextmenu={onContextMenu} onWheel={onWheel} />
|
||||
</ZoomPane>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default FlowRenderer;
|
||||
</script>
|
||||
@@ -1,52 +1,52 @@
|
||||
<template>
|
||||
<FlowRenderer
|
||||
:deleteKeyCode="deleteKeyCode"
|
||||
:selectionKeyCode="selectionKeyCode"
|
||||
:multiSelectionKeyCode="multiSelectionKeyCode"
|
||||
:zoomActivationKeyCode="zoomActivationKeyCode"
|
||||
:elementsSelectable="elementsSelectable"
|
||||
:zoomOnScroll="zoomOnScroll"
|
||||
:zoomOnPinch="zoomOnPinch"
|
||||
:zoomOnDoubleClick="zoomOnDoubleClick"
|
||||
:panOnScroll="panOnScroll"
|
||||
:panOnScrollSpeed="panOnScrollSpeed"
|
||||
:panOnScrollMode="panOnScrollMode"
|
||||
:paneMoveable="paneMoveable"
|
||||
:defaultPosition="defaultPosition"
|
||||
:defaultZoom="defaultZoom"
|
||||
:translateExtent="translateExtent"
|
||||
:delete-key-code="deleteKeyCode"
|
||||
:selection-key-code="selectionKeyCode"
|
||||
:multi-selection-key-code="multiSelectionKeyCode"
|
||||
:zoom-activation-key-code="zoomActivationKeyCode"
|
||||
:elements-selectable="elementsSelectable"
|
||||
:zoom-on-scroll="zoomOnScroll"
|
||||
:zoom-on-pinch="zoomOnPinch"
|
||||
:zoom-on-double-click="zoomOnDoubleClick"
|
||||
:pan-on-scroll="panOnScroll"
|
||||
:pan-on-scroll-speed="panOnScrollSpeed"
|
||||
:pan-on-scroll-mode="panOnScrollMode"
|
||||
:pane-moveable="paneMoveable"
|
||||
:default-position="defaultPosition"
|
||||
:default-zoom="defaultZoom"
|
||||
:translate-extent="translateExtent"
|
||||
>
|
||||
<NodeRenderer
|
||||
:nodeTypes="nodeTypes"
|
||||
:selectNodesOnDrag="selectNodesOnDrag"
|
||||
:snapToGrid="snapToGrid"
|
||||
:snapGrid="snapGrid"
|
||||
:onlyRenderVisibleElements="onlyRenderVisibleElements"
|
||||
:node-types="nodeTypes"
|
||||
:select-nodes-on-drag="selectNodesOnDrag"
|
||||
:snap-to-grid="snapToGrid"
|
||||
:snap-grid="snapGrid"
|
||||
:only-render-visible-elements="onlyRenderVisibleElements"
|
||||
/>
|
||||
<EdgeRenderer
|
||||
:edgeTypes="edgeTypes"
|
||||
:connectionLineType="connectionLineType"
|
||||
:connectionLineStyle="connectionLineStyle"
|
||||
:connectionLineComponent="connectionLineComponent"
|
||||
:connectionMode="connectionMode"
|
||||
:arrowHeadColor="arrowHeadColor"
|
||||
:markerEndId="markerEndId"
|
||||
:onlyRenderVisibleElements="onlyRenderVisibleElements"
|
||||
:edgeUpdaterRadius="edgeUpdaterRadius"
|
||||
:edge-types="edgeTypes"
|
||||
:connection-line-type="connectionLineType"
|
||||
:connection-line-style="connectionLineStyle"
|
||||
:connection-line-component="connectionLineComponent"
|
||||
:connection-mode="connectionMode"
|
||||
:arrow-head-color="arrowHeadColor"
|
||||
:marker-end-id="markerEndId"
|
||||
:only-render-visible-elements="onlyRenderVisibleElements"
|
||||
:edge-updater-radius="edgeUpdaterRadius"
|
||||
/>
|
||||
</FlowRenderer>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, inject, PropType, CSSProperties, watchEffect } from 'vue';
|
||||
import FlowRenderer from '../FlowRenderer/FlowRenderer.vue';
|
||||
import NodeRenderer from '../NodeRenderer/NodeRenderer.vue';
|
||||
import EdgeRenderer from '../EdgeRenderer/EdgeRenderer.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';
|
||||
|
||||
const GraphView = defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'GraphView',
|
||||
components: { NodeRenderer, EdgeRenderer, FlowRenderer },
|
||||
props: {
|
||||
@@ -220,20 +220,18 @@ const GraphView = defineComponent({
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
|
||||
const { onReady } = useZoomPanHelper(store);
|
||||
onReady(({ zoomIn, zoomOut, zoomTo, transform, fitView, initialized }) => {
|
||||
if (initialized) {
|
||||
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 { 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 = () => {
|
||||
@@ -281,6 +279,4 @@ const GraphView = defineComponent({
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default GraphView;
|
||||
</script>
|
||||
|
||||
+26
-24
@@ -1,21 +1,7 @@
|
||||
<template>
|
||||
<div class="revue-flow__nodes" :style="transformStyle">
|
||||
<template v-for="(node, i) of visibleNodes" :key="`node-${i}`">
|
||||
<Node
|
||||
:node="node"
|
||||
:snap-grid="snapGrid"
|
||||
:snap-to-grid="snapToGrid"
|
||||
:select-nodes-on-drag="selectNodesOnDrag"
|
||||
:type="nodeTypes[node.type]"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { getNodesInside } from '../../utils/graph';
|
||||
import { NodeTypesType, RevueFlowStore } from '../../types';
|
||||
import { computed, defineComponent, inject, PropType } from 'vue';
|
||||
import Node from '../../components/Nodes/Node.vue';
|
||||
import { getNodesInside } from '../../utils/graph';
|
||||
import { NodeTypesType, RevueFlowStore } from '../../types';
|
||||
|
||||
interface NodeRendererProps {
|
||||
nodeTypes: NodeTypesType;
|
||||
@@ -25,7 +11,7 @@ interface NodeRendererProps {
|
||||
onlyRenderVisibleElements: boolean;
|
||||
}
|
||||
|
||||
const NodeRenderer = defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'NodeRenderer',
|
||||
components: { Node },
|
||||
props: {
|
||||
@@ -79,12 +65,28 @@ const NodeRenderer = defineComponent({
|
||||
transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`
|
||||
}));
|
||||
|
||||
return {
|
||||
transformStyle,
|
||||
visibleNodes
|
||||
};
|
||||
return () => (
|
||||
<div class="revue-flow__nodes" style={transformStyle.value}>
|
||||
{visibleNodes.value?.map((node) => {
|
||||
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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default NodeRenderer;
|
||||
</script>
|
||||
@@ -1,39 +1,39 @@
|
||||
<template>
|
||||
<div class="revue-flow">
|
||||
<GraphView
|
||||
:nodeTypes="nodeTypesParsed"
|
||||
:edgeTypes="edgeTypesParsed"
|
||||
:connectionMode="connectionMode"
|
||||
:connectionLineType="connectionLineType"
|
||||
:connectionLineStyle="connectionLineStyle"
|
||||
:connectionLineComponent="connectionLineComponent"
|
||||
:selectionKeyCode="selectionKeyCode"
|
||||
:deleteKeyCode="deleteKeyCode"
|
||||
:multiSelectionKeyCode="multiSelectionKeyCode"
|
||||
:zoomActivationKeyCode="zoomActivationKeyCode"
|
||||
:snapToGrid="snapToGrid"
|
||||
:snapGrid="snapGrid"
|
||||
:onlyRenderVisibleElements="onlyRenderVisibleElements"
|
||||
:nodesDraggable="nodesDraggable"
|
||||
:nodesConnectable="nodesConnectable"
|
||||
:elementsSelectable="elementsSelectable"
|
||||
:selectNodesOnDrag="selectNodesOnDrag"
|
||||
:minZoom="minZoom"
|
||||
:maxZoom="maxZoom"
|
||||
:defaultZoom="defaultZoom"
|
||||
:defaultPosition="defaultPosition"
|
||||
:translateExtent="translateExtent"
|
||||
:nodeExtent="nodeExtent"
|
||||
:arrowHeadColor="arrowHeadColor"
|
||||
:markerEndId="markerEndId"
|
||||
:zoomOnScroll="zoomOnScroll"
|
||||
:zoomOnPinch="zoomOnPinch"
|
||||
:zoomOnDoubleClick="zoomOnDoubleClick"
|
||||
:panOnScroll="panOnScroll"
|
||||
:panOnScrollSpeed="panOnScrollSpeed"
|
||||
:panOnScrollMode="panOnScrollMode"
|
||||
:paneMoveable="paneMoveable"
|
||||
:edgeUpdaterRadius="edgeUpdaterRadius"
|
||||
:node-types="nodeTypesParsed"
|
||||
:edge-types="edgeTypesParsed"
|
||||
:connection-mode="connectionMode"
|
||||
:connection-line-type="connectionLineType"
|
||||
:connection-line-style="connectionLineStyle"
|
||||
:connection-line-component="connectionLineComponent"
|
||||
:selection-key-code="selectionKeyCode"
|
||||
:delete-key-code="deleteKeyCode"
|
||||
:multi-selection-key-code="multiSelectionKeyCode"
|
||||
:zoom-activation-key-code="zoomActivationKeyCode"
|
||||
:snap-to-grid="snapToGrid"
|
||||
:snap-grid="snapGrid"
|
||||
:only-render-visible-elements="onlyRenderVisibleElements"
|
||||
:nodes-draggable="nodesDraggable"
|
||||
:nodes-connectable="nodesConnectable"
|
||||
:elements-selectable="elementsSelectable"
|
||||
:select-nodes-on-drag="selectNodesOnDrag"
|
||||
:min-zoom="minZoom"
|
||||
:max-zoom="maxZoom"
|
||||
:default-zoom="defaultZoom"
|
||||
:default-position="defaultPosition"
|
||||
:translate-extent="translateExtent"
|
||||
:node-extent="nodeExtent"
|
||||
:arrow-head-color="arrowHeadColor"
|
||||
:marker-end-id="markerEndId"
|
||||
:zoom-on-scroll="zoomOnScroll"
|
||||
:zoom-on-pinch="zoomOnPinch"
|
||||
:zoom-on-double-click="zoomOnDoubleClick"
|
||||
:pan-on-scroll="panOnScroll"
|
||||
:pan-on-scroll-speed="panOnScrollSpeed"
|
||||
:pan-on-scroll-mode="panOnScrollMode"
|
||||
:pane-moveable="paneMoveable"
|
||||
:edge-updater-radius="edgeUpdaterRadius"
|
||||
/>
|
||||
<slot></slot>
|
||||
</div>
|
||||
@@ -68,8 +68,7 @@ const defaultEdgeTypes = {
|
||||
smoothstep: SmoothStepEdge
|
||||
};
|
||||
|
||||
const eventHooks = useRevueFlow();
|
||||
const RevueFlow = defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'RevueFlow',
|
||||
components: { GraphView },
|
||||
props: {
|
||||
@@ -254,12 +253,12 @@ const RevueFlow = defineComponent({
|
||||
default: 10
|
||||
}
|
||||
},
|
||||
emits: Object.keys(eventHooks),
|
||||
emits: Object.keys(useRevueFlow()),
|
||||
setup(props, { emit }) {
|
||||
const store = configureStore(initialState)();
|
||||
provide<RevueFlowStore>('store', store);
|
||||
eventHooks.bind(emit);
|
||||
provide<RevueFlowHooks>('hooks', eventHooks);
|
||||
const hooks = useRevueFlow().bind(emit);
|
||||
provide<RevueFlowHooks>('hooks', hooks);
|
||||
const elements = useVModel(props, 'modelValue', emit);
|
||||
|
||||
store.setElements(elements.value);
|
||||
@@ -289,5 +288,4 @@ const RevueFlow = defineComponent({
|
||||
};
|
||||
}
|
||||
});
|
||||
export default RevueFlow;
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { zoom, zoomIdentity } from 'd3-zoom';
|
||||
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, watchEffect } from 'vue';
|
||||
import useKeyPress from '../../hooks/useKeyPress';
|
||||
import useResizeHandler from '../../hooks/useResizeHandler';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { templateRef, whenever } from '@vueuse/core';
|
||||
|
||||
interface ZoomPaneProps {
|
||||
selectionKeyPressed: boolean;
|
||||
@@ -20,38 +21,12 @@ interface ZoomPaneProps {
|
||||
defaultPosition?: [number, number];
|
||||
defaultZoom?: number;
|
||||
translateExtent?: TranslateExtent;
|
||||
onMove?: (flowTransform?: FlowTransform) => void;
|
||||
onMoveStart?: (flowTransform?: FlowTransform) => void;
|
||||
onMoveEnd?: (flowTransform?: FlowTransform) => void;
|
||||
zoomActivationKeyCode?: KeyCode;
|
||||
}
|
||||
|
||||
const viewChanged = (prevTransform: FlowTransform, eventTransform: any): boolean =>
|
||||
prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k;
|
||||
|
||||
const eventToFlowTransform = (eventTransform: any): FlowTransform => ({
|
||||
x: eventTransform.x,
|
||||
y: eventTransform.y,
|
||||
zoom: eventTransform.k
|
||||
});
|
||||
|
||||
const ZoomPane = defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'ZoomPane',
|
||||
props: {
|
||||
onMove: {
|
||||
type: Function as unknown as PropType<ZoomPaneProps['onMove']>,
|
||||
required: false,
|
||||
default: () => {}
|
||||
},
|
||||
onMoveStart: {
|
||||
type: Function as unknown as PropType<ZoomPaneProps['onMoveStart']>,
|
||||
required: false,
|
||||
default: () => {}
|
||||
},
|
||||
onMoveEnd: {
|
||||
type: Function as unknown as PropType<ZoomPaneProps['onMoveEnd']>,
|
||||
required: false,
|
||||
default: () => {}
|
||||
},
|
||||
zoomOnScroll: {
|
||||
type: Boolean as PropType<ZoomPaneProps['zoomOnScroll']>,
|
||||
required: false,
|
||||
@@ -85,12 +60,12 @@ const ZoomPane = defineComponent({
|
||||
selectionKeyPressed: {
|
||||
type: Boolean as PropType<ZoomPaneProps['selectionKeyPressed']>,
|
||||
required: false,
|
||||
default: undefined
|
||||
default: false
|
||||
},
|
||||
elementsSelectable: {
|
||||
type: Boolean as PropType<ZoomPaneProps['elementsSelectable']>,
|
||||
required: false,
|
||||
default: undefined
|
||||
default: true
|
||||
},
|
||||
paneMoveable: {
|
||||
type: Boolean as PropType<ZoomPaneProps['paneMoveable']>,
|
||||
@@ -120,16 +95,23 @@ const ZoomPane = defineComponent({
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const zoomPane = ref<HTMLDivElement | null>(null);
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const zoomPane = templateRef<HTMLDivElement>('zoom-pane', null);
|
||||
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 });
|
||||
const zoomActivationKeyPressed = useKeyPress(props.zoomActivationKeyCode);
|
||||
|
||||
watchEffect(() => {
|
||||
if (zoomPane.value) useResizeHandler(zoomPane.value, store.updateSize);
|
||||
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
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (zoomPane.value && props.defaultPosition && props.defaultZoom) {
|
||||
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);
|
||||
@@ -152,43 +134,41 @@ const ZoomPane = defineComponent({
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (store.d3Selection && store.d3Zoom) {
|
||||
if (props.panOnScroll && zoomActivationKeyPressed) {
|
||||
store.d3Selection
|
||||
.on('wheel', (event: any) => {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
whenever(zoomActivationKeyPressed, () => {
|
||||
if (props.panOnScroll) {
|
||||
store.d3Selection
|
||||
?.on('wheel', (event: WheelEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
const currentZoom = store.d3Selection?.property('__zoom').k || 1;
|
||||
const currentZoom = store.d3Selection?.property('__zoom').k || 1;
|
||||
|
||||
if (event.ctrlKey && props.zoomOnPinch) {
|
||||
const point = pointer(event);
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
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;
|
||||
// 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -197,13 +177,9 @@ const ZoomPane = defineComponent({
|
||||
if (props.selectionKeyPressed) {
|
||||
store.d3Zoom.on('zoom', null);
|
||||
} else {
|
||||
store.d3Zoom.on('zoom', (event: any) => {
|
||||
store.d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
store.transform = [event.transform.x, event.transform.y, event.transform.k];
|
||||
|
||||
if (props.onMove) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
props.onMove(flowTransform);
|
||||
}
|
||||
hooks.move.trigger(eventToFlowTransform(event.transform));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -211,41 +187,33 @@ const ZoomPane = defineComponent({
|
||||
|
||||
watchEffect(() => {
|
||||
if (store.d3Zoom) {
|
||||
if (props.onMoveStart) {
|
||||
store.d3Zoom.on('start', (event: any) => {
|
||||
if (viewChanged(prevTransform.value, event.transform)) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
prevTransform.value = flowTransform;
|
||||
store.d3Zoom.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
if (viewChanged(prevTransform.value, event.transform)) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
prevTransform.value = flowTransform;
|
||||
|
||||
if (props.onMoveStart) props.onMoveStart(flowTransform);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
store.d3Zoom.on('start', null);
|
||||
}
|
||||
hooks.moveStart.trigger(flowTransform);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (store.d3Zoom) {
|
||||
if (props.onMoveEnd) {
|
||||
store.d3Zoom.on('end', (event: any) => {
|
||||
if (viewChanged(prevTransform.value, event.transform)) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
prevTransform.value = flowTransform;
|
||||
store.d3Zoom.on('end', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
if (viewChanged(prevTransform.value, event.transform)) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
prevTransform.value = flowTransform;
|
||||
|
||||
if (props.onMoveEnd) props.onMoveEnd(flowTransform);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
store.d3Zoom.on('end', null);
|
||||
}
|
||||
hooks.moveEnd.trigger(flowTransform);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (store.d3Zoom) {
|
||||
store.d3Zoom.filter((event: any) => {
|
||||
store.d3Zoom.filter((event: MouseEvent) => {
|
||||
const zoomScroll = props.zoomOnScroll;
|
||||
const pinchZoom = props.zoomOnPinch && event.ctrlKey;
|
||||
|
||||
@@ -264,20 +232,20 @@ const ZoomPane = defineComponent({
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.target.closest('.nowheel') && event.type === 'wheel') {
|
||||
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.closest('.revue-flow__node') || event.target.closest('.revue-flow__edge')) &&
|
||||
((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.closest('.revue-flow__nodesselection') && event.type !== 'wheel') {
|
||||
if ((event.target as Element).closest('.revue-flow__nodesselection') && event.type !== 'wheel') {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -302,11 +270,9 @@ const ZoomPane = defineComponent({
|
||||
});
|
||||
|
||||
return () => (
|
||||
<div class="revue-flow__renderer revue-flow__zoompane" ref={zoomPane}>
|
||||
<div class="revue-flow__renderer revue-flow__zoompane" ref="zoom-pane">
|
||||
{slots.default ? slots.default() : ''}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default ZoomPane;
|
||||
|
||||
Reference in New Issue
Block a user