update: Add slots to CustomConnectionLine.vue, Edge.vue, Node.vue
This commit is contained in:
@@ -4,25 +4,20 @@ import { getSourceTargetNodes } from './utils'
|
||||
import MarkerDefinitions from './MarkerDefinitions.vue'
|
||||
import Edge from '~/components/Edges/Edge.vue'
|
||||
import ConnectionLine from '~/components/ConnectionLine/ConnectionLine.vue'
|
||||
import { ConnectionLineType, CustomConnectionLine, Dimensions, EdgeType, Transform } from '~/types'
|
||||
import { ConnectionLineType, EdgeType } from '~/types'
|
||||
import { Store } from '~/context'
|
||||
|
||||
interface EdgeRendererProps {
|
||||
edgeTypes: Record<string, EdgeType>
|
||||
dimensions: Dimensions
|
||||
transform: Transform
|
||||
connectionLineType?: ConnectionLineType
|
||||
connectionLineStyle?: CSSProperties
|
||||
customConnectionLine?: CustomConnectionLine
|
||||
arrowHeadColor?: string
|
||||
markerEndId?: string
|
||||
edgeUpdaterRadius?: number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<EdgeRendererProps>(), {
|
||||
transform: () => [0, 0, 1],
|
||||
arrowHeadColor: '#b1b1b7',
|
||||
dimensions: () => ({ width: 0, height: 0 }),
|
||||
connectionLineType: ConnectionLineType.Bezier,
|
||||
edgeUpdaterRadius: 10,
|
||||
})
|
||||
@@ -35,10 +30,10 @@ const connectionLineVisible = computed(
|
||||
)
|
||||
</script>
|
||||
<template>
|
||||
<svg :width="props.dimensions.width" :height="props.dimensions.height" class="revue-flow__edges">
|
||||
<svg :width="store.dimensions.width" :height="store.dimensions.height" class="revue-flow__edges">
|
||||
<MarkerDefinitions :color="props.arrowHeadColor" />
|
||||
<g
|
||||
:transform="props.transform.length && `translate(${props.transform[0]},${props.transform[1]}) scale(${props.transform[2]})`"
|
||||
:transform="store.transform.length && `translate(${store.transform[0]},${store.transform[1]}) scale(${store.transform[2]})`"
|
||||
>
|
||||
<template v-for="edge of store.edges" :key="edge.id">
|
||||
<Edge
|
||||
@@ -46,19 +41,24 @@ const connectionLineVisible = computed(
|
||||
:edge="edge"
|
||||
:nodes="getSourceTargetNodes(edge, store.nodes)"
|
||||
:type="props.edgeTypes[edge.type || 'default']"
|
||||
:dimensions="props.dimensions"
|
||||
:transform="props.transform"
|
||||
:marker-end-id="props.markerEndId"
|
||||
:edge-updater-radius="props.edgeUpdaterRadius"
|
||||
/>
|
||||
>
|
||||
<template #default="edgeProps">
|
||||
<slot name="edge" v-bind="edgeProps"></slot>
|
||||
</template>
|
||||
</Edge>
|
||||
</template>
|
||||
<ConnectionLine
|
||||
v-if="connectionLineVisible"
|
||||
:source-node="sourceNode"
|
||||
:connection-line-style="props.connectionLineStyle"
|
||||
:connection-line-type="props.connectionLineType"
|
||||
:custom-connection-line="props.customConnectionLine"
|
||||
/>
|
||||
>
|
||||
<template #default="customConnectionLineProps">
|
||||
<slot name="custom-connection-line" v-bind="customConnectionLineProps"></slot>
|
||||
</template>
|
||||
</ConnectionLine>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
+26
-22
@@ -13,12 +13,11 @@ import {
|
||||
PanOnScrollMode,
|
||||
NodeType,
|
||||
EdgeType,
|
||||
CustomConnectionLine,
|
||||
KeyCode,
|
||||
TranslateExtent,
|
||||
NodeExtent,
|
||||
} from '~/types'
|
||||
import configureStore from '~/store/configure-store'
|
||||
import useFlowStore from '~/store/useFlowStore'
|
||||
import { initialState } from '~/store'
|
||||
import { DefaultNode, InputNode, OutputNode } from '~/components/Nodes'
|
||||
import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge } from '~/components/Edges'
|
||||
@@ -34,7 +33,6 @@ export interface FlowProps {
|
||||
connectionMode?: ConnectionMode
|
||||
connectionLineType?: ConnectionLineType
|
||||
connectionLineStyle?: CSSProperties
|
||||
customConnectionLine?: CustomConnectionLine
|
||||
deleteKeyCode?: KeyCode
|
||||
selectionKeyCode?: KeyCode
|
||||
multiSelectionKeyCode?: KeyCode
|
||||
@@ -118,7 +116,7 @@ const defaultEdgeTypes: Record<string, EdgeType> = {
|
||||
|
||||
let store = inject(Store)!
|
||||
if (!store) {
|
||||
store = configureStore({
|
||||
store = useFlowStore({
|
||||
...initialState,
|
||||
...props,
|
||||
})()
|
||||
@@ -160,25 +158,31 @@ const edgeTypes = createEdgeTypes({ ...defaultEdgeTypes, ...props.edgeTypes })
|
||||
:pan-on-scroll-mode="props.panOnScrollMode"
|
||||
:pane-moveable="props.paneMoveable"
|
||||
>
|
||||
<template #default="{ transform, dimensions }">
|
||||
<SelectionPane
|
||||
:delete-key-code="props.deleteKeyCode"
|
||||
:multi-selection-key-code="props.multiSelectionKeyCode"
|
||||
:selection-key-code="props.selectionKeyCode"
|
||||
<SelectionPane
|
||||
:delete-key-code="props.deleteKeyCode"
|
||||
:multi-selection-key-code="props.multiSelectionKeyCode"
|
||||
:selection-key-code="props.selectionKeyCode"
|
||||
>
|
||||
<NodeRenderer :node-types="nodeTypes">
|
||||
<template #default="nodeProps">
|
||||
<slot name="node" v-bind="nodeProps"></slot>
|
||||
</template>
|
||||
</NodeRenderer>
|
||||
<EdgeRenderer
|
||||
:connection-line-type="props.connectionLineType"
|
||||
:connection-line-style="props.connectionLineStyle"
|
||||
:arrow-head-color="props.arrowHeadColor"
|
||||
:marker-end-id="props.markerEndId"
|
||||
:edge-types="edgeTypes"
|
||||
>
|
||||
<NodeRenderer :node-types="nodeTypes" :transform="transform" :dimensions="dimensions" />
|
||||
<EdgeRenderer
|
||||
:connection-line-type="props.connectionLineType"
|
||||
:connection-line-style="props.connectionLineStyle"
|
||||
:custom-connection-line="props.customConnectionLine"
|
||||
:arrow-head-color="props.arrowHeadColor"
|
||||
:marker-end-id="props.markerEndId"
|
||||
:edge-types="edgeTypes"
|
||||
:transform="transform"
|
||||
:dimensions="dimensions"
|
||||
/>
|
||||
</SelectionPane>
|
||||
</template>
|
||||
<template #edge="edgeProps">
|
||||
<slot name="edge" v-bind="edgeProps"></slot>
|
||||
</template>
|
||||
<template #custom-connection-line="customConnectionLineProps">
|
||||
<slot name="custom-connection-line" v-bind="customConnectionLineProps"></slot>
|
||||
</template>
|
||||
</EdgeRenderer>
|
||||
</SelectionPane>
|
||||
</ZoomPane>
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import Node from '~/components/Nodes/Node.vue'
|
||||
import { NodeType, Node as TNode, Transform, Dimensions } from '~/types'
|
||||
import { NodeType, Node as TNode } from '~/types'
|
||||
import { getNodesInside } from '~/utils/graph'
|
||||
import { Store } from '~/context'
|
||||
|
||||
interface NodeRendererProps {
|
||||
nodeTypes: Record<string, NodeType>
|
||||
dimensions: Dimensions
|
||||
transform: Transform
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<NodeRendererProps>(), {
|
||||
transform: () => [0, 0, 1],
|
||||
dimensions: () => ({ width: 0, height: 0 }),
|
||||
})
|
||||
const props = defineProps<NodeRendererProps>()
|
||||
|
||||
const store = inject(Store)!
|
||||
|
||||
@@ -25,10 +20,10 @@ const getNodes = computed(() =>
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: props.dimensions.width,
|
||||
height: props.dimensions.height,
|
||||
width: store.dimensions.width,
|
||||
height: store.dimensions.height,
|
||||
},
|
||||
props.transform,
|
||||
store.transform,
|
||||
true,
|
||||
)
|
||||
: store.nodes,
|
||||
@@ -48,20 +43,24 @@ const selected = (nodeId: string) => store.selectedElements?.some(({ id }) => id
|
||||
<template>
|
||||
<div
|
||||
class="revue-flow__nodes"
|
||||
:style="{ transform: `translate(${props.transform[0]}px,${props.transform[1]}px) scale(${props.transform[2]})` }"
|
||||
:style="{ transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})` }"
|
||||
>
|
||||
<template v-for="node of getNodes" :key="node.id">
|
||||
<Node
|
||||
v-if="!node.isHidden"
|
||||
:node="node"
|
||||
:type="type(node)"
|
||||
:scale="props.transform[2]"
|
||||
:scale="store.transform[2]"
|
||||
:snap-grid="store.snapToGrid ? store.snapGrid : undefined"
|
||||
:selected="selected(node.id)"
|
||||
:selectable="node.selectable || store.elementsSelectable"
|
||||
:connectable="node.connectable || store.nodesConnectable"
|
||||
:draggable="node.draggable || store.nodesDraggable"
|
||||
/>
|
||||
>
|
||||
<template #default="nodeProps">
|
||||
<slot v-bind="nodeProps"></slot>
|
||||
</template>
|
||||
</Node>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
import useKeyPress from '~/composables/useKeyPress'
|
||||
import { KeyCode } from '~/types'
|
||||
import useGlobalKeyHandler from '~/composables/useGlobalKeyHandler'
|
||||
import { ElementId, FlowElement, KeyCode } from '~/types'
|
||||
import NodesSelection from '~/components/NodesSelection/NodesSelection.vue'
|
||||
import UserSelection from '~/components/UserSelection/UserSelection.vue'
|
||||
import { Hooks, Store } from '~/context'
|
||||
import { getConnectedEdges, isNode } from '~/utils/graph'
|
||||
|
||||
interface SelectionPaneProps {
|
||||
selectionKeyCode?: KeyCode
|
||||
@@ -18,7 +18,6 @@ const props = withDefaults(defineProps<SelectionPaneProps>(), {
|
||||
})
|
||||
const store = inject(Store)!
|
||||
const hooks = inject(Hooks)!
|
||||
const keyPressed = useKeyPress(props.selectionKeyCode)
|
||||
|
||||
const onClick = (event: MouseEvent) => {
|
||||
hooks.paneClick.trigger(event)
|
||||
@@ -30,18 +29,28 @@ const onContextMenu = (event: MouseEvent) => hooks.paneContextMenu.trigger(event
|
||||
|
||||
const onWheel = (event: WheelEvent) => hooks.paneScroll.trigger(event)
|
||||
|
||||
useGlobalKeyHandler({
|
||||
onElementsRemove: hooks.elementsRemove.trigger,
|
||||
deleteKeyCode: props.deleteKeyCode,
|
||||
multiSelectionKeyCode: props.multiSelectionKeyCode,
|
||||
const selectionKeyPresed = useKeyPress(props.selectionKeyCode)
|
||||
|
||||
useKeyPress(props.deleteKeyCode, (keyPressed) => {
|
||||
if (keyPressed && store.selectedElements) {
|
||||
const selectedNodes = store.selectedElements.filter(isNode)
|
||||
const connectedEdges = getConnectedEdges(selectedNodes, store.edges)
|
||||
const elementsToRemove = [...store.selectedElements, ...connectedEdges].reduce(
|
||||
(res, item) => res.set(item.id, item),
|
||||
new Map<ElementId, FlowElement>(),
|
||||
)
|
||||
|
||||
hooks.elementsRemove.trigger(Array.from(elementsToRemove.values()))
|
||||
store.unsetNodesSelection()
|
||||
store.resetSelectedElements()
|
||||
}
|
||||
})
|
||||
|
||||
const userSelectionVisible = computed(() => keyPressed.value && (store.selectionActive || store.elementsSelectable))
|
||||
const nodesSelectionVisible = computed(() => store.nodesSelectionActive)
|
||||
useKeyPress(props.multiSelectionKeyCode, (keyPressed) => (store.multiSelectionActive = keyPressed))
|
||||
</script>
|
||||
<template>
|
||||
<slot></slot>
|
||||
<UserSelection v-if="userSelectionVisible" id="user-selection" />
|
||||
<NodesSelection v-if="nodesSelectionVisible" id="nodes-selection" />
|
||||
<UserSelection v-if="selectionKeyPresed && (store.selectionActive || store.elementsSelectable)" id="user-selection" />
|
||||
<NodesSelection v-if="store.nodesSelectionActive" id="nodes-selection" />
|
||||
<div class="revue-flow__pane" @click="onClick" @contextmenu="onContextMenu" @wheel="onWheel" />
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { KeyCode, PanOnScrollMode } from '~/types'
|
||||
import { useZoom, useResizeHandler, useZoomPanHelper } from '~/composables'
|
||||
import { useZoom, useZoomPanHelper } from '~/composables'
|
||||
import { Hooks, Store } from '~/context'
|
||||
import { onLoadGetElements, onLoadProject, onLoadToObject } from '~/utils/graph'
|
||||
|
||||
@@ -36,9 +36,10 @@ const hooks = inject(Hooks)!
|
||||
|
||||
const zoomPaneEl = templateRef<HTMLDivElement>('zoom-pane', null)
|
||||
const { transform } = useZoom(zoomPaneEl, props)
|
||||
const { width, height } = useElementBounding(zoomPaneEl)
|
||||
watch(width, (val) => (store.dimensions.width = val), { flush: 'post' })
|
||||
watch(height, (val) => (store.dimensions.height = val), { flush: 'post' })
|
||||
watch(transform, (val) => (store.transform = val), { flush: 'post' })
|
||||
const dimensions = useResizeHandler(zoomPaneEl)
|
||||
watch(dimensions, (val) => (store.dimensions = val), { flush: 'post' })
|
||||
|
||||
const { zoomIn, zoomOut, zoomTo, transform: setTransform, fitView } = useZoomPanHelper()
|
||||
hooks.load.trigger({
|
||||
@@ -54,6 +55,6 @@ hooks.load.trigger({
|
||||
</script>
|
||||
<template>
|
||||
<div ref="zoom-pane" class="revue-flow__renderer revue-flow__zoompane">
|
||||
<slot :transform="transform" :dimensions="dimensions"></slot>
|
||||
<slot v-bind="{ transform, dimensions: { width, height } }"></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user