fix: zoom pan helper
This commit is contained in:
@@ -60,13 +60,13 @@ const toggleClassnames = () => {
|
|||||||
<Flow
|
<Flow
|
||||||
class="revue-flow-basic-example"
|
class="revue-flow-basic-example"
|
||||||
:elements="elements"
|
:elements="elements"
|
||||||
:on-elements-remove="onElementsRemove"
|
|
||||||
:on-connect="onConnect"
|
|
||||||
:on-node-drag-stop="onNodeDragStop"
|
|
||||||
:on-node-click="onElementClick"
|
|
||||||
:default-zoom="1.5"
|
:default-zoom="1.5"
|
||||||
:min-zoom="0.2"
|
:min-zoom="0.2"
|
||||||
:max-zoom="4"
|
:max-zoom="4"
|
||||||
|
@elements-remove="onElementsRemove"
|
||||||
|
@connect="onConnect"
|
||||||
|
@node-drag-stop="onNodeDragStop"
|
||||||
|
@node-click="onElementClick"
|
||||||
@elementClick="onElementClick"
|
@elementClick="onElementClick"
|
||||||
@load="onLoad"
|
@load="onLoad"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const onElementsRemove = (elementsToRemove: Elements) =>
|
|||||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value as Elements))
|
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value as Elements))
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Flow :elements="elements" :custom-connection-line="ConnectionLine" @elementsRemove="onElementsRemove" @connect="onConnect">
|
<Flow :elements="elements" :custom-connection-line="ConnectionLine" @elements-remove="onElementsRemove" @connect="onConnect">
|
||||||
<Background :variant="BackgroundVariant.Lines" />
|
<Background :variant="BackgroundVariant.Lines" />
|
||||||
</Flow>
|
</Flow>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -114,17 +114,17 @@ const onConnect = (params: Connection | Edge) =>
|
|||||||
<template>
|
<template>
|
||||||
<Flow
|
<Flow
|
||||||
:elements="elements"
|
:elements="elements"
|
||||||
:on-element-click="onElementClick"
|
|
||||||
:on-elements-remove="onElementsRemove"
|
|
||||||
:on-connect="onConnect"
|
|
||||||
:on-node-drag-stop="onNodeDragStop"
|
|
||||||
:style="`background: ${bgColor}`"
|
:style="`background: ${bgColor}`"
|
||||||
:on-load="onLoad"
|
|
||||||
:node-types="nodeTypes"
|
:node-types="nodeTypes"
|
||||||
:connection-line-style="connectionLineStyle"
|
:connection-line-style="connectionLineStyle"
|
||||||
:snap-to-grid="true"
|
:snap-to-grid="true"
|
||||||
:snap-grid="snapGrid"
|
:snap-grid="snapGrid"
|
||||||
:default-zoom="1.5"
|
:default-zoom="1.5"
|
||||||
|
@element-click="onElementClick"
|
||||||
|
@elements-remove="onElementsRemove"
|
||||||
|
@connect="onConnect"
|
||||||
|
@node-drag-stop="onNodeDragStop"
|
||||||
|
@load="onLoad"
|
||||||
>
|
>
|
||||||
<MiniMap :node-stroke-color="nodeStroke" :node-color="nodeColor" />
|
<MiniMap :node-stroke-color="nodeStroke" :node-color="nodeColor" />
|
||||||
<Controls />
|
<Controls />
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { RevueFlowHooks } from '~/hooks/RevueFlowHooks'
|
import { getEdgeCenter, getBezierPath, getMarkerEnd, ArrowHeadType, EdgeProps, ElementId, Position, Hooks, Store } from '~/index'
|
||||||
import {
|
|
||||||
getEdgeCenter,
|
|
||||||
getBezierPath,
|
|
||||||
getMarkerEnd,
|
|
||||||
ArrowHeadType,
|
|
||||||
EdgeProps,
|
|
||||||
ElementId,
|
|
||||||
Position,
|
|
||||||
RevueFlowStore,
|
|
||||||
} from '~/index'
|
|
||||||
|
|
||||||
interface CustomEdgeProps<T = any> extends EdgeProps<T> {
|
interface CustomEdgeProps<T = any> extends EdgeProps<T> {
|
||||||
id: ElementId
|
id: ElementId
|
||||||
@@ -25,8 +15,8 @@ interface CustomEdgeProps<T = any> extends EdgeProps<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<CustomEdgeProps>()
|
const props = defineProps<CustomEdgeProps>()
|
||||||
const store = inject<RevueFlowStore>('store')!
|
const store = inject(Store)!
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
const hooks = inject(Hooks)!
|
||||||
const onEdgeClick = (evt: Event, id: string) => {
|
const onEdgeClick = (evt: Event, id: string) => {
|
||||||
const edge = store.edges.find((edge) => edge.id === id)
|
const edge = store.edges.find((edge) => edge.id === id)
|
||||||
if (edge) {
|
if (edge) {
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import Flowy from '~/container/Flow/Flow.vue'
|
|
||||||
import { Elements } from '~/types'
|
|
||||||
|
|
||||||
const elements = ref<Elements>([
|
|
||||||
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
|
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
|
|
||||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
|
|
||||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
|
||||||
] as Elements)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Flowy :elements="elements">
|
|
||||||
<div>Foobar!</div>
|
|
||||||
</Flowy>
|
|
||||||
</template>
|
|
||||||
@@ -45,10 +45,6 @@ export const routes: RouterOptions['routes'] = [
|
|||||||
path: '/interaction',
|
path: '/interaction',
|
||||||
component: () => import('./Interaction/InteractionExample.vue'),
|
component: () => import('./Interaction/InteractionExample.vue'),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/super-flow',
|
|
||||||
component: () => import('./Superflow/Superflow.vue'),
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
export const router = createRouter({
|
export const router = createRouter({
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { HTMLAttributes } from 'vue'
|
import { HTMLAttributes } from 'vue'
|
||||||
import { BackgroundVariant, RevueFlowStore } from '~/types'
|
import { BackgroundVariant } from '~/types'
|
||||||
|
import { Store } from '~/context'
|
||||||
|
|
||||||
export interface BackgroundProps extends HTMLAttributes {
|
export interface BackgroundProps extends HTMLAttributes {
|
||||||
variant?: BackgroundVariant
|
variant?: BackgroundVariant
|
||||||
@@ -20,14 +21,23 @@ const defaultColors: Record<BackgroundVariant, string> = {
|
|||||||
[BackgroundVariant.Lines]: '#eee',
|
[BackgroundVariant.Lines]: '#eee',
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = inject<RevueFlowStore>('store')!
|
const store = inject(Store)!
|
||||||
// when there are multiple flows on a page we need to make sure that every background gets its own pattern.
|
// when there are multiple flows on a page we need to make sure that every background gets its own pattern.
|
||||||
|
|
||||||
const bgClasses = ['revue-flow__background']
|
const bgClasses = ['revue-flow__background']
|
||||||
const scaledGap = computed(() => props.gap && props.gap * store.transform[2])
|
const background = computed(() => {
|
||||||
const xOffset = computed(() => scaledGap.value && store.transform[0] % scaledGap.value)
|
const scaledGap = props.gap && props.gap * store.transform[2]
|
||||||
const yOffset = computed(() => scaledGap.value && store.transform[1] % scaledGap.value)
|
const xOffset = scaledGap && store.transform[0] % scaledGap
|
||||||
const size = computed(() => props.size || 0.4 * store.transform[2])
|
const yOffset = scaledGap && store.transform[1] % scaledGap
|
||||||
|
const size = props.size || 0.4 * store.transform[2]
|
||||||
|
|
||||||
|
return {
|
||||||
|
scaledGap,
|
||||||
|
xOffset,
|
||||||
|
yOffset,
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const isLines = props.variant === BackgroundVariant.Lines
|
const isLines = props.variant === BackgroundVariant.Lines
|
||||||
const bgColor = props.color ? props.color : defaultColors[props.variant || BackgroundVariant.Dots]
|
const bgColor = props.color ? props.color : defaultColors[props.variant || BackgroundVariant.Dots]
|
||||||
@@ -41,16 +51,23 @@ const patternId = `pattern-${Math.floor(Math.random() * 100000)}`
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<pattern :id="patternId" :x="xOffset" :y="yOffset" :width="scaledGap" :height="scaledGap" patternUnits="userSpaceOnUse">
|
<pattern
|
||||||
|
:id="patternId"
|
||||||
|
:x="background.xOffset"
|
||||||
|
:y="background.yOffset"
|
||||||
|
:width="background.scaledGap"
|
||||||
|
:height="background.scaledGap"
|
||||||
|
patternUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
<template v-if="isLines">
|
<template v-if="isLines">
|
||||||
<path
|
<path
|
||||||
:stroke="bgColor"
|
:stroke="bgColor"
|
||||||
:stroke-width="props.size"
|
:stroke-width="props.size"
|
||||||
:d="`M${scaledGap / 2} 0 V${scaledGap} M0 ${scaledGap / 2} H${scaledGap}`"
|
:d="`M${background.scaledGap / 2} 0 V${background.scaledGap} M0 ${background.scaledGap / 2} H${background.scaledGap}`"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<circle :cx="size" :cy="size" :r="size" :fill="bgColor" />
|
<circle :cx="background.size" :cy="background.size" :r="background.size" :fill="bgColor" />
|
||||||
</template>
|
</template>
|
||||||
</pattern>
|
</pattern>
|
||||||
<rect x="0" y="0" width="100%" height="100%" :fill="`url(#${patternId})`" />
|
<rect x="0" y="0" width="100%" height="100%" :fill="`url(#${patternId})`" />
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ import MinusIcon from '@/assets/icons/minus.svg'
|
|||||||
import Fitview from '@/assets/icons/fitview.svg'
|
import Fitview from '@/assets/icons/fitview.svg'
|
||||||
import Lock from '@/assets/icons/lock.svg'
|
import Lock from '@/assets/icons/lock.svg'
|
||||||
import Unlock from '@/assets/icons/unlock.svg'
|
import Unlock from '@/assets/icons/unlock.svg'
|
||||||
import { FitViewParams, RevueFlowStore, ZoomPanHelperFunctions } from '~/types'
|
import { FitViewParams } from '~/types'
|
||||||
import useZoomPanHelper from '~/hooks/useZoomPanHelper'
|
import useZoomPanHelper from '~/composables/useZoomPanHelper'
|
||||||
|
import { Store } from '~/context'
|
||||||
|
|
||||||
export interface ControlProps extends HTMLAttributes {
|
export interface ControlProps extends HTMLAttributes {
|
||||||
showZoom?: boolean
|
showZoom?: boolean
|
||||||
@@ -26,25 +27,24 @@ const props = withDefaults(defineProps<ControlProps>(), {
|
|||||||
showInteractive: true,
|
showInteractive: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const store = inject<RevueFlowStore>('store')!
|
const store = inject(Store)!
|
||||||
// const { onReady } = useZoomPanHelper()
|
const { zoomIn, zoomOut, fitView } = useZoomPanHelper()
|
||||||
|
|
||||||
const isInteractive = store.nodesDraggable && store.nodesConnectable && store.elementsSelectable
|
const isInteractive = store.nodesDraggable && store.nodesConnectable && store.elementsSelectable
|
||||||
const mapClasses = ['revue-flow__controls']
|
const mapClasses = ['revue-flow__controls']
|
||||||
|
|
||||||
/*
|
const onZoomInHandler = () => {
|
||||||
* const onZoomInHandler = () => {
|
zoomIn?.()
|
||||||
zoomHelper.value?.zoomIn?.()
|
|
||||||
props.onZoomIn?.()
|
props.onZoomIn?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
const onZoomOutHandler = () => {
|
const onZoomOutHandler = () => {
|
||||||
zoomHelper.value?.zoomOut?.()
|
zoomOut?.()
|
||||||
props.onZoomOut?.()
|
props.onZoomOut?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
const onFitViewHandler = () => {
|
const onFitViewHandler = () => {
|
||||||
zoomHelper.value?.fitView?.(props.fitViewParams)
|
fitView?.(props.fitViewParams)
|
||||||
props.onFitView?.()
|
props.onFitView?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +52,6 @@ const onInteractiveChangeHandler = () => {
|
|||||||
store.setInteractive?.(!isInteractive)
|
store.setInteractive?.(!isInteractive)
|
||||||
props.onInteractiveChange?.(!isInteractive)
|
props.onInteractiveChange?.(!isInteractive)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div :class="mapClasses">
|
<div :class="mapClasses">
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
import { HTMLAttributes } from 'vue'
|
import { HTMLAttributes } from 'vue'
|
||||||
import MiniMapNode from './MiniMapNode.vue'
|
import MiniMapNode from './MiniMapNode.vue'
|
||||||
import { getBoundsofRects, getRectOfNodes } from '~/utils/graph'
|
import { getBoundsofRects, getRectOfNodes } from '~/utils/graph'
|
||||||
import { Node, RevueFlowStore } from '~/types'
|
import { Node } from '~/types'
|
||||||
|
import { Store } from '~/context'
|
||||||
|
|
||||||
type StringFunc = (node: Node) => string
|
type StringFunc = (node: Node) => string
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ declare const window: any
|
|||||||
const defaultWidth = 200
|
const defaultWidth = 200
|
||||||
const defaultHeight = 150
|
const defaultHeight = 150
|
||||||
|
|
||||||
const store = inject<RevueFlowStore>('store')!
|
const store = inject(Store)!
|
||||||
|
|
||||||
const elementWidth = attrs.style?.width ?? defaultWidth
|
const elementWidth = attrs.style?.width ?? defaultWidth
|
||||||
const elementHeight = attrs.style?.height ?? defaultHeight
|
const elementHeight = attrs.style?.height ?? defaultHeight
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, CSSProperties } from 'vue'
|
import { CSSProperties } from 'vue'
|
||||||
|
|
||||||
interface MiniMapNodeProps {
|
interface MiniMapNodeProps {
|
||||||
x?: number
|
x?: number
|
||||||
@@ -8,11 +8,13 @@ interface MiniMapNodeProps {
|
|||||||
height?: number
|
height?: number
|
||||||
borderRadius?: number
|
borderRadius?: number
|
||||||
color?: string
|
color?: string
|
||||||
shapeRendering?: string
|
shapeRendering?: 'auto' | 'optimizeSpeed' | 'crispEdges' | 'geometricPrecision' | 'inherit'
|
||||||
strokeColor?: string
|
strokeColor?: string
|
||||||
strokeWidth?: number
|
strokeWidth?: number
|
||||||
}
|
}
|
||||||
const props = defineProps<MiniMapNodeProps>()
|
const props = withDefaults(defineProps<MiniMapNodeProps>(), {
|
||||||
|
shapeRendering: 'geometricPrecision',
|
||||||
|
})
|
||||||
const attrs = useAttrs()
|
const attrs = useAttrs()
|
||||||
|
|
||||||
const styles = (attrs.style ?? {}) as CSSProperties
|
const styles = (attrs.style ?? {}) as CSSProperties
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { CSSProperties } from 'vue'
|
import { CSSProperties } from 'vue'
|
||||||
import { ConnectionLineType, CustomConnectionLine, HandleElement, Node, Position } from '~/types'
|
import { ConnectionLineType, CustomConnectionLine, HandleElement, Node, Position } from '~/types'
|
||||||
import { getBezierPath, getSmoothStepPath } from '~/components/Edges/utils'
|
import { getBezierPath, getSmoothStepPath } from '~/components/Edges/utils'
|
||||||
import { Hooks, Store } from '~/context/symbols'
|
import { Hooks, Store } from '~/context'
|
||||||
|
|
||||||
interface ConnectionLineProps {
|
interface ConnectionLineProps {
|
||||||
sourceNode: Node
|
sourceNode: Node
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from
|
|||||||
import { isEdge } from '~/utils/graph'
|
import { isEdge } from '~/utils/graph'
|
||||||
import { ConnectionMode, Dimensions, Edge, EdgeType, Elements, Position, Transform } from '~/types'
|
import { ConnectionMode, Dimensions, Edge, EdgeType, Elements, Position, Transform } from '~/types'
|
||||||
import { onMouseDown } from '~/components/Handle/utils'
|
import { onMouseDown } from '~/components/Handle/utils'
|
||||||
import { Hooks, Store } from '~/context/symbols'
|
import { Hooks, Store } from '~/context'
|
||||||
|
|
||||||
interface EdgeProps {
|
interface EdgeProps {
|
||||||
type: EdgeType
|
type: EdgeType
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ElementId, Position } from '~/types'
|
import { ElementId, Position } from '~/types'
|
||||||
import { onMouseDown, ValidConnectionFunc } from '~/components/Handle/utils'
|
import { onMouseDown, ValidConnectionFunc } from '~/components/Handle/utils'
|
||||||
import { Hooks, Store } from '~/context/symbols'
|
import { Hooks, Store } from '~/context'
|
||||||
|
|
||||||
interface HandleProps {
|
interface HandleProps {
|
||||||
id?: string
|
id?: string
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { getHostForElement } from '~/utils'
|
import { getHostForElement } from '~/utils'
|
||||||
import { ElementId, ConnectionMode, Connection, HandleType, RevueFlowStore } from '~/types'
|
import { ElementId, ConnectionMode, Connection, HandleType, FlowStore, FlowHooks } from '~/types'
|
||||||
import { RevueFlowHooks } from '~/hooks/RevueFlowHooks'
|
|
||||||
|
|
||||||
export type ValidConnectionFunc = (connection: Connection) => boolean
|
export type ValidConnectionFunc = (connection: Connection) => boolean
|
||||||
|
|
||||||
@@ -71,13 +70,13 @@ function resetRecentHandle(hoveredHandle: Element): void {
|
|||||||
|
|
||||||
export function onMouseDown(
|
export function onMouseDown(
|
||||||
event: MouseEvent,
|
event: MouseEvent,
|
||||||
store: RevueFlowStore,
|
store: FlowStore,
|
||||||
hooks: {
|
hooks: {
|
||||||
connectStart: RevueFlowHooks['connectStart']
|
connectStart: FlowHooks['connectStart']
|
||||||
connectStop: RevueFlowHooks['connectStop']
|
connectStop: FlowHooks['connectStop']
|
||||||
connectEnd: RevueFlowHooks['connectEnd']
|
connectEnd: FlowHooks['connectEnd']
|
||||||
connect: RevueFlowHooks['connect']
|
connect: FlowHooks['connect']
|
||||||
edgeUpdateEnd: RevueFlowHooks['edgeUpdateEnd']
|
edgeUpdateEnd: FlowHooks['edgeUpdateEnd']
|
||||||
},
|
},
|
||||||
handleId: ElementId,
|
handleId: ElementId,
|
||||||
nodeId: ElementId,
|
nodeId: ElementId,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { DraggableEventListener } from '@braks/revue-draggable'
|
import { DraggableEventListener } from '@braks/revue-draggable'
|
||||||
import { Node, NodeDimensionUpdate, NodeType } from '~/types'
|
import { Node, NodeDimensionUpdate, NodeType } from '~/types'
|
||||||
import { Hooks, Store } from '~/context/symbols'
|
import { Hooks, Store } from '~/context'
|
||||||
|
|
||||||
interface NodeProps {
|
interface NodeProps {
|
||||||
node: Node
|
node: Node
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { DraggableEventListener } from '@braks/revue-draggable'
|
import { DraggableEventListener } from '@braks/revue-draggable'
|
||||||
import { Node } from '~/types'
|
import { Node } from '~/types'
|
||||||
import { isNode } from '~/utils/graph'
|
import { isNode } from '~/utils/graph'
|
||||||
import { Hooks, Store } from '~/context/symbols'
|
import { Hooks, Store } from '~/context'
|
||||||
|
|
||||||
interface NodesSelectionProps {
|
interface NodesSelectionProps {
|
||||||
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void
|
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import SelectionRect from './SelectionRect.vue'
|
import SelectionRect from './SelectionRect.vue'
|
||||||
import { getMousePosition } from '~/components/UserSelection/utils'
|
import { getMousePosition } from '~/components/UserSelection/utils'
|
||||||
import { Store } from '~/context/symbols'
|
import { Store } from '~/context'
|
||||||
|
|
||||||
const store = inject(Store)!
|
const store = inject(Store)!
|
||||||
const el = templateRef('user-selection', null)
|
const el = templateRef('user-selection', null)
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export { default as useGlobalKeyHandler } from './useGlobalKeyHandler'
|
||||||
|
export { default as useHooks } from './useHooks'
|
||||||
|
export { default as useKeyPress } from './useKeyPress'
|
||||||
|
export { default as useResizeHandler } from './useResizeHandler'
|
||||||
|
export { default as useUpdateNodeInternals } from './useUpdateNodeInternals'
|
||||||
|
export { default as useZoom } from './useZoom'
|
||||||
|
export { default as useZoomPanHelper } from './useZoomPanHelper'
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import useKeyPress from './useKeyPress'
|
import useKeyPress from './useKeyPress'
|
||||||
import { isNode, getConnectedEdges } from '~/utils/graph'
|
import { isNode, getConnectedEdges } from '~/utils/graph'
|
||||||
import { Elements, KeyCode, ElementId, FlowElement } from '~/types'
|
import { Elements, KeyCode, ElementId, FlowElement } from '~/types'
|
||||||
import { Store } from '~/context/symbols'
|
import { Store } from '~/context'
|
||||||
|
|
||||||
interface HookParams {
|
interface HookParams {
|
||||||
deleteKeyCode: KeyCode
|
deleteKeyCode: KeyCode
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// global hooks
|
// global hooks
|
||||||
import { Connection, Edge, Elements, FlowHooks, FlowTransform, Node, OnConnectStartParams, OnLoadParams } from '~/types'
|
import { Connection, Edge, Elements, FlowHooks, FlowTransform, Node, OnConnectStartParams, OnLoadParams } from '~/types'
|
||||||
|
|
||||||
export const useHooks = (): FlowHooks & { bind: (emit: (event: string, ...args: any[]) => void) => FlowHooks } => {
|
export default (): FlowHooks & { bind: (emit: (event: string, ...args: any[]) => void) => FlowHooks } => {
|
||||||
const eventHooks: FlowHooks = {
|
const eventHooks: FlowHooks = {
|
||||||
elementClick: createEventHook<{ event: MouseEvent; element: Node | Edge }>(),
|
elementClick: createEventHook<{ event: MouseEvent; element: Node | Edge }>(),
|
||||||
elementsRemove: createEventHook<Elements>(),
|
elementsRemove: createEventHook<Elements>(),
|
||||||
+13
-33
@@ -2,20 +2,10 @@ import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom'
|
|||||||
import { pointer, select } from 'd3-selection'
|
import { pointer, select } from 'd3-selection'
|
||||||
import { Ref } from 'vue'
|
import { Ref } from 'vue'
|
||||||
import { get } from '@vueuse/core'
|
import { get } from '@vueuse/core'
|
||||||
import {
|
import { D3Selection, D3Zoom, FlowTransform, KeyCode, PanOnScrollMode, Transform } from '~/types'
|
||||||
D3Selection,
|
|
||||||
D3Zoom,
|
|
||||||
FlowTransform,
|
|
||||||
InitD3ZoomPayload,
|
|
||||||
KeyCode,
|
|
||||||
PanOnScrollMode,
|
|
||||||
Transform,
|
|
||||||
TranslateExtent,
|
|
||||||
} from '~/types'
|
|
||||||
import { clamp } from '~/utils'
|
import { clamp } from '~/utils'
|
||||||
import useKeyPress from '~/hooks/useKeyPress'
|
import useKeyPress from '~/composables/useKeyPress'
|
||||||
import { Hooks } from '~/context/symbols'
|
import { Hooks, Store } from '~/context'
|
||||||
import { onLoadGetElements, onLoadProject, onLoadToObject } from '~/utils/graph'
|
|
||||||
|
|
||||||
const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean =>
|
const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean =>
|
||||||
prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k
|
prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k
|
||||||
@@ -30,11 +20,8 @@ interface UseZoomOptions {
|
|||||||
selectionKeyCode?: KeyCode
|
selectionKeyCode?: KeyCode
|
||||||
zoomActivationKeyCode?: KeyCode
|
zoomActivationKeyCode?: KeyCode
|
||||||
paneMoveable?: boolean
|
paneMoveable?: boolean
|
||||||
minZoom?: number
|
|
||||||
maxZoom?: number
|
|
||||||
defaultZoom?: number
|
defaultZoom?: number
|
||||||
defaultPosition?: [number, number]
|
defaultPosition?: [number, number]
|
||||||
translateExtent?: TranslateExtent
|
|
||||||
zoomOnScroll?: boolean
|
zoomOnScroll?: boolean
|
||||||
zoomOnPinch?: boolean
|
zoomOnPinch?: boolean
|
||||||
panOnScroll?: boolean
|
panOnScroll?: boolean
|
||||||
@@ -49,22 +36,12 @@ interface UseZoom {
|
|||||||
d3Selection: Ref<D3Selection>
|
d3Selection: Ref<D3Selection>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function (
|
export default function (el: Ref<HTMLDivElement>, options: UseZoomOptions): UseZoom {
|
||||||
el: Ref<HTMLDivElement>,
|
|
||||||
options: UseZoomOptions,
|
|
||||||
init: (initD3ZoomPayload: InitD3ZoomPayload) => void = () => {},
|
|
||||||
): UseZoom {
|
|
||||||
const {
|
const {
|
||||||
selectionKeyCode = 'Shift',
|
selectionKeyCode = 'Shift',
|
||||||
zoomActivationKeyCode = 'Meta',
|
zoomActivationKeyCode = 'Meta',
|
||||||
minZoom = 0.5,
|
|
||||||
maxZoom = 2,
|
|
||||||
defaultZoom = 1,
|
defaultZoom = 1,
|
||||||
defaultPosition = [0, 0],
|
defaultPosition = [0, 0],
|
||||||
translateExtent = [
|
|
||||||
[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
|
|
||||||
[Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
|
|
||||||
],
|
|
||||||
zoomOnScroll = true,
|
zoomOnScroll = true,
|
||||||
zoomOnPinch = true,
|
zoomOnPinch = true,
|
||||||
zoomOnDoubleClick = true,
|
zoomOnDoubleClick = true,
|
||||||
@@ -74,13 +51,17 @@ export default function (
|
|||||||
paneMoveable = true,
|
paneMoveable = true,
|
||||||
} = options
|
} = options
|
||||||
const hooks = inject(Hooks)!
|
const hooks = inject(Hooks)!
|
||||||
|
const store = inject(Store)!
|
||||||
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 })
|
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 })
|
||||||
|
|
||||||
const clampedX = clamp(defaultPosition[0], translateExtent[0][0], translateExtent[1][0])
|
const clampedX = clamp(defaultPosition[0], store.translateExtent[0][0], store.translateExtent[1][0])
|
||||||
const clampedY = clamp(defaultPosition[1], translateExtent[0][1], translateExtent[1][1])
|
const clampedY = clamp(defaultPosition[1], store.translateExtent[0][1], store.translateExtent[1][1])
|
||||||
const clampedZoom = clamp(defaultZoom, minZoom, maxZoom)
|
const clampedZoom = clamp(defaultZoom, store.minZoom, store.maxZoom)
|
||||||
const transform = ref<Transform>([clampedX, clampedY, clampedZoom])
|
const transform = ref<Transform>([clampedX, clampedY, clampedZoom])
|
||||||
const d3Zoom = ref(zoom<HTMLDivElement, any>().scaleExtent([minZoom, maxZoom]).translateExtent(translateExtent))
|
store.transform = transform.value
|
||||||
|
const d3Zoom = ref(
|
||||||
|
zoom<HTMLDivElement, any>().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(store.translateExtent),
|
||||||
|
)
|
||||||
const d3Selection = ref()
|
const d3Selection = ref()
|
||||||
|
|
||||||
until(el)
|
until(el)
|
||||||
@@ -93,8 +74,7 @@ export default function (
|
|||||||
|
|
||||||
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom)
|
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom)
|
||||||
d3z.transform(d3s, updatedTransform)
|
d3z.transform(d3s, updatedTransform)
|
||||||
init({ d3Zoom: d3z, d3ZoomHandler, d3Selection: d3s })
|
store.initD3Zoom({ d3Zoom: d3z, d3Selection: d3s, d3ZoomHandler })
|
||||||
|
|
||||||
const applyZoomHandlers = () => {
|
const applyZoomHandlers = () => {
|
||||||
d3z.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3z.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
if (viewChanged(prevTransform.value, event.transform)) {
|
if (viewChanged(prevTransform.value, event.transform)) {
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { zoomIdentity } from 'd3-zoom'
|
||||||
|
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '~/utils/graph'
|
||||||
|
import { FitViewParams, FlowTransform, Rect, UseZoomPanHelper, XYPosition } from '~/types'
|
||||||
|
import { Store } from '~/context'
|
||||||
|
|
||||||
|
const DEFAULT_PADDING = 0.1
|
||||||
|
|
||||||
|
export default function (): UseZoomPanHelper {
|
||||||
|
const store = inject(Store)!
|
||||||
|
return {
|
||||||
|
zoomIn: () => store.d3Selection && store.d3Zoom?.scaleBy(store.d3Selection, 1.2),
|
||||||
|
zoomOut: () => store.d3Selection && store.d3Zoom?.scaleBy(store.d3Selection, 1 / 1.2),
|
||||||
|
zoomTo: (zoomLevel: number) => store.d3Selection && store.d3Zoom?.scaleTo(store.d3Selection, zoomLevel),
|
||||||
|
transform: (transform: FlowTransform) => {
|
||||||
|
const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom)
|
||||||
|
|
||||||
|
store.d3Selection && store.d3Zoom?.transform(store.d3Selection, nextTransform)
|
||||||
|
},
|
||||||
|
fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {
|
||||||
|
if (!store.nodes.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const bounds = getRectOfNodes(options.includeHiddenNodes ? store.nodes : store.nodes.filter((node) => !node.isHidden))
|
||||||
|
const [x, y, zoom] = getTransformForBounds(
|
||||||
|
bounds,
|
||||||
|
store.dimensions.width,
|
||||||
|
store.dimensions.height,
|
||||||
|
options.minZoom || store.minZoom,
|
||||||
|
options.maxZoom || store.maxZoom,
|
||||||
|
options.padding || DEFAULT_PADDING,
|
||||||
|
)
|
||||||
|
const transform = zoomIdentity.translate(x, y).scale(zoom)
|
||||||
|
|
||||||
|
store.d3Selection && store.d3Zoom?.transform(store.d3Selection, transform)
|
||||||
|
},
|
||||||
|
setCenter: (x: number, y: number, zoom?: number) => {
|
||||||
|
const nextZoom = typeof zoom !== 'undefined' ? zoom : store.maxZoom
|
||||||
|
const centerX = store.dimensions.width / 2 - x * nextZoom
|
||||||
|
const centerY = store.dimensions.height / 2 - y * nextZoom
|
||||||
|
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom)
|
||||||
|
|
||||||
|
store.d3Selection && store.d3Zoom?.transform(store.d3Selection, transform)
|
||||||
|
},
|
||||||
|
fitBounds: (bounds: Rect, padding = DEFAULT_PADDING) => {
|
||||||
|
const [x, y, zoom] = getTransformForBounds(
|
||||||
|
bounds,
|
||||||
|
store.dimensions.width,
|
||||||
|
store.dimensions.height,
|
||||||
|
store.minZoom,
|
||||||
|
store.maxZoom,
|
||||||
|
padding,
|
||||||
|
)
|
||||||
|
const transform = zoomIdentity.translate(x, y).scale(zoom)
|
||||||
|
|
||||||
|
store.d3Selection && store.d3Zoom?.transform(store.d3Selection, transform)
|
||||||
|
},
|
||||||
|
project: (position: XYPosition) => {
|
||||||
|
return pointToRendererPoint(position, store.transform, store.snapToGrid, store.snapGrid)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ 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 { ConnectionLineType, ConnectionMode, CustomConnectionLine, Dimensions, EdgeType, Transform } from '~/types'
|
import { ConnectionLineType, ConnectionMode, CustomConnectionLine, Dimensions, EdgeType, Transform } from '~/types'
|
||||||
import { Store } from '~/context/symbols'
|
import { Store } from '~/context'
|
||||||
|
|
||||||
interface EdgeRendererProps {
|
interface EdgeRendererProps {
|
||||||
edgeTypes: Record<string, EdgeType>
|
edgeTypes: Record<string, EdgeType>
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ 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'
|
import { createNodeTypes } from '~/container/NodeRenderer/utils'
|
||||||
import { useHooks } from '~/hooks/RevueFlowHooks'
|
import { useHooks } from '~/composables'
|
||||||
import { Hooks, Store } from '~/context/symbols'
|
import { Hooks, Store } from '~/context'
|
||||||
|
|
||||||
export interface FlowProps {
|
export interface FlowProps {
|
||||||
elements: Elements
|
elements: Elements
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import Node from '~/components/Nodes/Node.vue'
|
import Node from '~/components/Nodes/Node.vue'
|
||||||
import { NodeType, Node as TNode, Transform, Dimensions } from '~/types'
|
import { NodeType, Node as TNode, Transform, Dimensions } from '~/types'
|
||||||
import { getNodesInside } from '~/utils/graph'
|
import { getNodesInside } from '~/utils/graph'
|
||||||
import { Store } from '~/context/symbols'
|
import { Store } from '~/context'
|
||||||
|
|
||||||
interface NodeRendererProps {
|
interface NodeRendererProps {
|
||||||
nodeTypes: Record<string, NodeType>
|
nodeTypes: Record<string, NodeType>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import useKeyPress from '~/hooks/useKeyPress'
|
import useKeyPress from '~/composables/useKeyPress'
|
||||||
import { KeyCode } from '~/types'
|
import { KeyCode } from '~/types'
|
||||||
import useGlobalKeyHandler from '~/hooks/useGlobalKeyHandler'
|
import useGlobalKeyHandler from '~/composables/useGlobalKeyHandler'
|
||||||
import NodesSelection from '~/components/NodesSelection/NodesSelection.vue'
|
import NodesSelection from '~/components/NodesSelection/NodesSelection.vue'
|
||||||
import UserSelection from '~/components/UserSelection/UserSelection.vue'
|
import UserSelection from '~/components/UserSelection/UserSelection.vue'
|
||||||
import { Hooks, Store } from '~/context/symbols'
|
import { Hooks, Store } from '~/context'
|
||||||
|
|
||||||
interface SelectionPaneProps {
|
interface SelectionPaneProps {
|
||||||
selectionKeyCode?: KeyCode
|
selectionKeyCode?: KeyCode
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { KeyCode, PanOnScrollMode } from '~/types'
|
import { KeyCode, PanOnScrollMode } from '~/types'
|
||||||
import useZoom from '~/composables/useZoom'
|
import { useZoom, useResizeHandler, useZoomPanHelper } from '~/composables'
|
||||||
import useResizeHandler from '~/hooks/useResizeHandler'
|
import { Hooks, Store } from '~/context'
|
||||||
import useZoomPanHelper from '~/hooks/useZoomPanHelper'
|
import { onLoadGetElements, onLoadProject, onLoadToObject } from '~/utils/graph'
|
||||||
import { Store } from '~/context/symbols'
|
|
||||||
|
|
||||||
interface ZoomPaneProps {
|
interface ZoomPaneProps {
|
||||||
selectionKeyCode?: KeyCode
|
selectionKeyCode?: KeyCode
|
||||||
@@ -33,25 +32,25 @@ const props = withDefaults(defineProps<ZoomPaneProps>(), {
|
|||||||
paneMoveable: true,
|
paneMoveable: true,
|
||||||
})
|
})
|
||||||
const store = inject(Store)!
|
const store = inject(Store)!
|
||||||
const zoomPaneEl = templateRef<HTMLDivElement>('zoom-pane', null)
|
const hooks = inject(Hooks)!
|
||||||
const { transform, d3Selection, d3Zoom } = useZoom(zoomPaneEl, props, (initD3ZoomPayload) => store.initD3Zoom(initD3ZoomPayload))
|
|
||||||
|
|
||||||
|
const zoomPaneEl = templateRef<HTMLDivElement>('zoom-pane', null)
|
||||||
|
const { transform } = useZoom(zoomPaneEl, props)
|
||||||
watch(transform, (val) => (store.transform = val), { flush: 'post' })
|
watch(transform, (val) => (store.transform = val), { flush: 'post' })
|
||||||
const dimensions = useResizeHandler(zoomPaneEl)
|
const dimensions = useResizeHandler(zoomPaneEl)
|
||||||
watch(dimensions, (val) => (store.dimensions = val), { flush: 'post' })
|
watch(dimensions, (val) => (store.dimensions = val), { flush: 'post' })
|
||||||
|
|
||||||
const getZoomPanHelper = reactify(useZoomPanHelper)
|
const { zoomIn, zoomOut, zoomTo, transform: setTransform, fitView } = useZoomPanHelper()
|
||||||
const zoomPanHelper = getZoomPanHelper(
|
hooks.load.trigger({
|
||||||
d3Zoom,
|
fitView: (params = { padding: 0.1 }) => fitView(params),
|
||||||
d3Selection,
|
zoomIn,
|
||||||
store.nodes,
|
zoomOut,
|
||||||
transform,
|
zoomTo,
|
||||||
dimensions,
|
setTransform,
|
||||||
store.minZoom,
|
project: onLoadProject(store),
|
||||||
store.maxZoom,
|
getElements: onLoadGetElements(store),
|
||||||
store.snapToGrid,
|
toObject: onLoadToObject(store),
|
||||||
store.snapGrid,
|
})
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div ref="zoom-pane" class="revue-flow__renderer revue-flow__zoompane">
|
<div ref="zoom-pane" class="revue-flow__renderer revue-flow__zoompane">
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ import { FlowHooks, FlowStore } from '~/types'
|
|||||||
|
|
||||||
export const Store: InjectionKey<FlowStore> = Symbol('store')
|
export const Store: InjectionKey<FlowStore> = Symbol('store')
|
||||||
export const Hooks: InjectionKey<FlowHooks> = Symbol('hooks')
|
export const Hooks: InjectionKey<FlowHooks> = Symbol('hooks')
|
||||||
|
export const NodeIdContextKey: InjectionKey<string> = Symbol('NodeIdContext')
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import { zoomIdentity } from 'd3-zoom'
|
|
||||||
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '~/utils/graph'
|
|
||||||
import {
|
|
||||||
FitViewParams,
|
|
||||||
FlowTransform,
|
|
||||||
ZoomPanHelperFunctions,
|
|
||||||
Rect,
|
|
||||||
XYPosition,
|
|
||||||
Node,
|
|
||||||
Transform,
|
|
||||||
SnapGrid,
|
|
||||||
Dimensions,
|
|
||||||
D3Zoom,
|
|
||||||
D3Selection,
|
|
||||||
} from '~/types'
|
|
||||||
|
|
||||||
const DEFAULT_PADDING = 0.1
|
|
||||||
|
|
||||||
type UseZoomPanHelper = (
|
|
||||||
d3Zoom: D3Zoom,
|
|
||||||
d3Selection: D3Selection,
|
|
||||||
nodes: Node[],
|
|
||||||
transform: Transform,
|
|
||||||
dimensions: Dimensions,
|
|
||||||
minZoom: number,
|
|
||||||
maxZoom: number,
|
|
||||||
snapToGrid: boolean,
|
|
||||||
snapGrid: SnapGrid,
|
|
||||||
) => ZoomPanHelperFunctions
|
|
||||||
|
|
||||||
export default (function (
|
|
||||||
d3Zoom,
|
|
||||||
d3Selection,
|
|
||||||
nodes = [],
|
|
||||||
transform = [0, 0, 0],
|
|
||||||
dimensions = {
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
},
|
|
||||||
minZoom = 0.5,
|
|
||||||
maxZoom = 2,
|
|
||||||
snapToGrid = false,
|
|
||||||
snapGrid = [15, 15],
|
|
||||||
): ZoomPanHelperFunctions {
|
|
||||||
return {
|
|
||||||
zoomIn: () => d3Zoom?.scaleBy(d3Selection, 1.2),
|
|
||||||
zoomOut: () => d3Zoom?.scaleBy(d3Selection, 1 / 1.2),
|
|
||||||
zoomTo: (zoomLevel: number) => d3Zoom.scaleTo(d3Selection, zoomLevel),
|
|
||||||
transform: (transform: FlowTransform) => {
|
|
||||||
const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom)
|
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, nextTransform)
|
|
||||||
},
|
|
||||||
fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {
|
|
||||||
if (!nodes.length) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const bounds = getRectOfNodes(options.includeHiddenNodes ? nodes : nodes.filter((node) => !node.isHidden))
|
|
||||||
const [x, y, zoom] = getTransformForBounds(
|
|
||||||
bounds,
|
|
||||||
dimensions.width,
|
|
||||||
dimensions.height,
|
|
||||||
options.minZoom || minZoom,
|
|
||||||
options.maxZoom || maxZoom,
|
|
||||||
options.padding || DEFAULT_PADDING,
|
|
||||||
)
|
|
||||||
const transform = zoomIdentity.translate(x, y).scale(zoom)
|
|
||||||
|
|
||||||
d3Zoom?.transform(d3Selection, transform)
|
|
||||||
},
|
|
||||||
setCenter: (x: number, y: number, zoom?: number) => {
|
|
||||||
const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom
|
|
||||||
const centerX = dimensions.width / 2 - x * nextZoom
|
|
||||||
const centerY = dimensions.height / 2 - y * nextZoom
|
|
||||||
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom)
|
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, transform)
|
|
||||||
},
|
|
||||||
fitBounds: (bounds: Rect, padding = DEFAULT_PADDING) => {
|
|
||||||
const [x, y, zoom] = getTransformForBounds(bounds, dimensions.width, dimensions.height, minZoom, maxZoom, padding)
|
|
||||||
const transform = zoomIdentity.translate(x, y).scale(zoom)
|
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, transform)
|
|
||||||
},
|
|
||||||
project: (position: XYPosition) => {
|
|
||||||
return pointToRendererPoint(position, transform, snapToGrid, snapGrid)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} as UseZoomPanHelper)
|
|
||||||
+3
-2
@@ -15,8 +15,9 @@ export {
|
|||||||
getTransformForBounds,
|
getTransformForBounds,
|
||||||
getRectOfNodes,
|
getRectOfNodes,
|
||||||
} from './utils/graph'
|
} from './utils/graph'
|
||||||
export { default as useZoomPanHelper } from './hooks/useZoomPanHelper'
|
export { default as useZoomPanHelper } from './composables/useZoomPanHelper'
|
||||||
export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals'
|
export { default as useUpdateNodeInternals } from './composables/useUpdateNodeInternals'
|
||||||
|
|
||||||
export * from './additional-components'
|
export * from './additional-components'
|
||||||
export * from './types'
|
export * from './types'
|
||||||
|
export * from './context'
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import { RevueFlowState, ConnectionMode } from '../types'
|
import { ConnectionMode, FlowState } from '~/types'
|
||||||
|
|
||||||
export const initialState: RevueFlowState = {
|
export const initialState: FlowState = {
|
||||||
dimensions: {
|
dimensions: {
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ export enum PanOnScrollMode {
|
|||||||
Horizontal = 'horizontal',
|
Horizontal = 'horizontal',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ZoomPanHelperFunctions {
|
export interface UseZoomPanHelper {
|
||||||
zoomIn: () => void
|
zoomIn: () => void
|
||||||
zoomOut: () => void
|
zoomOut: () => void
|
||||||
zoomTo: (zoomLevel: number) => void
|
zoomTo: (zoomLevel: number) => void
|
||||||
|
|||||||
+29
-6
@@ -10,9 +10,32 @@ import {
|
|||||||
Connection,
|
Connection,
|
||||||
FlowExportObject,
|
FlowExportObject,
|
||||||
NodeExtent,
|
NodeExtent,
|
||||||
RevueFlowStore,
|
Dimensions,
|
||||||
} from '../types'
|
FlowStore,
|
||||||
import { clampPosition, clamp } from './index'
|
} from '~/types'
|
||||||
|
|
||||||
|
export const isInputDOMNode = (e: KeyboardEvent | MouseEvent): boolean => {
|
||||||
|
const target = e.target as HTMLElement
|
||||||
|
return ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(target.nodeName) || target.hasAttribute('contentEditable')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getDimensions = (node: HTMLElement): Dimensions => ({
|
||||||
|
width: node.offsetWidth,
|
||||||
|
height: node.offsetHeight,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const clamp = (val: number, min = 0, max = 1): number => Math.min(Math.max(val, min), max)
|
||||||
|
|
||||||
|
export const clampPosition = (position: XYPosition, extent: NodeExtent): XYPosition => ({
|
||||||
|
x: clamp(position.x, extent[0][0], extent[1][0]),
|
||||||
|
y: clamp(position.y, extent[0][1], extent[1][1]),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getHostForElement = (element: HTMLElement): Document => {
|
||||||
|
const doc = element.getRootNode() as Document
|
||||||
|
if ('getElementFromPoint' in doc) return doc
|
||||||
|
else return window.document
|
||||||
|
}
|
||||||
|
|
||||||
export const isEdge = (element: Node | Connection | Edge): element is Edge =>
|
export const isEdge = (element: Node | Connection | Edge): element is Edge =>
|
||||||
'id' in element && 'source' in element && 'target' in element
|
'id' in element && 'source' in element && 'target' in element
|
||||||
@@ -136,7 +159,7 @@ export const pointToRendererPoint = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const onLoadProject =
|
export const onLoadProject =
|
||||||
(currentStore: RevueFlowStore) =>
|
(currentStore: FlowStore) =>
|
||||||
(position: XYPosition): XYPosition =>
|
(position: XYPosition): XYPosition =>
|
||||||
pointToRendererPoint(position, currentStore.transform, currentStore.snapToGrid, currentStore.snapGrid)
|
pointToRendererPoint(position, currentStore.transform, currentStore.snapToGrid, currentStore.snapGrid)
|
||||||
|
|
||||||
@@ -256,10 +279,10 @@ const parseElements = (nodes: Node[], edges: Edge[]): Elements => [
|
|||||||
...edges.map((e) => ({ ...e })),
|
...edges.map((e) => ({ ...e })),
|
||||||
]
|
]
|
||||||
|
|
||||||
export const onLoadGetElements = (currentStore: RevueFlowStore) => (): Elements =>
|
export const onLoadGetElements = (currentStore: FlowStore) => (): Elements =>
|
||||||
parseElements(currentStore.nodes || [], currentStore.edges || [])
|
parseElements(currentStore.nodes || [], currentStore.edges || [])
|
||||||
|
|
||||||
export const onLoadToObject = (currentStore: RevueFlowStore) => (): FlowExportObject => ({
|
export const onLoadToObject = (currentStore: FlowStore) => (): FlowExportObject => ({
|
||||||
elements: parseElements(currentStore.nodes || [], currentStore.edges || []),
|
elements: parseElements(currentStore.nodes || [], currentStore.edges || []),
|
||||||
position: [currentStore.transform[0], currentStore.transform[1]],
|
position: [currentStore.transform[0], currentStore.transform[1]],
|
||||||
zoom: currentStore.transform[2],
|
zoom: currentStore.transform[2],
|
||||||
|
|||||||
+1
-24
@@ -1,24 +1 @@
|
|||||||
import { Dimensions, XYPosition, NodeExtent } from '../types'
|
export * from './graph'
|
||||||
|
|
||||||
export const isInputDOMNode = (e: KeyboardEvent | MouseEvent): boolean => {
|
|
||||||
const target = e.target as HTMLElement
|
|
||||||
return ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(target.nodeName) || target.hasAttribute('contentEditable')
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getDimensions = (node: HTMLElement): Dimensions => ({
|
|
||||||
width: node.offsetWidth,
|
|
||||||
height: node.offsetHeight,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const clamp = (val: number, min = 0, max = 1): number => Math.min(Math.max(val, min), max)
|
|
||||||
|
|
||||||
export const clampPosition = (position: XYPosition, extent: NodeExtent): XYPosition => ({
|
|
||||||
x: clamp(position.x, extent[0][0], extent[1][0]),
|
|
||||||
y: clamp(position.y, extent[0][1], extent[1][1]),
|
|
||||||
})
|
|
||||||
|
|
||||||
export const getHostForElement = (element: HTMLElement): Document => {
|
|
||||||
const doc = element.getRootNode() as Document
|
|
||||||
if ('getElementFromPoint' in doc) return doc
|
|
||||||
else return window.document
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import { InjectionKey } from 'vue'
|
|
||||||
import { RevueFlowStore } from '../types'
|
|
||||||
import { RevueFlowHooks } from '../hooks/RevueFlowHooks'
|
|
||||||
|
|
||||||
export const StoreKey: InjectionKey<RevueFlowStore> = Symbol('store')
|
|
||||||
export const HooksKey: InjectionKey<RevueFlowHooks> = Symbol('hooks')
|
|
||||||
export const NodeIdContextKey: InjectionKey<string> = Symbol('NodeIdContext')
|
|
||||||
Reference in New Issue
Block a user